diff --git a/client.go b/client.go index d6de0de..8b3d1b7 100644 --- a/client.go +++ b/client.go @@ -7,9 +7,11 @@ import ( "encoding/binary" "fmt" "github.com/ginuerzh/gosocks5" + "github.com/gorilla/websocket" "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "io/ioutil" + "net/url" "log" "net" "net/http" @@ -18,7 +20,12 @@ import ( //"sync/atomic" ) -var sessionCount int64 +var ( + sessionCount int64 + clientConfig = &gosocks5.Config{ + MethodSelected: clientMethodSelected, + } +) func listenAndServe(addr string, handler func(net.Conn)) error { laddr, err := net.ResolveTCPAddr("tcp", addr) @@ -32,6 +39,12 @@ func listenAndServe(addr string, handler func(net.Conn)) error { } defer ln.Close() + for m, v := range Methods { + if Method == v { + clientConfig.Methods = []uint8{m} + } + } + for { conn, err := ln.AcceptTCP() if err != nil { @@ -43,33 +56,21 @@ func listenAndServe(addr string, handler func(net.Conn)) error { } } -func handshake(conn net.Conn, methods ...uint8) (method uint8, err error) { - nm := len(methods) - if nm == 0 { - nm = 1 +func clientMethodSelected(method uint8, conn net.Conn) (net.Conn, error) { + switch method { + case MethodTLS: + conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true}) + case MethodAES128, MethodAES192, MethodAES256, + MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: + cipher, _ := shadowsocks.NewCipher(Methods[method], Password) + conn = shadowsocks.NewConn(conn, cipher) + case gosocks5.MethodNoAcceptable: + fallthrough + default: + return nil, gosocks5.ErrBadMethod } - b := spool.Take() - defer spool.put(b) - - b = b[:2+nm] - b[0] = gosocks5.Ver5 - b[1] = uint8(nm) - copy(b[2:], methods) - - if _, err = conn.Write(b); err != nil { - return - } - - if _, err = io.ReadFull(conn, b[:2]); err != nil { - return - } - - if b[0] != gosocks5.Ver5 { - err = gosocks5.ErrBadVersion - } - method = b[1] - - return + + return conn, nil } func cliHandle(conn net.Conn) { @@ -80,40 +81,59 @@ func cliHandle(conn net.Conn) { fmt.Println("session end", atomic.AddInt64(&sessionCount, -1)) }() */ - - sconn, err := Connect(Saddr, Proxy) - if err != nil { - return - } - defer sconn.Close() - - method := gosocks5.MethodNoAuth - for m, v := range Methods { - if Method == v { - method = m + addr := Saddr + if strings.HasPrefix(addr, "http://") { + u, err := url.Parse(addr) + if err != nil { + log.Println(err) + return + } + addr = u.Host + if !strings.Contains(addr, ":") { + addr += ":80" } } - - method, err = handshake(sconn, method) + if strings.HasPrefix(addr, "https://") { + u, err := url.Parse(addr) + if err != nil { + log.Println(err) + return + } + addr = u.Host + if !strings.Contains(addr, ":") { + addr += ":443" + } + } + log.Println("connect:", addr) + c, err := Connect(addr, Proxy) if err != nil { + log.Println(err) return } - - switch method { - case MethodTLS: - sconn = tls.Client(sconn, &tls.Config{InsecureSkipVerify: true}) - case MethodAES128, MethodAES192, MethodAES256, - MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: - cipher, _ := shadowsocks.NewCipher(Methods[method], Password) - sconn = shadowsocks.NewConn(sconn, cipher) - case gosocks5.MethodNoAcceptable: - return + defer c.Close() + + if Websocket { + u, err := url.Parse(addr) + if err != nil { + log.Println(err) + return + } + ws, resp, err := websocket.NewClient(c, u, nil, 8192, 8192) + if err != nil { + log.Println(err) + return + } + resp.Body.Close() + + c = NewWSConn(ws) } + + c = gosocks5.ClientConn(c, clientConfig) if Shadows { cipher, _ := shadowsocks.NewCipher(SMethod, SPassword) conn = shadowsocks.NewConn(conn, cipher) - handleShadow(conn, sconn) + handleShadow(conn, c) return } @@ -138,7 +158,7 @@ func cliHandle(conn net.Conn) { return } - handleSocks5(conn, sconn) + handleSocks5(conn, c) return } @@ -158,7 +178,7 @@ func cliHandle(conn net.Conn) { if err != nil { return } - handleHttp(req, conn, sconn) + handleHttp(req, conn, c) } func handleSocks5(conn net.Conn, sconn net.Conn) { diff --git a/main.go b/main.go index c72788f..f8d0488 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,14 @@ package main import ( "flag" - "github.com/ginuerzh/gosocks5" + //"github.com/ginuerzh/gosocks5" "log" "time" ) var ( Laddr, Saddr, Proxy string + Websocket bool Shadows bool SMethod, SPassword string Method, Password string @@ -25,6 +26,7 @@ func init() { flag.StringVar(&CertFile, "cert", "", "cert file for tls") flag.StringVar(&KeyFile, "key", "", "key file for tls") flag.BoolVar(&Shadows, "ss", false, "run as shadowsocks server") + flag.BoolVar(&Websocket, "ws", false, "use websocket for tunnel") flag.StringVar(&SMethod, "sm", "rc4-md5", "shadowsocks cipher method") flag.StringVar(&SPassword, "sp", "ginuerzh@gmail.com", "shadowsocks cipher password") flag.Parse() @@ -41,13 +43,13 @@ var ( func main() { //log.Fatal(gost.Run()) if len(Saddr) == 0 { - srv := &gosocks5.Server{ - Addr: Laddr, - SelectMethod: selectMethod, - MethodSelected: methodSelected, - Handle: srvHandle, + var server Server + if Websocket { + server = &WSServer{Addr: Laddr} + } else { + server = &Socks5Server{Addr: Laddr} } - log.Fatal(srv.ListenAndServe()) + log.Fatal(server.ListenAndServe()) return } diff --git a/server.go b/server.go index 42c21ff..c999d17 100644 --- a/server.go +++ b/server.go @@ -1,223 +1,6 @@ package main -import ( - "github.com/ginuerzh/gosocks5" - "github.com/shadowsocks/shadowsocks-go/shadowsocks" - "net" - //"strconv" - "crypto/tls" - "log" -) - -const ( - rawCert = `-----BEGIN CERTIFICATE----- -MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD -bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj -bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf -d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x -5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ -VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK -kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P -8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu -MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF -BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG -9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0 -8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK -UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO -RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/ -BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS -uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA= ------END CERTIFICATE-----` - - rawKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ -y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0 -znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg -YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A -+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV -swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY -jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB -aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc -WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc -GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL -kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35 -ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp -woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx -AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5 -uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi -1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz -RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO -IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z -445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY -lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5 -hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i -kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB -nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X -2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH -85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz ------END RSA PRIVATE KEY-----` -) - - -func selectMethod(methods ...uint8) uint8 { - for _, method := range methods { - if _, ok := Methods[method]; ok { - return method - } - } - return gosocks5.MethodNoAuth +type Server interface { + ListenAndServe() error } -func methodSelected(method uint8, conn net.Conn) (net.Conn, error) { - switch method { - case MethodTLS: - var cert tls.Certificate - var err error - - if len(CertFile) == 0 || len(KeyFile) == 0 { - cert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey)) - } else { - cert, err = tls.LoadX509KeyPair(CertFile, KeyFile) - } - - if err != nil { - log.Println(err) - return nil, err - } - conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}}) - case MethodAES128, MethodAES192, MethodAES256, - MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: - cipher, err := shadowsocks.NewCipher(Methods[method], Password) - if err != nil { - return nil, err - } - conn = shadowsocks.NewConn(conn, cipher) - case gosocks5.MethodNoAcceptable: - return nil, gosocks5.ErrBadMethod - } - - return conn, nil -} - -func srvHandle(conn net.Conn) { - req, err := gosocks5.ReadRequest(conn) - if err != nil { - //log.Println(err) - return - } - - switch req.Cmd { - case gosocks5.CmdConnect: - //log.Println("connect", req.Addr.String()) - tconn, err := Connect(req.Addr.String(), Proxy) - if err != nil { - gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn) - return - } - defer tconn.Close() - - rep := gosocks5.NewReply(gosocks5.Succeeded, nil) - if err := rep.Write(conn); err != nil { - return - } - - if err := Transport(conn, tconn); err != nil { - log.Println(err) - } - case gosocks5.CmdBind: - l, err := net.ListenTCP("tcp", nil) - if err != nil { - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return - } - - addr := ToSocksAddr(l.Addr()) - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - //log.Println("bind:", addr) - rep := gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - return - } - - tconn, err := l.AcceptTCP() - if err != nil { - log.Println("accept:", err) - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return - } - defer tconn.Close() - l.Close() - - addr = ToSocksAddr(tconn.RemoteAddr()) - log.Println("accept peer:", addr.String()) - rep = gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - log.Println(err) - return - } - - if err := Transport(conn, tconn); err != nil { - log.Println(err) - } - case gosocks5.CmdUdp: - uconn, err := net.ListenUDP("udp", nil) - if err != nil { - log.Println(err) - gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) - return - } - defer uconn.Close() - - addr := ToSocksAddr(uconn.LocalAddr()) - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - //log.Println("udp:", addr) - rep := gosocks5.NewReply(gosocks5.Succeeded, addr) - if err := rep.Write(conn); err != nil { - log.Println(err) - return - } - srvTunnelUDP(conn, uconn) - } -} - -func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) { - go func() { - b := lpool.Take() - defer lpool.put(b) - - for { - n, addr, err := uconn.ReadFromUDP(b) - if err != nil { - log.Println(err) - return - } - - udp := gosocks5.NewUDPDatagram( - gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) - //log.Println("r", udp.Header) - if err := udp.Write(conn); err != nil { - log.Println(err) - return - } - } - }() - - for { - udp, err := gosocks5.ReadUDPDatagram(conn) - if err != nil { - log.Println(err) - return - } - //log.Println("w", udp.Header) - addr, err := net.ResolveUDPAddr("udp", udp.Header.Addr.String()) - if err != nil { - log.Println(err) - continue // drop silently - } - - if _, err := uconn.WriteToUDP(udp.Data, addr); err != nil { - log.Println(err) - return - } - } -} diff --git a/socks5.go b/socks5.go new file mode 100644 index 0000000..22aa07b --- /dev/null +++ b/socks5.go @@ -0,0 +1,261 @@ +package main + +import ( + "github.com/ginuerzh/gosocks5" + "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "net" + //"strconv" + "crypto/tls" + "log" +) + +const ( + rawCert = `-----BEGIN CERTIFICATE----- +MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD +bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj +bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf +d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x +5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ +VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK +kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P +8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu +MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF +BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG +9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0 +8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK +UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO +RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/ +BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS +uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA= +-----END CERTIFICATE-----` + + rawKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ +y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0 +znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg +YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A ++K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV +swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY +jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB +aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc +WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc +GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL +kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35 +ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp +woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx +AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5 +uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi +1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz +RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO +IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z +445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY +lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5 +hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i +kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB +nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X +2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH +85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz +-----END RSA PRIVATE KEY-----` +) + +var ( + serverConfig = &gosocks5.Config{ + SelectMethod: serverSelectMethod, + MethodSelected: serverMethodSelected, + } +) + +type Socks5Server struct { + Addr string // TCP address to listen on +} + + +func (s *Socks5Server) ListenAndServe() error { + addr, err := net.ResolveTCPAddr("tcp", s.Addr) + if err != nil { + return err + } + + ln, err := net.ListenTCP("tcp", addr) + if err != nil { + return err + } + defer ln.Close() + + for { + conn, err := ln.AcceptTCP() + if err != nil { + log.Println("accept:", err) + continue + } + //log.Println("accept", conn.RemoteAddr()) + + go socks5Handle(gosocks5.ServerConn(conn, serverConfig)) + } +} + + +func serverSelectMethod(methods ...uint8) uint8 { + for _, method := range methods { + if _, ok := Methods[method]; ok { + return method + } + } + return gosocks5.MethodNoAuth +} + +func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) { + switch method { + case MethodTLS: + var cert tls.Certificate + var err error + + if len(CertFile) == 0 || len(KeyFile) == 0 { + cert, err = tls.X509KeyPair([]byte(rawCert), []byte(rawKey)) + } else { + cert, err = tls.LoadX509KeyPair(CertFile, KeyFile) + } + + if err != nil { + log.Println(err) + return nil, err + } + conn = tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}}) + case MethodAES128, MethodAES192, MethodAES256, + MethodDES, MethodBF, MethodCAST5, MethodRC4MD5, MethodRC4, MethodTable: + cipher, err := shadowsocks.NewCipher(Methods[method], Password) + if err != nil { + return nil, err + } + conn = shadowsocks.NewConn(conn, cipher) + case gosocks5.MethodNoAcceptable: + return nil, gosocks5.ErrBadMethod + } + + return conn, nil +} + +func socks5Handle(conn net.Conn) { + defer conn.Close() + + req, err := gosocks5.ReadRequest(conn) + if err != nil { + //log.Println(err) + return + } + + switch req.Cmd { + case gosocks5.CmdConnect: + //log.Println("connect", req.Addr.String()) + tconn, err := Connect(req.Addr.String(), Proxy) + if err != nil { + gosocks5.NewReply(gosocks5.HostUnreachable, nil).Write(conn) + return + } + defer tconn.Close() + + rep := gosocks5.NewReply(gosocks5.Succeeded, nil) + if err := rep.Write(conn); err != nil { + return + } + + if err := Transport(conn, tconn); err != nil { + log.Println(err) + } + case gosocks5.CmdBind: + l, err := net.ListenTCP("tcp", nil) + if err != nil { + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return + } + + addr := ToSocksAddr(l.Addr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + //log.Println("bind:", addr) + rep := gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + return + } + + tconn, err := l.AcceptTCP() + if err != nil { + log.Println("accept:", err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return + } + defer tconn.Close() + l.Close() + + addr = ToSocksAddr(tconn.RemoteAddr()) + log.Println("accept peer:", addr.String()) + rep = gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + log.Println(err) + return + } + + if err := Transport(conn, tconn); err != nil { + log.Println(err) + } + case gosocks5.CmdUdp: + uconn, err := net.ListenUDP("udp", nil) + if err != nil { + log.Println(err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return + } + defer uconn.Close() + + addr := ToSocksAddr(uconn.LocalAddr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + //log.Println("udp:", addr) + rep := gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + log.Println(err) + return + } + srvTunnelUDP(conn, uconn) + } +} + +func srvTunnelUDP(conn net.Conn, uconn *net.UDPConn) { + go func() { + b := lpool.Take() + defer lpool.put(b) + + for { + n, addr, err := uconn.ReadFromUDP(b) + if err != nil { + log.Println(err) + return + } + + udp := gosocks5.NewUDPDatagram( + gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) + //log.Println("r", udp.Header) + if err := udp.Write(conn); err != nil { + log.Println(err) + return + } + } + }() + + for { + udp, err := gosocks5.ReadUDPDatagram(conn) + if err != nil { + log.Println(err) + return + } + //log.Println("w", udp.Header) + addr, err := net.ResolveUDPAddr("udp", udp.Header.Addr.String()) + if err != nil { + log.Println(err) + continue // drop silently + } + + if _, err := uconn.WriteToUDP(udp.Data, addr); err != nil { + log.Println(err) + return + } + } +} diff --git a/ws.go b/ws.go new file mode 100644 index 0000000..017fd4b --- /dev/null +++ b/ws.go @@ -0,0 +1,69 @@ +package main + +import ( + "github.com/gorilla/websocket" + "net/http" + "log" + "time" + "github.com/ginuerzh/gosocks5" +) + +type WSConn struct { + *websocket.Conn +} + +func NewWSConn(conn *websocket.Conn) *WSConn { + c := &WSConn{} + c.Conn = conn + + return c +} + +func (conn *WSConn) Read(b []byte) (n int, err error) { + _, b, err = conn.ReadMessage() + n = len(b) + + return +} + +func (conn *WSConn) Write(b []byte) (n int, err error) { + n = len(b) + err = conn.WriteMessage(websocket.BinaryMessage, b) + return +} + +func (conn *WSConn) SetDeadline(t time.Time) error { + if err := conn.SetReadDeadline(t); err != nil { + return err + } + return conn.SetWriteDeadline(t) +} + +type WSServer struct { + Addr string +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 8192, + WriteBufferSize: 8192, + CheckOrigin: func(r *http.Request) bool{ return true;}, +} + + +func (s *WSServer) handle(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println(err) + return + } + defer conn.Close() + + c := NewWSConn(conn) + + socks5Handle(gosocks5.ServerConn(c, serverConfig)) +} + +func (s *WSServer) ListenAndServe() error { + http.HandleFunc("/", s.handle) + return http.ListenAndServe(s.Addr, nil) +} \ No newline at end of file