Golang制作简单代理定制

当前有很多代理工具,例如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
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容