A SOCKS (SOCKS4, SOCKS4A and SOCKS5) Proxy Package for Go
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.

219 lines
5.4 KiB

  1. // Copyright 2012, Hailiang Wang. 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. /*
  5. Package socks implements a SOCKS (SOCKS4, SOCKS4A and SOCKS5) proxy client.
  6. A complete example using this package:
  7. package main
  8. import (
  9. "code.as/core/socks"
  10. "fmt"
  11. "net/http"
  12. "io/ioutil"
  13. )
  14. func main() {
  15. dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:1080")
  16. tr := &http.Transport{Dial: dialSocksProxy}
  17. httpClient := &http.Client{Transport: tr}
  18. bodyText, err := TestHttpsGet(httpClient, "https://h12.io/about")
  19. if err != nil {
  20. fmt.Println(err.Error())
  21. }
  22. fmt.Print(bodyText)
  23. }
  24. func TestHttpsGet(c *http.Client, url string) (bodyText string, err error) {
  25. resp, err := c.Get(url)
  26. if err != nil { return }
  27. defer resp.Body.Close()
  28. body, err := ioutil.ReadAll(resp.Body)
  29. if err != nil { return }
  30. bodyText = string(body)
  31. return
  32. }
  33. */
  34. package socks
  35. import (
  36. "errors"
  37. "fmt"
  38. "net"
  39. "strconv"
  40. )
  41. // Constants to choose which version of SOCKS protocol to use.
  42. const (
  43. SOCKS4 = iota
  44. SOCKS4A
  45. SOCKS5
  46. )
  47. // DialSocksProxy returns the dial function to be used in http.Transport object.
  48. // Argument socksType should be one of SOCKS4, SOCKS4A and SOCKS5.
  49. // Argument proxy should be in this format "127.0.0.1:1080".
  50. func DialSocksProxy(socksType int, proxy string) func(string, string) (net.Conn, error) {
  51. if socksType == SOCKS5 {
  52. return func(_, targetAddr string) (conn net.Conn, err error) {
  53. return dialSocks5(proxy, targetAddr)
  54. }
  55. }
  56. // SOCKS4, SOCKS4A
  57. return func(_, targetAddr string) (conn net.Conn, err error) {
  58. return dialSocks4(socksType, proxy, targetAddr)
  59. }
  60. }
  61. func dialSocks5(proxy, targetAddr string) (conn net.Conn, err error) {
  62. // dial TCP
  63. conn, err = net.Dial("tcp", proxy)
  64. if err != nil {
  65. return
  66. }
  67. // version identifier/method selection request
  68. req := []byte{
  69. 5, // version number
  70. 1, // number of methods
  71. 0, // method 0: no authentication (only anonymous access supported for now)
  72. }
  73. resp, err := sendReceive(conn, req)
  74. if err != nil {
  75. return
  76. } else if len(resp) != 2 {
  77. err = errors.New("Server does not respond properly.")
  78. return
  79. } else if resp[0] != 5 {
  80. err = errors.New("Server does not support Socks 5.")
  81. return
  82. } else if resp[1] != 0 { // no auth
  83. err = errors.New("socks method negotiation failed.")
  84. return
  85. }
  86. // detail request
  87. host, port, err := splitHostPort(targetAddr)
  88. req = []byte{
  89. 5, // version number
  90. 1, // connect command
  91. 0, // reserved, must be zero
  92. 3, // address type, 3 means domain name
  93. byte(len(host)), // address length
  94. }
  95. req = append(req, []byte(host)...)
  96. req = append(req, []byte{
  97. byte(port >> 8), // higher byte of destination port
  98. byte(port), // lower byte of destination port (big endian)
  99. }...)
  100. resp, err = sendReceive(conn, req)
  101. if err != nil {
  102. return
  103. } else if len(resp) != 10 {
  104. err = errors.New("Server does not respond properly.")
  105. } else if resp[1] != 0 {
  106. err = errors.New("Can't complete SOCKS5 connection.")
  107. }
  108. return
  109. }
  110. func dialSocks4(socksType int, proxy, targetAddr string) (conn net.Conn, err error) {
  111. // dial TCP
  112. conn, err = net.Dial("tcp", proxy)
  113. if err != nil {
  114. return
  115. }
  116. // connection request
  117. host, port, err := splitHostPort(targetAddr)
  118. if err != nil {
  119. return
  120. }
  121. ip := net.IPv4(0, 0, 0, 1).To4()
  122. if socksType == SOCKS4 {
  123. ip, err = lookupIP(host)
  124. if err != nil {
  125. return
  126. }
  127. }
  128. req := []byte{
  129. 4, // version number
  130. 1, // command CONNECT
  131. byte(port >> 8), // higher byte of destination port
  132. byte(port), // lower byte of destination port (big endian)
  133. ip[0], ip[1], ip[2], ip[3], // special invalid IP address to indicate the host name is provided
  134. 0, // user id is empty, anonymous proxy only
  135. }
  136. if socksType == SOCKS4A {
  137. req = append(req, []byte(host+"\x00")...)
  138. }
  139. resp, err := sendReceive(conn, req)
  140. if err != nil {
  141. return
  142. } else if len(resp) != 8 {
  143. err = errors.New("Server does not respond properly.")
  144. return
  145. }
  146. switch resp[1] {
  147. case 90:
  148. // request granted
  149. case 91:
  150. err = errors.New("Socks connection request rejected or failed.")
  151. case 92:
  152. err = errors.New("Socks connection request rejected becasue SOCKS server cannot connect to identd on the client.")
  153. case 93:
  154. err = errors.New("Socks connection request rejected because the client program and identd report different user-ids.")
  155. default:
  156. err = errors.New("Socks connection request failed, unknown error.")
  157. }
  158. return
  159. }
  160. func sendReceive(conn net.Conn, req []byte) (resp []byte, err error) {
  161. _, err = conn.Write(req)
  162. if err != nil {
  163. return
  164. }
  165. resp, err = readAll(conn)
  166. return
  167. }
  168. func readAll(conn net.Conn) (resp []byte, err error) {
  169. resp = make([]byte, 1024)
  170. n, err := conn.Read(resp)
  171. resp = resp[:n]
  172. return
  173. }
  174. func lookupIP(host string) (ip net.IP, err error) {
  175. ips, err := net.LookupIP(host)
  176. if err != nil {
  177. return
  178. }
  179. if len(ips) == 0 {
  180. err = errors.New(fmt.Sprintf("Cannot resolve host: %s.", host))
  181. return
  182. }
  183. ip = ips[0].To4()
  184. if len(ip) != net.IPv4len {
  185. fmt.Println(len(ip), ip)
  186. err = errors.New("IPv6 is not supported by SOCKS4.")
  187. return
  188. }
  189. return
  190. }
  191. func splitHostPort(addr string) (host string, port uint16, err error) {
  192. host, portStr, err := net.SplitHostPort(addr)
  193. portInt, err := strconv.ParseUint(portStr, 10, 16)
  194. port = uint16(portInt)
  195. return
  196. }