Socks5简介
Socks5是一个代理协议,在客户端和服务器之间扮演中间角色,支持TCP/UDP传输协议。
有以下功能和特点
- 支持IPv4和IPv6
- 用户验证
- 数据加密
- UPD转发
协议的交互过程
- PC发起访问服务器的请求
- Socks5客户端拦截请求,Socks5客户端主动跟Socks5代理服务器建立TCP连接。
- Socks5客户端跟代理服务器认证
- 认证通过后,Socks5代理服务器与服务器建立连接
- 请求建立连接后Socks5代理服务器与服务器进行数据交互
- Socks5代理服务器把数据通过socket转发给Socks5客户端
- 客户端将数据转发给PC
应用场景
- 绕过网络封锁
- 加速网络连接
- 绕过地理限制
- 企业内部网络
实战
搭建TCP echo server
TCP echo server 是一种基于TCP协议的服务器程序,主要功能是接受客户端发送的数据,并将数据原样返回给客户端,可以方便的检查客户端和服务端之间的通信是否正常.
创建一个网络端口
server,err :=net.Listen("tcp","127.0.0.1:1080")
if err !=nil{
panic(err)
}
panic()函数
panic()是Go语言中用于引发程序崩溃的函数,调用的好死后会导致程序正常的控制流程中断,立即开始执行清理工作(调用defer语句),然后退出当前的协程最终退出整个程序
恢复panic(recover())
在一些情况中可以使用recover()函数从panic中恢复,通常在defer中使用recover()来捕获panic,使得程序在发生错误后继续执行或执行一些必要的清理工作
defer func()
{
if r:=recover();r!nil{
fmt.Println("Recovered from panic",r)
}
}()
// 在下面写panic,程序遇到崩溃的时候会调用recover,
//捕捉到错误信息恢复程序运行,不影响之后的代码
调用服务器连接并接受请求
在大多数服务器程序的基本设计中需要用到死循环来保证服务器能够
- 持续监听和接受客户端连接
- 并发处理多个客户端的请求,避免阻塞
- 保持稳定运行,而不是在处理一个连接后退出
于是代码如下
for {
// 这里用server.Accept()去接受一个请求,如果成功的话会返回一个连接
client, err := server.Accept()
if err != nil {
log.Printf("Accept failed %v", err)
continue
}
go process(client)
}
这里的go会启动一个协程(goroutine)这个可以类比成子线程,但是这个比子线程占用要小
使得这个process函数在一个新的协程中执行,而不是主协程,能够让程序在并发环境中执行多个任务,这样服务器就能够同时服务多个客户端,从而实现并发处理大量客户端的需求
将请求中的数据读取
func process(conn net.Conn) {
// 这一行代码的意思是,在这个函数结束的时候,要把这个连接关闭
// 使得这个连接的生命周期就是这个函数的生命周期
defer conn.Close()
reader := bufio.NewReader(conn)
for {
// 用reader.ReadByte使得我们每次都读取一个字节
b, err := reader.ReadByte()
if err != nil {
break
}
// 然后用conn.Write来把字节写入
_, err = conn.Write([]byte{
b})
if err != nil {
break
}
}
}
bufio.NewReader()
这里调用bufio.NewReader创建一个只读带缓冲的流,这样比直接读取更加高效,能够减少读取次数减少对底层资源的调用,把数据批量的读取到缓存区,然后逐行,逐字节或按照固定大小提供,提高性能
这里代码中可能以为是一个一个字节读入会很慢,实际上在读第一个字节的时候就可能把后面1k的已经预读取了,后面调用就很快了
完整代码
package main
import (
"bufio"
"log"
"net"
)
// TCP echo server 是一种基于TCP协议的服务器程序,主要功能是接受客户端发送的数据,并将数据原样返回给客户端,
// 可以方便的检查客户端和服务端之间的通信是否正常
func main() {
// 用net.Listen来增加一个端口
server, err := net.Listen("tcp", "127.0.0.1:1080")
if err != nil {
panic(err)
}
for {
// 这里用server.Accept()去接受一个请求,如果成功的话会返回一个连接
client, err := server.Accept()
if err != nil {
log.Printf("Accept failed %v", err)
continue
}
// 然后调用这个process函数去处理这个连接
// 这里的go会启动一个协程(goroutine)这个可以类比成子线程,但是这个比子线程占用的要小
// 这样使得这个process函数在一个新的协程中执行,而不是主协程。
// 这种方法可以让程序在并发环境中异步执行多个任务
// 这样服务器就能同时服务多个客户端,从而实现并发处理大量客户端的需求
go process(client)
}
}
func process(conn net.Conn) {
// 这一行代码的意思是,在这个函数结束的时候,要把这个连接关闭
// 使得这个连接的生命周期就是这个函数的生命周期
defer conn.Close()
// 用bufio.NewReader创建一个只读带缓冲的的流,比直接读取更加高效
// 减少读取次数减少对底层资源的调用,把数据批量的读取到缓存区
// 然后逐行,逐字节或按照固定大小提供,提高性能
reader := bufio.NewReader(conn)
for {
// 用reader.ReadByte使得我们每次都读取一个字节
b, err := reader.ReadByte()
if err != nil {
break
}
// 然后用conn.Write来把字节写入
_, err = conn.Write([]byte{
b})
if err != nil {
break
}
}
}
启动
先打开一个终端指定到这个文件目录中
/goLearnProject$ go run tcpEchoServer.go
run后面的是你的文件名
然后打开另外一个终端同样指定到这个文件目录
/goLearnProject$ nc 127.0.0.1 1080
接下来在这个终端输入你想要发送的内容就可以了
他会自动返回你刚刚输入的内容
实现协议认证阶段
相关定义
const socks5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const stypeIPV6 = 0x04
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
修改process方法
我们刚刚的process方法是为了测试服务器与客户端之间的连接,现在要用来测试认证。
代码如下
func process(conn net.Conn) {
// 这一行代码的意思是,在这个函数结束的时候,要把这个连接关闭
// 使得这个连接的生命周期就是这个函数的生命周期
defer conn.Close()
// 用bufio.NewReader创建一个只读带缓冲的的流,比直接读取更加高效
// 减少读取次数减少对底层资源的调用,把数据批量的读取到缓存区
// 然后逐行,逐字节或按照固定大小提供,提高性能
reader := bufio.NewReader(conn)
// 调用 auth 函数执行 SOCKS5 的认证
err := auth(reader, conn)
if err != nil {
log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
return
}
log.Println("auth success")
}
这里调用auth函数来处理认证,进行数据字段的检测
auth方法
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
// +----+----------+----------+
// |VER | NMETHODS | METHODS |
// +----+----------+----------+
// | 1 | 1 | 1 to 255 |
// +----+----------+----------+
// VER: 协议版本,socks5为0x05
// NMETHODS: 支持认证的方法数量
// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
// X’00’ NO AUTHENTICATION REQUIRED
// X’02’ USERNAME/PASSWORD
ver, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read ver err:%v", err)
}
if ver != socks5Ver {