当编写一个SOCKS5代理服务器时,需要考虑多个步骤,包括初始化、握手、连接请求和数据传输:
-
初始化: 在主函数中,创建一个TCP监听器,以侦听客户端的连接请求。
-
握手阶段: 当客户端连接到代理服务器时,首先进行SOCKS5协议的握手阶段。这包括以下步骤:
- 读取客户端发送的握手版本和支持的认证方法列表。
- 选择一个支持的认证方法并发送回客户端,表明无需认证。
-
连接请求: 客户端与代理服务器完成握手后,客户端会发送连接请求。这包括以下步骤:
- 读取客户端发送的连接请求版本、命令(通常为0x01表示CONNECT)、保留字段、地址类型(IPv4、IPv6、域名)以及目标地址和端口。
- 解析目标地址和端口,根据地址类型连接目标主机。
-
与目标主机连接: 根据客户端发送的目标地址和端口,代理服务器与目标主机建立连接。
-
确认连接: 代理服务器要向客户端发送连接已建立的确认,以告知客户端连接已成功建立。
-
数据传输: 在确认连接之后,开始在客户端与目标主机之间传输数据。这包括以下步骤:
- 从客户端读取数据,并将其转发到目标主机。
- 从目标主机读取数据,并将其转发回客户端。
-
错误处理: 在每个阶段都需要考虑错误处理,包括连接错误、读写错误等。在出现错误时,应关闭连接以确保资源被正确释放。
-
并发支持: 为了同时处理多个客户端连接,可以使用并发。每当有新的客户端连接时,可以在单独的goroutine中处理该连接,以防止阻塞主循环。
package main
import (
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
// Read and respond to the initial SOCKS5 handshake
version := make([]byte, 1)
conn.Read(version)
nMethods := make([]byte, 1)
conn.Read(nMethods)
methods := make([]byte, nMethods[0])
conn.Read(methods)
conn.Write([]byte{0x05, 0x00})
// Read and respond to the connection request
reqHeader := make([]byte, 4)
conn.Read(reqHeader)
if reqHeader[0] != 0x05 || reqHeader[1] != 0x01 || reqHeader[2] != 0x00 {
fmt.Println("Invalid SOCKS5 request")
return
}
addrType := reqHeader[3]
var destAddr string
switch addrType {
case 0x01: // IPv4
ip := make([]byte, 4)
conn.Read(ip)
destAddr = net.IP(ip).String()
case 0x03: // Domain name
domainLen := make([]byte, 1)
conn.Read(domainLen)
domain := make([]byte, domainLen[0])
conn.Read(domain)
destAddr = string(domain)
case 0x04: // IPv6
ipv6 := make([]byte, 16)
conn.Read(ipv6)
destAddr = net.IP(ipv6).String()
default:
fmt.Println("Unsupported address type")
return
}
portBytes := make([]byte, 2)
conn.Read(portBytes)
destPort := int(portBytes[0])<<8 | int(portBytes[1])
// Establish connection to the destination
destConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", destAddr, destPort))
if err != nil {
fmt.Println("Error connecting to destination:", err)
return
}
defer destConn.Close()
// Respond to the client that the connection is established
conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
// Start forwarding data between the client and the destination
go func() {
_, err := conn.Copy(destConn)
if err != nil {
fmt.Println("Error copying data:", err)
}
}()
_, err = destConn.Copy(conn)
if err != nil {
fmt.Println("Error copying data:", err)
}
}
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
fmt.Println("Error starting server:", err)
return
}
defer listener.Close()
fmt.Println("SOCKS5 Proxy server is listening on 127.0.0.1:1080")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go handleConnection(conn)
}
}