本站源代码
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.

318 lines
9.0KB

  1. // Copyright 2018 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 socks provides a SOCKS version 5 client implementation.
  5. //
  6. // SOCKS protocol version 5 is defined in RFC 1928.
  7. // Username/Password authentication for SOCKS version 5 is defined in
  8. // RFC 1929.
  9. package socks
  10. import (
  11. "context"
  12. "errors"
  13. "io"
  14. "net"
  15. "strconv"
  16. )
  17. // A Command represents a SOCKS command.
  18. type Command int
  19. func (cmd Command) String() string {
  20. switch cmd {
  21. case CmdConnect:
  22. return "socks connect"
  23. case cmdBind:
  24. return "socks bind"
  25. default:
  26. return "socks " + strconv.Itoa(int(cmd))
  27. }
  28. }
  29. // An AuthMethod represents a SOCKS authentication method.
  30. type AuthMethod int
  31. // A Reply represents a SOCKS command reply code.
  32. type Reply int
  33. func (code Reply) String() string {
  34. switch code {
  35. case StatusSucceeded:
  36. return "succeeded"
  37. case 0x01:
  38. return "general SOCKS server failure"
  39. case 0x02:
  40. return "connection not allowed by ruleset"
  41. case 0x03:
  42. return "network unreachable"
  43. case 0x04:
  44. return "host unreachable"
  45. case 0x05:
  46. return "connection refused"
  47. case 0x06:
  48. return "TTL expired"
  49. case 0x07:
  50. return "command not supported"
  51. case 0x08:
  52. return "address type not supported"
  53. default:
  54. return "unknown code: " + strconv.Itoa(int(code))
  55. }
  56. }
  57. // Wire protocol constants.
  58. const (
  59. Version5 = 0x05
  60. AddrTypeIPv4 = 0x01
  61. AddrTypeFQDN = 0x03
  62. AddrTypeIPv6 = 0x04
  63. CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
  64. cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
  65. AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
  66. AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
  67. AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
  68. StatusSucceeded Reply = 0x00
  69. )
  70. // An Addr represents a SOCKS-specific address.
  71. // Either Name or IP is used exclusively.
  72. type Addr struct {
  73. Name string // fully-qualified domain name
  74. IP net.IP
  75. Port int
  76. }
  77. func (a *Addr) Network() string { return "socks" }
  78. func (a *Addr) String() string {
  79. if a == nil {
  80. return "<nil>"
  81. }
  82. port := strconv.Itoa(a.Port)
  83. if a.IP == nil {
  84. return net.JoinHostPort(a.Name, port)
  85. }
  86. return net.JoinHostPort(a.IP.String(), port)
  87. }
  88. // A Conn represents a forward proxy connection.
  89. type Conn struct {
  90. net.Conn
  91. boundAddr net.Addr
  92. }
  93. // BoundAddr returns the address assigned by the proxy server for
  94. // connecting to the command target address from the proxy server.
  95. func (c *Conn) BoundAddr() net.Addr {
  96. if c == nil {
  97. return nil
  98. }
  99. return c.boundAddr
  100. }
  101. // A Dialer holds SOCKS-specific options.
  102. type Dialer struct {
  103. cmd Command // either CmdConnect or cmdBind
  104. proxyNetwork string // network between a proxy server and a client
  105. proxyAddress string // proxy server address
  106. // ProxyDial specifies the optional dial function for
  107. // establishing the transport connection.
  108. ProxyDial func(context.Context, string, string) (net.Conn, error)
  109. // AuthMethods specifies the list of request authentication
  110. // methods.
  111. // If empty, SOCKS client requests only AuthMethodNotRequired.
  112. AuthMethods []AuthMethod
  113. // Authenticate specifies the optional authentication
  114. // function. It must be non-nil when AuthMethods is not empty.
  115. // It must return an error when the authentication is failed.
  116. Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
  117. }
  118. // DialContext connects to the provided address on the provided
  119. // network.
  120. //
  121. // The returned error value may be a net.OpError. When the Op field of
  122. // net.OpError contains "socks", the Source field contains a proxy
  123. // server address and the Addr field contains a command target
  124. // address.
  125. //
  126. // See func Dial of the net package of standard library for a
  127. // description of the network and address parameters.
  128. func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
  129. if err := d.validateTarget(network, address); err != nil {
  130. proxy, dst, _ := d.pathAddrs(address)
  131. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  132. }
  133. if ctx == nil {
  134. proxy, dst, _ := d.pathAddrs(address)
  135. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
  136. }
  137. var err error
  138. var c net.Conn
  139. if d.ProxyDial != nil {
  140. c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
  141. } else {
  142. var dd net.Dialer
  143. c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
  144. }
  145. if err != nil {
  146. proxy, dst, _ := d.pathAddrs(address)
  147. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  148. }
  149. a, err := d.connect(ctx, c, address)
  150. if err != nil {
  151. c.Close()
  152. proxy, dst, _ := d.pathAddrs(address)
  153. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  154. }
  155. return &Conn{Conn: c, boundAddr: a}, nil
  156. }
  157. // DialWithConn initiates a connection from SOCKS server to the target
  158. // network and address using the connection c that is already
  159. // connected to the SOCKS server.
  160. //
  161. // It returns the connection's local address assigned by the SOCKS
  162. // server.
  163. func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
  164. if err := d.validateTarget(network, address); err != nil {
  165. proxy, dst, _ := d.pathAddrs(address)
  166. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  167. }
  168. if ctx == nil {
  169. proxy, dst, _ := d.pathAddrs(address)
  170. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
  171. }
  172. a, err := d.connect(ctx, c, address)
  173. if err != nil {
  174. proxy, dst, _ := d.pathAddrs(address)
  175. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  176. }
  177. return a, nil
  178. }
  179. // Dial connects to the provided address on the provided network.
  180. //
  181. // Unlike DialContext, it returns a raw transport connection instead
  182. // of a forward proxy connection.
  183. //
  184. // Deprecated: Use DialContext or DialWithConn instead.
  185. func (d *Dialer) Dial(network, address string) (net.Conn, error) {
  186. if err := d.validateTarget(network, address); err != nil {
  187. proxy, dst, _ := d.pathAddrs(address)
  188. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  189. }
  190. var err error
  191. var c net.Conn
  192. if d.ProxyDial != nil {
  193. c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
  194. } else {
  195. c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
  196. }
  197. if err != nil {
  198. proxy, dst, _ := d.pathAddrs(address)
  199. return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
  200. }
  201. if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
  202. c.Close()
  203. return nil, err
  204. }
  205. return c, nil
  206. }
  207. func (d *Dialer) validateTarget(network, address string) error {
  208. switch network {
  209. case "tcp", "tcp6", "tcp4":
  210. default:
  211. return errors.New("network not implemented")
  212. }
  213. switch d.cmd {
  214. case CmdConnect, cmdBind:
  215. default:
  216. return errors.New("command not implemented")
  217. }
  218. return nil
  219. }
  220. func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
  221. for i, s := range []string{d.proxyAddress, address} {
  222. host, port, err := splitHostPort(s)
  223. if err != nil {
  224. return nil, nil, err
  225. }
  226. a := &Addr{Port: port}
  227. a.IP = net.ParseIP(host)
  228. if a.IP == nil {
  229. a.Name = host
  230. }
  231. if i == 0 {
  232. proxy = a
  233. } else {
  234. dst = a
  235. }
  236. }
  237. return
  238. }
  239. // NewDialer returns a new Dialer that dials through the provided
  240. // proxy server's network and address.
  241. func NewDialer(network, address string) *Dialer {
  242. return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
  243. }
  244. const (
  245. authUsernamePasswordVersion = 0x01
  246. authStatusSucceeded = 0x00
  247. )
  248. // UsernamePassword are the credentials for the username/password
  249. // authentication method.
  250. type UsernamePassword struct {
  251. Username string
  252. Password string
  253. }
  254. // Authenticate authenticates a pair of username and password with the
  255. // proxy server.
  256. func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
  257. switch auth {
  258. case AuthMethodNotRequired:
  259. return nil
  260. case AuthMethodUsernamePassword:
  261. if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
  262. return errors.New("invalid username/password")
  263. }
  264. b := []byte{authUsernamePasswordVersion}
  265. b = append(b, byte(len(up.Username)))
  266. b = append(b, up.Username...)
  267. b = append(b, byte(len(up.Password)))
  268. b = append(b, up.Password...)
  269. // TODO(mikio): handle IO deadlines and cancelation if
  270. // necessary
  271. if _, err := rw.Write(b); err != nil {
  272. return err
  273. }
  274. if _, err := io.ReadFull(rw, b[:2]); err != nil {
  275. return err
  276. }
  277. if b[0] != authUsernamePasswordVersion {
  278. return errors.New("invalid username/password version")
  279. }
  280. if b[1] != authStatusSucceeded {
  281. return errors.New("username/password authentication failed")
  282. }
  283. return nil
  284. }
  285. return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
  286. }
上海开阖软件有限公司 沪ICP备12045867号-1