Go编写流量代理工具

目录

    • 这是一个演示
    • 主要分为俩包:
    • 流程:
    • 逻辑:(端口随意,本地ssh为例)
    • 用法:
    • 文件地址:
    • 代码如下:

这是一个演示

代理本地HTTP服务
在这里插入图片描述
代理局域网SSH服务
在这里插入图片描述
在这里插入图片描述
其他的TCP服务没测试了


主要分为俩包:

main -》 主函数,包含Server、Client函数和goroutine函数
utils -》 工具函数,包含发包和读包函数,现在使用的是json序列化没有加密(内置AES256加密,可以把粘包问题处理后进行使用)


流程:

返回数据呈现给用户:

本地Conn -》服务器中转Conn -》 服务器代理Conn

用户发送请求到本地:

服务器代理Conn -》 本地中转Conn -》 本地Conn


逻辑:(端口随意,本地ssh为例)

  • Server (对外IP 1.2.3.4)
    1. 先监听0.0.0.0:2222(中转代理服务)
      • 有连接 -》进行②
      • 无连接 -》 继续听(每隔5秒)
    2. 再监听0.0.0.0:3333(代理出来的端口)

  • Client
    1. 先连接127.0.0.1:22(本地服务)(这两个先后顺序无所谓)
      • 成功 -》进行②
      • 失败-》 继续连(每隔5秒)
    2. 再连接1.2.3.4:2222(中转代理服务)

用法:

使用方法:
服务器模式:程序名 -s R1地址:端口 Up地址:端口例子:程序名 -s x.x.x.x:2222 x.x.x.x:3333
客户端模式:程序名 -c 本地地址:端口 R1地址:端口例子:程序名 -c 127.0.0.1:22 x.x.x.x:2222
显示帮助信息:程序名 -h

文件地址:

Github地址:https://github.com/jumppppp/go/tree/master/htools/pangolin

代码如下:

package mainimport ("encoding/json""flag""fmt""io""net""pangolin/utils""time"
)type Client_t struct{ConnLocal net.ConnBufR1 [1024]byteConnR1 net.ConnConnectedR1 bool
}
type Server_t struct{LnR1 net.ListenerConnR1 net.ConnConnectedUp boolLnUp net.ListenerConnUp net.ConnBufR1 [1024]byte
}func printUsage() {fmt.Println("使用方法:")fmt.Println("服务器模式:")fmt.Println("  程序名 -s R1地址:端口 Up地址:端口")fmt.Println("  例子:程序名 -s x.x.x.x:2222 x.x.x.x:3333")fmt.Println("客户端模式:")fmt.Println("  程序名 -c 本地地址:端口 R1地址:端口")fmt.Println("  例子:程序名 -c 127.0.0.1:22 x.x.x.x:2222")fmt.Println("显示帮助信息:")fmt.Println("  程序名 -h")
}
func main() {var (helpFlag   boolserverFlag boolclientFlag bool)// 设置命令行参数flag.BoolVar(&helpFlag, "h", false, "显示帮助信息")flag.BoolVar(&serverFlag, "s", false, "使用服务器模式")flag.BoolVar(&clientFlag, "c", false, "使用客户端模式")flag.Parse()if helpFlag {printUsage()return}args := flag.Args()if serverFlag {if len(args) != 2 {fmt.Println("错误:请提供R1地址和Up地址")printUsage()return}HpR1 := args[0]HpUp := args[1]Server(HpR1, HpUp)} else if clientFlag {if len(args) != 2 {fmt.Println("错误:请提供本地地址和R1地址")printUsage()return}HpLocal := args[0]HpR1 := args[1]Client(HpR1, HpLocal)} else {fmt.Println("错误:请提供-s或-c选项")printUsage()}
}
func Server(HpR1 string,HpUp string)(){ms:=&Server_t{}var err errorms.LnR1,err = net.Listen("tcp",HpR1)if err!=nil{fmt.Println("远程服务R1 No=",err)}fmt.Println("远程服务R1...")ms.LnUp,err = net.Listen("tcp",HpUp)if err!=nil{fmt.Println("远程服务Up No=",err)}fmt.Println("远程服务Up...")go ms.handleServerR1()ms.handleServerUp()}
func (this * Server_t)handleServerR1()(){var err errorfor{this.ConnR1,err  = this.LnR1.Accept()if err != nil {fmt.Println("Accept error:", err)continue // 继续等待下一个连接请求}fmt.Println(this.ConnR1.RemoteAddr().String()," Connect ",this.ConnR1.LocalAddr().String())for{tf := &utils.Transfer{Conn: this.ConnR1,}data,err,offset,data2:=tf.ReadPkgNo()if err!=nil{fmt.Println("R R1 No=",err)if err == io.EOF { // 远程主机已经关闭连接fmt.Println("Connection closed by remote host")}break}switch data.Type{case utils.BackType:// fmt.Println(data)for {if this.ConnectedUp{this.ConnUp.Write(data.Data)if offset!=0{this.ConnUp.Write(data2.Data)}break}else{fmt.Println("未检测到Up连接 等待5秒")time.Sleep(5*time.Second)}}case utils.GoType:fmt.Println(data)default:fmt.Println(data)}}this.ConnR1.Close() // 关闭连接}}
func (this * Server_t)handleServerUp()(){var err errorfor {this.ConnUp, err = this.LnUp.Accept()if err != nil {fmt.Println("Accept error:", err)continue // 继续等待下一个连接请求}fmt.Println(this.ConnUp.RemoteAddr().String(), " Connect ", this.ConnUp.LocalAddr().String())this.ConnectedUp = truefor {n, err := this.ConnUp.Read(this.BufR1[:])if err != nil {fmt.Println("R Up No", err)if err == io.EOF { // 远程主机已经关闭连接fmt.Println("Connection closed by remote host")}this.ConnectedUp = falsebreak // 退出当前循环并关闭连接}tf:=&utils.Transfer{Conn: this.ConnR1,}//发送返回包var GoMsg utils.MessageGoMsg.Type = utils.GoTypeGoMsg.Data = this.BufR1[:n]GoMsgM,err:=json.Marshal(GoMsg)if err!=nil{fmt.Println("No=",err)}err = tf.WritePkgNo(GoMsgM)if err!=nil{fmt.Println("WNo=",err)}for i := range this.BufR1 {this.BufR1[i] = 0}}this.ConnUp.Close() // 关闭连接}}
func Client(HpR1 string,HpLocal string)(){mc:=&Client_t{}go mc.handleR1(HpR1)mc.handleLocal(HpLocal)}
func (this * Client_t)handleLocal(hp string){//做最大次数尝试var err errorfor{this.ConnLocal,err = net.Dial("tcp",hp)if err!=nil{fmt.Println("本地服务连接No=",err)}fmt.Println("本地服务已连接")for{n,err := this.ConnLocal.Read(this.BufR1[:])if err!=nil{fmt.Println("R Local No=",err)if err == io.EOF { // 远程主机已经关闭连接fmt.Println("Connection closed by remote host")}break}if this.ConnectedR1{tf:=&utils.Transfer{Conn: this.ConnR1,}//发送返回包var BackMsg utils.MessageBackMsg.Type = utils.BackTypeBackMsg.Data = this.BufR1[:n]BackMsgM,err:=json.Marshal(BackMsg)if err!=nil{fmt.Println("No=",err)}fmt.Println("读到",n,"发送",len(BackMsgM))err = tf.WritePkgNo(BackMsgM)if err!=nil{fmt.Println("WNo=",err)}for i := range this.BufR1 {this.BufR1[i] = 0}}else{fmt.Println("未检测到R1连接 等待5秒")time.Sleep(5*time.Second)}}this.ConnLocal.Close()}}
func (this * Client_t)handleR1(hp string){// defer HR.Done()var err errorfor{this.ConnR1,err = net.Dial("tcp",hp)if err!=nil{fmt.Println("远程服务R1 等待5秒 No=",err)time.Sleep(5*time.Second)continue}fmt.Println("远程服务已连接")this.ConnectedR1 = truefor{tf := &utils.Transfer{Conn: this.ConnR1,}data,err,offset,data2:=tf.ReadPkgNo()if err!=nil{fmt.Println("R R1 No=",err)if err == io.EOF { // 远程主机已经关闭连接fmt.Println("Connection closed by remote host")}this.ConnectedR1 = falsebreak}switch data.Type{case utils.BackType:fmt.Println(data)case utils.GoType:// fmt.Println(data)this.ConnLocal.Write(data.Data)if offset!=0{this.ConnLocal.Write(data2.Data)}default:fmt.Println(data)}}this.ConnR1.Close()}}
package utilsimport ("bytes""crypto/aes""crypto/cipher""crypto/rand""encoding/binary""encoding/hex""encoding/json""errors""fmt""net"
)
var (GoType string ="Go"BackType string = "Back"
)type Message struct{Type string `json:"type"`Data []byte	`json:"data"`
}
type Transfer struct{Conn net.Conn	//文件描述符Buf [4096]byte //缓冲
}
func (this *Transfer)WritePkgNo(data []byte) (err error) {pkgLen := uint32(len(data))binary.BigEndian.PutUint32(this.Buf[:4], pkgLen)n := copy(this.Buf[4:], data)if n!=int(pkgLen){fmt.Println("Copy No=",n,"!=",int(pkgLen))return}lastLen :=int(pkgLen)+4// fmt.Println("W=",this.Buf[:4],string(this.Buf[4:lastLen]),int(pkgLen),string(data))fmt.Println("W=",int(pkgLen))n,err=this.Conn.Write(this.Buf[:lastLen])if err!=nil || int(lastLen)!=n{fmt.Println(err,int(lastLen),"!=",n)return}return
}func (this *Transfer)ReadPkgNo()(msg Message,err error,offset int,msg2 Message){n,err:=this.Conn.Read(this.Buf[:])if err!=nil{fmt.Println("R No=",err)return}var pkgLen uint32pkgLen = binary.BigEndian.Uint32(this.Buf[:4])lastLen:=int(pkgLen)+4// fmt.Println("R=",this.Buf[:4],string(this.Buf[4:lastLen]),int(pkgLen),string(this.Buf[4:n]))fmt.Println("W=",int(pkgLen))if n!=lastLen{fmt.Println("[1]",int(lastLen),"!=",n)offset = lastLenvar pkgLen2 uint32pkgLen2 = binary.BigEndian.Uint32(this.Buf[offset:offset+4])if n!= offset+4+int(pkgLen2){fmt.Println("[2]",int(offset+4+int(pkgLen2)),"!=",n)}err = json.Unmarshal(this.Buf[offset+4:offset+4+int(pkgLen2)],&msg2)if err!=nil{fmt.Println("Unmarshal No=",err)return}fmt.Println("第二个包",msg2)}else{offset = 0}err = json.Unmarshal(this.Buf[4:lastLen],&msg)if err!=nil{fmt.Println("Unmarshal No=",err)return}return 
}func (this *Transfer)WritePkg(data []byte) (err error) {//加密流程,长度+原始内容 -》 替换过的密钥(32)+长度(4)+加密内容pkgLen := uint32(len(data))fmt.Println("write n=",int(pkgLen))var BufTemp [4096]bytebinary.BigEndian.PutUint32(BufTemp[:4], pkgLen)n := copy(BufTemp[4:], data)if n!=int(pkgLen){fmt.Println("WNo=",n,"!=",int(pkgLen))return}lastLenTemp := 4+n// fmt.Println(string(BufTemp[:]))//加密程序oldKey,newKey:= OutKey()// fmt.Println(string(oldKey),len(oldKey),string(newKey),len(newKey))ciphertext, err := EncryptAES(BufTemp[:lastLenTemp],[]byte(oldKey))if err != nil {fmt.Println("EncodeNo=",err)return}EnpkgLen := uint32(len(ciphertext))binary.BigEndian.PutUint32(this.Buf[32:36], EnpkgLen)copy(this.Buf[:32], newKey)n =copy(this.Buf[36:],ciphertext)lastLen := 36+n// fmt.Println(string(this.Buf[:32]),this.Buf[32:36],this.Buf[36:lastLen])//发包n, err = this.Conn.Write(this.Buf[:lastLen])if err != nil || n!=int(lastLen) {fmt.Println("WNo=", err,n,"!=",int(lastLen))return}//调试// fmt.Println("W=",this.Buf[:4],string(this.Buf[4:lastLen]),"n=",n)return
}
func (this *Transfer)ReadPkg()(msg Message,err error){//解密流程,替换过的密钥(32)+长度(4)+加密内容 -》  长度+原始内容 //fmt.Println("Reading client send message...")n,err:=this.Conn.Read(this.Buf[:])if err!=nil{return}if n<=32{err  = fmt.Errorf("The Readpkg is Null %v %v %v %v",string(this.Buf[:n]),this.Buf[:n],this.Conn.RemoteAddr().String(),this.Conn.LocalAddr().String())return}var EnpkgLen uint32EnpkgLen = binary.BigEndian.Uint32(this.Buf[32:36])newKey := string(this.Buf[:32])oldKey := KeyOut(newKey)// fmt.Println(newKey,oldKey,EnpkgLen)EncodeData := this.Buf[36:36+EnpkgLen]// fmt.Println(EncodeData)// decrypt ciphertext with AES-256-CBCdecryptedPlaintext, err := DecryptAES(EncodeData, []byte(oldKey))if err != nil {fmt.Println("DecodeNo=",err)return}n = copy(this.Buf[:],decryptedPlaintext)// fmt.Println(string(decryptedPlaintext),n)//调试var pkgLen uint32pkgLen = binary.BigEndian.Uint32(this.Buf[:4])//调试fmt.Println("read n=",int(pkgLen))// fmt.Println("R=",this.Buf[:4],string(this.Buf[4:n]),"n=",n)if err!=nil||(n-4)!=int(pkgLen){fmt.Println("RNo=",err,(n-4),"!=",int(pkgLen))return}//调试err = json.Unmarshal(this.Buf[4:n],&msg)if err!=nil{fmt.Println("No=",err)}return 
}func OutKey() (oldKey string, newKey string) {// 生成32字节的随机密钥key := make([]byte, 16)if _, err := rand.Read(key); err != nil {panic(err)}oldKey = hex.EncodeToString(key)// 转换为16进制字符串并替换字符replacementMap := map[rune]rune{'0': 'f','1': 'e','2': 'd','3': 'c','4': 'b','5': 'a','6': '9','7': '8','8': '7','9': '6','a': '5','b': '4','c': '3','d': '2','e': '1','f': '0',}var buffer bytes.Bufferfor _, r := range oldKey {if original, ok := replacementMap[r]; ok {buffer.WriteRune(original)} else {buffer.WriteRune(r)}}newKey = buffer.String()return
}func KeyOut(hexKey string) string {// 构造字符替换表replacementMap := map[rune]rune{'f': '0','e': '1','d': '2','c': '3','b': '4','a': '5','9': '6','8': '7','7': '8','6': '9','5': 'a','4': 'b','3': 'c','2': 'd','1': 'e','0': 'f',}// 转换为字节数组并替换字符keyBytes, err := hex.DecodeString(hexKey)if err != nil {panic(err)}var buffer bytes.Bufferfor _, b := range keyBytes {hexStr := hex.EncodeToString([]byte{b})rune1 := rune(hexStr[0])rune2 := rune(hexStr[1])if original1, ok := replacementMap[rune1]; ok {buffer.WriteRune(original1)} else {buffer.WriteRune(rune1)}if original2, ok := replacementMap[rune2]; ok {buffer.WriteRune(original2)} else {buffer.WriteRune(rune2)}}replacedHexKey := buffer.String()return replacedHexKey
}
// EncryptAES encrypts plaintext using AES-256-CBC with the given key
func EncryptAES(plaintext []byte, key []byte) ([]byte, error) {// create AES cipher blockblock, err := aes.NewCipher(key)if err != nil {return nil, err}// pad plaintext with PKCS#7 paddingpaddedPlaintext := pkcs7Pad(plaintext, aes.BlockSize)// generate random IViv := make([]byte, aes.BlockSize)if _, err := rand.Read(iv); err != nil {return nil, err}// create CBC mode encryptercbc := cipher.NewCBCEncrypter(block, iv)// encrypt plaintextciphertext := make([]byte, len(paddedPlaintext))cbc.CryptBlocks(ciphertext, paddedPlaintext)// append IV to the beginning of the ciphertextciphertext = append(iv, ciphertext...)return ciphertext, nil
}// DecryptAES decrypts ciphertext using AES-256-CBC with the given key
func DecryptAES(ciphertext []byte, key []byte) ([]byte, error) {if len(ciphertext) < aes.BlockSize*2 {return nil, errors.New("invalid ciphertext")}// extract IV from the beginning of the ciphertextiv := ciphertext[:aes.BlockSize]ciphertext = ciphertext[aes.BlockSize:]// create AES cipher blockblock, err := aes.NewCipher(key)if err != nil {return nil, err}// create CBC mode decryptercbc := cipher.NewCBCDecrypter(block, iv)// decrypt ciphertextdecryptedPlaintext := make([]byte, len(ciphertext))cbc.CryptBlocks(decryptedPlaintext, ciphertext)// unpad plaintext by removing PKCS#7 paddingplaintext, err := pkcs7Unpad(decryptedPlaintext, aes.BlockSize)if err != nil {return nil, err}return plaintext, nil
}func pkcs7Pad(data []byte, blockSize int) []byte {padding := blockSize - len(data)%blockSizepadText := bytes.Repeat([]byte{byte(padding)}, padding)return append(data, padText...)
}func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {length := len(data)if length == 0 {return nil, errors.New("empty data")}if length%blockSize != 0 {return nil, errors.New("invalid data length")}padding := int(data[length-1])if padding > blockSize || padding == 0 {return nil, errors.New("invalid padding")}for i := 1; i <= padding; i++ {if data[length-i] != byte(padding) {return nil, errors.New("invalid padding")}}return data[:length-padding], nil
}

代码还会出现一些问题,粘包问题,不能多个连接等问题

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

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

相关文章

WebView头部添加android原生视频播放

需求&#xff1a;我们需要做一个h5页面&#xff0c;并且可以现实加载更多&#xff0c;并且头部我们想要加一个视频播放器&#xff0c;因为h5不够丝滑。 话不多说咱们直接上代码 Xml布局&#xff1a; <?xml version"1.0" encoding"utf-8"?> <…

SQL之收集SQL Server线程等待信息

要知道线程等待时间是制约SQL Server效率的重要原因&#xff0c;这一个随笔中将学习怎样收集SQL Server中的线程等待时间&#xff0c;类型等信息&#xff0c;这些信息是进行数据库优化的依据。 sys.dm_os_wait_stats 这是一个系统视图&#xff0c;里面存储线程所遇到的所有的等…

3D深度视觉与myCobot 320机械臂无序抓取

今天我记录使用myCobot320 M5跟FS820-E1深度相机进行一个无序抓取物体的分享。 为什么会选择深度相机和机械臂做一个案例呢&#xff1f; 2D相机&#xff08;最常见使用的相机&#xff09;可以捕捉二维图像&#xff0c;也就是在水平和垂直方向上的像素值。它们通常用于拍摄静态…

关于IE11的样式兼容问题记录

1、布局如下 <div class"map-label" :class"{active:isActive}" :style"{marginTop:${marTopVal}px}"><transition name"slide-fade"><div class"transition-out" v-if"show"><div class&q…

地图可视化开发的平台如何选择?

地图数据的日益丰富和人们对数据可视化需求不断提高&#xff0c;地图可视化已经成为了信息化建设中重要的组成部分&#xff0c;在各个行业和领域中都有广泛的应用。地图可视化开发平台选择至关重要&#xff0c;不仅会影响到可视化效果&#xff0c;还会影响到开发难度、维护成本…

OpenCV:深入Feature2D组件——角点检测

角点检测 1 Harris角点检测1.1 兴趣点与角点1.2 角点检测1.3 harris角点检测1.4 实现harris角点检测&#xff1a;cornerHarris()函数1.5 综合案例&#xff1a;harris角点检测与测绘 2. Shi—Tomasi角点检测2.1Shi—Tomasi角点检测概述2.2 确定图像强角点&#xff1a;goodFeatur…

leetcode 21.合并两个有序链表

⭐️ 往期相关文章 &#x1f4ab;链接1&#xff1a;链表中倒数第k个结点(快慢指针问题) &#x1f4ab;链接2&#xff1a;leetcode 876.链表的中间结点(快慢指针问题) &#x1f4ab;链接3&#xff1a;leetcode 206.反转链表 &#x1f4ab;链接4&#xff1a;leetcode 203.移除链…

RabbitMQ入门指南

人生永没有终点。只有等到你瞑目的那一刻&#xff0c;才能说你走完了人生路&#xff0c;在此之前&#xff0c;新的第一次始终有&#xff0c;新的挑战依然在&#xff0c;新的感悟不断涌现。 文章目录 一、MQ与RabbitMQ概述1. MQ简述2. MQ的优势3. MQ的劣势4. 常见的MQ产品5. Rab…

FastDFS【SpringBoot操作FastDFS、复习】(三)-全面详解(学习总结---从入门到深化)

目录 SpringBoot操作FastDFS 复习&#xff1a; SpringBoot操作FastDFS 由GitHub大牛tobato在原作者YuQing与yuqih发布的JAVA客户端基 础上进行了大量重构工作&#xff0c;并于GitHub上发布了FastDFS-Client1.26.5。 主要特性 1 对关键部分代码加入了单元测试&#xff0c;便于…

使用家庭宽带和摄像头,实现公网直播

那天去逛商场看到有个营业厅&#xff0c;本想进去问问有没有存话费送话费的活动&#xff0c;结果被忽悠办了一个19.9升千兆宽带加送一个路由器的业务。 网络环境验证 听他们说现在家庭宽带都是有公网IPV6地址的&#xff0c;立马用电脑试了下确实有IPV6地址。 赶紧随便写了几行…

C++ 数据结构图(1)

1. 图的基本概念 图是由顶点集合及顶点间的关系组成的一种数据结构&#xff1a;G (V&#xff0c; E) &#xff0c;其中&#xff1a; 顶点集合 V {x|x 属于某个数据对象集 } 是有穷非空集合 &#xff1b; E {(x,y)|x,y 属于 V} 或者 E {<x, y>|x,y 属于 V &&…

那些无法避免的弯路

近日&#xff0c;某高校毕业生在校期间窃取学校内网数据&#xff0c;收集全校学生个人隐私信息的新闻引发了人们对互联网生活中个人信息安全问题的再度关注。在大数据时代&#xff0c;算法分发带来了隐私侵犯&#xff0c;在享受消费生活等便捷权利的同时&#xff0c;似乎又有不…