• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Go学习笔记——SOCKS5代理服务器|青训营

武飞扬头像
枳幕
帮助9

引言

在网络通信中,代理服务器扮演着重要角色。SOCKS5代理是一种常用的网络代理协议,它可以在应用层与传输层之间转发TCP/UDP流量,实现用户在局域网或公网之间的匿名访问和数据传输。本文将介绍如何使用Go语言实现一个简单的SOCKS5代理服务器。

技术栈

本项目将使用以下技术栈进行实现:

  • Go语言:Go语言提供了强大的标准库和并发模型,适合构建高性能的网络应用程序。
  • 网络编程:使用Go语言标准库中的net包进行网络通信和处理。
  • 协议解析:通过解析SOCKS5代理协议实现代理服务器与客户端之间的交互。
  • 配置管理:使用Go语言的配置库,实现代理服务器的配置文件管理。

项目实现步骤

【原理】

  • 了解一下 socks5 协议的工作原理。正常浏览器访问一个网站,如果不经过代理服务器的话,就是先和对方的网站建立 TCP 连接,然后三次握手,握手完之后发起 HTTP 请求,然后服务返回 HTTP 响应。如果设置代理服务器之后,流程会变得复杂一些。
  • 首先是浏览器和 socks5 代理建立 TCP 连接,代理再和真正的服务器建立 TCP 连接。

这里可以分成四个阶段

1. 握手阶段
graph LR
    A[浏览器] -->|建立TCP连接| B[Socks5代理服务器]
    B -->|发送初始请求| C{握手阶段}
    C -->|准备建立代理连接| B
    B -->|回复确定| A
  • 浏览器与Socks5代理服务器建立TCP连接。
  • 浏览器发送一个初始请求给代理服务器,请求建立Socks5代理连接。
  • 代理服务器回复客户端,表示准备好建立代理连接。
  • 握手阶段完成。

创建TCP ehco server,代码如下

package main

import (
	"bufio"
	"log"
	"net"
)

func main() {
	// 监听地址和端口
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil {
		panic(err)
	}

	for {
		// 接受客户端连接
		client, err := server.Accept()
		if err != nil {
			log.Printf("Accept failed %v", err)
			continue
		}
		// 启动协程处理客户端连接
		go process(client)
	}
}

func process(conn net.Conn) {
	// 处理完连接后关闭连接
	defer conn.Close()

	reader := bufio.NewReader(conn)
	for {
		// 读取客户端发送的数据
		b, err := reader.ReadByte()
		if err != nil {
			break
		}

		// 将读取到的数据写入到客户端连接中,实现中继功能
		_, err = conn.Write([]byte{b})
		if err != nil {
			break
		}
	}
}

  • 在终端输入nc命令:
nc 127.0.0.1 1080

当我们输入hello 代理服务器会返回输入的值

2. 认证阶段
graph LR
    A[浏览器] -->|可选认证方法请求| B[Socks5代理服务器]
    B -->|选择一种认证方法| A
    A -->|发送用户名和密码| B
    B -->|验证身份成功| C{认证阶段完成}
  • 如果在握手阶段,Socks5代理服务器要求进行身份验证,则浏览器发送包含认证方法的请求给代理服务器。
  • 代理服务器从浏览器发送的认证方法列表中选择一种方法,并回复浏览器确认的方法。
  • 如果方法需要进行用户名和密码认证,则浏览器发送用户名和密码给代理服务器。
  • 代理服务器验证身份信息,如果认证成功,则认证阶段完成。
func auth(reader *bufio.Reader, conn net.Conn) (err error) {
	// 读取协议版本(VER)
	//  ---- ---------- ---------- 
	// |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 failed:%w", err)
	}
	// 检查协议版本是否为 SOCKS5
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}

	// 读取支持的认证方法数(NMETHODS)
	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)
	}
	log.Println("ver", ver, "method", method)

	// 回复客户端的协议版本和认证方法(VER, METHOD)
	//  ---- -------- 
	// |VER | METHOD |
	//  ---- -------- 
	// | 1  |   1    |
	//  ---- -------- 
	_, err = conn.Write([]byte{socks5Ver, 0x00})
	if err != nil {
		return fmt.Errorf("write failed:%w", err)
	}

	return nil
}
3. 请求阶段
graph LR
    A[浏览器] -->|发送请求| B[Socks5代理服务器]
    B -->|建立与目标网站TCP连接| C[目标网站]
    B -->|转发请求| C
    C -->|发送响应| B
    B -->|返回响应给浏览器| A
  • 浏览器发送一个请求给代理服务器,该请求指定要访问的目标网站的地址和端口号。
  • 代理服务器接收到请求后,与目标网站建立TCP连接。
  • 代理服务器向目标网站发送请求,并将目标网站的响应返回给浏览器。
  • 请求阶段完成后,浏览器将通过代理服务器获得目标网站返回的HTTP响应。
func connect(reader *bufio.Reader, conn net.Conn) (err error) {
	//  ---- ----- ------- ------ ---------- ---------- 
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	//  ---- ----- ------- ------ ---------- ---------- 
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	//  ---- ----- ------- ------ ---------- ---------- 
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节

	// 读取协议头部字段,包括协议版本(VER)、命令(CMD)、目标地址类型(ATYP)
	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]
	// 检查协议版本是否为 SOCKS5
	if ver != socks5Ver {
		return fmt.Errorf("not supported ver:%v", ver)
	}
	// 检查命令是否为 CONNECT 请求
	if cmd != cmdBind {
		return fmt.Errorf("not supported cmd:%v", cmd)
	}

	addr := ""
	switch atyp {
	case atypeIPV4:
		// 读取 IPv4 地址
		_, err = io.ReadFull(reader, buf)
		if err != nil {
			return fmt.Errorf("read atyp 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 host failed:%w", err)
		}
		addr = string(host)
	case atypeIPV6:
		// 返回不支持 IPv6的信息
		return errors.New("IPv6: no supported yet")
	default:
		return errors.New("invalid atyp")
	}

	// 读取目标端口
	_, err = io.ReadFull(reader, buf[:2])
	if err != nil {
		return fmt.Errorf("read port failed:%w", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])

	log.Println("dial", addr, port) //只是输出日志

	//  ---- ----- ------- ------ ---------- ---------- 
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	//  ---- ----- ------- ------ ---------- ---------- 
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	//  ---- ----- ------- ------ ---------- ---------- 
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT

	// 回复客户端,表示连接成功
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("write failed: %w", err)
	}

	return nil
}
4. 中继(relay)阶段
graph LR
    A[浏览器] -->|中继数据| B[Socks5代理服务器]
    B -->|转发数据| C[目标网站]
    C -->|转发响应| B
    B -->|持续中继数据| A
    A -->|关闭连接| B
    C -->|关闭连接| B
  • 一旦请求阶段成功,代理服务器开始在浏览器和目标网站之间中继数据。
  • 客户端发送的数据被代理服务器转发给目标网站,目标网站的响应被代理服务器转发给浏览器。
  • 中继阶段持续进行,直到浏览器或目标网站关闭连接或代理服务器检测到连接超时。

SwitchyOmega插件

  • 浏览器安装SwitchyOmega插件
  • 新建情景模式
  • 代理协议 SOCKS5 代理服务器 127.0.0.1 代理端口 1080
  • 运行编写的代码
  • 连接方式选择刚刚新建的情景模式,即可调用代理服务器

预期效果

通过完成该项目,读者可以了解SOCKS5代理协议的基本原理和实现方式,掌握Go语言网络编程的基础知识,同时也可以通过该项目的代码和实现思路,进行二次开发和扩展,实现更加复杂的代理功能。

学新通

完整代码

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhcfef
系列文章
更多 icon
同类精品
更多 icon
继续加载