GO 的 socks5代理 编写

news2024/11/18 14:36:28

这里学习一下 socks5 代理的编写

网上有很多 学习一下

go 语言实战入门案例之实现Socks5 - 知乎

滑动验证页面

socks5协议原理学习-腾讯云开发者社区-腾讯云 (tencent.com)

首先我们要了解一下socks5的代理方式

socks5 是基于

  • 认证
  • 建立连接
  • 转发数据

所形成的代理 我们只需要按照这三个写出代码即可 

首先就是socks5的认证

Socks5Atuth

这里首先要认证 那么我们首先要确定是不是socks5代理

 在socks5中 前两个字节 分别是 socks版本号 和 支持的认证方式

前面两个 1

所以这里我们开始读取前面的 版本号 ver socks5是固定值 0x05

这里开始编写一下监听

package main

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

const socks5Ver = 0x05
const cmdVer = 0x01
const aytpVerIpv4 = 0x01
const aytpVerUrl = 0x03   //这里是下面的常量 不需要理会即可 当作值即可

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("Accpet error :", err)
			continue
		}
		//确定ip端口 进行链接后 开始进程
		fmt.Println("开始监听", client, client.RemoteAddr())
	}
}

 然后开始处理数据

首先我们需要开启一个协程 处理

func process(conn net.Conn) {
	defer conn.Close()
	readio := bufio.NewReader(conn)
	err := auth(readio, conn)
}

这里我们首先处理一下认证的信息

	// +----+----------+----------+
	// |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

	// 版本和NMETHODS值都是单字节的,所以ReadByte读一个字节就好了

这里需要认证的东西就是这些 我们首先进行认证 ver

func auth(readio *bufio.Reader, conn net.Conn) (err error) {
	ver, err := readio.ReadByte()
	fmt.Println(ver)
	return nil
}

这里是通过 readbyte 读取一个字节 我们输出一下就知道是多少了

发现我们通过浏览器进行代理 第一个byte是 5 对应 socks5

如果将代理设置为 socks4 那么这里就是对应4

 然后这里进行匹配 如果不是就输出错误 这样 ver认证就结束了

认证的ver

func auth(readio *bufio.Reader, conn net.Conn) (err error) {
	ver, err := readio.ReadByte()
	if err != nil {
		return fmt.Errorf("ver error:", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("ver num error: ", err)
	}

}

认证method        

这里是socks5的认证,是否需要认证

	methodSize, err := readio.ReadByte()
	fmt.Println(methodSize)
	return

其实这里就是读取一个字节的大小

func auth(readio *bufio.Reader, conn net.Conn) (err error) {
	ver, err := readio.ReadByte()
	if err != nil {
		return fmt.Errorf("ver error:", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("ver num error: ", err)
	}
	methodSize, err := readio.ReadByte() //确定切片大小 为 1字节
	method := make([]byte, methodSize)   //创建一个大小的切片
	_, err = io.ReadFull(readio, method) // 这里是判断是否读了
	if err != nil {
		return fmt.Errorf("read method error :", err)
	}
	_, err = conn.Write([]byte{socks5Ver, 0x00}) // 这里是握手的回复 说明我们不需要认证
	if err != nil {
		return fmt.Errorf("write error:", err)
	}
	return nil
}

这里注意

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

这里其实是监听后告诉 不需要进行 认证

当我们认证完后开始处理浏览器的报文

Socks5Connect

	// 读取浏览器发送的报文
	// +----+-----+-------+------+----------+----------+
	// |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个字节


这里是浏览器发送的报文 我们依旧先进行鉴定
func connnect(readio *bufio.Reader, conn net.Conn) (err error) {
	buf := make([]byte, 4)
	_, err = io.ReadFull(readio, buf) // 这里是读取字节数 读取前面4个作为一个切片
	if err != nil {
		return fmt.Errorf("read header error :", err)
	}
	var ver, cmd, atyp = buf[0], buf[1], buf[3] // 这里对应报文的字节认证的位置
	fmt.Println(ver, cmd, atyp)
	return
}

这里就很明显了 5 对应 socks5 1 对应 链接的请求 3 对应 url 是一个域名

+----+--------+-------------------+--------------+---------------+
|VER |  CMD   |       ATYP        |   HOST SIZE  |      HOST     |
+----+--------+-------------------+--------------+---------------+
| 05 |  01    |       03          |      11      | www.exa.com   |
+----+--------+-------------------+--------------+---------------+


首先之前的都已经被读了


ver 

cmd

atyp

然后再读 就是 host size 这里是 后面 host 的地址长度

这里开源发现 cn.bing.com的长度是11 因为url是可变的 所以后面的host也是可变的

我们首先读取字节长度 然后作为 值传递给切片 读取该长度的值 这样我们就开源获取到url地址

	_, err = io.ReadFull(readio, buf[:2]) //再向后读取两个字节 是port
	if err != nil {
		return fmt.Errorf("port error:", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])  //这里是大端序监听端口
	fmt.Println(port)
	fmt.Println(addr)
	return

这里的原理是这样的

首先我们读取两个字节

443 [1 187]

然后进行大端序排序 0 在高 80 在低

这个时候就是

[1 187]

然后我们进uint16处理

[1 187]

将高位字节(1)左移 8 位,使其占据 uint16 的高 8 位,得到结果 00000001 00000000

将低位字节(187)放在 uint16 的低 8 位,得到结果 00000001 10111011

 00000001 10111011

443

这样我们就获取到了端口

然后我们就可以进行拼接url

	dest := fmt.Sprintf("%v:%v", addr, port)  
	fmt.Println(dest)

 然后我们开始建立tcp请求

	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port)) //建立tcp协议
	if err != nil {
		return fmt.Errorf("dial error:", err)
	}
	defer dest.Close()
	log.Println("访问:", addr, port)

完整代码

package main

import (
	"bufio"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdVer = 0x01
const aytpVerIpv4 = 0x01
const aytpVerUrl = 0x03

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("Accpet error :", err)
			continue
		}
		//确定ip端口 进行链接后 开始进程
		fmt.Println("开始监听")
		go process(client)
	}
}
func process(conn net.Conn) {
	defer conn.Close()
	readio := bufio.NewReader(conn)
	err := auth(readio, conn)
	if err != nil {
		fmt.Errorf("ip: %v,auth error :", conn.RemoteAddr().String(), err)
	}
	err = connnect(readio, conn)
}

func auth(readio *bufio.Reader, conn net.Conn) (err error) {
	ver, err := readio.ReadByte()
	if err != nil {
		return fmt.Errorf("ver error:", err)
	}
	if ver != socks5Ver {
		return fmt.Errorf("ver num error: ", err)
	}
	methodSize, err := readio.ReadByte() //确定切片大小 为 1字节
	method := make([]byte, methodSize)   //创建一个大小的切片
	_, err = io.ReadFull(readio, method) // 这里是判断是否读了
	if err != nil {
		return fmt.Errorf("read method error :", err)
	}
	_, err = conn.Write([]byte{socks5Ver, 0x00}) // 这里是握手的回复 说明我们不需要认证
	if err != nil {
		return fmt.Errorf("write error:", err)
	}
	return nil
}

func connnect(readio *bufio.Reader, conn net.Conn) (err error) {
	buf := make([]byte, 4)
	_, err = io.ReadFull(readio, buf) // 这里是读取字节数 读取前面4个作为一个切片
	if err != nil {
		return fmt.Errorf("read header error :", err)
	}
	var ver, cmd, atyp = buf[0], buf[1], buf[3] // 这里对应报文的字节认证的位置
	if ver != socks5Ver {
		return fmt.Errorf("connect ver error:", err)
	}
	if cmd != cmdVer {
		return fmt.Errorf("connect cmd	 error:", err)
	}
	addr := ""
	switch atyp {
	case aytpVerIpv4:
		_, err = io.ReadFull(readio, buf)
		if err != nil {
			return fmt.Errorf("ipv4 error:", err)
		}

		addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]) //读取ip地址
	case aytpVerUrl: //这里解析的是url
		hostSize, err := readio.ReadByte() //获取url的字节长度
		if err != nil {
			return fmt.Errorf("read hostSize failed:%w", err)
		}
		host := make([]byte, hostSize) //获取url字符的字节
		_, err = io.ReadFull(readio, host)
		if err != nil {
			return fmt.Errorf("read host failed:%w", err)
		}
		addr = string(host)
	default:
		return fmt.Errorf("not yet", atyp)
	}

	_, err = io.ReadFull(readio, buf[:2]) //再向后读取两个字节 是port
	if err != nil {
		return fmt.Errorf("port error:", err)
	}
	port := binary.BigEndian.Uint16(buf[:2])                       //这里是大端序监听端口
	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port)) //建立tcp协议
	if err != nil {
		return fmt.Errorf("dial error:", err)
	}
	defer dest.Close()
	log.Println("访问:", addr, port)
	_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
	if err != nil {
		return fmt.Errorf("写入错误:", err)
	}
	ctx, cancel := context.WithCancel(context.Background()) //启动一个可以取消的上下文功能
	defer cancel()
	go func() {

		_, _ = io.Copy(dest, readio) //这里是从我们的请求中读取出来 写入请求中
		cancel()
	}()
	go func() {
		_, _ = io.Copy(conn, dest) //这里从请求中写入返回报文中
		cancel()
	}()
	<-ctx.Done() // 这里是 只要管道输出内容了就停止 所以上面两个协程 只要有一个输出 就取消
	return nil
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录一下github深度学习的错误

1.[visdom]无法正常启动服务问题解决 在Anaconda命令窗口中&#xff1a; 使用python -m visdom.server启动visdom服务时&#xff0c;卡在&#xff1a; Checking for scripts. Downloading scripts, this may take a little while 无法下载和启动服务。 ERROR&#xff1a;由…

JS逆向实战——开发者工具检测

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、背景 在JS逆向领域&#xff0c;Chrome开发者工具是核心&#xff0c;抓包、调试、看调用栈等都离不开它。可以说&#xff0c;逆向人…

PFA洗瓶耐温范围广应用化学实验耐强酸

PFA洗瓶&#xff1a;科技让实验更便捷 在实验室里&#xff0c;洗瓶是常用工具之一。而PFA洗瓶则是一种特殊塑料制作的洗瓶&#xff0c;它的外观半透明&#xff0c;方便观察液体。 PFA洗瓶的耐温范围非常广&#xff0c;可以承受-200℃到260℃的温度&#xff0c;这意味着它可以…

vmware离线安装docker-compose

vmware离线安装docker-compose 最近安装docker-compose&#xff0c;发现git取拉取&#xff0c;不是拒绝连接就是报443错误&#xff0c;或者其他错误 最后发现用包直接传上去好用&#xff0c;不用git拉取了 离线安装docker-compose 本文章给的docker-compose离线包&#xff0c;…

超短焦投影仪是不是智商税?实测分享,当贝U1用起来是真的香

选购投影仪的时候&#xff0c;很多人都是先看亮度、分辨率等参数&#xff0c;而我的建议是先看投射比。因为用过投影仪的朋友都知道&#xff0c;投影仪对空间的距离是有要求的&#xff0c;如果你买的是投射比为1.2:1的投影仪&#xff0c;那么可能在小空间里就没法施展&#xff…

HTML_有哪些字体样式及使用

文章目录 &#x1f431;‍&#x1f409;一、字体样式的基本概念&#xff1a;&#x1f431;‍&#x1f409;二、css字体样式属性有&#xff1a;&#x1f923;1、设置字体类型&#xff08;font-family&#xff09;&#x1f923;2、设置字体大小&#xff08;font-size&#xff09;…

CogVLM与CogAgent:开源视觉语言模型的新里程碑

引言 随着机器学习的快速发展&#xff0c;视觉语言模型&#xff08;VLM&#xff09;的研究取得了显著的进步。今天&#xff0c;我们很高兴介绍两款强大的开源视觉语言模型&#xff1a;CogVLM和CogAgent。这两款模型在图像理解和多轮对话等领域表现出色&#xff0c;为人工智能的…

B038-Spring基础

目录 mybatis高级查询(动态sql)springspring简介IOC和AOP介绍入门案例导包核心配置文件获取对象 迫切加载和懒加载BeanFactory和ApplicationContext区别和联系spring管理beanDI依赖注入xml注入注解注入(简单介绍 后面用) Spring测试bean的作用域bean的生命周期多例默认是懒加载…

Unity3d C#利用Editor编辑器拓展实现配置UI背景样式一键设置UI背景样式功能(含源码)

前言 在开发UI滚动列表的时候&#xff0c;经常会有每项的背景图不统一的情况&#xff0c;会间隔重复的情况居多。这种情况下&#xff0c;手动去设置间隔一行的背景图或者颜色是比较麻烦的。在此背景下&#xff0c;笔者尝试写个小工具&#xff0c;在搭建UI时配置一下循环背景的…

如何打造自己的知识付费小程序平台

在当今知识付费的浪潮中&#xff0c;我们经常可以看到各种知识付费平台如雨后春笋般涌现。然而&#xff0c;这些平台往往只是一个过客&#xff0c;让我们短暂停留后&#xff0c;便淹没在信息的海洋中。如果你有一个出色的课程&#xff0c;为什么不让它在一个属于你自己的平台上…

人工智能与自动驾驶:智能出行时代的未来之路

一、前言 首先&#xff0c;我们先来说下什么是人工智能&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机系统能够模拟、仿真人类智能的技术和科学领域。它涉及构建智能代理&#xff0c;使其能够感知环境、理解和…

Linux多版本cuda切换

目标 将cuda版本从10.0切换为11.1 步骤 查看当前cuda版本&#xff1a; nvcc -V编辑.bashrc文件&#xff1a; vim ~/.bashrc在文件中添加以下几行&#xff08;若已存在则忽略&#xff09;&#xff1a; export PATH$PATH:/usr/local/cuda/bin export LD_LIBRARY_PATH$LD_LI…

Android解决报错 superclass access check failed: class

Android解决报错 superclass access check failed: class 前言&#xff1a; 最近在打开之前的项目demo时&#xff0c;出现一个错误Cause: superclass access check failed: class butterknife.compiler.ButterKnifeProcessor$RScanner 1.错误信息如下&#xff1a; Executio…

【Ehcache技术专题】「入门到精通」带你一起从零基础进行分析和开发Ehcache框架的实战指南(3-储存方式)

这里写目录标题 Ehcache的存储方式堆内存&#xff08;MemoryStore&#xff09;指定可用内存Xml代码Xml代码Xml代码 驱除策略元素过期Xml代码 非堆内存&#xff08;BigMemory&#xff09;磁盘&#xff08;DiskStore&#xff09;指定可用容量Xml代码 元素过期 Ehcache的存储方式 …

外卖系统海外版:技术智能引领全球美食新潮流

随着全球数字化浪潮的推动&#xff0c;外卖系统海外版不仅是食客们品味美食的便捷通道&#xff0c;更是技术智能在美食领域的引领者。本文将深入剖析其背后的技术实现&#xff0c;揭开代码带来的美食革新。 多语言支持&#xff1a;构建全球美食沟通桥梁 def multilingual_su…

【UE5.1】M4自动地形材质+UltraDynamicSky+Oceanology插件的使用记录

目录 效果 步骤 一、项目准备 二、插件使用记录 准备过程 M4自动地形插件使用过程 超动态天空插件使用过程 运行时修改天空效果 运行时修改天气效果 海洋插件使用过程 在海洋中游泳 效果 步骤 一、项目准备 1. 创建一个第三人称游戏工程 2. 将M4文件夹和Ultr…

【Java】网络编程-UDP字典服务器客户端简单代码编写

上文讲了UDP回响服务器客户端简单代码编写 本文将讲述UDP字典服务器客户端简单代码编写。所谓回显&#xff0c;就是指客户端向服务器发送一个报文&#xff0c;从服务器那里得到一条一模一样的回响报文 而我们的字典功能呢&#xff0c;则是实现了输入中文&#xff0c;得到对应…

Spring事务浅析

一:Spring事务简介 什么是事务&#xff1a; 数据库事务是指作为单个逻辑工作单元执行的一系列操作&#xff0c;这些操作要么一起成功&#xff0c;要么一起失败&#xff0c;是一个不可分割的工作单元。 在我们日常工作中&#xff0c;涉及到事务的场景非常多&#xff0c;一个…

MyBatis持久层框架

四、MyBatis持久层框架 目录 一、Mybatis简介 1. 简介2. 持久层框架对比3. 快速入门&#xff08;基于Mybatis3方式&#xff09; 二、日志框架扩展 1. 用日志打印替代sout2. Java日志体系演变3. 最佳拍档用法4. Lombok插件的使用 4.1 Lombok简介4.2 Lombok安装4.3 Lombok使用注…

Kioptrix-1

信息收集 # nmap -sn 192.168.1.0/24 -oN live.nmap Starting Nmap 7.94 ( https://nmap.org ) at 2023-12-18 20:02 CST Nmap scan report for 192.168.1.1 (192.168.1.1) Host is up (0.00025s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 0bc…