diff --git a/cmd/sower/proxy.go b/cmd/sower/proxy.go index f81272b..fe9127c 100644 --- a/cmd/sower/proxy.go +++ b/cmd/sower/proxy.go @@ -152,68 +152,39 @@ func ServeSocks5(ln net.Listener, r *router.Router) { teeConn := teeconn.New(conn) - buf1 := make([]byte, 1) - if _, err := teeConn.Read(buf1); err != nil { - log.Warn().Err(err). - Msg("read socks5 version") - return - } + if addr, err := socks5.New().Unwrap(teeConn); err == nil { + teeConn.Stop().Reset() - if buf1[0] != 5 { - teeConn.Reread() - serveHttpProxy(teeConn, r) + host, port := addr.(*socks5.AddrHead).Addr() + r.RouteHandle(teeConn, host, port) return } - teeConn.Stop().Reread() - addr, err := socks5.New().Unwrap(teeConn) + teeConn.Reread() + bufioConn := &transport.BufioConn{ + Reader: bufio.NewReader(teeConn), + Conn: teeConn, + } + req, err := http.ReadRequest(bufioConn.Reader) if err != nil { - log.Warn().Err(err). - Msgf("parse socks5 target: %s", addr) + log.Error().Err(err).Msg("read http request") return } - host, port := addr.(*socks5.AddrHead).Addr() - r.RouteHandle(teeConn, host, port) -} - -func serveHttpProxy(teeConn *teeconn.Conn, r *router.Router) { - req, err := http.ReadRequest(bufio.NewReader(teeConn)) + host, port, err := router.ParseHostPort(req.Host, req.URL) if err != nil { - log.Warn().Err(err). - Msg("read http request") + bufioConn.Write([]byte("HTTP/1.1 400 Bad Request\r\n\r\n")) return } - teeConn.Stop() - switch req.Method { - case "CONNECT": - if _, err := teeConn.Write([]byte(req.Proto + " 200 Connection established\r\n\r\n")); err != nil { - log.Warn().Err(err). - Msg("write https proxy response") - return - } - - host, port, err := router.ParseHostPort(req.Host, 443) - if err != nil { - log.Warn().Err(err). - Msg("parse https_proxy target") - return - } - - rc, err := r.ProxyDial("tcp", host, port) - if err != nil { - log.Warn().Err(err). - Str("host", host). - Msg("dial proxy") - return - } - defer rc.Close() - - relay.Relay(teeConn, rc) - - default: - teeConn.Reread() - r.RouteHandle(teeConn, req.Host, 80) + if req.Method == http.MethodConnect { + // https + teeConn.Stop().Reset() + bufioConn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) + } else { + // http + teeConn.Stop().Reread() } + + r.RouteHandle(bufioConn, host, port) } diff --git a/go.mod b/go.mod index dae80ed..c04f5a8 100644 --- a/go.mod +++ b/go.mod @@ -1,37 +1,36 @@ module github.com/wweir/sower -go 1.22 - -toolchain go1.22.3 +go 1.23 require ( - github.com/cristalhq/aconfig v0.18.5 + github.com/cristalhq/aconfig v0.18.6 github.com/cristalhq/aconfig/aconfighcl v0.17.1 github.com/cristalhq/aconfig/aconfigtoml v0.17.1 github.com/cristalhq/aconfig/aconfigyaml v0.17.1 github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 github.com/libp2p/go-reuseport v0.4.0 - github.com/miekg/dns v1.1.59 - github.com/oschwald/geoip2-golang v1.9.0 + github.com/miekg/dns v1.1.62 + github.com/oschwald/geoip2-golang v1.11.0 github.com/pkg/errors v0.9.1 - github.com/sower-proxy/conns v0.0.3 + github.com/sower-proxy/conns v0.1.0 github.com/sower-proxy/deferlog v1.0.7 github.com/sower-proxy/mem v0.0.3 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.28.0 ) require ( - github.com/BurntSushi/toml v1.3.2 // indirect + github.com/BurntSushi/toml v1.1.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/oschwald/maxminddb-golang v1.12.0 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/oschwald/maxminddb-golang v1.13.0 // indirect github.com/rs/zerolog v1.32.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3ffec12..4b75e96 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,9 @@ +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cristalhq/aconfig v0.17.0/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E= -github.com/cristalhq/aconfig v0.18.5 h1:QqXH/Gy2c4QUQJTV2BN8UAuL/rqZ3IwhvxeC8OgzquA= -github.com/cristalhq/aconfig v0.18.5/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E= +github.com/cristalhq/aconfig v0.18.6 h1:8KRBznzdjUUiaa7HeIpYbMx1uPE1/xOBEU1ajsnmNME= +github.com/cristalhq/aconfig v0.18.6/go.mod h1:9ogrGEt9yU5V4pif/ThkVUfhj8JkdV+iDeahZGgfnDU= github.com/cristalhq/aconfig/aconfighcl v0.17.1 h1:/hJvmmoP3akvjY8qARiXgN30m/WXeBvpQdQ8v4DE8W8= github.com/cristalhq/aconfig/aconfighcl v0.17.1/go.mod h1:oOPqMmdUjVW6pKKO1zkOwQKHSUD0rlu8F5do+pylOQc= github.com/cristalhq/aconfig/aconfigtoml v0.17.1 h1:TA3xH8mALD8YeULsr4v87cMbKGBma35lWeRga4Xn11Q= @@ -23,15 +22,16 @@ github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8S github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= -github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= -github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= -github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= -github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w= +github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= +github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= +github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -39,31 +39,31 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/sower-proxy/conns v0.0.3 h1:3jbkvPhDwC+ph/NZi7Gywks9WakHK4ept7CkjTiNUJ8= -github.com/sower-proxy/conns v0.0.3/go.mod h1:p3ZM/8MolzW4TN0LaiPJnwnhhif3y9sBi2vQEWHvwhg= +github.com/sower-proxy/conns v0.1.0 h1:Is5xeRjGnwBChUgyMhncjgfttou23O2502S8otxbdaM= +github.com/sower-proxy/conns v0.1.0/go.mod h1:p3ZM/8MolzW4TN0LaiPJnwnhhif3y9sBi2vQEWHvwhg= github.com/sower-proxy/deferlog v1.0.7 h1:2gRkdTeSHPhnAj6uwBJ74ufqEmn1spT5o1Y3jGXY9zU= github.com/sower-proxy/deferlog v1.0.7/go.mod h1:TVXk/Pm7TAs1ZqayZfrxSbwCl+SSm7+p1xcEPkKHWvQ= github.com/sower-proxy/mem v0.0.3 h1:JK86etQ2QxRUXhs9JhoKYz5p3pFBSrI3UXVhwm44OzM= github.com/sower-proxy/mem v0.0.3/go.mod h1:tM3P+gCAZMVBnOixGVAHS34si9Iwj2HU6M4IVEv4rK0= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/router/util.go b/router/util.go index 790b76b..2a0c301 100644 --- a/router/util.go +++ b/router/util.go @@ -2,17 +2,23 @@ package router import ( "net" + "net/url" "strconv" ) -func ParseHostPort(hostport string, defaultPort uint16) (string, uint16, error) { +func ParseHostPort(hostport string, u *url.URL) (string, uint16, error) { host, port, err := net.SplitHostPort(hostport) if err != nil { + if err.(*net.AddrError).Err == "missing port in address" { + switch u.Scheme { + case "http": + return hostport, 80, nil + case "https": + return hostport, 443, nil + } + } return "", 0, err } - if port == "" { - return host, defaultPort, nil - } portInt, err := strconv.ParseUint(port, 10, 16) if err != nil { diff --git a/transport/socks5/rfc.go b/transport/socks5/rfc.go index 5925270..5f3d347 100644 --- a/transport/socks5/rfc.go +++ b/transport/socks5/rfc.go @@ -70,6 +70,7 @@ type addrType interface { } // ATYP: +// // 0x01 -> net.IPv4len // 0x03 -> first byte is length // 0x04 -> net.IPv6len @@ -81,6 +82,7 @@ type addrTypeIPv4 struct { func (a *addrTypeIPv4) Fulfill(r io.Reader) error { return binary.Read(r, binary.BigEndian, a) } + func (a *addrTypeIPv4) Addr() (string, uint16) { return net.IP(a.DST_ADDR[:]).String(), a.DST_PORT } @@ -108,6 +110,7 @@ func (a *addrTypeDomain) Fulfill(r io.Reader) error { return nil } + func (a *addrTypeDomain) Addr() (string, uint16) { return string(a.DST_ADDR[:]), a.DST_PORT } @@ -120,6 +123,7 @@ type addrTypeIPv6 struct { func (a *addrTypeIPv6) Fulfill(r io.Reader) error { return binary.Read(r, binary.BigEndian, a) } + func (a *addrTypeIPv6) Addr() (string, uint16) { return net.IP(a.DST_ADDR[:]).String(), a.DST_PORT } diff --git a/transport/socks5/socks5.go b/transport/socks5/socks5.go index d736e00..f83ad94 100644 --- a/transport/socks5/socks5.go +++ b/transport/socks5/socks5.go @@ -28,14 +28,16 @@ func New() *Socks5 { return &Socks5{} } -var noAuthResp = authResp{VER: 5, METHOD: 0} -var succHeadResp = respHead{VER: 5, REP: 0, RSV: 0, ATYP: 1} +var ( + noAuthResp = authResp{VER: 5, METHOD: 0} + succHeadResp = respHead{VER: 5, REP: 0, RSV: 0, ATYP: 1} +) func (s *Socks5) Unwrap(conn net.Conn) (net.Addr, error) { - { //auth + { // auth auth := new(authReq) - if err := auth.Fulfill(conn); err != nil && !auth.IsValid() { - return nil, errors.Errorf("read head: %v, err: %s", auth, err) + if err := auth.Fulfill(conn); err != nil || !auth.IsValid() { + return nil, errors.Errorf("read auth head: %v, err: %s", auth, err) } if err := binary.Write(conn, binary.BigEndian, noAuthResp); err != nil { diff --git a/transport/transport.go b/transport/transport.go index 82f5f09..ec9ab90 100644 --- a/transport/transport.go +++ b/transport/transport.go @@ -1,6 +1,7 @@ package transport import ( + "bufio" "net" ) @@ -8,3 +9,14 @@ type Transport interface { Unwrap(conn net.Conn) (net.Addr, error) Wrap(conn net.Conn, tgtHost string, tgtPort uint16) error } + +var _ net.Conn = (*BufioConn)(nil) + +type BufioConn struct { + *bufio.Reader + net.Conn +} + +func (c *BufioConn) Read(b []byte) (n int, err error) { + return c.Reader.Read(b) +} diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index d9cb631..519431e 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -71,6 +71,7 @@ func (*domain) Network() string { return "tcp" } func (a *domain) String() string { return net.JoinHostPort(a.ADDR, strconv.Itoa(int(a.PORT))) } + func (a *domain) Fulfill(r io.Reader) error { buf := make([]byte, 1) if n, err := r.Read(buf); err != nil || n != 1 { @@ -127,12 +128,12 @@ func (t *Trojan) Unwrap(conn net.Conn) (net.Addr, error) { head.CMD, head.ATYP = buf[58], buf[59] switch head.ATYP { - case 0x01: //ipv4 + case 0x01: // ipv4 addr := &ipv4Addr{} err := binary.Read(conn, binary.BigEndian, addr) return addr, errors.Wrap(err, "read addr") - case 0x04: //ipv6 + case 0x04: // ipv6 addr := &ipv6Addr{} err := binary.Read(conn, binary.BigEndian, addr) return addr, errors.Wrap(err, "read addr")