完整实现流程

启动一个服务

  1. ln, err := net.Listen("tcp", ":8888")
  2. if err != nil {
  3. fmt.Println("Server listen fail")
  4. return
  5. }
  6. for {
  7. sc, err := ln.Accept()
  8. if err != nil {
  9. fmt.Println("Server accept fail")
  10. continue
  11. }
  12. go handleServer(sc)
  13. }

解析请求流程

确认版本和认证方式

服务端会接收到三个字节依次表示 VER、NMETHODS、METHODS,在得到连接请求时,需要响应客户端 VER 和采用的 METHOD,此处响应 5、0 表示无需认证

  1. buf := make([]byte, 1024)
  2. // read VER, NMETHODS, METHODS
  3. if _, err := io.ReadFull(sc, buf[:2]); err != nil {
  4. return
  5. }
  6. nMethods := buf[1]
  7. if _, err := io.ReadFull(sc, buf[:nMethods]); err != nil {
  8. return
  9. }
  10. // write VER METHOD
  11. if _, err := sc.Write([]byte{5, 0}); err != nil {
  12. return
  13. }

建立连接

客户端建立连接时会带上 VER、CMD、RSV、ATYP、DST.ADDR、DST.PORT(详见 socks5 协议原理),服务端需要响应客户端连接成功

  1. // read VER CMD RSV ATYP DST.ADDR DST.PORT
  2. if _, err := io.ReadFull(sc, buf[:3]); err != nil {
  3. return
  4. }
  5. // reply success
  6. _, _ = sc.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0})

转发请求

首先需要从请求里面解析出 remoteAddr,

  1. b := make(byte[], 1024)
  2. _, err := io.ReadFull(r, b[:1])
  3. if err != nil {
  4. return "", err
  5. }
  6. host, port := "", ""
  7. switch b[0] {
  8. case 3:
  9. _, err = io.ReadFull(r, b[1:2])
  10. if err != nil {
  11. return "", err
  12. }
  13. _, err = io.ReadFull(r, b[2:2+int(b[1])+2])
  14. host = string(b[2 : 2+int(b[1])])
  15. port = strconv.Itoa((int(b[2+int(b[1])]) << 8) | int(b[2+int(b[1])+1]))
  16. case 1:
  17. _, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
  18. host = net.IP(b[1 : 1+net.IPv4len]).String()
  19. port = strconv.Itoa((int(b[1+net.IPv4len]) << 8) | int(b[1+net.IPv4len+1]))
  20. case 4:
  21. _, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
  22. host = net.IP(b[1 : 1+net.IPv6len]).String()
  23. port = strconv.Itoa((int(b[1+net.IPv6len]) << 8) | int(b[1+net.IPv6len+1]))
  24. default:
  25. return "", errors.New("invalid aType")
  26. }
  27. address := net.JoinHostPort(host, port)
  28. return address, nil

然后跟 remoteAddr 建立连接

  1. rc, err := net.Dial("tcp", addr)
  2. if err != nil {
  3. fmt.Println("server dial fail")
  4. return
  5. }
  6. defer rc.Close()

最后中转客户端和远端 remote 之间交互的字节流

  1. go func() {
  2. io.Copy(right, left)
  3. }()
  4. io.Copy(left, right)

完整代码

  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "net"
  7. "strconv"
  8. )
  9. func main() {
  10. ln, err := net.Listen("tcp", ":8888")
  11. if err != nil {
  12. fmt.Println("Server listen fail")
  13. return
  14. }
  15. for {
  16. sc, err := ln.Accept()
  17. if err != nil {
  18. fmt.Println("Server accept fail")
  19. continue
  20. }
  21. go handleServer(sc)
  22. }
  23. }
  24. func handleServer(sc net.Conn) {
  25. defer sc.Close()
  26. buf := make([]byte, 1024)
  27. // read VER, NMETHODS, METHODS
  28. if _, err := io.ReadFull(sc, buf[:2]); err != nil {
  29. return
  30. }
  31. nmethods := buf[1]
  32. if _, err := io.ReadFull(sc, buf[:nmethods]); err != nil {
  33. return
  34. }
  35. // write VER METHOD
  36. if _, err := sc.Write([]byte{5, 0}); err != nil {
  37. return
  38. }
  39. // read VER CMD RSV ATYP DST.ADDR DST.PORT
  40. if _, err := io.ReadFull(sc, buf[:3]); err != nil {
  41. return
  42. }
  43. _, _ = sc.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0})
  44. b := make([]byte, 1024)
  45. addr, err := ReadAddr(sc, b)
  46. if err != nil {
  47. return
  48. }
  49. rc, err := net.Dial("tcp", addr)
  50. if err != nil {
  51. fmt.Println("server dial fail")
  52. return
  53. }
  54. defer rc.Close()
  55. if err = relay(sc, rc); err != nil {
  56. fmt.Printf("relay error: %v", err)
  57. return
  58. }
  59. }
  60. func ReadAddr(r io.Reader, b []byte) (string, error) {
  61. _, err := io.ReadFull(r, b[:1])
  62. if err != nil {
  63. return "", err
  64. }
  65. host, port := "", ""
  66. switch b[0] {
  67. case 3:
  68. _, err = io.ReadFull(r, b[1:2])
  69. if err != nil {
  70. return "", err
  71. }
  72. _, err = io.ReadFull(r, b[2:2+int(b[1])+2])
  73. host = string(b[2 : 2+int(b[1])])
  74. port = strconv.Itoa((int(b[2+int(b[1])]) << 8) | int(b[2+int(b[1])+1]))
  75. case 1:
  76. _, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
  77. host = net.IP(b[1 : 1+net.IPv4len]).String()
  78. port = strconv.Itoa((int(b[1+net.IPv4len]) << 8) | int(b[1+net.IPv4len+1]))
  79. case 4:
  80. _, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
  81. host = net.IP(b[1 : 1+net.IPv6len]).String()
  82. port = strconv.Itoa((int(b[1+net.IPv6len]) << 8) | int(b[1+net.IPv6len+1]))
  83. default:
  84. return "", errors.New("invalid aType")
  85. }
  86. address := net.JoinHostPort(host, port)
  87. return address, nil
  88. }
  89. func relay(left, right net.Conn) error {
  90. go func() {
  91. io.Copy(right, left)
  92. }()
  93. io.Copy(left, right)
  94. return nil
  95. }

上述代码运行成功之后,配合浏览器的插件 SwitchyOmega 即可实现代理