socks5_read_request_handle.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package socks5
  2. import (
  3. "context"
  4. "ehang.io/nps/core"
  5. "encoding/binary"
  6. "errors"
  7. "io"
  8. "net"
  9. "strconv"
  10. )
  11. type Request struct {
  12. core.NpsPlugin
  13. }
  14. const (
  15. ipV4 = 1
  16. domainName = 3
  17. ipV6 = 4
  18. connectMethod = 1
  19. bindMethod = 2
  20. associateMethod = 3
  21. // The maximum packet size of any udp Associate packet, based on ethernet's max size,
  22. // minus the IP and UDP headerrequest. IPv4 has a 20 byte header, UDP adds an
  23. // additional 4 byterequest. This is a total overhead of 24 byterequest. Ethernet's
  24. // max packet size is 1500 bytes, 1500 - 24 = 1476.
  25. maxUDPPacketSize = 1476
  26. commandNotSupported = 7
  27. addrTypeNotSupported = 8
  28. succeeded = 0
  29. )
  30. func (request *Request) Run(ctx context.Context) (context.Context, error) {
  31. clientConn := request.GetClientConn(ctx)
  32. /*
  33. The SOCKS request is formed as follows:
  34. +----+-----+-------+------+----------+----------+
  35. |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
  36. +----+-----+-------+------+----------+----------+
  37. | 1 | 1 | X'00' | 1 | Variable | 2 |
  38. +----+-----+-------+------+----------+----------+
  39. */
  40. header := make([]byte, 3)
  41. _, err := io.ReadFull(clientConn, header)
  42. if err != nil {
  43. return ctx, errors.New("illegal request" + err.Error())
  44. }
  45. switch header[1] {
  46. case connectMethod:
  47. ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "tcp")
  48. return request.doConnect(ctx, clientConn)
  49. case bindMethod:
  50. return ctx, request.handleBind()
  51. case associateMethod:
  52. ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "udp")
  53. return request.handleUDP(ctx, clientConn)
  54. default:
  55. request.sendReply(clientConn, commandNotSupported)
  56. return ctx, errors.New("command not supported")
  57. }
  58. return ctx, nil
  59. }
  60. func (request *Request) sendReply(clientConn net.Conn, rep uint8) error {
  61. reply := []byte{
  62. 5,
  63. rep,
  64. 0,
  65. 1,
  66. }
  67. localAddr := clientConn.LocalAddr().String()
  68. localHost, localPort, _ := net.SplitHostPort(localAddr)
  69. ipBytes := net.ParseIP(localHost).To4()
  70. nPort, _ := strconv.Atoi(localPort)
  71. reply = append(reply, ipBytes...)
  72. portBytes := make([]byte, 2)
  73. binary.BigEndian.PutUint16(portBytes, uint16(nPort))
  74. reply = append(reply, portBytes...)
  75. _, err := clientConn.Write(reply)
  76. return err
  77. }
  78. //do conn
  79. func (request *Request) doConnect(ctx context.Context, clientConn net.Conn) (context.Context, error) {
  80. addrType := make([]byte, 1)
  81. clientConn.Read(addrType)
  82. var host string
  83. switch addrType[0] {
  84. case ipV4:
  85. ipv4 := make(net.IP, net.IPv4len)
  86. clientConn.Read(ipv4)
  87. host = ipv4.String()
  88. case ipV6:
  89. ipv6 := make(net.IP, net.IPv6len)
  90. clientConn.Read(ipv6)
  91. host = ipv6.String()
  92. case domainName:
  93. var domainLen uint8
  94. binary.Read(clientConn, binary.BigEndian, &domainLen)
  95. domain := make([]byte, domainLen)
  96. clientConn.Read(domain)
  97. host = string(domain)
  98. default:
  99. request.sendReply(clientConn, addrTypeNotSupported)
  100. return ctx, errors.New("target address type is not support")
  101. }
  102. var port uint16
  103. binary.Read(clientConn, binary.BigEndian, &port)
  104. ctx = context.WithValue(ctx, core.PROXY_CONNECTION_ADDR, host)
  105. ctx = context.WithValue(ctx, core.PROXY_CONNECTION_PORT, port)
  106. request.sendReply(clientConn, succeeded)
  107. return ctx, nil
  108. }
  109. // passive mode
  110. func (request *Request) handleBind() error {
  111. return nil
  112. }
  113. //udp
  114. func (request *Request) handleUDP(ctx context.Context, clientConn net.Conn) (context.Context, error) {
  115. /*
  116. +----+------+------+----------+----------+----------+
  117. |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
  118. +----+------+------+----------+----------+----------+
  119. | 2 | 1 | 1 | Variable | 2 | Variable |
  120. +----+------+------+----------+----------+----------+
  121. */
  122. buf := make([]byte, 3)
  123. clientConn.Read(buf)
  124. // relay udp datagram silently, without any notification to the requesting client
  125. if buf[2] != 0 {
  126. // does not support fragmentation, drop it
  127. dummy := make([]byte, maxUDPPacketSize)
  128. clientConn.Read(dummy)
  129. return ctx, errors.New("does not support fragmentation, drop")
  130. }
  131. return request.doConnect(ctx, clientConn)
  132. }