这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
今天学习了如何使用Go语言实现实现了socks5协议的服务器的搭建,下面就让我们一起来看看吧
什么是socks5协议?
简单来说,socks5协议可以在客户端和服务端之间担当中间联系人这个角色,它可以使某些位于内网中的客户端通过socks5服务器与外网进行数据交换,其本质是客户端与socks5服务器使用TCP|IP协议进行连接,客户端向socks5服务器发送请求,socks5服务器将请求发送给对应服务器请求资源,接收到数据后再发送给客户端,这样就实现了代理服务器的功能,而客户端到底请求什么数据,这些socks5服务器并不关心
具体实现思路
首先需要让服务器启动后监听一个端口,监听到客户端发送的请求后再进行下一步处理
func main() {
server, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
panic(err)
}
for {
client, err2 := server.Accept()
if err2 != nil {
fmt.Printf("Accept failed%v", err2)
continue
}
go process(client)
}
首先是客户端发送认证请求,请求中应该包含版本号、请求方法的数目、请求方法的信息,服务器接受认证请求,选择提供的可用的相应方式响应请求,如果没有可用的请求方法,则断开与客户端的连接
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
ver, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver failed:%w", err)
}
if ver != socks5Ver {
return fmt.Errorf("not support ver :%v", ver)
}
methodsize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodsize failed:%w", err)
}
method := make([]byte, methodsize)
_, err = io.ReadFull(reader, method)
if err != nil {
return fmt.Errorf("read method failed:%w", err)
}
_, err = conn.Write([]byte{socks5Ver, 0x00})
if err != nil {
return fmt.Errorf("write back failed:%w", err)
}
return nil
}
socks5服务器响应后,客户带应该再次发送代理请求,服务器根据客户端发送的请求来确定资源地址类型,使用net.Get()来建立与远程服务器的连接,并使用同时开两个协程,使用通过io.Copy()函数来互相传递客户端与服务器端的信息实现代理的功能,在客户端请求远程服务器资源时,服务器充当中介,来传递请求与回应,socks5服务器对于 传递的信息并不感兴趣
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
ver, cmd, atyp := buf[0], buf[1], buf[3]
if ver != socks5Ver {
return fmt.Errorf("not unspoort ver:%v", ver)
}
if cmd != cmdBind {
return fmt.Errorf("not support cmd:%v", cmdBind)
}
addr := ""
switch atyp {
case atypIPV4:
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read atypIPV4 failed:%w", err)
}
addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
case atypeHOST:
hostsize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read hostsize failed:%w", err)
}
host := make([]byte, hostsize)
_, err = io.ReadFull(reader, host)
if err != nil {
return fmt.Errorf("read atypHost failed:%w", err)
}
addr = string(host)
case atypeIPV6:
return errors.New("IPV6: not support yet")
default:
return errors.New("invalid atyp")
}
_, err = io.ReadFull(reader, buf[:2])
if err != nil {
return fmt.Errorf("read ports failed:%w", err)
}
port := binary.BigEndian.Uint16(buf[:2])
log.Println("Dial ", addr, port)
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
if err != nil {
return fmt.Errorf("write back failed:%w", err)
}
dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
if err != nil {
return fmt.Errorf("dail failed :%w", err)
}
defer dest.Close()
ctx, cancle := context.WithCancel(context.Background())
defer cancle()
go func() {
_, _ = io.Copy(dest, reader)
cancle()
}()
go func() {
_, _ = io.Copy(conn, dest)
cancle()
}()
<-ctx.Done()
return nil
}
总的流程再梳理一下,socks5服务器在与客户端完成TCP协议三次握手后,建立连接,客户端再向socks5服务器发送认证请求以确认完成代理,确认完成后,客户端再发送代理请求,包含请求的资源地址,服务器响应代理请求并解析地址后向目标服务器发送连接请求后,在socks5服务器中建立双向数据传输通道,socks5服务器只是一个中介,这还可以增强通信的安全性 最后我们实现process函数,在函数中先实现auth()函数处理认证请求,再用connect()函数实现请求资源与双向资源传输
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed %v", conn.RemoteAddr(), err)
return
}
log.Println("auth success")
err = connect(reader, conn)
if err != nil {
log.Println("client %v connect failed %v", conn.RemoteAddr(), err)
}
log.Println("connect success")
}
这样,socks5协议实现的服务器就完成了