package gost import ( "bytes" "crypto/tls" //"errors" "github.com/ginuerzh/gosocks5" "github.com/golang/glog" //"os/exec" //"io" //"io/ioutil" "net" "net/url" "strconv" "time" ) const ( MethodTLS uint8 = 0x80 // extended method for tls MethodTLSAuth uint8 = 0x82 // extended method for tls+auth ) const ( CmdUdpConnect uint8 = 0xF1 // extended method for udp local port forwarding CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp ) type clientSelector struct { methods []uint8 user *url.Userinfo tlsConfig *tls.Config } func (selector *clientSelector) Methods() []uint8 { return selector.methods } func (selector *clientSelector) Select(methods ...uint8) (method uint8) { return } func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { 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 { glog.V(LWARNING).Infoln("socks5 auth:", err) return nil, err } glog.V(LDEBUG).Infoln(req) resp, err := gosocks5.ReadUserPassResponse(conn) if err != nil { glog.V(LWARNING).Infoln("socks5 auth:", err) return nil, err } glog.V(LDEBUG).Infoln(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 tlsConfig *tls.Config } func (selector *serverSelector) Methods() []uint8 { return selector.methods } func (selector *serverSelector) Select(methods ...uint8) (method uint8) { glog.V(LDEBUG).Infof("%d %d %v", gosocks5.Ver5, len(methods), methods) method = gosocks5.MethodNoAuth for _, m := range methods { if m == MethodTLS { method = m break } } // when user/pass is set, auth is mandatory if selector.users != 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) { glog.V(LDEBUG).Infof("%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 { glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } glog.V(LDEBUG).Infoln("[socks5]", req.String()) valid := false for _, user := range selector.users { username := user.Username() password, _ := user.Password() if (req.Username == username && req.Password == password) || (req.Username == username && password == "") || (username == "" && req.Password == password) { valid = true break } } if len(selector.users) > 0 && !valid { resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) if err := resp.Write(conn); err != nil { glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } glog.V(LDEBUG).Infoln("[socks5]", resp) glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required") return nil, gosocks5.ErrAuthFailure } resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) if err := resp.Write(conn); err != nil { glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } glog.V(LDEBUG).Infoln(resp) case gosocks5.MethodNoAcceptable: return nil, gosocks5.ErrBadMethod } return conn, nil } type Socks5Server struct { conn net.Conn Base *ProxyServer } func NewSocks5Server(conn net.Conn, base *ProxyServer) *Socks5Server { return &Socks5Server{conn: conn, Base: base} } func (s *Socks5Server) HandleRequest(req *gosocks5.Request) { glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, req) switch req.Cmd { case gosocks5.CmdConnect: glog.V(LINFO).Infof("[socks5-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr) s.handleConnect(req) case gosocks5.CmdBind: glog.V(LINFO).Infof("[socks5-bind] %s - %s", s.conn.RemoteAddr(), req.Addr) s.handleBind(req) case CmdUdpConnect: glog.V(LINFO).Infof("[udp] %s - %s", s.conn.RemoteAddr(), req.Addr) s.handleUDPConnect(req) case gosocks5.CmdUdp: glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr) s.handleUDPRelay(req) case CmdUdpTun: glog.V(LINFO).Infof("[socks5-udp] %s - %s", s.conn.RemoteAddr(), req.Addr) s.handleUDPTunnel(req) default: glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd) } } func (s *Socks5Server) handleConnect(req *gosocks5.Request) { cc, err := s.Base.Chain.Dial(req.Addr.String()) if err != nil { glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) rep.Write(s.conn) glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep) return } defer cc.Close() rep := gosocks5.NewReply(gosocks5.Succeeded, nil) if err := rep.Write(s.conn); err != nil { glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err) return } glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep) glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr) //Transport(conn, cc) s.Base.transport(s.conn, cc) glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr) } func (s *Socks5Server) handleBind(req *gosocks5.Request) { cc, err := s.Base.Chain.GetConn() // connection error if err != nil && err != ErrEmptyChain { glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(s.conn) glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply) return } // serve socks5 bind if err == ErrEmptyChain { s.bindOn(req.Addr.String()) return } defer cc.Close() // forward request req.Write(cc) glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr()) s.Base.transport(s.conn, cc) glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr()) } func (s *Socks5Server) handleUDPConnect(req *gosocks5.Request) { cc, err := s.Base.Chain.GetConn() // connection error if err != nil && err != ErrEmptyChain { glog.V(LWARNING).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(s.conn) glog.V(LDEBUG).Infof("[udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply) return } // serve udp connect if err == ErrEmptyChain { s.udpConnect(req.Addr.String()) return } defer cc.Close() // forward request if err := req.Write(cc); err != nil { glog.V(LINFO).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err) gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn) return } glog.V(LINFO).Infof("[udp] %s <-> %s", s.conn.RemoteAddr(), req.Addr) s.Base.transport(s.conn, cc) glog.V(LINFO).Infof("[udp] %s >-< %s", s.conn.RemoteAddr(), req.Addr) } func (s *Socks5Server) handleUDPRelay(req *gosocks5.Request) { bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String()) relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error if err != nil { glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(s.conn) glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply) return } defer relay.Close() socksAddr := ToSocksAddr(relay.LocalAddr()) socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(s.conn); err != nil { glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err) return } glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), reply.Addr, reply) glog.V(LINFO).Infof("[socks5-udp] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), req.Addr, socksAddr) cc, err := s.Base.Chain.GetConn() // connection error if err != nil && err != ErrEmptyChain { glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), socksAddr, err) return } // serve as standard socks5 udp relay local <-> remote if err == ErrEmptyChain { peer, er := net.ListenUDP("udp", nil) if er != nil { glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), socksAddr, er) return } defer peer.Close() go s.transportUDP(relay, peer) } // forward udp local <-> tunnel if err == nil { defer cc.Close() cc.SetWriteDeadline(time.Now().Add(WriteTimeout)) req := gosocks5.NewRequest(CmdUdpTun, nil) if err := req.Write(cc); err != nil { glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), cc.RemoteAddr(), err) return } cc.SetWriteDeadline(time.Time{}) glog.V(LDEBUG).Infof("[socks5-udp] %s -> %s\n%s", s.conn.RemoteAddr(), cc.RemoteAddr(), req) cc.SetReadDeadline(time.Now().Add(ReadTimeout)) reply, err = gosocks5.ReadReply(cc) if err != nil { glog.V(LWARNING).Infoln("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), cc.RemoteAddr(), err) return } glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), cc.RemoteAddr(), reply) if reply.Rep != gosocks5.Succeeded { glog.V(LWARNING).Infoln("[socks5-udp] %s <- %s : udp associate failed", s.conn.RemoteAddr(), cc.RemoteAddr()) return } cc.SetReadDeadline(time.Time{}) glog.V(LINFO).Infof("[socks5-udp] %s <-> %s [tun: %s]", s.conn.RemoteAddr(), socksAddr, reply.Addr) go s.tunnelUDP(relay, cc, true) } glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), socksAddr) b := make([]byte, SmallBufferSize) for { _, err := s.conn.Read(b) // discard any data from tcp connection if err != nil { glog.V(LWARNING).Infof("[socks5-udp] %s - %s : %s", s.conn.RemoteAddr(), socksAddr, err) break // client disconnected } } glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), socksAddr) } func (s *Socks5Server) handleUDPTunnel(req *gosocks5.Request) { cc, err := s.Base.Chain.GetConn() // connection error if err != nil && err != ErrEmptyChain { glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err) reply := gosocks5.NewReply(gosocks5.Failure, nil) reply.Write(s.conn) glog.V(LDEBUG).Infof("[socks5-udp] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, reply) return } // serve tunnel udp, tunnel <-> remote, handle tunnel udp request if err == ErrEmptyChain { bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String()) uc, err := net.ListenUDP("udp", bindAddr) if err != nil { glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err) return } defer uc.Close() socksAddr := ToSocksAddr(uc.LocalAddr()) socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(s.conn); err != nil { glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", s.conn.RemoteAddr(), socksAddr, err) return } glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", s.conn.RemoteAddr(), socksAddr, reply) glog.V(LINFO).Infof("[socks5-udp] %s <-> %s", s.conn.RemoteAddr(), socksAddr) s.tunnelUDP(uc, s.conn, false) glog.V(LINFO).Infof("[socks5-udp] %s >-< %s", s.conn.RemoteAddr(), socksAddr) return } defer cc.Close() // tunnel <-> tunnel, direct forwarding req.Write(cc) glog.V(LINFO).Infof("[socks5-udp] %s <-> %s [tun]", s.conn.RemoteAddr(), cc.RemoteAddr()) s.Base.transport(s.conn, cc) glog.V(LINFO).Infof("[socks5-udp] %s >-< %s [tun]", s.conn.RemoteAddr(), cc.RemoteAddr()) } func (s *Socks5Server) bindOn(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 { glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn) return } socksAddr := ToSocksAddr(ln.Addr()) // Issue: may not reachable when host has multi-interface socksAddr.Host, _, _ = net.SplitHostPort(s.conn.LocalAddr().String()) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) if err := reply.Write(s.conn); err != nil { glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err) ln.Close() return } glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), addr, reply) glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", s.conn.RemoteAddr(), addr, 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 <- s.Base.transport(s.conn, pc1) }() return errc } defer pc2.Close() for { select { case err := <-accept(): if err != nil || pconn == nil { glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err) return } defer pconn.Close() reply := gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(pconn.RemoteAddr())) if err := reply.Write(pc2); err != nil { glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", s.conn.RemoteAddr(), addr, err) } glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", s.conn.RemoteAddr(), addr, reply) glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", s.conn.RemoteAddr(), socksAddr, pconn.RemoteAddr()) glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", s.conn.RemoteAddr(), pconn.RemoteAddr()) if err = s.Base.transport(pc2, pconn); err != nil { glog.V(LWARNING).Infoln(err) } glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", s.conn.RemoteAddr(), pconn.RemoteAddr()) return case err := <-pipe(): glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %v", s.conn.RemoteAddr(), addr, err) ln.Close() return } } } func (s *Socks5Server) udpConnect(addr string) { raddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { glog.V(LINFO).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) gosocks5.NewReply(gosocks5.Failure, nil).Write(s.conn) return } if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(s.conn); err != nil { glog.V(LINFO).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), addr, err) return } glog.V(LINFO).Infof("[udp] %s <-> %s", s.conn.RemoteAddr(), raddr) defer glog.V(LINFO).Infof("[udp] %s >-< %s", s.conn.RemoteAddr(), raddr) for { dgram, err := gosocks5.ReadUDPDatagram(s.conn) if err != nil { glog.V(LWARNING).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) return } go func() { b := make([]byte, LargeBufferSize) relay, err := net.DialUDP("udp", nil, raddr) if err != nil { glog.V(LWARNING).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), raddr, err) return } defer relay.Close() if _, err := relay.Write(dgram.Data); err != nil { glog.V(LWARNING).Infof("[udp] %s -> %s : %s", s.conn.RemoteAddr(), raddr, err) return } glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", s.conn.RemoteAddr(), raddr, len(dgram.Data)) relay.SetReadDeadline(time.Now().Add(time.Second * 60)) n, err := relay.Read(b) if err != nil { glog.V(LWARNING).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), raddr, err) return } relay.SetReadDeadline(time.Time{}) glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", s.conn.RemoteAddr(), raddr, n) s.conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(s.conn); err != nil { glog.V(LWARNING).Infof("[udp] %s <- %s : %s", s.conn.RemoteAddr(), raddr, err) return } s.conn.SetWriteDeadline(time.Time{}) }() } } func (s *Socks5Server) transportUDP(relay, peer *net.UDPConn) (err error) { errc := make(chan error, 2) var clientAddr *net.UDPAddr go func() { b := make([]byte, LargeBufferSize) for { n, laddr, err := relay.ReadFromUDP(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 _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) } }() go func() { b := make([]byte, LargeBufferSize) for { n, raddr, err := peer.ReadFromUDP(b) if err != nil { errc <- err return } if clientAddr == nil { continue } buf := bytes.Buffer{} dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, ToSocksAddr(raddr)), b[:n]) dgram.Write(&buf) if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", relay.LocalAddr(), raddr, len(dgram.Data)) } }() select { case err = <-errc: //log.Println("w exit", err) } return } func (s *Socks5Server) tunnelUDP(uc *net.UDPConn, cc net.Conn, client bool) (err error) { errc := make(chan error, 2) var clientAddr *net.UDPAddr go func() { b := make([]byte, LargeBufferSize) for { n, addr, err := uc.ReadFromUDP(b) if err != nil { errc <- err return } var dgram *gosocks5.UDPDatagram if client { // pipe from relay to tunnel dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) if err != nil { errc <- err return } if clientAddr == nil { clientAddr = addr } dgram.Header.Rsv = uint16(len(dgram.Data)) if err := dgram.Write(cc); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } else { // pipe from peer to tunnel dgram = gosocks5.NewUDPDatagram( gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) if err := dgram.Write(cc); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[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 { errc <- err return } if client { // pipe from tunnel to relay if clientAddr == nil { continue } dgram.Header.Rsv = 0 buf := bytes.Buffer{} dgram.Write(&buf) if _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } else { // pipe from tunnel to peer addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) if err != nil { continue // drop silently } if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil { errc <- err return } glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data)) } } }() select { case err = <-errc: } return } 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), } }