go 新手,想用 go 语言实现一个简单的 http 抓包工具,按照需求输出 http request 和 response 的结果,类似格式这样:
2018-01-03 16:34:04.511 192.168.0.3 100.72.13.1 > GET safedog.jcloud.com /safeCenter/uai.html?data=GNFltny5lT4wKmP2kV2UN1fuUwbJXlYYVxevSodjDvmkP%2FgC2MWX7XW%2FzkKx420J4C6tz60iDO38TevM1%2FKwz2Eho53wA95naaSa1YehC431nZ3MvKWcQvRNt9Dt%2BGatstbZ1EeAGCqFCAvpwUfdPw%3D%3D HTTP/1.1 - -
2018-01-03 16:34:04.516 100.72.13.1 192.168.0.3 < - - - HTTP/1.1 200 OK
这个 https://github.com/google/gopacket 实现了 libcap 抓包封装,然后官方有个 https://github.com/google/gopacket/tree/master/examples/httpassembly 例子 它在 run 函数里面用了 go 的 net/http.ReadRequest 来解析流量数据,在 BPF 设置为'tcp and dst port 80'过滤是没问题的,但是如果想要同时解析 request,response 会报错。于是我又用了 http.ReadResponse 这个函数,单独测试的时候这个函数解析 response 包是没问题的,但要两个同时使用来解析同一个 tcp 流的数据,单纯的 if else 结构判断会报错或者导致 cpu 占满,代码如下:
func (h *httpStream) run() {
buf := bufio.NewReader(&h.r)
for {
req, err1 := http.ReadRequest(buf)
if (err1 != io.EOF && err1 == nil) {
req.Body.Close()
log.Println("Received request from stream", h.net, h.transport, ":", req.Host)
}
resp, err2 := http.ReadResponse(buf, req)
if (err2 != io.EOF && err2 == nil) {
resp.Body.Close()
log.Println("Received response from stream", h.net, h.transport, ":", resp.Status)
}
}
}
官方的函数是这个:
func (h *httpStream) run() {
buf := bufio.NewReader(&h.r)
for {
req, err := http.ReadRequest(buf)
if err == io.EOF {
// We must read until we see an EOF... very important!
return
} else if err != nil {
log.Println("Error reading stream", h.net, h.transport, ":", err)
} else {
bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
req.Body.Close()
log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body")
}
}
}
我试过在 else if err != nil 这个分支进行 response 判断,还是有问题。不是很懂这个逻辑需要怎么弄,希望各位大神指点一二。 还有是不是跟它用到的 github.com/google/gopacket/tcpassembly 这个 tcp 流重组有关系,说实话源码工作流程没怎么看懂。
1
goofool 2018-01-04 09:21:26 +08:00
ReadRequest 不是已经把 buf 读完了吗,ReadResponse 有东西读吗
|