package gost import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net" "net/url" "strconv" "sync" "time" "github.com/go-gost/gosocks4" "github.com/go-gost/gosocks5" "github.com/go-log/log" smux "github.com/xtaci/smux" ) const ( // MethodTLS is an extended SOCKS5 method with tls encryption support. MethodTLS uint8 = 0x80 // MethodTLSAuth is an extended SOCKS5 method with tls encryption and authentication support. MethodTLSAuth uint8 = 0x82 // MethodMux is an extended SOCKS5 method for stream multiplexing. MethodMux = 0x88 ) const ( // CmdMuxBind is an extended SOCKS5 request CMD for // multiplexing transport with the binding server. CmdMuxBind uint8 = 0xF2 // CmdUDPTun is an extended SOCKS5 request CMD for UDP over TCP. CmdUDPTun uint8 = 0xF3 ) var ( _ net.PacketConn = (*socks5UDPTunnelConn)(nil) ) type clientSelector struct { methods []uint8 User *url.Userinfo TLSConfig *tls.Config } func (selector *clientSelector) Methods() []uint8 { if Debug { log.Log("[socks5] methods:", selector.methods) } return selector.methods } func (selector *clientSelector) AddMethod(methods ...uint8) { selector.methods = append(selector.methods, methods...) } func (selector *clientSelector) Select(methods ...uint8) (method uint8) { return } func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { if Debug { log.Log("[socks5] method selected:", method) } switch method { case MethodTLS: conn = tls.Client(conn, selector.TLSConfig) case gosocks5.MethodUserPass, MethodTLSAuth: if method == MethodTLSAuth { conn = tls.Client(conn, selector.TLSConfig) } var username, password string if selector.User != nil { username = selector.User.Username() password, _ = selector.User.Password() } req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) if err := req.Write(conn); err != nil { log.Log("[socks5]", err) return nil, err } if Debug { log.Log("[socks5]", req) } resp, err := gosocks5.ReadUserPassResponse(conn) if err != nil { log.Log("[socks5]", err) return nil, err } if Debug { log.Log("[socks5]", resp) } if resp.Status != gosocks5.Succeeded { return nil, gosocks5.ErrAuthFailure } case gosocks5.MethodNoAcceptable: return nil, gosocks5.ErrBadMethod } return conn, nil } type serverSelector struct { methods []uint8 // Users []*url.Userinfo Authenticator Authenticator TLSConfig *tls.Config } func (selector *serverSelector) Methods() []uint8 { return selector.methods } func (selector *serverSelector) AddMethod(methods ...uint8) { selector.methods = append(selector.methods, methods...) } func (selector *serverSelector) Select(methods ...uint8) (method uint8) { if Debug { log.Logf("[socks5] %d %d %v", gosocks5.Ver5, len(methods), methods) } method = gosocks5.MethodNoAuth for _, m := range methods { if m == MethodTLS { method = m break } } // when Authenticator is set, auth is mandatory if selector.Authenticator != nil { if method == gosocks5.MethodNoAuth { method = gosocks5.MethodUserPass } if method == MethodTLS { method = MethodTLSAuth } } return } func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { if Debug { log.Logf("[socks5] %d %d", gosocks5.Ver5, method) } switch method { case MethodTLS: conn = tls.Server(conn, selector.TLSConfig) case gosocks5.MethodUserPass, MethodTLSAuth: if method == MethodTLSAuth { conn = tls.Server(conn, selector.TLSConfig) } req, err := gosocks5.ReadUserPassRequest(conn) if err != nil { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err) return nil, err } if Debug { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), req.String()) } if selector.Authenticator != nil && !selector.Authenticator.Authenticate(req.Username, req.Password) { resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) if err := resp.Write(conn); err != nil { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err) return nil, err } if Debug { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), resp) } log.Logf("[socks5] %s - %s: proxy authentication required", conn.RemoteAddr(), conn.LocalAddr()) return nil, gosocks5.ErrAuthFailure } resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) if err := resp.Write(conn); err != nil { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err) return nil, err } if Debug { log.Logf("[socks5] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), resp) } case gosocks5.MethodNoAcceptable: return nil, gosocks5.ErrBadMethod } return conn, nil } type socks5Connector struct { User *url.Userinfo } // SOCKS5Connector creates a connector for SOCKS5 proxy client. // It accepts an optional auth info for SOCKS5 Username/Password Authentication. func SOCKS5Connector(user *url.Userinfo) Connector { return &socks5Connector{User: user} } func (c *socks5Connector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "tcp", address, options...) } func (c *socks5Connector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "udp", "udp4", "udp6": cnr := &socks5UDPTunConnector{User: c.User} return cnr.ConnectContext(ctx, conn, network, address, options...) } opts := &ConnectOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) user := opts.User if user == nil { user = c.User } cc, err := socks5Handshake(conn, selectorSocks5HandshakeOption(opts.Selector), userSocks5HandshakeOption(user), noTLSSocks5HandshakeOption(opts.NoTLS), ) if err != nil { return nil, err } conn = cc host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } p, _ := strconv.Atoi(port) req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ Type: gosocks5.AddrDomain, Host: host, Port: uint16(p), }) if err := req.Write(conn); err != nil { return nil, err } if Debug { log.Log("[socks5]", req) } reply, err := gosocks5.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Log("[socks5]", reply) } if reply.Rep != gosocks5.Succeeded { return nil, errors.New("Service unavailable") } return conn, nil } type socks5BindConnector struct { User *url.Userinfo } // SOCKS5BindConnector creates a connector for SOCKS5 bind. // It accepts an optional auth info for SOCKS5 Username/Password Authentication. func SOCKS5BindConnector(user *url.Userinfo) Connector { return &socks5BindConnector{User: user} } func (c *socks5BindConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "tcp", address, options...) } func (c *socks5BindConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "udp", "udp4", "udp6": return nil, fmt.Errorf("%s unsupported", network) } opts := &ConnectOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) user := opts.User if user == nil { user = c.User } cc, err := socks5Handshake(conn, selectorSocks5HandshakeOption(opts.Selector), userSocks5HandshakeOption(user), noTLSSocks5HandshakeOption(opts.NoTLS), ) if err != nil { return nil, err } conn = cc laddr, err := net.ResolveTCPAddr("tcp", address) if err != nil { log.Log(err) return nil, err } req := gosocks5.NewRequest(gosocks5.CmdBind, &gosocks5.Addr{ Type: gosocks5.AddrIPv4, Host: laddr.IP.String(), Port: uint16(laddr.Port), }) if err := req.Write(conn); err != nil { return nil, err } if Debug { log.Log("[socks5] bind\n", req) } reply, err := gosocks5.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Log("[socks5] bind\n", reply) } if reply.Rep != gosocks5.Succeeded { log.Logf("[socks5] bind on %s failure", address) return nil, fmt.Errorf("SOCKS5 bind on %s failure", address) } baddr, err := net.ResolveTCPAddr("tcp", reply.Addr.String()) if err != nil { return nil, err } log.Logf("[socks5] bind on %s OK", baddr) return &socks5BindConn{Conn: conn, laddr: baddr}, nil } type socks5MuxBindConnector struct{} // Socks5MuxBindConnector creates a Connector for SOCKS5 multiplex bind client. func Socks5MuxBindConnector() Connector { return &socks5MuxBindConnector{} } func (c *socks5MuxBindConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "tcp", address, options...) } // NOTE: the conn must be *muxBindClientConn. func (c *socks5MuxBindConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "udp", "udp4", "udp6": return nil, fmt.Errorf("%s unsupported", network) } accepter, ok := conn.(Accepter) if !ok { return nil, errors.New("wrong connection type") } return accepter.Accept() } type socks5MuxBindTransporter struct { bindAddr string sessions map[string]*muxSession // server addr to session mapping sessionMutex sync.Mutex } // SOCKS5MuxBindTransporter creates a Transporter for SOCKS5 multiplex bind client. func SOCKS5MuxBindTransporter(bindAddr string) Transporter { return &socks5MuxBindTransporter{ bindAddr: bindAddr, sessions: make(map[string]*muxSession), } } func (tr *socks5MuxBindTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) { opts := &DialOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = DialTimeout } tr.sessionMutex.Lock() defer tr.sessionMutex.Unlock() session, ok := tr.sessions[addr] if session != nil && session.IsClosed() { delete(tr.sessions, addr) ok = false } if !ok { if opts.Chain == nil { conn, err = net.DialTimeout("tcp", addr, timeout) } else { conn, err = opts.Chain.Dial(addr) } if err != nil { return } session = &muxSession{conn: conn} tr.sessions[addr] = session } return session.conn, nil } func (tr *socks5MuxBindTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) { opts := &HandshakeOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = HandshakeTimeout } tr.sessionMutex.Lock() defer tr.sessionMutex.Unlock() conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) session, ok := tr.sessions[opts.Addr] if !ok || session.session == nil { s, err := tr.initSession(conn, tr.bindAddr, opts) if err != nil { conn.Close() delete(tr.sessions, opts.Addr) return nil, err } session = s tr.sessions[opts.Addr] = session } return &muxBindClientConn{session: session}, nil } func (tr *socks5MuxBindTransporter) initSession(conn net.Conn, addr string, opts *HandshakeOptions) (*muxSession, error) { if opts == nil { opts = &HandshakeOptions{} } cc, err := socks5Handshake(conn, userSocks5HandshakeOption(opts.User), ) if err != nil { return nil, err } conn = cc bindAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return nil, err } req := gosocks5.NewRequest(CmdMuxBind, &gosocks5.Addr{ Type: gosocks5.AddrIPv4, Host: bindAddr.IP.String(), Port: uint16(bindAddr.Port), }) if err = req.Write(conn); err != nil { return nil, err } if Debug { log.Log("[socks5] mbind\n", req) } reply, err := gosocks5.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Log("[socks5] mbind\n", reply) } if reply.Rep != gosocks5.Succeeded { log.Logf("[socks5] mbind on %s failure", addr) return nil, fmt.Errorf("SOCKS5 mbind on %s failure", addr) } baddr, err := net.ResolveTCPAddr("tcp", reply.Addr.String()) if err != nil { return nil, err } log.Logf("[socks5] mbind on %s OK", baddr) // Upgrade connection to multiplex stream. session, err := smux.Server(conn, smux.DefaultConfig()) if err != nil { return nil, err } return &muxSession{conn: conn, session: session}, nil } func (tr *socks5MuxBindTransporter) Multiplex() bool { return true } type socks5UDPConnector struct { User *url.Userinfo } // SOCKS5UDPConnector creates a connector for SOCKS5 UDP relay. // It accepts an optional auth info for SOCKS5 Username/Password Authentication. func SOCKS5UDPConnector(user *url.Userinfo) Connector { return &socks5UDPConnector{User: user} } func (c *socks5UDPConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "udp", address, options...) } func (c *socks5UDPConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "tcp", "tcp4", "tcp6": return nil, fmt.Errorf("%s unsupported", network) } opts := &ConnectOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) user := opts.User if user == nil { user = c.User } cc, err := socks5Handshake(conn, selectorSocks5HandshakeOption(opts.Selector), userSocks5HandshakeOption(user), noTLSSocks5HandshakeOption(opts.NoTLS), ) if err != nil { return nil, err } conn = cc taddr, err := net.ResolveUDPAddr("udp", address) if err != nil { return nil, err } req := gosocks5.NewRequest(gosocks5.CmdUdp, &gosocks5.Addr{ Type: gosocks5.AddrIPv4, }) if err := req.Write(conn); err != nil { return nil, err } if Debug { log.Log("[socks5] udp\n", req) } reply, err := gosocks5.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Log("[socks5] udp\n", reply) } if reply.Rep != gosocks5.Succeeded { log.Logf("[socks5] udp relay failure") return nil, fmt.Errorf("SOCKS5 udp relay failure") } baddr, err := net.ResolveUDPAddr("udp", reply.Addr.String()) if err != nil { return nil, err } log.Logf("[socks5] udp associate on %s OK", baddr) uc, err := net.DialUDP("udp", nil, baddr) if err != nil { return nil, err } // log.Logf("udp laddr:%s, raddr:%s", uc.LocalAddr(), uc.RemoteAddr()) return &socks5UDPConn{UDPConn: uc, taddr: taddr}, nil } type socks5UDPTunConnector struct { User *url.Userinfo } // SOCKS5UDPTunConnector creates a connector for SOCKS5 UDP-over-TCP relay. // It accepts an optional auth info for SOCKS5 Username/Password Authentication. func SOCKS5UDPTunConnector(user *url.Userinfo) Connector { return &socks5UDPTunConnector{User: user} } func (c *socks5UDPTunConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "udp", address, options...) } func (c *socks5UDPTunConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "tcp", "tcp4", "tcp6": return nil, fmt.Errorf("%s unsupported", network) } opts := &ConnectOptions{} for _, option := range options { option(opts) } user := opts.User if user == nil { user = c.User } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) taddr, _ := net.ResolveUDPAddr("udp", address) return newSocks5UDPTunnelConn(conn, nil, taddr, selectorSocks5HandshakeOption(opts.Selector), userSocks5HandshakeOption(user), noTLSSocks5HandshakeOption(opts.NoTLS), ) } type socks4Connector struct{} // SOCKS4Connector creates a Connector for SOCKS4 proxy client. func SOCKS4Connector() Connector { return &socks4Connector{} } func (c *socks4Connector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "tcp", address, options...) } func (c *socks4Connector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "udp", "udp4", "udp6": return nil, fmt.Errorf("%s unsupported", network) } opts := &ConnectOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) taddr, err := net.ResolveTCPAddr("tcp4", address) if err != nil { return nil, err } if len(taddr.IP) == 0 { taddr.IP = net.IPv4zero } req := gosocks4.NewRequest(gosocks4.CmdConnect, &gosocks4.Addr{ Type: gosocks4.AddrIPv4, Host: taddr.IP.String(), Port: uint16(taddr.Port), }, nil, ) if err := req.Write(conn); err != nil { return nil, err } if Debug { log.Logf("[socks4] %s", req) } reply, err := gosocks4.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Logf("[socks4] %s", reply) } if reply.Code != gosocks4.Granted { return nil, fmt.Errorf("[socks4] %d", reply.Code) } return conn, nil } type socks4aConnector struct{} // SOCKS4AConnector creates a Connector for SOCKS4A proxy client. func SOCKS4AConnector() Connector { return &socks4aConnector{} } func (c *socks4aConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) { return c.ConnectContext(context.Background(), conn, "tcp", address, options...) } func (c *socks4aConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) { switch network { case "udp", "udp4", "udp6": return nil, fmt.Errorf("%s unsupported", network) } opts := &ConnectOptions{} for _, option := range options { option(opts) } timeout := opts.Timeout if timeout <= 0 { timeout = ConnectTimeout } conn.SetDeadline(time.Now().Add(timeout)) defer conn.SetDeadline(time.Time{}) host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } p, _ := strconv.Atoi(port) req := gosocks4.NewRequest(gosocks4.CmdConnect, &gosocks4.Addr{Type: gosocks4.AddrDomain, Host: host, Port: uint16(p)}, nil) if err := req.Write(conn); err != nil { return nil, err } if Debug { log.Logf("[socks4a] %s", req) } reply, err := gosocks4.ReadReply(conn) if err != nil { return nil, err } if Debug { log.Logf("[socks4a] %s", reply) } if reply.Code != gosocks4.Granted { return nil, fmt.Errorf("[socks4a] %d", reply.Code) } return conn, nil } type socks5Handler struct { selector *serverSelector options *HandlerOptions } // SOCKS5Handler creates a server Handler for SOCKS5 proxy server. func SOCKS5Handler(opts ...HandlerOption) Handler { h := &socks5Handler{} h.Init(opts...) return h } func (h *socks5Handler) Init(options ...HandlerOption) { if h.options == nil { h.options = &HandlerOptions{} } for _, opt := range options { opt(h.options) } tlsConfig := h.options.TLSConfig if tlsConfig == nil { tlsConfig = DefaultTLSConfig } h.selector = &serverSelector{ // socks5 server selector // Users: h.options.Users, Authenticator: h.options.Authenticator, TLSConfig: tlsConfig, } // methods that socks5 server supported h.selector.AddMethod( gosocks5.MethodNoAuth, gosocks5.MethodUserPass, MethodTLS, MethodTLSAuth, ) } func (h *socks5Handler) Handle(conn net.Conn) { defer conn.Close() conn = gosocks5.ServerConn(conn, h.selector) req, err := gosocks5.ReadRequest(conn) if err != nil { log.Logf("[socks5] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { log.Logf("[socks5] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), req) } switch req.Cmd { case gosocks5.CmdConnect: h.handleConnect(conn, req) case gosocks5.CmdBind: h.handleBind(conn, req) case gosocks5.CmdUdp: h.handleUDPRelay(conn, req) case CmdMuxBind: h.handleMuxBind(conn, req) case CmdUDPTun: h.handleUDPTunnel(conn, req) default: log.Logf("[socks5] %s - %s : Unrecognized request: %d", conn.RemoteAddr(), conn.LocalAddr(), req.Cmd) } } func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) { host := req.Addr.String() log.Logf("[socks5] %s -> %s -> %s", conn.RemoteAddr(), h.options.Node.String(), host) if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { log.Logf("[socks5] %s - %s : Unauthorized to tcp connect to %s", conn.RemoteAddr(), conn.LocalAddr(), host) rep := gosocks5.NewReply(gosocks5.NotAllowed, nil) rep.Write(conn) if Debug { log.Logf("[socks5] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } if h.options.Bypass.Contains(host) { log.Logf("[socks5] %s - %s : Bypass %s", conn.RemoteAddr(), conn.LocalAddr(), host) rep := gosocks5.NewReply(gosocks5.NotAllowed, nil) rep.Write(conn) if Debug { log.Logf("[socks5] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } retries := 1 if h.options.Chain != nil && h.options.Chain.Retries > 0 { retries = h.options.Chain.Retries } if h.options.Retries > 0 { retries = h.options.Retries } var err error var cc net.Conn var route *Chain for i := 0; i < retries; i++ { route, err = h.options.Chain.selectRouteFor(host) if err != nil { log.Logf("[socks5] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) continue } buf := bytes.Buffer{} fmt.Fprintf(&buf, "%s -> %s -> ", conn.RemoteAddr(), h.options.Node.String()) for _, nd := range route.route { fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) } fmt.Fprintf(&buf, "%s", host) log.Log("[route]", buf.String()) cc, err = route.Dial(host, TimeoutChainOption(h.options.Timeout), HostsChainOption(h.options.Hosts), ResolverChainOption(h.options.Resolver), ) if err == nil { break } log.Logf("[socks5] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) } if err != nil { rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) rep.Write(conn) if Debug { log.Logf("[socks5] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } defer cc.Close() rep := gosocks5.NewReply(gosocks5.Succeeded, nil) if err := rep.Write(conn); err != nil { log.Logf("[socks5] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { log.Logf("[socks5] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } log.Logf("[socks5] %s <-> %s", conn.RemoteAddr(), host) transport(conn, cc) log.Logf("[socks5] %s >-< %s", conn.RemoteAddr(), host) } func (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) { addr := req.Addr.String() log.Logf("[socks5-bind] %s -> %s -> %s", conn.RemoteAddr(), h.options.Node.String(), addr) if h.options.Chain.IsEmpty() { if !Can("rtcp", addr, h.options.Whitelist, h.options.Blacklist) { log.Logf("[socks5-bind] %s - %s : Unauthorized to tcp bind to %s", conn.RemoteAddr(), conn.LocalAddr(), addr) return } h.bindOn(conn, addr) return } cc, err := h.options.Chain.Conn() if err != nil { log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(conn) if Debug { log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply) } return } // forward request // note: this type of request forwarding is defined when starting server, // so we don't need to authenticate it, as it's as explicit as whitelisting defer cc.Close() req.Write(cc) log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), addr) transport(conn, cc) log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), addr) } func (h *socks5Handler) bindOn(conn net.Conn, addr string) { bindAddr, _ := net.ResolveTCPAddr("tcp", addr) ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error if err != nil { log.Logf("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) return } socksAddr := toSocksAddr(ln.Addr()) // Issue: may not reachable when host has multi-interface socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(conn); err != nil { log.Logf("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) ln.Close() return } if Debug { log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply) } log.Logf("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), conn.LocalAddr(), socksAddr) var pconn net.Conn accept := func() <-chan error { errc := make(chan error, 1) go func() { defer close(errc) defer ln.Close() c, err := ln.AcceptTCP() if err != nil { errc <- err return } pconn = c }() return errc } pc1, pc2 := net.Pipe() pipe := func() <-chan error { errc := make(chan error, 1) go func() { defer close(errc) defer pc1.Close() errc <- transport(conn, pc1) }() return errc } defer pc2.Close() for { select { case err := <-accept(): if err != nil || pconn == nil { log.Logf("[socks5-bind] %s <- %s : %v", conn.RemoteAddr(), addr, err) return } defer pconn.Close() reply := gosocks5.NewReply(gosocks5.Succeeded, toSocksAddr(pconn.RemoteAddr())) if err := reply.Write(pc2); err != nil { log.Logf("[socks5-bind] %s <- %s : %v", conn.RemoteAddr(), addr, err) } if Debug { log.Logf("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply) } log.Logf("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), socksAddr, pconn.RemoteAddr()) log.Logf("[socks5-bind] %s <-> %s", conn.RemoteAddr(), pconn.RemoteAddr()) if err = transport(pc2, pconn); err != nil { log.Logf("[socks5-bind] %s - %s : %v", conn.RemoteAddr(), pconn.RemoteAddr(), err) } log.Logf("[socks5-bind] %s >-< %s", conn.RemoteAddr(), pconn.RemoteAddr()) return case err := <-pipe(): if err != nil { log.Logf("[socks5-bind] %s -> %s : %v", conn.RemoteAddr(), addr, err) } ln.Close() return } } } func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) { addr := req.Addr.String() if !Can("udp", addr, h.options.Whitelist, h.options.Blacklist) { log.Logf("[socks5-udp] Unauthorized to udp connect to %s", addr) rep := gosocks5.NewReply(gosocks5.NotAllowed, nil) rep.Write(conn) if Debug { log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) } return } relay, err := net.ListenUDP("udp", nil) if err != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(conn) if Debug { log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply) } return } defer relay.Close() socksAddr := toSocksAddr(relay.LocalAddr()) socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(conn); err != nil { log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply) } log.Logf("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), conn.LocalAddr(), socksAddr) // serve as standard socks5 udp relay local <-> remote if h.options.Chain.IsEmpty() { peer, er := net.ListenUDP("udp", nil) if er != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), er) return } defer peer.Close() go h.transportUDP(relay, peer) log.Logf("[socks5-udp] %s <-> %s : associated on %s", conn.RemoteAddr(), conn.LocalAddr(), socksAddr) if err := h.discardClientData(conn); err != nil { log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) } log.Logf("[socks5-udp] %s >-< %s : associated on %s", conn.RemoteAddr(), conn.LocalAddr(), socksAddr) return } // forward udp local <-> tunnel cc, err := h.options.Chain.Conn() // connection error if err != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, err) return } defer cc.Close() cc, err = socks5Handshake(cc, userSocks5HandshakeOption(h.options.Chain.LastNode().User)) if err != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, err) return } cc.SetWriteDeadline(time.Now().Add(WriteTimeout)) r := gosocks5.NewRequest(CmdUDPTun, nil) if err := r.Write(cc); err != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err) return } cc.SetWriteDeadline(time.Time{}) if Debug { log.Logf("[socks5-udp] %s -> %s\n%s", conn.RemoteAddr(), cc.RemoteAddr(), r) } cc.SetReadDeadline(time.Now().Add(ReadTimeout)) reply, err = gosocks5.ReadReply(cc) if err != nil { log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err) return } if Debug { log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), cc.RemoteAddr(), reply) } if reply.Rep != gosocks5.Succeeded { log.Logf("[socks5-udp] %s <- %s : udp associate failed", conn.RemoteAddr(), cc.RemoteAddr()) return } cc.SetReadDeadline(time.Time{}) log.Logf("[socks5-udp] %s <-> %s [tun: %s]", conn.RemoteAddr(), socksAddr, reply.Addr) go h.tunnelClientUDP(relay, cc) log.Logf("[socks5-udp] %s <-> %s", conn.RemoteAddr(), socksAddr) if err := h.discardClientData(conn); err != nil { log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), socksAddr, err) } log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr) } func (h *socks5Handler) discardClientData(conn net.Conn) (err error) { b := make([]byte, tinyBufferSize) n := 0 for { n, err = conn.Read(b) // discard any data from tcp connection if err != nil { if err == io.EOF { // disconnect normally err = nil } break // client disconnected } log.Logf("[socks5-udp] read %d UNEXPECTED TCP data from client", n) } return } func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) { errc := make(chan error, 2) var clientAddr net.Addr go func() { b := mPool.Get().([]byte) defer mPool.Put(b) for { n, laddr, err := relay.ReadFrom(b) if err != nil { errc <- err return } if clientAddr == nil { clientAddr = laddr } dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) if err != nil { errc <- err return } raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) if err != nil { continue // drop silently } if h.options.Bypass.Contains(raddr.String()) { log.Log("[socks5-udp] [bypass] write to", raddr) continue // bypass } if _, err := peer.WriteTo(dgram.Data, raddr); err != nil { errc <- err return } if Debug { log.Logf("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) } } }() go func() { b := mPool.Get().([]byte) defer mPool.Put(b) for { n, raddr, err := peer.ReadFrom(b) if err != nil { errc <- err return } if clientAddr == nil { continue } if h.options.Bypass.Contains(raddr.String()) { log.Log("[socks5-udp] [bypass] read from", raddr) continue // bypass } buf := bytes.Buffer{} dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n]) dgram.Write(&buf) if _, err := relay.WriteTo(buf.Bytes(), clientAddr); err != nil { errc <- err return } if Debug { log.Logf("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) } } }() select { case err = <-errc: //log.Println("w exit", err) } return } func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) { errc := make(chan error, 2) var clientAddr *net.UDPAddr go func() { b := mPool.Get().([]byte) defer mPool.Put(b) for { n, addr, err := uc.ReadFromUDP(b) if err != nil { log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err) errc <- err return } // glog.V(LDEBUG).Infof("read udp %d, % #x", n, b[:n]) // pipe from relay to tunnel dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) if err != nil { errc <- err return } if clientAddr == nil { clientAddr = addr } raddr := dgram.Header.Addr.String() if h.options.Bypass.Contains(raddr) { log.Log("[udp-tun] [bypass] write to", raddr) continue // bypass } dgram.Header.Rsv = uint16(len(dgram.Data)) if err := dgram.Write(cc); err != nil { errc <- err return } if Debug { log.Logf("[udp-tun] %s >>> %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } } }() go func() { for { dgram, err := gosocks5.ReadUDPDatagram(cc) if err != nil { log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err) errc <- err return } // pipe from tunnel to relay if clientAddr == nil { continue } raddr := dgram.Header.Addr.String() if h.options.Bypass.Contains(raddr) { log.Log("[udp-tun] [bypass] read from", raddr) continue // bypass } dgram.Header.Rsv = 0 buf := bytes.Buffer{} dgram.Write(&buf) if _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil { errc <- err return } if Debug { log.Logf("[udp-tun] %s <<< %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } } }() select { case err = <-errc: } return } func (h *socks5Handler) handleUDPTunnel(conn net.Conn, req *gosocks5.Request) { // serve tunnel udp, tunnel <-> remote, handle tunnel udp request if h.options.Chain.IsEmpty() { addr := req.Addr.String() if !Can("rudp", addr, h.options.Whitelist, h.options.Blacklist) { log.Logf("[socks5] udp-tun Unauthorized to udp bind to %s", addr) return } bindAddr, _ := net.ResolveUDPAddr("udp", addr) uc, err := net.ListenUDP("udp", bindAddr) if err != nil { log.Logf("[socks5] udp-tun %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) return } defer uc.Close() socksAddr := toSocksAddr(uc.LocalAddr()) socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(conn); err != nil { log.Logf("[socks5] udp-tun %s <- %s : %s", conn.RemoteAddr(), socksAddr, err) return } if Debug { log.Logf("[socks5] udp-tun %s <- %s\n%s", conn.RemoteAddr(), socksAddr, reply) } log.Logf("[socks5] udp-tun %s <-> %s", conn.RemoteAddr(), socksAddr) h.tunnelServerUDP(conn, uc) log.Logf("[socks5] udp-tun %s >-< %s", conn.RemoteAddr(), socksAddr) return } cc, err := h.options.Chain.Conn() // connection error if err != nil { log.Logf("[socks5] udp-tun %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(conn) log.Logf("[socks5] udp-tun %s -> %s\n%s", conn.RemoteAddr(), req.Addr, reply) return } defer cc.Close() cc, err = socks5Handshake(cc, userSocks5HandshakeOption(h.options.Chain.LastNode().User)) if err != nil { log.Logf("[socks5] udp-tun %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) return } // tunnel <-> tunnel, direct forwarding // note: this type of request forwarding is defined when starting server // so we don't need to authenticate it, as it's as explicit as whitelisting req.Write(cc) log.Logf("[socks5] udp-tun %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr()) transport(conn, cc) log.Logf("[socks5] udp-tun %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr()) } func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err error) { errc := make(chan error, 2) go func() { b := mPool.Get().([]byte) defer mPool.Put(b) for { n, addr, err := pc.ReadFrom(b) if err != nil { // log.Logf("[udp-tun] %s : %s", cc.RemoteAddr(), err) errc <- err return } if h.options.Bypass.Contains(addr.String()) { log.Log("[socks5] udp-tun bypass read from", addr) continue // bypass } // pipe from peer to tunnel dgram := gosocks5.NewUDPDatagram( gosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n]) if err := dgram.Write(cc); err != nil { log.Logf("[socks5] udp-tun %s <- %s : %s", cc.RemoteAddr(), dgram.Header.Addr, err) errc <- err return } if Debug { log.Logf("[socks5] udp-tun %s <<< %s length: %d", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data)) } } }() go func() { for { dgram, err := gosocks5.ReadUDPDatagram(cc) if err != nil { // log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err) errc <- err return } // pipe from tunnel to peer addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) if err != nil { continue // drop silently } if h.options.Bypass.Contains(addr.String()) { log.Log("[socks5] udp-tun bypass write to", addr) continue // bypass } if _, err := pc.WriteTo(dgram.Data, addr); err != nil { log.Logf("[socks5] udp-tun %s -> %s : %s", cc.RemoteAddr(), addr, err) errc <- err return } if Debug { log.Logf("[socks5] udp-tun %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data)) } } }() select { case err = <-errc: } return } func (h *socks5Handler) handleMuxBind(conn net.Conn, req *gosocks5.Request) { if h.options.Chain.IsEmpty() { addr := req.Addr.String() if !Can("rtcp", addr, h.options.Whitelist, h.options.Blacklist) { log.Logf("Unauthorized to tcp mbind to %s", addr) return } h.muxBindOn(conn, addr) return } cc, err := h.options.Chain.Conn() if err != nil { log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(conn) if Debug { log.Logf("[socks5] mbind %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply) } return } // forward request // note: this type of request forwarding is defined when starting server, // so we don't need to authenticate it, as it's as explicit as whitelisting. defer cc.Close() req.Write(cc) log.Logf("[socks5] mbind %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr()) transport(conn, cc) log.Logf("[socks5] mbind %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr()) } func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) { bindAddr, _ := net.ResolveTCPAddr("tcp", addr) ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error if err != nil { log.Logf("[socks5] mbind %s -> %s : %s", conn.RemoteAddr(), addr, err) gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) return } defer ln.Close() socksAddr := toSocksAddr(ln.Addr()) // Issue: may not reachable when host has multi-interface. socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(conn); err != nil { log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), addr, err) return } if Debug { log.Logf("[socks5] mbind %s <- %s\n%s", conn.RemoteAddr(), addr, reply) } log.Logf("[socks5] mbind %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr) // Upgrade connection to multiplex stream. s, err := smux.Client(conn, smux.DefaultConfig()) if err != nil { log.Logf("[socks5] mbind %s - %s : %s", conn.RemoteAddr(), socksAddr, err) return } log.Logf("[socks5] mbind %s <-> %s", conn.RemoteAddr(), socksAddr) defer log.Logf("[socks5] mbind %s >-< %s", conn.RemoteAddr(), socksAddr) session := &muxSession{ conn: conn, session: s, } defer session.Close() go func() { for { conn, err := session.Accept() if err != nil { log.Logf("[socks5] mbind accept : %v", err) ln.Close() return } conn.Close() // we do not handle incoming connection. } }() for { cc, err := ln.Accept() if err != nil { log.Logf("[socks5] mbind %s <- %s : %v", conn.RemoteAddr(), socksAddr, err) return } log.Logf("[socks5] mbind %s <- %s : ACCEPT peer %s", conn.RemoteAddr(), socksAddr, cc.RemoteAddr()) go func(c net.Conn) { defer c.Close() sc, err := session.GetConn() if err != nil { log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), socksAddr, err) return } defer sc.Close() transport(sc, c) }(cc) } } // TODO: support ipv6 and domain func toSocksAddr(addr net.Addr) *gosocks5.Addr { host := "0.0.0.0" port := 0 if addr != nil { h, p, _ := net.SplitHostPort(addr.String()) host = h port, _ = strconv.Atoi(p) } return &gosocks5.Addr{ Type: gosocks5.AddrIPv4, Host: host, Port: uint16(port), } } type socks4Handler struct { options *HandlerOptions } // SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server. func SOCKS4Handler(opts ...HandlerOption) Handler { h := &socks4Handler{} h.Init(opts...) return h } func (h *socks4Handler) Init(options ...HandlerOption) { if h.options == nil { h.options = &HandlerOptions{} } for _, opt := range options { opt(h.options) } } func (h *socks4Handler) Handle(conn net.Conn) { defer conn.Close() req, err := gosocks4.ReadRequest(conn) if err != nil { log.Logf("[socks4] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { log.Logf("[socks4] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), req) } switch req.Cmd { case gosocks4.CmdConnect: h.handleConnect(conn, req) case gosocks4.CmdBind: log.Logf("[socks4-bind] %s - %s", conn.RemoteAddr(), req.Addr) h.handleBind(conn, req) default: log.Logf("[socks4] %s - %s : Unrecognized request: %d", conn.RemoteAddr(), conn.LocalAddr(), req.Cmd) } } func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) { addr := req.Addr.String() log.Logf("[socks4] %s -> %s -> %s", conn.RemoteAddr(), h.options.Node.String(), addr) if !Can("tcp", addr, h.options.Whitelist, h.options.Blacklist) { log.Logf("[socks4] %s - %s : Unauthorized to tcp connect to %s", conn.RemoteAddr(), conn.LocalAddr(), addr) rep := gosocks4.NewReply(gosocks4.Rejected, nil) rep.Write(conn) if Debug { log.Logf("[socks4] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } if h.options.Bypass.Contains(addr) { log.Log("[socks4] %s - %s : Bypass %s", conn.RemoteAddr(), conn.LocalAddr(), addr) rep := gosocks4.NewReply(gosocks4.Rejected, nil) rep.Write(conn) if Debug { log.Logf("[socks4] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } retries := 1 if h.options.Chain != nil && h.options.Chain.Retries > 0 { retries = h.options.Chain.Retries } if h.options.Retries > 0 { retries = h.options.Retries } var err error var cc net.Conn var route *Chain for i := 0; i < retries; i++ { route, err = h.options.Chain.selectRouteFor(addr) if err != nil { log.Logf("[socks4] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) continue } buf := bytes.Buffer{} fmt.Fprintf(&buf, "%s -> %s -> ", conn.RemoteAddr(), h.options.Node.String()) for _, nd := range route.route { fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String()) } fmt.Fprintf(&buf, "%s", addr) log.Log("[route]", buf.String()) cc, err = route.Dial(addr, TimeoutChainOption(h.options.Timeout), HostsChainOption(h.options.Hosts), ResolverChainOption(h.options.Resolver), ) if err == nil { break } log.Logf("[socks4] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) } if err != nil { rep := gosocks4.NewReply(gosocks4.Failed, nil) rep.Write(conn) if Debug { log.Logf("[socks4] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } return } defer cc.Close() rep := gosocks4.NewReply(gosocks4.Granted, nil) if err := rep.Write(conn); err != nil { log.Logf("[socks4] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } if Debug { log.Logf("[socks4] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), rep) } log.Logf("[socks4] %s <-> %s", conn.RemoteAddr(), addr) transport(conn, cc) log.Logf("[socks4] %s >-< %s", conn.RemoteAddr(), addr) } func (h *socks4Handler) handleBind(conn net.Conn, req *gosocks4.Request) { // TODO: serve socks4 bind if h.options.Chain.IsEmpty() { reply := gosocks4.NewReply(gosocks4.Rejected, nil) reply.Write(conn) if Debug { log.Logf("[socks4-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply) } return } cc, err := h.options.Chain.Conn() // connection error if err != nil && err != ErrEmptyChain { log.Logf("[socks4-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) reply := gosocks4.NewReply(gosocks4.Failed, nil) reply.Write(conn) if Debug { log.Logf("[socks4-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply) } return } defer cc.Close() // forward request req.Write(cc) log.Logf("[socks4-bind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr()) transport(conn, cc) log.Logf("[socks4-bind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr()) } type socks5HandshakeOptions struct { selector gosocks5.Selector user *url.Userinfo tlsConfig *tls.Config noTLS bool } type socks5HandshakeOption func(opts *socks5HandshakeOptions) func selectorSocks5HandshakeOption(selector gosocks5.Selector) socks5HandshakeOption { return func(opts *socks5HandshakeOptions) { opts.selector = selector } } func userSocks5HandshakeOption(user *url.Userinfo) socks5HandshakeOption { return func(opts *socks5HandshakeOptions) { opts.user = user } } func noTLSSocks5HandshakeOption(noTLS bool) socks5HandshakeOption { return func(opts *socks5HandshakeOptions) { opts.noTLS = noTLS } } func socks5Handshake(conn net.Conn, opts ...socks5HandshakeOption) (net.Conn, error) { options := socks5HandshakeOptions{} for _, opt := range opts { opt(&options) } selector := options.selector if selector == nil { cs := &clientSelector{ TLSConfig: &tls.Config{InsecureSkipVerify: true}, User: options.user, } cs.AddMethod( gosocks5.MethodNoAuth, gosocks5.MethodUserPass, ) if !options.noTLS { cs.AddMethod(MethodTLS) } selector = cs } cc := gosocks5.ClientConn(conn, selector) if err := cc.Handleshake(); err != nil { return nil, err } return cc, nil } func getSocks5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) { c, err := chain.Conn() if err != nil { return nil, err } node := chain.LastNode() conn, err := newSocks5UDPTunnelConn(c, addr, nil, userSocks5HandshakeOption(node.User), noTLSSocks5HandshakeOption(node.GetBool("notls")), ) if err != nil { c.Close() } return conn, err } type socks5UDPTunnelConn struct { net.Conn taddr net.Addr } func newSocks5UDPTunnelConn(conn net.Conn, raddr, taddr net.Addr, opts ...socks5HandshakeOption) (net.Conn, error) { cc, err := socks5Handshake(conn, opts...) if err != nil { return nil, err } req := gosocks5.NewRequest(CmdUDPTun, toSocksAddr(raddr)) if err := req.Write(cc); err != nil { return nil, err } if Debug { log.Log("[socks5] udp-tun", req) } reply, err := gosocks5.ReadReply(cc) if err != nil { return nil, err } if Debug { log.Log("[socks5] udp-tun", reply) } if reply.Rep != gosocks5.Succeeded { return nil, errors.New("socks5 UDP tunnel failure") } baddr, err := net.ResolveUDPAddr("udp", reply.Addr.String()) if err != nil { return nil, err } log.Logf("[socks5] udp-tun associate on %s OK", baddr) return &socks5UDPTunnelConn{ Conn: cc, taddr: taddr, }, nil } func (c *socks5UDPTunnelConn) Read(b []byte) (n int, err error) { n, _, err = c.ReadFrom(b) return } func (c *socks5UDPTunnelConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { dgram, err := gosocks5.ReadUDPDatagram(c.Conn) if err != nil { return } n = copy(b, dgram.Data) addr, err = net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) return } func (c *socks5UDPTunnelConn) Write(b []byte) (n int, err error) { return c.WriteTo(b, c.taddr) } func (c *socks5UDPTunnelConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(len(b)), 0, toSocksAddr(addr)), b) if err = dgram.Write(c.Conn); err != nil { return } return len(b), nil } // socks5BindConn is a connection for SOCKS5 bind client. type socks5BindConn struct { raddr net.Addr laddr net.Addr net.Conn handshaked bool handshakeMux sync.Mutex } // Handshake waits for a peer to connect to the bind port. func (c *socks5BindConn) Handshake() (err error) { c.handshakeMux.Lock() defer c.handshakeMux.Unlock() if c.handshaked { return nil } c.handshaked = true rep, err := gosocks5.ReadReply(c.Conn) if err != nil { return fmt.Errorf("bind: read reply %v", err) } if rep.Rep != gosocks5.Succeeded { return fmt.Errorf("bind: peer connect failure") } c.raddr, err = net.ResolveTCPAddr("tcp", rep.Addr.String()) return } func (c *socks5BindConn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { return } return c.Conn.Read(b) } func (c *socks5BindConn) Write(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { return } return c.Conn.Write(b) } func (c *socks5BindConn) LocalAddr() net.Addr { return c.laddr } func (c *socks5BindConn) RemoteAddr() net.Addr { return c.raddr } type socks5UDPConn struct { *net.UDPConn taddr net.Addr } func (c *socks5UDPConn) Read(b []byte) (n int, err error) { n, _, err = c.ReadFrom(b) return } func (c *socks5UDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { data := mPool.Get().([]byte) defer mPool.Put(data) n, err = c.UDPConn.Read(data) if err != nil { return } dg, err := gosocks5.ReadUDPDatagram(bytes.NewReader(data[:n])) if err != nil { return } n = copy(b, dg.Data) addr, err = net.ResolveUDPAddr("udp", dg.Header.Addr.String()) return } func (c *socks5UDPConn) Write(b []byte) (int, error) { return c.WriteTo(b, c.taddr) } func (c *socks5UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) { adr, err := gosocks5.NewAddr(addr.String()) if err != nil { return 0, err } h := gosocks5.NewUDPHeader(0, 0, adr) dg := gosocks5.NewUDPDatagram(h, b) if err = dg.Write(c.UDPConn); err != nil { return 0, err } return len(b), nil } // a dummy client conn for multiplex bind used by SOCKS5 multiplex bind client connector type muxBindClientConn struct { nopConn session *muxSession } func (c *muxBindClientConn) Accept() (net.Conn, error) { return c.session.Accept() }