socks4.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package socks4
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "io"
  7. "net"
  8. "net/netip"
  9. "strconv"
  10. "github.com/metacubex/mihomo/component/auth"
  11. )
  12. const Version = 0x04
  13. type Command = uint8
  14. const (
  15. CmdConnect Command = 0x01
  16. CmdBind Command = 0x02
  17. )
  18. type Code = uint8
  19. const (
  20. RequestGranted Code = 90
  21. RequestRejected Code = 91
  22. RequestIdentdFailed Code = 92
  23. RequestIdentdMismatched Code = 93
  24. )
  25. var (
  26. errVersionMismatched = errors.New("version code mismatched")
  27. errCommandNotSupported = errors.New("command not supported")
  28. errIPv6NotSupported = errors.New("IPv6 not supported")
  29. ErrRequestRejected = errors.New("request rejected or failed")
  30. ErrRequestIdentdFailed = errors.New("request rejected because SOCKS server cannot connect to identd on the client")
  31. ErrRequestIdentdMismatched = errors.New("request rejected because the client program and identd report different user-ids")
  32. ErrRequestUnknownCode = errors.New("request failed with unknown code")
  33. )
  34. var subnet = netip.PrefixFrom(netip.IPv4Unspecified(), 24)
  35. func ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, user string, err error) {
  36. var req [8]byte
  37. if _, err = io.ReadFull(rw, req[:]); err != nil {
  38. return
  39. }
  40. if req[0] != Version {
  41. err = errVersionMismatched
  42. return
  43. }
  44. if command = req[1]; command != CmdConnect {
  45. err = errCommandNotSupported
  46. return
  47. }
  48. var (
  49. dstIP = netip.AddrFrom4(*(*[4]byte)(req[4:8])) // [4]byte
  50. dstPort = req[2:4] // [2]byte
  51. )
  52. var (
  53. host string
  54. port string
  55. code uint8
  56. userID []byte
  57. )
  58. if userID, err = readUntilNull(rw); err != nil {
  59. return
  60. }
  61. user = string(userID)
  62. if isReservedIP(dstIP) {
  63. var target []byte
  64. if target, err = readUntilNull(rw); err != nil {
  65. return
  66. }
  67. host = string(target)
  68. }
  69. port = strconv.Itoa(int(binary.BigEndian.Uint16(dstPort)))
  70. if host != "" {
  71. addr = net.JoinHostPort(host, port)
  72. } else {
  73. addr = net.JoinHostPort(dstIP.String(), port)
  74. }
  75. // SOCKS4 only support USERID auth.
  76. if authenticator == nil || authenticator.Verify(user, "") {
  77. code = RequestGranted
  78. } else {
  79. code = RequestIdentdMismatched
  80. err = ErrRequestIdentdMismatched
  81. }
  82. var reply [8]byte
  83. reply[0] = 0x00 // reply code
  84. reply[1] = code // result code
  85. copy(reply[4:8], dstIP.AsSlice())
  86. copy(reply[2:4], dstPort)
  87. _, wErr := rw.Write(reply[:])
  88. if err == nil {
  89. err = wErr
  90. }
  91. return
  92. }
  93. func ClientHandshake(rw io.ReadWriter, addr string, command Command, userID string) (err error) {
  94. host, portStr, err := net.SplitHostPort(addr)
  95. if err != nil {
  96. return err
  97. }
  98. port, err := strconv.ParseUint(portStr, 10, 16)
  99. if err != nil {
  100. return err
  101. }
  102. dstIP, err := netip.ParseAddr(host)
  103. if err != nil /* HOST */ {
  104. dstIP = netip.AddrFrom4([4]byte{0, 0, 0, 1})
  105. } else if dstIP.Is6() /* IPv6 */ {
  106. return errIPv6NotSupported
  107. }
  108. req := &bytes.Buffer{}
  109. req.WriteByte(Version)
  110. req.WriteByte(command)
  111. _ = binary.Write(req, binary.BigEndian, uint16(port))
  112. req.Write(dstIP.AsSlice())
  113. req.WriteString(userID)
  114. req.WriteByte(0) /* NULL */
  115. if isReservedIP(dstIP) /* SOCKS4A */ {
  116. req.WriteString(host)
  117. req.WriteByte(0) /* NULL */
  118. }
  119. if _, err = rw.Write(req.Bytes()); err != nil {
  120. return err
  121. }
  122. var resp [8]byte
  123. if _, err = io.ReadFull(rw, resp[:]); err != nil {
  124. return err
  125. }
  126. if resp[0] != 0x00 {
  127. return errVersionMismatched
  128. }
  129. switch resp[1] {
  130. case RequestGranted:
  131. return nil
  132. case RequestRejected:
  133. return ErrRequestRejected
  134. case RequestIdentdFailed:
  135. return ErrRequestIdentdFailed
  136. case RequestIdentdMismatched:
  137. return ErrRequestIdentdMismatched
  138. default:
  139. return ErrRequestUnknownCode
  140. }
  141. }
  142. // For version 4A, if the client cannot resolve the destination host's
  143. // domain name to find its IP address, it should set the first three bytes
  144. // of DSTIP to NULL and the last byte to a non-zero value. (This corresponds
  145. // to IP address 0.0.0.x, with x nonzero. As decreed by IANA -- The
  146. // Internet Assigned Numbers Authority -- such an address is inadmissible
  147. // as a destination IP address and thus should never occur if the client
  148. // can resolve the domain name.)
  149. func isReservedIP(ip netip.Addr) bool {
  150. return !ip.IsUnspecified() && subnet.Contains(ip)
  151. }
  152. func readUntilNull(r io.Reader) ([]byte, error) {
  153. buf := &bytes.Buffer{}
  154. var data [1]byte
  155. for {
  156. if _, err := r.Read(data[:]); err != nil {
  157. return nil, err
  158. }
  159. if data[0] == 0 {
  160. return buf.Bytes(), nil
  161. }
  162. buf.WriteByte(data[0])
  163. }
  164. }