You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
5.7 KiB

  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package proxy
  5. import (
  6. "errors"
  7. "io"
  8. "net"
  9. "strconv"
  10. )
  11. // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
  12. // with an optional username and password. See RFC 1928 and RFC 1929.
  13. func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
  14. s := &socks5{
  15. network: network,
  16. addr: addr,
  17. forward: forward,
  18. }
  19. if auth != nil {
  20. s.user = auth.User
  21. s.password = auth.Password
  22. }
  23. return s, nil
  24. }
  25. type socks5 struct {
  26. user, password string
  27. network, addr string
  28. forward Dialer
  29. }
  30. const socks5Version = 5
  31. const (
  32. socks5AuthNone = 0
  33. socks5AuthPassword = 2
  34. )
  35. const socks5Connect = 1
  36. const (
  37. socks5IP4 = 1
  38. socks5Domain = 3
  39. socks5IP6 = 4
  40. )
  41. var socks5Errors = []string{
  42. "",
  43. "general failure",
  44. "connection forbidden",
  45. "network unreachable",
  46. "host unreachable",
  47. "connection refused",
  48. "TTL expired",
  49. "command not supported",
  50. "address type not supported",
  51. }
  52. // Dial connects to the address addr on the given network via the SOCKS5 proxy.
  53. func (s *socks5) Dial(network, addr string) (net.Conn, error) {
  54. switch network {
  55. case "tcp", "tcp6", "tcp4":
  56. default:
  57. return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
  58. }
  59. conn, err := s.forward.Dial(s.network, s.addr)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if err := s.connect(conn, addr); err != nil {
  64. conn.Close()
  65. return nil, err
  66. }
  67. return conn, nil
  68. }
  69. // connect takes an existing connection to a socks5 proxy server,
  70. // and commands the server to extend that connection to target,
  71. // which must be a canonical address with a host and port.
  72. func (s *socks5) connect(conn net.Conn, target string) error {
  73. host, portStr, err := net.SplitHostPort(target)
  74. if err != nil {
  75. return err
  76. }
  77. port, err := strconv.Atoi(portStr)
  78. if err != nil {
  79. return errors.New("proxy: failed to parse port number: " + portStr)
  80. }
  81. if port < 1 || port > 0xffff {
  82. return errors.New("proxy: port number out of range: " + portStr)
  83. }
  84. // the size here is just an estimate
  85. buf := make([]byte, 0, 6+len(host))
  86. buf = append(buf, socks5Version)
  87. if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
  88. buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
  89. } else {
  90. buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
  91. }
  92. if _, err := conn.Write(buf); err != nil {
  93. return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
  94. }
  95. if _, err := io.ReadFull(conn, buf[:2]); err != nil {
  96. return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  97. }
  98. if buf[0] != 5 {
  99. return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
  100. }
  101. if buf[1] == 0xff {
  102. return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
  103. }
  104. // See RFC 1929
  105. if buf[1] == socks5AuthPassword {
  106. buf = buf[:0]
  107. buf = append(buf, 1 /* password protocol version */)
  108. buf = append(buf, uint8(len(s.user)))
  109. buf = append(buf, s.user...)
  110. buf = append(buf, uint8(len(s.password)))
  111. buf = append(buf, s.password...)
  112. if _, err := conn.Write(buf); err != nil {
  113. return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
  114. }
  115. if _, err := io.ReadFull(conn, buf[:2]); err != nil {
  116. return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  117. }
  118. if buf[1] != 0 {
  119. return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
  120. }
  121. }
  122. buf = buf[:0]
  123. buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
  124. if ip := net.ParseIP(host); ip != nil {
  125. if ip4 := ip.To4(); ip4 != nil {
  126. buf = append(buf, socks5IP4)
  127. ip = ip4
  128. } else {
  129. buf = append(buf, socks5IP6)
  130. }
  131. buf = append(buf, ip...)
  132. } else {
  133. if len(host) > 255 {
  134. return errors.New("proxy: destination host name too long: " + host)
  135. }
  136. buf = append(buf, socks5Domain)
  137. buf = append(buf, byte(len(host)))
  138. buf = append(buf, host...)
  139. }
  140. buf = append(buf, byte(port>>8), byte(port))
  141. if _, err := conn.Write(buf); err != nil {
  142. return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
  143. }
  144. if _, err := io.ReadFull(conn, buf[:4]); err != nil {
  145. return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  146. }
  147. failure := "unknown error"
  148. if int(buf[1]) < len(socks5Errors) {
  149. failure = socks5Errors[buf[1]]
  150. }
  151. if len(failure) > 0 {
  152. return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
  153. }
  154. bytesToDiscard := 0
  155. switch buf[3] {
  156. case socks5IP4:
  157. bytesToDiscard = net.IPv4len
  158. case socks5IP6:
  159. bytesToDiscard = net.IPv6len
  160. case socks5Domain:
  161. _, err := io.ReadFull(conn, buf[:1])
  162. if err != nil {
  163. return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  164. }
  165. bytesToDiscard = int(buf[0])
  166. default:
  167. return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
  168. }
  169. if cap(buf) < bytesToDiscard {
  170. buf = make([]byte, bytesToDiscard)
  171. } else {
  172. buf = buf[:bytesToDiscard]
  173. }
  174. if _, err := io.ReadFull(conn, buf); err != nil {
  175. return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  176. }
  177. // Also need to discard the port number
  178. if _, err := io.ReadFull(conn, buf[:2]); err != nil {
  179. return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
  180. }
  181. return nil
  182. }