package proxy import ( "encoding/binary" "errors" "io" "net" "strconv" "github.com/astaxie/beego/logs" "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/file" ) const ( ipV4 = 1 domainName = 3 ipV6 = 4 connectMethod = 1 bindMethod = 2 associateMethod = 3 // The maximum packet size of any udp Associate packet, based on ethernet's max size, // minus the IP and UDP headers. IPv4 has a 20 byte header, UDP adds an // additional 4 bytes. This is a total overhead of 24 bytes. Ethernet's // max packet size is 1500 bytes, 1500 - 24 = 1476. maxUDPPacketSize = 1476 ) const ( succeeded uint8 = iota serverFailure notAllowed networkUnreachable hostUnreachable connectionRefused ttlExpired commandNotSupported addrTypeNotSupported ) const ( UserPassAuth = uint8(2) userAuthVersion = uint8(1) authSuccess = uint8(0) authFailure = uint8(1) ) type Sock5ModeServer struct { BaseServer listener net.Listener } //req func (s *Sock5ModeServer) handleRequest(c net.Conn) { /* The SOCKS request is formed as follows: +----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ */ header := make([]byte, 3) _, err := io.ReadFull(c, header) if err != nil { logs.Warn("illegal request", err) c.Close() return } switch header[1] { case connectMethod: s.handleConnect(c) case bindMethod: s.handleBind(c) case associateMethod: s.handleUDP(c) default: s.sendReply(c, commandNotSupported) c.Close() } } //reply func (s *Sock5ModeServer) sendReply(c net.Conn, rep uint8) { reply := []byte{ 5, rep, 0, 1, } localAddr := c.LocalAddr().String() localHost, localPort, _ := net.SplitHostPort(localAddr) ipBytes := net.ParseIP(localHost).To4() nPort, _ := strconv.Atoi(localPort) reply = append(reply, ipBytes...) portBytes := make([]byte, 2) binary.BigEndian.PutUint16(portBytes, uint16(nPort)) reply = append(reply, portBytes...) c.Write(reply) } //do conn func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) { addrType := make([]byte, 1) c.Read(addrType) var host string switch addrType[0] { case ipV4: ipv4 := make(net.IP, net.IPv4len) c.Read(ipv4) host = ipv4.String() case ipV6: ipv6 := make(net.IP, net.IPv6len) c.Read(ipv6) host = ipv6.String() case domainName: var domainLen uint8 binary.Read(c, binary.BigEndian, &domainLen) domain := make([]byte, domainLen) c.Read(domain) host = string(domain) default: s.sendReply(c, addrTypeNotSupported) return } var port uint16 binary.Read(c, binary.BigEndian, &port) // connect to host addr := net.JoinHostPort(host, strconv.Itoa(int(port))) var ltype string if command == associateMethod { ltype = common.CONN_UDP } else { ltype = common.CONN_TCP } s.DealClient(conn.NewConn(c), s.task.Client, addr, nil, ltype, func() { s.sendReply(c, succeeded) }, s.task.Flow, s.task.Target.LocalProxy) return } //conn func (s *Sock5ModeServer) handleConnect(c net.Conn) { s.doConnect(c, connectMethod) } // passive mode func (s *Sock5ModeServer) handleBind(c net.Conn) { } //udp func (s *Sock5ModeServer) handleUDP(c net.Conn) { /* +----+------+------+----------+----------+----------+ |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +----+------+------+----------+----------+----------+ | 2 | 1 | 1 | Variable | 2 | Variable | +----+------+------+----------+----------+----------+ */ buf := make([]byte, 3) c.Read(buf) // relay udp datagram silently, without any notification to the requesting client if buf[2] != 0 { // does not support fragmentation, drop it logs.Warn("does not support fragmentation, drop") dummy := make([]byte, maxUDPPacketSize) c.Read(dummy) } s.doConnect(c, associateMethod) } //new conn func (s *Sock5ModeServer) handleConn(c net.Conn) { buf := make([]byte, 2) if _, err := io.ReadFull(c, buf); err != nil { logs.Warn("negotiation err", err) c.Close() return } if version := buf[0]; version != 5 { logs.Warn("only support socks5, request from: ", c.RemoteAddr()) c.Close() return } nMethods := buf[1] methods := make([]byte, nMethods) if len, err := c.Read(methods); len != int(nMethods) || err != nil { logs.Warn("wrong method") c.Close() return } if (s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "") || (s.task.Client.Cnf.MultiUser && len(s.task.Client.Cnf.MultiUserMap) > 0) { buf[1] = UserPassAuth c.Write(buf) if err := s.Auth(c); err != nil { c.Close() logs.Warn("Validation failed:", err) return } } else { buf[1] = 0 c.Write(buf) } s.handleRequest(c) } //socks5 auth func (s *Sock5ModeServer) Auth(c net.Conn) error { header := []byte{0, 0} if _, err := io.ReadAtLeast(c, header, 2); err != nil { return err } if header[0] != userAuthVersion { return errors.New("验证方式不被支持") } userLen := int(header[1]) user := make([]byte, userLen) if _, err := io.ReadAtLeast(c, user, userLen); err != nil { return err } if _, err := c.Read(header[:1]); err != nil { return errors.New("密码长度获取错误") } passLen := int(header[0]) pass := make([]byte, passLen) if _, err := io.ReadAtLeast(c, pass, passLen); err != nil { return err } var U, P string if s.task.Client.Cnf.MultiUser { // enable multi user auth U = string(user) var ok bool P, ok = s.task.Client.Cnf.MultiUserMap[U] if !ok { return errors.New("验证不通过") } } else { U = s.task.Client.Cnf.U P = s.task.Client.Cnf.P } if string(user) == U && string(pass) == P { if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil { return err } return nil } else { if _, err := c.Write([]byte{userAuthVersion, authFailure}); err != nil { return err } return errors.New("验证不通过") } } //start func (s *Sock5ModeServer) Start() error { return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) { if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error()) c.Close() return } logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr()) s.handleConn(c) s.task.Client.AddConn() }, &s.listener) } //new func NewSock5ModeServer(bridge NetBridge, task *file.Tunnel) *Sock5ModeServer { s := new(Sock5ModeServer) s.bridge = bridge s.task = task return s } //close func (s *Sock5ModeServer) Close() error { return s.listener.Close() }