文档章节

用golang写一个http代理,可以抓包和科学上网

sheepbao
 sheepbao
发布于 2016/06/15 17:57
字数 1091
阅读 1515
收藏 35

用golang写一个http代理,可以抓包和科学上网

##缘起

因为在工作中需要对上网进行限制,只让我们的app上网,意思就是放行app请求的所有域名或ip,而其他域名都禁止,所以我需要对app的http请求进行抓包。上网搜了一下,win下的fiddler不错,可惜我用的是Linux系统,fiddler不跨平台,找了下linux下的抓包软件,当然tcpdump和wireshare是足够强大的,完全可以实现我要的小小要求,但用起来有一定的复杂性,门槛稍高。在网上找到其他类似的软件还挺多,charles、NProxy等,最后发现mitmproxy最符合我的胃口,如图: mitproy 但是mitmproxy的安装依赖太多,python就是这样,一不小心就报错了,so,想着用golang实现类似的功能,自己也很喜欢go语言,如果有空能安静写写自己喜欢的代码,是多么的幸福。想好了就实践,Let's do it,当然凡事都有个从简到繁的过程,下面的特性慢慢增加。

##特性 特性,也可以说功能,以下是实现或者将要实现的特性

  • [X] http代理
  • [X] http请求响应的抓取
  • [ ] 修改http请求
  • [ ] 重复请求
  • [ ] 同时监听多端口
  • [ ] 支持socks5、websocket、https协议
  • [ ] 界面支持终端和网页两种形式

##安装

git clone https://github.com/sheepbao/gomitmproxy.git
cd gomitmproxy 
go build 

##使用

  • http代理
gomimtproxy 

不带任何参数,表示http代理,默认端口8080,更改端口用 -port

  • http抓包
gomimtproxy -m 

加 -m 参数,表示抓取http请求和响应 fetch http

  • http代理科学上网

    首先你得有个墙外的服务器,如阿里香港的服务器,为图中的Server,假设其ip地址为:22.222.222.222

在Server执行:
gomitmproxy -port 8888
在你自己电脑执行:
gomitmproxy -port 8080 -raddr 22.222.222.222:8888

然后浏览器设置代理,ip为localhost,端口为8080,即可实现科学上网

proxy

###源码简析

对于网络编程,Anything is a socket!
实现http代理并不难,简单地说就是用代理服务器代替客户端去请求web服务,然后在把请求的结果返回给客户端。
先来个示意图:

proxy-2

1. 客户端发起一个到gomitmproxy的连接,并且提交了HTTP CONNECT请求。 
2. gomitmproxy返回200表示连接已经建立。 
    if req.Method == "CONNECT" {
        b := []byte("HTTP/1.1 200 Connection Established\r\n" +
            "Proxy-Agent: golang_proxy/" + Version + "\r\n\r\n")
        _, err := conn.Write(b)
        if err != nil {
            logger.Println("Write Connect err:", err)
            return
        }
    } else {
        req.Header.Del("Proxy-Connection")
        req.Header.Set("Connection", "Keep-Alive")
        if err = req.Write(conn_proxy); err != nil {
            logger.Println("send to server err", err)
            return
        }
    }

3. gomitmproxy连接服务器的host,并将客户端的请求发送给服务器,如果没加 -m 参数,那直接将客户端和服务器的io口通过gomitmproxy连接,gomitmproxy很像水管的连接器,把断开的水管连接起来。
//两个io口的连接
func Transport(conn1, conn2 net.Conn) (err error) {
    rChan := make(chan error, 1)
    wChan := make(chan error, 1)

    go MyCopy(conn1, conn2, wChan)
    go MyCopy(conn2, conn1, rChan)

    select {
    case err = <-wChan:
    case err = <-rChan:
    }

    return
}

func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) {
    _, err := io.Copy(dst, src)
    ch <- err
}
如果加了 -m 参数,表示要截取http请求和响应。
//打印http请求和响应
func httpDump(req *http.Request, resp *http.Response) {
    defer resp.Body.Close()
    var respStatusStr string
    respStatus := resp.StatusCode
    respStatusHeader := int(math.Floor(float64(respStatus / 100)))
    switch respStatusHeader {
    case 2:
        respStatusStr = Green("<--" + strconv.Itoa(respStatus))
    case 3:
        respStatusStr = Yellow("<--" + strconv.Itoa(respStatus))
    case 4:
        respStatusStr = Magenta("<--" + strconv.Itoa(respStatus))
    case 5:
        respStatusStr = Red("<--" + strconv.Itoa(respStatus))
    }
    fmt.Println(Green("Request:"))
    fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr)
    for headerName, headerContext := range req.Header {
        fmt.Printf("%s: %s\n", Blue(headerName), headerContext)
    }
    fmt.Println(Green("Response:"))
    for headerName, headerContext := range resp.Header {
        fmt.Printf("%s: %s\n", Blue(headerName), headerContext)
    }

    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        logger.Println("read resp body err:", err)
    } else {
        acceptEncode := resp.Header["Content-Encoding"]
        var respBodyBin bytes.Buffer
        w := bufio.NewWriter(&respBodyBin)
        w.Write(respBody)
        w.Flush()
        for _, compress := range acceptEncode {
            switch compress {
            case "gzip":
                r, err := gzip.NewReader(&respBodyBin)
                if err != nil {
                    logger.Println("gzip reader err:", err)
                } else {
                    defer r.Close()
                    respBody, _ = ioutil.ReadAll(r)
                }
                break
            case "deflate":
                r := flate.NewReader(&respBodyBin)
                defer r.Close()
                respBody, _ = ioutil.ReadAll(r)
                break
            }
        }
        fmt.Printf("%s\n", string(respBody))
    }

    fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################"))
}

4. gomitmproxy将请求服务器的响应发送给客户端。
        resp, err := http.ReadResponse(bufio.NewReader(conn_proxy), req)
        if err != nil {
            logger.Println("read response err:", err)
            return
        }

        respDump, err := httputil.DumpResponse(resp, true)
        if err != nil {
            logger.Println("respDump err:", err)
        }

        _, err = conn.Write(respDump)
        if err != nil {
            logger.Println("conn write err:", err)
        }

总结

源码放在github上了,欢迎各位指导和贡献!

The more you know, the more you know you don't know!

© 著作权归作者所有

sheepbao

sheepbao

粉丝 7
博文 18
码字总数 16873
作品 1
深圳
后端工程师
私信 提问
加载中

评论(6)

thinkgood
thinkgood

引用来自“宅男小何”的评论

没做认证吗?

引用来自“sheepbao”的评论

谢谢提意见,http认证要加很容易,在http header加,等我加上https就更安全了
代理服务器很容易写,关键是https, 很期待楼主的大作
macnie
macnie

引用来自“红薯”的评论

代码放到码云上呗 git.oschina.net

马云都打不开啦!
sheepbao
sheepbao 博主

引用来自“宅男小何”的评论

没做认证吗?
谢谢提意见,http认证要加很容易,在http header加,等我加上https就更安全了
sheepbao
sheepbao 博主

引用来自“红薯”的评论

代码放到码云上呗 git.oschina.net
可以滴,在码云也有账号
宅男小何
宅男小何
没做认证吗?
红薯
红薯
代码放到码云上呗 git.oschina.net
http代理软件--go-proxy

goproxy是一个由golang编写的http代理软件。包含了两段代理,代理流程为 浏览器->本地代理(加密数据)->远程代理->真实远程服务器 。在代理的同时保证了数据传输的安全。可用于科学上网。简...

陆奕言
2015/01/03
6.1K
1
Golang 语言实现的 Mitmproxy--GomitmProxy

GomitmProxy是想用golang语言实现的mitmproxy,主要实现http代理,目前实现了http代理和https抓包功能。 安装使用 git clone https://github.com/sheepbao/gomitmproxy.git cd gomitmproxy g...

sheepbao
2016/07/04
255
0
利用Fiddler对Android模拟器网络请求进行抓包【转】

在Android的开发调试过程中,特别是针对网络编程的情况,很多时候我们希望能够对Android的网络请求进行抓包,用来定位以及分析我们程序的问题。下面我介绍使用Fiddler对Android模拟器的网络请...

拉风的道长
2016/01/18
467
0
Windows 10 Metro Apps使用Proxifier的方法

在校园里,经常要使用代理上网的方式,Proxifier可以让不支持通过代理服务器的软件能通过HTTPS或SOCKS代理或代理链,俗称“透明代理”,支持Xp,Vista,Windows 7操作系统。虽然Proxifier在W...

xiaoxin
2016/02/17
3.3K
0
用SS+Privoxy+树莓派让Node爬虫科学上网

前言 之前一直想要做一个feed读取器,并利用feed获取的链接去抓取资讯全文。然而有一些feed需要科学上网才能获取,这就让我萌生了:如何让Node科学上网来进行爬虫抓取。 查阅了资料发现,Nod...

尺子先生
2017/05/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

不就是SELECT COUNT语句吗,竟然能被面试官虐的体无完肤

数据库查询相信很多人都不陌生,所有经常有人调侃程序员就是CRUD专员,这所谓的CRUD指的就是数据库的增删改查。 在数据库的增删改查操作中,使用最频繁的就是查询操作。而在所有查询操作中,...

HollisChuang
18分钟前
3
0
乐观锁和悲观锁

乐观锁和悲观锁 在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性的问题 事务的特性:原子性、一致性、隔离性、持久性 1、丢失修改:T1和T2两个事务对同一个数据进行修改,T1先...

大瑞清_liurq
26分钟前
5
0
Scrum敏捷价值观与原则

Scrum是一种迭代式增量软件开发过程,通常用于敏捷软件开发。如果还不知道Scrum敏捷开发的朋友们,请出门左转,点击 Scrum 了解。 敏捷价值观 个体和互动 高于 流程和工具 工作的软件 高于 ...

醉美閑聖
26分钟前
4
0
android焦点

final RelativeLayout relativeLayout=new RelativeLayout(context); relativeLayout.setClickable(true); relativeLayout.setFocusable(true); rel......

安卓工程师王恒
28分钟前
3
0
IP地址分配与中继设备简介

1. TCP/IP模型 TCP/IP协议是在OSI参考模型出现之间就被开发的,并广泛部署在计算机网络中。但是,后来由于概念混淆,TCP/IP模型的层次和名称往往与OSI模型的层次名称相互借用。如图1.所示。 ...

xiangyunyan
30分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部