当前位置: 首页 > news >正文

go入门实践四-go实现一个简单的tcp-socks5代理服务

文章目录

    • 前言
    • socks协议简介
    • go实现一个简单的socks5代理
    • 运行与压测
    • 抓包验证

前言

SOCKS是一种网络传输协议,主要用于客户端与外网服务器之间通讯的中间传递。协议在应用层和传输层之间。

本文使用先了解socks协议。然后实现一个socks5的tcp代理服务端。最后,进行抓包验证。

本文完整代码见仓库:laboratory/16-go-socks5


socks协议简介

socks协议相对http和tcp协议,还是比较简单。当然,想要搞明白每个细节,也非一件容易的事情。

关于协议本身的介绍见:rfc1928、RFC 1928 - SOCKS 5 协议中文文档「译」

文档总是枯燥的,可以边看文档边看代码:实战:150行Go实现高性能socks5代理


go实现一个简单的socks5代理

了解协议后,我们来实现一个tcp的socks5服务端代理。

网上有很多这样的示例,见:socks - Search Results - Go Packages

本节的代码参考自:实战:150行Go实现高性能socks5代理 、Subsocks: 用 Go 实现一个 Socks5 安全代理 - Luyu Huang's Blog

完整代码见仓库,下面是主要的代码。

package socks5import ("bufio""encoding/binary""errors""fmt""go-socks5-demo/config""go-socks5-demo/utils""io""net""strconv"log "github.com/sirupsen/logrus"
)const SOCKS5VERSION uint8 = 5const (MethodNoAuth uint8 = iotaMethodGSSAPIMethodUserPassMethodNoAcceptable uint8 = 0xFF
)const (RequestConnect uint8 = iota + 1RequestBindRequestUDP
)const (RequestAtypIPV4       uint8 = iotaRequestAtypDomainname uint8 = 3RequestAtypIPV6       uint8 = 4
)const (Succeeded uint8 = iotaFailureAllowedNetUnreachableHostUnreachableConnRefusedTTLExpiredCmdUnsupportedAddrUnsupported
)type Proxy struct {Inbound struct {reader *bufio.Readerwriter net.Conn}Request struct {atyp uint8addr string}OutBound struct {reader *bufio.Readerwriter net.Conn}
}func Start() error {// 读取配置文件中的监听地址和端口log.Debug("socks5 server start")listenPort := config.Conf.ListenPortlistenIp := config.Conf.ListenIpif listenPort <= 0 || listenPort > 65535 {log.Error("invalid listen port:", listenPort)return errors.New("invalid listen port")}//创建监听addr, _ := net.ResolveTCPAddr("tcp", listenIp+":"+strconv.Itoa(listenPort))listener, err := net.ListenTCP("tcp", addr)if err != nil {log.Error("fail in listen port:", listenPort, err)return errors.New("fail in listen port")}// 建立连接for {conn, _ := listener.Accept()go socks5Handle(conn)}
}func socks5Handle(conn net.Conn) {proxy := &Proxy{}proxy.Inbound.reader = bufio.NewReader(conn)proxy.Inbound.writer = connerr := handshake(proxy)if err != nil {log.Warn("fail in handshake", err)return}transport(proxy)
}func handshake(proxy *Proxy) error {err := auth(proxy)if err != nil {log.Warn(err)return err}err = readRequest(proxy)if err != nil {log.Warn(err)return err}err = replay(proxy)if err != nil {log.Warn(err)return err}return err
}func auth(proxy *Proxy) error {/*Read+----+----------+----------+|VER | NMETHODS | METHODS  |+----+----------+----------+| 1  |    1     | 1 to 255 |+----+----------+----------+*/buf := utils.SPool.Get().([]byte)defer utils.SPool.Put(buf)n, err := io.ReadFull(proxy.Inbound.reader, buf[:2])if n != 2 {return errors.New("fail to read socks5 request:" + err.Error())}ver, nmethods := uint8(buf[0]), int(buf[1])if ver != SOCKS5VERSION {return errors.New("only support socks5 version")}_, err = io.ReadFull(proxy.Inbound.reader, buf[:nmethods])if err != nil {return errors.New("fail to read methods" + err.Error())}supportNoAuth := falsefor _, m := range buf[:nmethods] {switch m {case MethodNoAuth:supportNoAuth = true}}if !supportNoAuth {return errors.New("no only support no auth")}/*replay+----+--------+|VER | METHOD |+----+--------+| 1  |   1    |+----+--------+*/n, err = proxy.Inbound.writer.Write([]byte{0x05, 0x00}) // 无需认证if n != 2 {return errors.New("fail to wirte socks method " + err.Error())}return nil
}func readRequest(proxy *Proxy) error {/*Read+----+-----+-------+------+----------+----------+|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |+----+-----+-------+------+----------+----------+| 1  |  1  | X'00' |  1   | Variable |    2     |+----+-----+-------+------+----------+----------+*/buf := utils.SPool.Get().([]byte)defer utils.SPool.Put(buf)n, err := io.ReadFull(proxy.Inbound.reader, buf[:4])if n != 4 {return errors.New("fail to read request " + err.Error())}ver, cmd, _, atyp := uint8(buf[0]), uint8(buf[1]), uint8(buf[2]), uint8(buf[3])if ver != SOCKS5VERSION {return errors.New("only support socks5 version")}if cmd != RequestConnect {return errors.New("only support connect requests")}var addr stringswitch atyp {case RequestAtypIPV4:_, err = io.ReadFull(proxy.Inbound.reader, buf[:4])if err != nil {return errors.New("fail in read requests ipv4 " + err.Error())}addr = string(buf[:4])case RequestAtypDomainname:_, err = io.ReadFull(proxy.Inbound.reader, buf[:1])if err != nil {return errors.New("fail in read requests domain len" + err.Error())}domainLen := int(buf[0])_, err = io.ReadFull(proxy.Inbound.reader, buf[:domainLen])if err != nil {return errors.New("fail in read requests domain " + err.Error())}addr = string(buf[:domainLen])case RequestAtypIPV6:_, err = io.ReadFull(proxy.Inbound.reader, buf[:16])if err != nil {return errors.New("fail in read requests ipv4 " + err.Error())}addr = string(buf[:16])}_, err = io.ReadFull(proxy.Inbound.reader, buf[:2])if err != nil {return errors.New("fail in read requests port " + err.Error())}port := binary.BigEndian.Uint16(buf[:2])proxy.Request.atyp = atypproxy.Request.addr = fmt.Sprintf("%s:%d", addr, port)log.Debug("request is", proxy.Request)return nil
}func replay(proxy *Proxy) error {/*write+----+-----+-------+------+----------+----------+|VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |+----+-----+-------+------+----------+----------+| 1  |  1  | X'00' |  1   | Variable |    2     |+----+-----+-------+------+----------+----------+*/conn, err := net.Dial("tcp", proxy.Request.addr)if err != nil {log.Warn("fail to connect ", proxy.Request.addr)_, rerr := proxy.Inbound.writer.Write([]byte{SOCKS5VERSION, HostUnreachable, 0x00, 0x01, 0, 0, 0, 0, 0, 0})if rerr != nil {return errors.New("fail in replay " + err.Error())}return errors.New("fail in connect addr " + proxy.Request.addr + err.Error())}_, err = proxy.Inbound.writer.Write([]byte{SOCKS5VERSION, Succeeded, 0x00, 0x01, 0, 0, 0, 0, 0, 0})if err != nil {return errors.New("fail in replay " + err.Error())}proxy.OutBound.reader = bufio.NewReader(conn)proxy.OutBound.writer = connreturn nil
}func transport(proxy *Proxy) {// 语义上是注释的动作;但是iobuf.reader中无法获取rd值// io.Copy(proxy.OutBound.writer, proxy.Inbound.reader)go io.Copy(proxy.OutBound.writer, proxy.Inbound.writer) // outbound <- inbound// io.Copy(proxy.Inbound.writer, proxy.OutBound.reader)go io.Copy(proxy.Inbound.writer, proxy.OutBound.writer) // inbound <- outbound
}

运行与压测

运行测试:可以在浏览器中安装和配置下Proxy SwitchyOmega。插件将http流量转换成socks5,流量通过代理,浏览器可正常上网。访问百度,看B站都没问题。

压测:略。


抓包验证

关于wireshare的使用,可见:wireshark入门指北

  1. 客户端连接到 SOCKS 服务端,发送的协议版本与方法选择消息。

    +----+----------+----------+
    |VER | NMETHODS | METHODS  |
    +----+----------+----------+
    | 1  |    1     | 1 to 255 |
    +----+----------+----------+
    

    在这里插入图片描述

    socks5–客户端只支持1种方法–不验证

  2. 服务器从给出的方法进行选择,并且发送方法选择消息

    +----+--------+
    |VER | METHOD |
    +----+--------+
    | 1  |   1    |
    +----+--------+
    

    在这里插入图片描述

    socks5–不验证

  3. 客户端发送要请求的目标地址。

    +----+-----+-------+------+----------+----------+
    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+
    

    在这里插入图片描述

    客户端,告诉服务端:要请求www.bing.com,目标端口是443。

  4. 服务端根据请求信息,去建立连接。并返回连接情况。

    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+  
    

    在这里插入图片描述

    服务单与目标地址连接成功。这里没有设置与目标建立连接的地址和端口。对于UDP而言,这里必须返回。因为客户端与服务端协商完之后,客户端需要将udp的流量发送到这个地址和端口。

  5. socks协议到这里已经结束。后面则是数据转发。由于转发过程是在传输层。所以无论是http还是https,该代理程序都可以很好的运行。

相关文章:

go入门实践四-go实现一个简单的tcp-socks5代理服务

文章目录 前言socks协议简介go实现一个简单的socks5代理运行与压测抓包验证 前言 SOCKS是一种网络传输协议&#xff0c;主要用于客户端与外网服务器之间通讯的中间传递。协议在应用层和传输层之间。 本文使用先了解socks协议。然后实现一个socks5的tcp代理服务端。最后&#…...

div 中元素居中的N种常用方法

本文主要记录几种常用的div盒子水平垂直都居中的方法。本文主要参考了该篇博文并实践加以记录说明以加深理解记忆 css之div盒子居中常用方法大全 本文例子使用的 html body结构下的div 盒子模型如下&#xff1a; <body><div class"container"><div c…...

Java获取指定文件夹下目录下所有视频并复制到另一个地方

import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption;public class VideoCopier {public static void main(String[] args) {// 指定源文件夹路径和目标文件夹路径String sourceFolderPath "path/to…...

windows server 2016 搭建使用 svn 服务器教程

参考教程&#xff1a; https://zhuanlan.zhihu.com/p/428552058 https://blog.csdn.net/weixin_33897722/article/details/85602029 配置环境 windows server 2016 远程服务器公网 ip 安装 SVN 服务端 下载 svn 服务端安装包&#xff1a;https://www.visualsvn.com/download…...

【Python】如何判断时间序列数据是否为平稳时间序列或非平稳时间序列?

判断时间序列数据是否为平稳时间序列或非平稳时间序列&#xff0c;通常可以通过以下方法&#xff1a; &#xff08;1&#xff09;观察时间序列数据的均值和方差是否随时间变化而发生明显的改变。若均值和方差变化明显&#xff0c;则该时间序列数据可能为非平稳时间序列&#x…...

Labview控制APx(Audio Precision)进行测试测量(六)

用 LabVIEW 驱动 VIs生成任意波形 在 APx500 应用程序中&#xff0c;默认波形类型为正弦。这是指 APx 内置的正弦发生器&#xff0c;根据信号路径设置&#xff0c;许多测量还允许其他内置波形&#xff0c;如方波&#xff0c;分裂正弦波或分裂相位&#xff0c;以及使用导入的。w…...

【Linux】网络协议总结

目录 网络协议总结 应用层 传输层 网络层 数据链路层 网络协议总结 应用层 应用层的作用&#xff1a;负责应用程序间沟通&#xff0c;完成一系列业务处理所需服务。能够根据自己的需求&#xff0c;设计对应的应用层协议。了解HTTP协议。理解DNS的原理和工作流程。 传…...

如何轻松注册企业邮箱?快速掌握超简单的注册技巧!

随着互联网的发展&#xff0c;越来越多的企业开始使用电子邮件作为通信工具。企业邮箱不仅可以提高企业的工作效率&#xff0c;还可以使企业通信更加便捷、保密性更高。那么&#xff0c;企业邮箱怎么注册申请呢&#xff1f;下面我们来详细介绍一下。 第一步&#xff1a;选择邮箱…...

【行为型设计模式】C#设计模式之观察者模式

题目&#xff1a;假设你正在开发一个简单的新闻发布系统&#xff0c;该系统允许用户订阅不同的新闻频道&#xff0c;并在有新闻发布时向订阅者发送通知。使用观察者模式设计和实现该系统。观察者模式的相关概念和定义&#xff1a; 观察者模式是一种行为设计模式&#xff0c;它定…...

《Java面向对象程序设计》学习笔记——第 8 章 设计模式

​专栏&#xff1a;《Java面向对象程序设计》学习笔记 第 8 章 设计模式 一个好的设计系统往往是易维护、易扩展、易复用的。 8.1 设计模式简介 8.1.1 什么是设计模式 一个设计模式 (pattern) 是针对某一类问题的最佳解决方案&#xff0c;而且己经被成功应用于许多系统的设…...

Java学习笔记28——字节流1

IO流概述和分类 IO流IO流的分类字节流字节流写数据FileOutputStream字节流写数据的三种方式字节流写数据的两个问题字节流写数据加异常处理 IO流 IO&#xff1a;输入输出 流&#xff1a;一种抽象的概念&#xff0c;是对数据传输的总称&#xff0c;流的本质就是数据传输 IO流的…...

C++连接串口方式(MFC版本)(简单版本)

ComSerialPort.h /*_________________________串口________________________________*/class Com_SerialPort { public:Com_SerialPort();Com_SerialPort(int port, int baudRate, int byteSize, int parity, int stopBits);~Com_SerialPort(); public:bool Connect(bool bMut…...

ospf重发布

华子目录 一、实验拓扑二、实验要求三、实验思路1、配置接口IP地址以及环回地址&#xff08;以此类推&#xff09;2、配置动态路由协议3、重发布4、更改接口类型5、配置路由策略 一、实验拓扑 二、实验要求 1、使用双点双向重发布2、所有路由器进行最佳选路3、存在备份路径&am…...

基于weka手工实现K-means

一、K-means聚类算法 K均值聚类&#xff08;K-means clustering&#xff09;是一种常见的无监督学习算法&#xff0c;用于将数据集中的样本划分为K个不同的类别或簇。它通过最小化样本点与所属簇中心点之间的距离来确定最佳的簇划分。 K均值聚类的基本思想如下&#xff1a; …...

分布式系统监控zabbix安装部署及使用

分布式系统监控zabbix安装部署及使用 一.zabbix监控 1.什么是zabbix zabbix&#xff1a;是一款开源免费的&#xff0c;自动化发现服务与网络设备的分布式监控&#xff0c;可以监视应用层服务并以web前端页面集中管理并展示。 2.zabbix功能 监控服务器cpu负载、服务器内存使…...

【H5】使用 JavaScript 和 CSS 来完成实现鼠标接触时显示一个图片弹窗

以下是一个示例&#xff0c;演示了如何在鼠标接触时显示一个图片弹窗&#xff1a; HTML: <a href"#" class"popup-link" target"_blank"><i class"fab fa-weixin"></i> </a><div id"popup-containe…...

CSS选择器分类梳理并高亮重点

前言 主要内容来自菜鸟教程 CSS 选择器 | 菜鸟教程 分类是我自己理解的分类&#xff0c;示例说明优化统一了表述风格。 正文 选择器CSS示例示例说明基础*2*选择所有元素:root3:root选择文档的根元素element1p选择所有<p>元素:not(selector)3:not(p)选择所有并非p元素…...

Python批量给excel文件加密

有时候我们需要定期给公司外部发邮件&#xff0c;在自动化发邮件的时候需要对文件进行加密传输。本文和你一起来探索用python给单个文件和批量文件加密。    python自动化发邮件可参考【干货】用Python每天定时发送监控邮件。 文章目录 一、安装pypiwin32包二、定义给excel加…...

程序设计 树基础

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…...

Java 并发编程与CAS基本原理

一、Java并发基础知识 Java里的程序天生就是多线程的&#xff0c;那么有几种新启线程的方式&#xff1f; 两种,启动线程的方式只有&#xff1a; 1、X extends Thread;&#xff0c;然后X.start&#xff1b; 2、X implements Runnable&#xff1b;然后交给Thread运行。 Java…...

3个高级技巧:用ComfyUI Manager彻底改变你的AI绘画工作流

3个高级技巧&#xff1a;用ComfyUI Manager彻底改变你的AI绘画工作流 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various cu…...

零门槛网络拓扑革命:Topology一站式可视化解决方案

零门槛网络拓扑革命&#xff1a;Topology一站式可视化解决方案 【免费下载链接】topology 项目地址: https://gitcode.com/gh_mirrors/top/topology 在复杂的网络环境中&#xff0c;你是否曾为难以理清设备连接关系而烦恼&#xff1f;是否因为传统绘图工具操作繁琐、无…...

Java高频面试题:Kafka的消费消息是如何传递的?

大家好&#xff0c;我是锋哥。今天分享关于【Java高频面试题&#xff1a;Kafka的消费消息是如何传递的&#xff1f;】面试题 。希望对大家有帮助&#xff1b;Java高频面试题&#xff1a;Kafka的消费消息是如何传递的&#xff1f;在 Kafka 中&#xff0c;消息消费的传递是通过消…...

告别WSL安装等待:Phi-3-mini-4k-instruct-gguf提供离线配置与问题排查手册

告别WSL安装等待&#xff1a;Phi-3-mini-4k-instruct-gguf提供离线配置与问题排查手册 1. 为什么你需要这份指南 如果你正在Windows上尝试安装WSL&#xff08;Windows Subsystem for Linux&#xff09;&#xff0c;很可能已经遇到了"wsl --install下载太慢"这个令人…...

IEEE IoT-J | CoDrone:Depth Anything V2+VLM云边端协同,无人机自主导航飞行距离+40%

导读&#xff1a;———————————————————————————————————————————无人机自主导航面临一个两难困境&#xff1a;板载浅层DNN感知能力有限&#xff0c;遇到复杂环境容易碰撞&#xff1b;将计算卸载到边缘服务器又受网络波动影响&#x…...

你的B站视频收藏计划为何总是半途而废?DownKyi用3个认知升级解决90%下载难题

你的B站视频收藏计划为何总是半途而废&#xff1f;DownKyi用3个认知升级解决90%下载难题 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频…...

LangGraph 实战指南:拒绝 AI 应用面条代码,像搭地铁一样构建企业级 Agent

LangGraph 实战指南&#xff1a;拒绝 AI 应用面条代码&#xff0c;像搭地铁一样构建企业级 Agent 文章目录LangGraph 实战指南&#xff1a;拒绝 AI 应用面条代码&#xff0c;像搭地铁一样构建企业级 Agent前言&#xff1a;那个让程序员崩溃的周五晚上一、LangGraph 是什么&…...

AI读脸术镜像测评:OpenCV DNN模型真实表现,年龄性别识别效果如何?

AI读脸术镜像测评&#xff1a;OpenCV DNN模型真实表现&#xff0c;年龄性别识别效果如何&#xff1f; 1. 技术背景与镜像特点 1.1 人脸属性识别技术现状 人脸属性识别作为计算机视觉的基础任务之一&#xff0c;在智能安防、用户画像分析、个性化推荐等领域有着广泛应用。传统…...

Kandinsky-5.0-I2V-Lite-5s效果展示:AI生成插画→动态叙事短片(5秒内完成情绪传递)

Kandinsky-5.0-I2V-Lite-5s效果展示&#xff1a;AI生成插画→动态叙事短片&#xff08;5秒内完成情绪传递&#xff09; 1. 开箱即用的动态叙事工具 Kandinsky-5.0-I2V-Lite-5s是一款让人眼前一亮的轻量级图生视频模型。它最吸引人的特点是&#xff1a;你只需要准备一张静态插…...

告别生硬过渡:用Pop实现丝滑手势交互的3个实战技巧

告别生硬过渡&#xff1a;用Pop实现丝滑手势交互的3个实战技巧 【免费下载链接】pop An extensible iOS and OS X animation library, useful for physics-based interactions. 项目地址: https://gitcode.com/gh_mirrors/po/pop Pop是一款强大的iOS和OS X动画库&#x…...