diff --git a/proxy/http.go b/proxy/http.go index 8a01a96..6d4b93d 100644 --- a/proxy/http.go +++ b/proxy/http.go @@ -11,7 +11,6 @@ import ( "net" "net/http" "net/url" - "sync" ) type http11 struct { @@ -20,65 +19,6 @@ type http11 struct { forward Dialer } -type httpConn struct { - net.Conn - reader *bufio.Reader - req *http.Request - - sync.Mutex - connErr error - connResp bool -} - -func (c *httpConn) readConnectResponse() error { - c.Lock() - defer c.Unlock() - - // double check - if c.connResp { - return c.connErr - } - - // set connResp - c.connResp = true - - resp, err := http.ReadResponse(c.reader, c.req) - - // release req - c.req = nil - - if err != nil { - c.connErr = err - return c.connErr - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - c.connErr = errors.New(resp.Status) - return c.connErr - } - - return c.connErr -} - -func (c *httpConn) Read(p []byte) (int, error) { - if !c.connResp { - err := c.readConnectResponse() - if err != nil { - return 0, err - } - } - return c.reader.Read(p) -} - -func newHttpConn(conn net.Conn, req *http.Request) *httpConn { - return &httpConn{ - Conn: conn, - reader: bufio.NewReader(conn), - req: req, - } -} - func basicAuth(username, password string) string { auth := username + ":" + password return base64.StdEncoding.EncodeToString([]byte(auth)) @@ -101,6 +41,10 @@ func (h *http11) Dial(network, addr string) (net.Conn, error) { Header: make(http.Header), } + req.Header.Set("Proxy-Connection", "keep-alive") + + // req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") + if h.user != nil { if password, ok := h.user.Password(); ok { req.Header.Set("Proxy-Authorization", "Basic "+basicAuth(h.user.Username(), password)) @@ -112,7 +56,20 @@ func (h *http11) Dial(network, addr string) (net.Conn, error) { return nil, err } - return newHttpConn(conn, req), nil + var resp *http.Response + resp, err = http.ReadResponse(bufio.NewReader(conn), nil) + if err != nil { + conn.Close() + return nil, err + } + + //defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + conn.Close() + return nil, errors.New(resp.Status) + } + + return conn, nil } func init() { diff --git a/proxy/proxy.go b/proxy/proxy.go index 115afa8..e8be54b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -9,8 +9,12 @@ import ( "errors" "net" "net/url" + + "github.com/op/go-logging" ) +var logger = logging.MustGetLogger("kone") + // A Dialer is a means to establish a connection. type Dialer interface { // Dial connects to the given address via the proxy. @@ -25,6 +29,7 @@ var proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error)) // a URL with that scheme and a forwarding Dialer. Registered schemes are used // by FromURL. func registerDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) { + logger.Debugf("register proxy schema %s", scheme) proxySchemes[scheme] = f } diff --git a/tcp_relay.go b/tcp_relay.go index 2b495c8..98877d9 100644 --- a/tcp_relay.go +++ b/tcp_relay.go @@ -14,12 +14,6 @@ import ( "github.com/xjdrew/kone/tcpip" ) -type halfCloseConn interface { - net.Conn - CloseRead() error - CloseWrite() error -} - type TCPRelay struct { one *One nat *Nat @@ -28,15 +22,9 @@ type TCPRelay struct { } func copy(src net.Conn, dst net.Conn, ch chan<- int64) { - written, _ := io.Copy(dst, src) - ch <- written -} + defer dst.Close() -func copyAndClose(src halfCloseConn, dst halfCloseConn, ch chan<- int64) { written, _ := io.Copy(dst, src) - - dst.CloseWrite() - src.CloseRead() ch <- written } @@ -87,7 +75,7 @@ func (r *TCPRelay) handleConn(conn net.Conn) { if proxy == "DIRECT" { // impossible conn.Close() - logger.Errorf("[tcp] %s > %s traffic dead loop", conn.LocalAddr(), remoteAddr) + logger.Errorf("[tcp relay] %s > %s traffic dead loop", conn.LocalAddr(), remoteAddr) return } @@ -95,27 +83,22 @@ func (r *TCPRelay) handleConn(conn net.Conn) { tunnel, err := proxies.Dial(proxy, remoteAddr) if err != nil { conn.Close() - logger.Errorf("[tcp] dial %s by proxy %q failed: %s", remoteAddr, proxy, err) + logger.Errorf("[tcp relay] dial %s by proxy %q failed: %s", remoteAddr, proxy, err) return } + logger.Debugf("[tcp relay] new tunnel, to %s through %s", remoteAddr, proxy) + uploadChan := make(chan int64) downloadChan := make(chan int64) - connHCC, connOK := conn.(halfCloseConn) - tunnelHCC, tunnelOK := tunnel.(halfCloseConn) - if connOK && tunnelOK { - go copyAndClose(connHCC, tunnelHCC, uploadChan) - go copyAndClose(tunnelHCC, connHCC, downloadChan) - } else { - go copy(conn, tunnel, uploadChan) - go copy(tunnel, conn, downloadChan) - defer conn.Close() - defer tunnel.Close() - } + go copy(conn, tunnel, uploadChan) + go copy(tunnel, conn, downloadChan) + connData.Upload = <-uploadChan connData.Download = <-downloadChan + logger.Debugf("[tcp relay] domain %s, upload %v bytes, download %v bytes", remoteAddr, connData.Upload, connData.Download) if r.one.manager != nil { r.one.manager.dataCh <- connData }