Go实现socks5服务器

SOCKS5 是一个代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。

请添加图片描述
我们先来看一下最终写完的代理服务器的效果。我们启动这个程序,然后在浏览器里面配置使用这个代理,此时我们打开网页。
代理服务器的日志,会打印出你访问的网站的域名或者IP,这说明我们的网络流量是通过这个代理服务器的。我们也能在命令行去测试我们的代理服务器。我们可以用curl --socks5 +代理服务器地址,后面加一个可访问的URL,如果代理服务器工作正常的话,那么 curl命令就会正常返回。

socks5协议原理:

请添加图片描述

源代码:

package mainimport ("bufio""context""encoding/binary""errors""fmt""io""log""net"
)const socks5Ver = 0x05
const cmdBind = 0x01
const atypeIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04func 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)err := auth(reader, conn)if err != nil {log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)return}err = connect(reader, conn)if err != nil {log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)return}
}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// Read VER, NMETHODS, and METHODSver, err := reader.ReadByte()if err != nil {return fmt.Errorf("read ver failed:%w", err)}if ver != socks5Ver {return fmt.Errorf("not supported 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)}// +----+--------+// |VER | METHOD |// +----+--------+// | 1  |   1    |// +----+--------+// Write VER and METHOD_, err = conn.Write([]byte{socks5Ver, 0x00})if err != nil {return fmt.Errorf("write failed:%w", err)}return nil
}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个字节// Read VER, CMD, RSV, and ATYPbuf := 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 supported ver:%v", ver)}if cmd != cmdBind {return fmt.Errorf("not supported cmd:%v", cmd)}addr := ""switch atyp {case atypeIPV4:_, 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: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])dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))if err != nil {return fmt.Errorf("dial dst failed:%w", err)}defer dest.Close()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// Send response to client_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})if err != nil {return fmt.Errorf("write failed: %w", err)}ctx, cancel := context.WithCancel(context.Background())defer cancel()go func() {_, _ = io.Copy(dest, reader)cancel()}()go func() {_, _ = io.Copy(conn, dest)cancel()}()<-ctx.Done()return nil
}

解释:主函数开始监听在本地地址(127.0.0.1)的1080端口上,接受客户端的连接请求,并在新的goroutine中处理客户端连接。process函数用于处理客户端连接。首先关闭连接,然后创建一个bufio.Reader用于读取数据。接下来调用auth函数进行认证,然后调用connect函数进行连接。auth函数用于处理认证过程。根据SOCKS5协议规定的格式,从reader中读取协议版本和支持的认证方法,然后向客户端返回协议版本和选择的认证方法。这里只实现了不需要认证的方式。connect函数用于处理连接请求。根据SOCKS5协议规定的格式,从reader中读取版本、命令、地址类型和目标地址信息。根据地址类型的不同,解析出目标地址和端口号。然后建立与目标服务器的连接,并将连接成功的响应发送给客户端。接下来,使用goroutine并发地将客户端的数据复制到目标服务器和将目标服务器的数据复制回客户端,从而实现了数据的转发。最后,使用<-ctx.Done()阻塞等待上述goroutine的完成。

将这段代码在goland上运行,并在cmd命令行(管理员)中输入curl --socks5 127.0.0.1 1080 -v http://www.baidu.com以测试:
请添加图片描述

socks5服务器实现成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/20397.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web3 智能合约通过数组方式存储更多学生数据

之前的文章 Web3 在Truffle项目中编写出自己的第一个solidity智能合约带大家写了一个相对捡漏的智能合约 这样 每次 我们set 后 都会将原来的数据覆盖掉了 那么 有些人可能会想 那我们弄个数组 将新的数据全部加到数组里不就行了吗&#xff1f; 这个想法其实就很不错 我们可以…

智能汽车的主动悬架工作原理详述

摘要&#xff1a; 本文将详细介绍主动悬架功能原理设计。 主动悬架是车辆上的一种汽车悬架。它使用车载系统来控制车轮相对于底盘或车身的垂直运动&#xff0c;而不是由大弹簧提供的被动悬架&#xff0c;后者的运动完全由路面决定。主动悬架分为两类&#xff1a;真正的主动悬架…

OpenCV 入门教程:轮廓特征和轮廓匹配

OpenCV 入门教程&#xff1a;轮廓特征和轮廓匹配 导语一、轮廓特征二、轮廓匹配三、示例应用3.1 目标识别3.2 形状分析 总结 导语 轮廓特征和轮廓匹配是图像处理中用于描述和比较轮廓的技术。通过提取轮廓的形状、面积、周长等特征&#xff0c;并进行比较和匹配&#xff0c;我…

Android 系统的分区介绍

由于Android系统采用Linux架构&#xff0c;所以Android的系统分区可以类比同样采用Linux架构的操作系统&#xff08;如Windows&#xff09;。 Android系统分区分类 现在一般常见的Android分区方式共有三种&#xff0c;在不同的Android系统版本上会采用不同的分区方式。 1、传…

应用级监控方案Spring Boot Admin

1.简介 Spring Boot Admin为项目常用的监控方式&#xff0c;可以动态的监控服务是否运行和运行的参数&#xff0c;如类的调用情况、流量等。其中分为server与client&#xff1a; server&#xff1a; 提供展示UI与监控服务。client&#xff1a;加入server&#xff0c;被监控的…

遥感云大数据在灾害、水体与湿地领域案例实践及GPT【洪涝灾害、洪水敏感性和风险模拟、河道轮廓监测、地下水变化、红树林遥感制图】

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

换零钱II:Python代码解Java题目

银行现存零钱面值种类动态变化但数量无限&#xff0c;类方法change()完成指定金额的最少零钱个数兑换。 (本笔记适合学透python基本数据结构&#xff0c;熟悉class的基构造&#xff0c;对类内全局变量有一定认的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1…

从CPU缓存结构到原子操作

文章目录 一、CPU缓存结构1.1 CPU的多级缓存1.2 Cache Line 二、写回策略三、缓存一致性问题及解决方案3.1 缓存一致性问题3.2 解决方案3.2.1 总线嗅探3.2.2 事务的串行化3.2.3 MESI 四、原子操作4.1 什么是原子操作4.2 c 标准库的原子类型4.2.1 atomic<T\>4.2.2 is_lock…

这款语音同声翻译软件为你提供智能翻译体验

林娜&#xff1a;嘿&#xff0c;你知道吗&#xff1f;现在有很多语音翻译软件可以帮助我们进行实时的语言翻译&#xff01; 罗伟&#xff1a;真的吗&#xff1f;有什么语音翻译软件呢&#xff1f;能告诉我一些详情吗&#xff1f; 林娜&#xff1a;当然&#xff0c;接下来我将…

SpringBoot+JWT实现单点登录解决方案

一、什么是单点登录? 单点登录是一种统一认证和授权机制&#xff0c;指在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的系统&#xff0c;不需要重新登录验证。 单点登录一般用于互相授信的系统&#xff0c;实现单一位置登录&#xff0c;其他信任的…

AI热潮中的黑马:曾经的显卡制造商是如何跻身AI巨头之列的?

导读&#xff1a;在芯片行业的风云变幻中,英伟达如何从一家显卡制造商翻身成为AI巨头?面对行业迭代速度极快、激烈竞争和显著的高成本特点,英伟达是如何构建属于自己的护城河,又是如何突破技术瓶颈,跑在前列的呢?他们是如何将"摩尔定律"推进到更高阶段创造出属于自…

性能测试工具 Jmeter 测试 Dubbo 接口脚本编写

目录 前言&#xff1a; 1、背景 2、工具准备 3、创建一个 maven 项目&#xff0c;此处可以创建一个 quickstart&#xff0c;参考截图 4、以上配置完毕后&#xff0c;开始撸代码 5、上面那个类是不需要从 jmeter 中获取参数&#xff0c;如果要从 jmeter 中获取相关的参数&…