Go编写流量代理工具

news/2024/10/18 3:28:57/

目录

    • 这是一个演示
    • 主要分为俩包:
    • 流程:
    • 逻辑:(端口随意,本地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.ppmy.cn/news/702548.html

相关文章

硕士程序员被本科嘲讽,读研有啥用?

相信大家对于一件事感触越来越深&#xff0c;就是大家都在努力提高学历和自我认知水平&#xff0c;因为社会对人才的要求越来越严格&#xff0c;研究生毕业找不到好工作的一大把。但也有很多人的观点是本科毕业出来工作&#xff0c;至少在年龄上是有优势的&#xff0c;别人硕士…

韩信点兵-输出韩信至少拥有的士兵人数。

在中国数学史上&#xff0c;广泛流传着一个“韩信点兵”的故事&#xff1a;韩信是汉高祖刘邦手下的大将&#xff0c;他英勇善战&#xff0c;智谋超群&#xff0c;为汉朝建立了卓越的功劳。据说韩信的数学水平也非常高超&#xff0c;他在点兵的时候&#xff0c;为了知道有多少兵…

C语言题目:韩信点兵

题目&#xff1a;韩信点兵&#xff0c;韩信带1500名士兵打仗&#xff0c;战死四五百人&#xff0c;站3人一排&#xff0c;多出2人&#xff1b;站5人一排&#xff0c;多出4人&#xff1b;站7人一排&#xff0c;多出6人。编程计算还有多少士兵&#xff1f; 死四五百人&#xff0c…

文明与征服武则天邪道阵容,教你以弱胜强(T3打T4)

英雄搭配 先来介绍下英雄和技能搭配 海尔曼&#xff1a;瑞士卫队&#xff0c;固守坚城 汉尼拔&#xff1a;毒蝎反噬&#xff0c;阿萨辛派刺客 武则天&#xff1a;斯巴达的壁垒&#xff0c;陷阵之志 古卷技能搭配&#xff1a;兵种克制 无畏奋战 战争宣言(主要就是刷战功&#…

王者荣耀坦克位思路和上分必读知识

如果仅仅为了冲王者&#xff0c;而不考虑大杀四方的快感&#xff0c;使用坦克位是条捷径。升王者的路上我有许多会玩的打野和射手都卡在铂金和钻石&#xff0c;正是因为没有一个靠谱坦克配合&#xff0c;总是卡在进阶段上不去。与其一群ad喊来个坦克&#xff0c;不如自已当坦带…

VIVADO ERROR:[Opt 31-30] Blackbox xxx is driving pin D of primitive cell xxx.

ERROR: [Opt 31-30] Blackbox xxx is driving pin D of primitive cell xxx. This blackbox cannot be found in the existing library. vivado问题 vivado综合通过&#xff0c;实现报错&#xff1a; ERROR: [Opt 31-30] Blackbox xxx is driving pin D of primitive cell xx…

anaconda 2023.3 win10 安装,镜像配置,存储路径更改超详细教程

现在 ai 很火&#xff0c;做 ai 开发&#xff0c;离不开 python&#xff0c;anaconda 是 python 开发的重要工具&#xff0c;这里介绍一下 anaconda 最新版的 2023.3 在 win10 的安装&#xff0c;镜像配置&#xff0c;以及虚拟环境存储路径的更改。 1. anaconda 2023.3 安装 …

MacBook Air连不上家里Wi-Fi,但手机平板都可以

MacBook Air连不上家里Wi-Fi&#xff0c;但手机平板都可以 家里面Wi-Fi更换以后&#xff0c;MacBook Air连不上家里Wi-Fi&#xff0c;但手机平板都可以&#xff0c;一开始以为是电脑问题&#xff0c;把所有方法都尝试后发现没有用&#xff0c;电脑可以连接其他Wi-Fi&#xff0…