Golang制作简单代理定制

魔王大柚子 · · 338 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

当前有很多代理工具,例如fiddler(免费),charles(收费)等,他们功能都非常强大,为什么我还要自己做自己的代理工具呢?
我也不晓得,就是有些功能自定义可能使用方便一些吧大概

  1. 首先go环境的搭建,就不在赘述,我使用vscode+go的一些组件,安装方法参考:https://blog.csdn.net/AdolphKevin/article/details/105480530
  2. 首先初始化mod
go mod init mitm
#会生成一个go.mod的文件
  1. 使用github上现成的编写自己的自定义
package proxy

import (
    "bytes"
    "crypto/tls"
    "github.com/ouqiang/goproxy"
    "io/ioutil"
    "log"
    "mitm/util"
    "mitm/watcher"
    "net/http"
    "net/url"
    "os"
    "strings"
    "sync"
    // "time"
)

// Cache 实现证书缓存接口
type Cache struct {
    m sync.Map
}

//Set 234
func (c *Cache) Set(host string, cert *tls.Certificate) {
    c.m.Store(host, cert)
}

//Get 123
func (c *Cache) Get(host string) *tls.Certificate {
    v, ok := c.m.Load(host)
    if !ok {
        return nil
    }

    return v.(*tls.Certificate)
}

//Conf 123
type Conf struct {
    NotConnectLIst      []string
    ReqHeaderMap        map[string]string
    ReqHostMap          map[string]string
    ResShowBodyList     []string
    ParentProxy         string
    ParentProxyShowList []string
    ResInfoMap          map[string]string //暂时不做

}

// ReadFile 123
func (c *Conf) ReadFile() {
    list := util.ReadFile("mitm.conf")
    // log.Println(list)
    c1 := &Conf{NotConnectLIst: make([]string, 0, 0), ReqHeaderMap: make(map[string]string), ReqHostMap: make(map[string]string), ResShowBodyList: make([]string, 0, 0), ResInfoMap: make(map[string]string), ParentProxy: "", ParentProxyShowList: make([]string, 0, 0)}
    for _, line := range list {
        if !strings.HasPrefix(line, "#") && len(strings.TrimSpace(line)) > 0 {
            lineInner := strings.Split(line, "=")
            switch lineInner[0] {
            case "NotConnect":
                context := strings.Replace(line, "NotConnect=", "", 1)
                c1.NotConnectLIst = append(c1.NotConnectLIst, context)
            case "ReqHeader":
                c1.ReqHeaderMap[lineInner[1]] = lineInner[2]
            case "ReqHost":
                c1.ReqHostMap[lineInner[1]] = lineInner[2]
            case "ResBody":
                c1.ResShowBodyList = append(c1.ResShowBodyList, strings.Replace(line, "ResBody=", "", 1))
            case "Proxy":
                c1.ParentProxy = strings.Replace(line, "Proxy=", "", 1)
            case "ProxyShow":
                c1.ParentProxyShowList = append(c1.ParentProxyShowList, strings.Replace(line, "ProxyShow=", "", 1))

            }
        }
    }
    c.compare(c1)
    // c.ParentProxy = c1.ParentProxy
    // log.Println(c)
    // log.Println(c1)
    c.NotConnectLIst = c1.NotConnectLIst
    c.ReqHeaderMap = c1.ReqHeaderMap
    c.ReqHostMap = c1.ReqHostMap
    c.ResShowBodyList = c1.ResShowBodyList
    c.ParentProxy = c1.ParentProxy
    c.ParentProxyShowList = c1.ParentProxyShowList

}
func (c *Conf) compare(c1 *Conf) {
    // log.Println(c1)
    // log.Println(c)

    if c.ParentProxy != c1.ParentProxy {
        log.Println("修改代理地址:" + ",从" + c.ParentProxy + "修改成" + c1.ParentProxy)
    }
    for _, con := range c.NotConnectLIst {
        isC := false
        for _, con1 := range c1.NotConnectLIst {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("拒绝连接移除:" + con)
        }
    }
    for _, con := range c1.NotConnectLIst {
        isC := false
        for _, con1 := range c.NotConnectLIst {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("拒绝连接添加:" + con)
        }
    }
    for _, con := range c.ParentProxyShowList {
        isC := false
        for _, con1 := range c1.ParentProxyShowList {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("代理可见移除:" + con)
        }
    }
    for _, con := range c1.ParentProxyShowList {
        isC := false
        for _, con1 := range c.ParentProxyShowList {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("代理可见添加:" + con)
        }
    }
    for _, con := range c.ResShowBodyList {
        isC := false
        for _, con1 := range c1.ResShowBodyList {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("相应结果移除:" + con)
        }
    }
    for _, con := range c1.ResShowBodyList {
        isC := false
        for _, con1 := range c.ResShowBodyList {
            if con == con1 {
                isC = true
            }

        }
        if !isC {
            log.Println("相应结果添加:" + con)
        }
    }
    for k, v := range c.ReqHeaderMap {
        v1, ok := c1.ReqHeaderMap[k]
        if !ok {
            log.Println("移除:" + k + v)
        } else {
            if v != v1 {
                log.Println("修改:" + k + "值,从" + v + "修改成" + v1)
            }
        }

    }
    for k, v := range c1.ReqHeaderMap {
        _, ok := c.ReqHeaderMap[k]
        if !ok {
            log.Println("添加:" + k + v)
        }

    }
    for k, v := range c.ReqHostMap {
        v1, ok := c1.ReqHostMap[k]
        if !ok {
            log.Println("移除:" + k + v)
        } else {
            if v != v1 {
                log.Println("修改:" + k + "值,从" + v + "修改成" + v1)
            }
        }

    }
    for k, v := range c1.ReqHostMap {
        _, ok := c.ReqHostMap[k]
        if !ok {
            log.Println("添加:" + k + v)
        }

    }

}

func match(source string, filter string) bool {
    tmp := strings.TrimSpace(filter)
    if len(tmp) > 0 && tmp != "all" && tmp != "ALL" {
        sign := tmp[:1]
        switch sign {
        case "!":
            return source != tmp[1:]
        case "^":
            return !strings.Contains(source, tmp[1:])
        case "~":
            return strings.Contains(source, tmp[1:])
        default:
            return source == tmp[1:]
        }

    } else {
        return true
    }
}

// CheckFile 123
func (c *Conf) CheckFile() {

    if util.IsExist("mitm.conf") {
        c.ReadFile()
    } else {
        f, err := os.Create("mitm.conf")
        defer f.Close()
        if err != nil {
            // 创建文件失败处理
            panic(err)

        } else {
            confsource := `#!V0.0.1
#对于mitm的配置文件,使用键值对的模式
#匹配规则
#!xxx 不等于xxx
#^xxx 不含有xxx
#~xxx 含有xxx
#xxx  等于xxx 
#'#'开头注释一行
#可配置的规则如下:
#
#拒绝连接部分,格式:NotConnect=匹配url
#NotConnect=~www.google.com
#添加/修改请求头,默认添加fromroute: mitm,格式:ReqHeader=匹配url/all=key: value
#ReqHeader=all=xxx: yyy
#ReqHeader=~www.baidu.c=xxx1: yyy1
#修改请求方法,格式:ReqMethod=匹配url/all=method,未完成!
#ReqMethod=~www.baidu.c=post
#修改请求url,格式:ReqURL=匹配url/all=method,未完成!
#ReqURL=~www.baidu.c=URL
#修改请求内容,格式:ReqBody=匹配url/all=Body,未完成!
#ReqBody=~www.baidu.c={"aa":1}
#修改请求域名,格式:ReqHost=匹配url/all=Host
#ReqHost=~www.baidu.c=xxx
#显示相应结果,格式:ResBody=xxx
#ResBody=~www.baidu.c
#配置二级代理路径,唯一性不可多配制,多配置读取最后配置
#Proxy=10.xx.xx.xx:xxxx
#配置二级代理可见url,格式:ProxyShow=xxx
#ProxyShow=~www.baidu.c
#配置二级代理不可见url,格式:ProxyNotShow=xxx,未完成!
#ProxyNotShow=~www.baidu.c
`
            _, err = f.Write([]byte(confsource))
            if err != nil {
                // 写入失败处理
                panic(err)

            }
            c.ReadFile()
        }
    }
}

var conf *Conf

// EventHandler 2134
type EventHandler struct{}

// Connect qwe
func (e *EventHandler) Connect(ctx *goproxy.Context, rw http.ResponseWriter) {


    // 禁止访问某个域名
    for _, con := range conf.NotConnectLIst {
        if match(ctx.Req.URL.String(), con) {
            rw.WriteHeader(http.StatusForbidden)
            ctx.Abort()
            return
        }
    }
    
}

//Auth 234
func (e *EventHandler) Auth(ctx *goproxy.Context, rw http.ResponseWriter) {
    // 身份验证
}

// BeforeRequest 234
func (e *EventHandler) BeforeRequest(ctx *goproxy.Context) {

    for k, v := range conf.ReqHeaderMap {
        if match(ctx.Req.URL.String(), k) {
            v1 := strings.Split(v, ": ")
            ctx.Req.Header.Set(v1[0], v1[1])
        }
    }

    for k, v := range conf.ReqHostMap {
        if match(ctx.Req.URL.String(), k) {
            ctx.Req.Host = v

            ctx.Req.URL.Host = v
        }
    }

    

    ctx.Req.Header.Set("fromroute", "mitm")


}

// BeforeResponse 2131
func (e *EventHandler) BeforeResponse(ctx *goproxy.Context, resp *http.Response, err error) {
    if err != nil {
        log.Println("Perr:", err)
        return
    }

    // 修改response
    for _, con := range conf.ResShowBodyList {
        if match(ctx.Req.URL.String(), con) {
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                // 错误处理
                log.Println(err)
                return
            }
            var str string = string(body[:])
            log.Println(str)
            resp.Body = ioutil.NopCloser(bytes.NewReader(body))
        }
    }

    

}

// ParentProxy 设置上级代理
func (e *EventHandler) ParentProxy(req *http.Request) (*url.URL, error) {
    // return req.URL, nil
    // log.Println(conf)
    res := true
    for _, con := range conf.ParentProxyShowList {
        if !match(req.URL.String(), con) {
            res = false
            break

        }
    }
    if res {
        if conf.ParentProxy != "" {
            if strings.HasPrefix(conf.ParentProxy, "http://") {
                return url.Parse(conf.ParentProxy)
            }
            return url.Parse("http://" + conf.ParentProxy)

        }

    }

    return nil, nil

}

// Finish 13
func (e *EventHandler) Finish(ctx *goproxy.Context) {
    // fmt.Printf("请求结束 URL:%s\n", ctx.Req.URL)

}

// ErrorLog 记录错误日志
func (e *EventHandler) ErrorLog(err error) {
    // log.Println("err:", err)
}

// GetProxy 代理的处理
func GetProxy() *goproxy.Proxy {
    conf = &Conf{NotConnectLIst: make([]string, 0, 0), ReqHeaderMap: make(map[string]string), ReqHostMap: make(map[string]string), ResShowBodyList: make([]string, 0, 0), ResInfoMap: make(map[string]string), ParentProxy: "", ParentProxyShowList: make([]string, 0, 0)}
    conf.CheckFile()
    go watcher.WatchFile("mitm.conf", conf.ReadFile)
    return goproxy.New(goproxy.WithDelegate(&EventHandler{}), goproxy.WithDecryptHTTPS(&Cache{}))

}


  1. 由于本人技术低,使用fsnotify发现浪费大部分功能,就傻傻轮询文件
package watcher

import (
    "log"
    "mitm/util"
    "os"
    "time"
)

var mtime int64

// WatchFile 3424
func WatchFile(file string, f1 func()) {

    if !util.IsExist(file) {
        log.Fatal("no" + file)
        return
    }
    mtime = time.Now().Unix()
    for {
        mt := GetFileModTime(file)
        if mt > mtime {
            mtime = mt
            f1()
        } else {
            time.Sleep(time.Second * time.Duration(3))
        }
    }

}

//GetFileModTime 123
func GetFileModTime(path string) int64 {

    fi, err := os.Stat(path)
    if err != nil {
        log.Println("stat  error")
        return time.Now().Unix()
    }

    return fi.ModTime().Unix()
}

  1. https需要的证书
package cert

import (
    "bufio"
    // "fmt"
    "log"
    "mitm/util"
    "os"
)

//AddCRT 添加证书
func AddCRT() {
    if !util.IsExist("./mitmproxy.crt") {
        datasource := `-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIBATANBgkqhkiG9w0BAQsFADBZMQ4wDAYDVQQGEwVDaGlu
YTEPMA0GA1UECBMGRnVKaWFuMQ8wDQYDVQQHEwZYaWFtZW4xDTALBgNVBAoTBE1h
cnMxFjAUBgNVBAMTDWdvLW1pdG0tcHJveHkwIBcNMTgwMzE4MDkwMDQ0WhgPMjA2
ODAzMTgwOTAwNDRaMFkxDjAMBgNVBAYTBUNoaW5hMQ8wDQYDVQQIEwZGdUppYW4x
DzANBgNVBAcTBlhpYW1lbjENMAsGA1UEChMETWFyczEWMBQGA1UEAxMNZ28tbWl0
bS1wcm94eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANiuppEbanTv
iCs47AFIAy+AVXDhaInal4fGmN+kG1txO4YPygKGrdjokCZtkL6ZK61izFg6BLX+
p65j8wnAPZPZr3Zu5vlcDM7baO9ddxtnXm/fACPEuMIvgmG7zxE9CeX3LY7tsq10
hg8uKMnYGTy5Ce0hkuYn8Od0yHseGFWCmaCAHIcshbvQFxPGn42X/zWrEHDEgWtG
fOlamBBTSbNza11H8udLkXlr+N+vv/P/eKjpeIf/xzPCdiUOxdD+NHCeeSgho3Sm
P0T6ia4L7MVW0XUg7CseVVh+9TddO6QefmM1+AsWU/ektD+cUMtlWoDXE8idlpoZ
cMVJfq/6Sa9nG280fCPjd4wFLqbR67BHQkoPjQ1vmRgs4xvD04m796dRPpTDepb/
xvTTMcwgAC5tur/E5SHpr8hx9X6xGPfUUMiKyBQlSgLH4V02SjAmScxqt5AWZcT/
syLHg7BhjxwBGoCwcE8zWHCJarQ0t28Z7ptyL3DXPaJ7Vd2CvLJrekvtnm9B28aU
9KOC9JL3DKzFaRrhTYb0VNLfoLV8kRJCzZI6HAwiKcAAEIXi8on6YwqLvEIxo5AL
0gTeIf/nJU2W4OY640fIdwEvcaH4Wj2bKMRaTWvQGM1TJe4hoCN/c3mVopotCb44
IGC5R0XmVImVxZmdyCXJAfY1jYrWHA2ZAgMBAAGjRTBDMA4GA1UdDwEB/wQEAwIB
BjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSfjEyzebvckLQu+eZjlmJF
W0/ZmjANBgkqhkiG9w0BAQsFAAOCAgEAXHGvSFwtcqX6lD9rmLTPeViTIan5hG5T
sEWsPp/kI6j579OavwCr7mk4nUjsKFaOEzN78C4k6s4gDWWePoJhlsXB4KefriW4
gWevzwgRTrIlaw3EDb+fCle0HcsCI/wwxDD8eafUxEyqBGrhLJFiUIxvOcD+yP2Q
mX3Z89Pd38Qvkb9zneJdXo2wHMq0MGKlTPdE04rg1OsuPNnSwRhtem9/E4eCtumF
JoQEQtp440wpvrbZljR18Ahd+xNh6dyaD0prnrUEGsUkC1hMb3nUWmw6dZEA5rCv
8aW5ZMm9Jr7pW7yzrm8J4II1bY5v6i7+qvOFDAf1nEnVshcSCiHu6xzgtwoGtsP8
mSOquiWwiceJL6q8xh6nOD3SYm2mZwA1n7Nl3mRJE/RgbwJNkveMrmZ6CKUm3N/x
eqd5yhTLsD7sf3+d4B7i6fAZ+csccWaDuquVI9cXi2OoMKgIFeeVwJ1FCeLY0Nah
nPlNUA0h7xKeDIHtlGsSOng6uiEVVVXGS+j9V6h+Z55AsuOAoHYOBDoXfr0Y4Bww
irCRNyFcDrKoyILOOUiPxoEcclrwUBTB78JxVA8xKTbAh0aZQRZOZOz49qF4gA1d
1riiUHJIG2sD+54UEdFoR5nhZ4/RLGqQ/Kmch5VnPp7De4OzSMd/KkQDWEjR+AA1
CDPlL4gNB6s=
-----END CERTIFICATE-----
`
        f, err := os.Create("./mitmproxy.crt")
        defer f.Close()
        if err != nil {
            // 创建文件失败处理
            panic(err)

        } else {
            _, err = f.Write([]byte(datasource))
            if err != nil {
                // 写入失败处理
                panic(err)

            }
        }
        log.Println("已创建https证书,请安装至根目录:mitmproxy.crt\n回车关闭~")
        reader := bufio.NewReader(os.Stdin)
        reader.ReadString('\n')
        os.Exit(1)
    }
}

  1. 最后执行的main
package main

import (

    // "github.com/fsnotify/fsnotify"
    "log"
    "mitm/cert"
    "mitm/proxy"
    "net/http"
    "time"
)

func main() {
    // log.Println("代理地址:http://当前机器ip:8088")

    cert.AddCRT()
    log.Println("代理地址:http://当前机器ip:8088")

    server := &http.Server{
        Addr:         ":8088",
        Handler:      proxy.GetProxy(),
        ReadTimeout:  1 * time.Minute,
        WriteTimeout: 1 * time.Minute,
    }
    // log.Println("代理地址:http://当前机器ip:8088")
    err := server.ListenAndServe()
    if err != nil {
        panic(err)
    }
    // log.Println("代理地址:http://当前机器ip:8088")
}

  1. 其他
    问我这些还不够,还有一个util呢?只是实现了读取文件和判断文件在不在的方法
package util

import (
    "bufio"
    "log"
    "os"
    "strings"
)

// IsExist 判断文件是否存在
func IsExist(fileAddr string) bool {
    // 读取文件信息,判断文件是否存在
    _, err := os.Stat(fileAddr)
    if err != nil {
        // log.Println(err)
        if os.IsExist(err) { // 根据错误类型进行判断
            return true
        }
        return false
    }
    return true
}

//ReadFile 123
func ReadFile(file string) []string {
    files, err := os.Open(file)
    if err != nil {
        log.Fatal(err)
    }
    defer files.Close()
    scanner := bufio.NewScanner(files)
    list := make([]string, 0, 0)
    for scanner.Scan() {

        lineText := strings.TrimSpace(scanner.Text())

        if lineText != "" {
            list = append(list, lineText)
        }

    }
    return list
}


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:魔王大柚子

查看原文:Golang制作简单代理定制

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

338 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传