/
pcap2json.go
115 lines (101 loc) · 2.94 KB
/
pcap2json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package main
import (
"github.com/akrennmair/gopcap"
"fmt"
"os"
"bufio"
"bytes"
"json"
"strings"
"http"
)
var out *bufio.Writer
var errout *bufio.Writer
// structure of JSON-serialised packets
type RequestPacket struct {
SrcIp string
DestIp string
SrcPort uint16
DestPort uint16
Time string
Flags string
// TODO: consider providing alternate for serialisation
Request *http.Request
}
// Begin capturing network traffic, returning a handle on the capture which can
// be used to process captured packets.
func OpenCaptureOrDie() *pcap.Pcap {
devs, err := pcap.Findalldevs()
if err != "" {
fmt.Fprintf(errout, "pcap2json: couldn't find any devices: %s\n", err)
}
if 0 == len(devs) {
os.Exit(1)
}
// TODO: support passing preferred device through on the cmd-line
h, err := pcap.Openlive(devs[0].Name, 65535, true, 0)
if h == nil {
fmt.Fprintf(errout, "pcap2json: %s\n", err)
errout.Flush()
}
// TODO: support passing filter through on the cmd-line
expr := "not port 22"
ferr := h.Setfilter(expr)
if ferr != "" {
fmt.Fprintf(out, "pcap2json: %s\n", ferr)
out.Flush()
}
return h
}
// Return the string representation of the provided packet.
func PacketAsString(pkt *pcap.Packet) string {
buf := bytes.NewBufferString("")
for i := uint32(0); i < pkt.Caplen; i++ {
fmt.Fprintf(buf, "%c", pkt.Data[i])
}
return string(buf.Bytes())
}
// Attempt to extract an HTTP request from the packet and serialise with the
// provided encoder.
func SerialisePacket(pkt *pcap.Packet, enc *json.Encoder) {
// TODO: serialise packet details for non-HTTP packets
// TODO: naively assumes requests are contained in a single packet and
// HTTP verbs are not otherwise contained in a request
// rfc 2616
httpMethods := [...]string{"OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"}
// TODO: IPv6
if hdr, ok := pkt.Headers[0].(*pcap.Iphdr); ok {
if tcpHdr, ok := pkt.Headers[1].(*pcap.Tcphdr); ok {
// TODO: Consider peeking at other ports
if tcpHdr.DestPort == 80 {
pktString := PacketAsString(pkt)
for _, verb := range httpMethods {
if strings.Contains(pktString, verb) {
unparsedReqs := strings.Split(pktString, verb)
for _, unparsed := range unparsedReqs {
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(verb + unparsed)))
// TODO: serialise details of packets we fail to parse
if err == nil {
rp := RequestPacket{hdr.SrcAddr(), hdr.DestAddr(), tcpHdr.SrcPort, tcpHdr.DestPort, pkt.TimeString(), tcpHdr.FlagsString(), req}
enc.Encode(rp)
}
}
}
}
}
out.Flush()
}
}
}
// Open a capture session, attempt to parse any requests to port 80 and
// serialise to stdout as JSON.
func main() {
out = bufio.NewWriter(os.Stdout)
errout = bufio.NewWriter(os.Stderr)
c := OpenCaptureOrDie()
enc := json.NewEncoder(os.Stdout)
for pkt := c.Next(); pkt != nil; pkt = c.Next() {
pkt.Decode()
SerialisePacket(pkt, enc)
}
}