GO语言学习记录之二:Socks5代理 | 青训营笔记

83 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

Socks5简介

SOCKS是一种网络传输协议,为Socket Secure的缩写,主要用于客户端与外网服务器之间通讯的中间传递。它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器。

这篇文章主要来介绍如何通过GO语言来完成一个简单的Socks5代理服务器。

Socks5代理

用GO语言来完成Socks5主要用到net模块的内容,同时要求你了解在socks5协议的一些消息格式。

根据以下流程图,在代理通信过程中,主要包含三个步骤:

  • 代理服务器和客户端Client进行认证
  • 代理服务器获取目标Server的信息,建立TCP连接
  • 代理服务器作为中间角色,互相传送消息

Socks5 Proxy.png

认证阶段

客户端向代理服务器发送认证消息,格式为:

VERNMMETHODSMETHODS
111 to 255
  • VER: 协议版本,socks5为0x05
  • NMETHODS:METHODS部分的长度
  • METHODS: METHODS是客户端支持的认证方式列表,每个方法占1字节。当前的定义是
    • 0x01 GSSAPI
    • 0x02 用户名、密码认证
    • 0x03 - 0x7F由IANA分配(保留)
    • 0x80 - 0xFE为私人方法保留
    • 0xFF 无可接受的方法

于是就需要这样读取以下信息:

ver, err := reader.ReadByte()

methodSize, err := reader.ReadByte()

method := make([]byte, methodSize)
_, err = io.ReadFull(reader, method)

接下来代理服务器向客户端返回认证成功消息,格式为

VERMETHOD
11

我们这里VER返回socks的协议版本,METHOD返回0,见上面的说明。

_, err = conn.Write([]byte{socks5Ver, 0x00})

连接阶段

连接阶段需要获取客户端发过来的关于目标Server的信息,消息格式为:

VERCMDRSVATYPDST.ADDRDST.PORT
11X'00'1Variable2
  • VER 版本号,socks5的值为0x05
  • CMD 0x01表示CONNECT请求
  • RSV 保留字段,值为0x00
  • ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
    • 0x01 IPv4地址,DST.ADDR部分4字节长度
    • 0x03 域名,DST.ADDR部分第一个字节为域名长度,DST.ADDR剩余的内容为域名,没有\0结尾。
    • 0x04 IPv6地址,16个字节长度。
  • DST.ADDR 一个可变长度的值
  • DST.PORT 目标端口,固定2个字节

代理服务器解析消息内容,并根据atyp获取目标服务的地址和端口:

buf := make([]byte, 4)
_, err := io.ReadFull(reader, buf)
ver, cmd, atyp := buf[0], buf[1], buf[3]

// ...

解析后,代理服务器就可以向DST发起TCP连接,这里借助net模块即可完成

dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))

发起TCP连接成功后,代理服务器需要返回给客户端一个同样的如上的消息格式,这里可以正常根据上面的IP返回,也可以简单处理返回一下:

_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})

通信阶段

互发消息就可以了,不需要再解析消息格式了。

go func() {
        _, _ = io.Copy(dest, conn)
        cancel()
}()

go func() {
        _, _ = io.Copy(conn, dest)
        cancel()
}()

<-ctx.Done()

代理检验

你可以借助curl命令或SwitchyOmega这一个浏览器插件来启动一个socks5客户端进行检测,可模仿如下配置:

image.png

相关文档