diff options
Diffstat (limited to 'vendor/github.com/google/gopacket/examples')
13 files changed, 2154 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go new file mode 100644 index 0000000..1a0e33e --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/arpscan/arpscan.go @@ -0,0 +1,188 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// arpscan implements ARP scanning of all interfaces' local networks using +// gopacket and its subpackages. This example shows, among other things: +// * Generating and sending packet data +// * Reading in packet data and interpreting it +// * Use of the 'pcap' subpackage for reading/writing +package main + +import ( + "bytes" + "encoding/binary" + "errors" + "log" + "net" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" +) + +func main() { + // Get a list of all interfaces. + ifaces, err := net.Interfaces() + if err != nil { + panic(err) + } + + var wg sync.WaitGroup + for _, iface := range ifaces { + wg.Add(1) + // Start up a scan on each interface. + go func(iface net.Interface) { + defer wg.Done() + if err := scan(&iface); err != nil { + log.Printf("interface %v: %v", iface.Name, err) + } + }(iface) + } + // Wait for all interfaces' scans to complete. They'll try to run + // forever, but will stop on an error, so if we get past this Wait + // it means all attempts to write have failed. + wg.Wait() +} + +// scan scans an individual interface's local network for machines using ARP requests/replies. +// +// scan loops forever, sending packets out regularly. It returns an error if +// it's ever unable to write a packet. +func scan(iface *net.Interface) error { + // We just look for IPv4 addresses, so try to find if the interface has one. + var addr *net.IPNet + if addrs, err := iface.Addrs(); err != nil { + return err + } else { + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok { + if ip4 := ipnet.IP.To4(); ip4 != nil { + addr = &net.IPNet{ + IP: ip4, + Mask: ipnet.Mask[len(ipnet.Mask)-4:], + } + break + } + } + } + } + // Sanity-check that the interface has a good address. + if addr == nil { + return errors.New("no good IP network found") + } else if addr.IP[0] == 127 { + return errors.New("skipping localhost") + } else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff { + return errors.New("mask means network is too large") + } + log.Printf("Using network range %v for interface %v", addr, iface.Name) + + // Open up a pcap handle for packet reads/writes. + handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) + if err != nil { + return err + } + defer handle.Close() + + // Start up a goroutine to read in packet data. + stop := make(chan struct{}) + go readARP(handle, iface, stop) + defer close(stop) + for { + // Write our scan packets out to the handle. + if err := writeARP(handle, iface, addr); err != nil { + log.Printf("error writing packets on %v: %v", iface.Name, err) + return err + } + // We don't know exactly how long it'll take for packets to be + // sent back to us, but 10 seconds should be more than enough + // time ;) + time.Sleep(10 * time.Second) + } +} + +// readARP watches a handle for incoming ARP responses we might care about, and prints them. +// +// readARP loops until 'stop' is closed. +func readARP(handle *pcap.Handle, iface *net.Interface, stop chan struct{}) { + src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet) + in := src.Packets() + for { + var packet gopacket.Packet + select { + case <-stop: + return + case packet = <-in: + arpLayer := packet.Layer(layers.LayerTypeARP) + if arpLayer == nil { + continue + } + arp := arpLayer.(*layers.ARP) + if arp.Operation != layers.ARPReply || bytes.Equal([]byte(iface.HardwareAddr), arp.SourceHwAddress) { + // This is a packet I sent. + continue + } + // Note: we might get some packets here that aren't responses to ones we've sent, + // if for example someone else sends US an ARP request. Doesn't much matter, though... + // all information is good information :) + log.Printf("IP %v is at %v", net.IP(arp.SourceProtAddress), net.HardwareAddr(arp.SourceHwAddress)) + } + } +} + +// writeARP writes an ARP request for each address on our local network to the +// pcap handle. +func writeARP(handle *pcap.Handle, iface *net.Interface, addr *net.IPNet) error { + // Set up all the layers' fields we can. + eth := layers.Ethernet{ + SrcMAC: iface.HardwareAddr, + DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + EthernetType: layers.EthernetTypeARP, + } + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + Protocol: layers.EthernetTypeIPv4, + HwAddressSize: 6, + ProtAddressSize: 4, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(iface.HardwareAddr), + SourceProtAddress: []byte(addr.IP), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + } + // Set up buffer and options for serialization. + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + } + // Send one packet for every address. + for _, ip := range ips(addr) { + arp.DstProtAddress = []byte(ip) + gopacket.SerializeLayers(buf, opts, ð, &arp) + if err := handle.WritePacketData(buf.Bytes()); err != nil { + return err + } + } + return nil +} + +// ips is a simple and not very good method for getting all IPv4 addresses from a +// net.IPNet. It returns all IPs it can over the channel it sends back, closing +// the channel when done. +func ips(n *net.IPNet) (out []net.IP) { + num := binary.BigEndian.Uint32([]byte(n.IP)) + mask := binary.BigEndian.Uint32([]byte(n.Mask)) + num &= mask + for mask < 0xffffffff { + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], num) + out = append(out, net.IP(buf[:])) + mask += 1 + num += 1 + } + return +} diff --git a/vendor/github.com/google/gopacket/examples/bidirectional/main.go b/vendor/github.com/google/gopacket/examples/bidirectional/main.go new file mode 100644 index 0000000..4b0b240 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/bidirectional/main.go @@ -0,0 +1,192 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides an example of connecting up bidirectional streams from +// the unidirectional streams provided by gopacket/tcpassembly. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "log" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var snaplen = flag.Int("s", 16<<10, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") + +// key is used to map bidirectional streams to each other. +type key struct { + net, transport gopacket.Flow +} + +// String prints out the key in a human-readable fashion. +func (k key) String() string { + return fmt.Sprintf("%v:%v", k.net, k.transport) +} + +// timeout is the length of time to wait befor flushing connections and +// bidirectional stream pairs. +const timeout time.Duration = time.Minute * 5 + +// myStream implements tcpassembly.Stream +type myStream struct { + bytes int64 // total bytes seen on this stream. + bidi *bidi // maps to my bidirectional twin. + done bool // if true, we've seen the last packet we're going to for this stream. +} + +// bidi stores each unidirectional side of a bidirectional stream. +// +// When a new stream comes in, if we don't have an opposite stream, a bidi is +// created with 'a' set to the new stream. If we DO have an opposite stream, +// 'b' is set to the new stream. +type bidi struct { + key key // Key of the first stream, mostly for logging. + a, b *myStream // the two bidirectional streams. + lastPacketSeen time.Time // last time we saw a packet from either stream. +} + +// myFactory implements tcpassmebly.StreamFactory +type myFactory struct { + // bidiMap maps keys to bidirectional stream pairs. + bidiMap map[key]*bidi +} + +// New handles creating a new tcpassembly.Stream. +func (f *myFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream { + // Create a new stream. + s := &myStream{} + + // Find the bidi bidirectional struct for this stream, creating a new one if + // one doesn't already exist in the map. + k := key{netFlow, tcpFlow} + bd := f.bidiMap[k] + if bd == nil { + bd = &bidi{a: s, key: k} + log.Printf("[%v] created first side of bidirectional stream", bd.key) + // Register bidirectional with the reverse key, so the matching stream going + // the other direction will find it. + f.bidiMap[key{netFlow.Reverse(), tcpFlow.Reverse()}] = bd + } else { + log.Printf("[%v] found second side of bidirectional stream", bd.key) + bd.b = s + // Clear out the bidi we're using from the map, just in case. + delete(f.bidiMap, k) + } + s.bidi = bd + return s +} + +// emptyStream is used to finish bidi that only have one stream, in +// collectOldStreams. +var emptyStream = &myStream{done: true} + +// collectOldStreams finds any streams that haven't received a packet within +// 'timeout', and sets/finishes the 'b' stream inside them. The 'a' stream may +// still receive packets after this. +func (f *myFactory) collectOldStreams() { + cutoff := time.Now().Add(-timeout) + for k, bd := range f.bidiMap { + if bd.lastPacketSeen.Before(cutoff) { + log.Printf("[%v] timing out old stream", bd.key) + bd.b = emptyStream // stub out b with an empty stream. + delete(f.bidiMap, k) // remove it from our map. + bd.maybeFinish() // if b was the last stream we were waiting for, finish up. + } + } +} + +// Reassembled handles reassembled TCP stream data. +func (s *myStream) Reassembled(rs []tcpassembly.Reassembly) { + for _, r := range rs { + // For now, we'll simply count the bytes on each side of the TCP stream. + s.bytes += int64(len(r.Bytes)) + if r.Skip > 0 { + s.bytes += int64(r.Skip) + } + // Mark that we've received new packet data. + // We could just use time.Now, but by using r.Seen we handle the case + // where packets are being read from a file and could be very old. + if s.bidi.lastPacketSeen.After(r.Seen) { + s.bidi.lastPacketSeen = r.Seen + } + } +} + +// ReassemblyComplete marks this stream as finished. +func (s *myStream) ReassemblyComplete() { + s.done = true + s.bidi.maybeFinish() +} + +// maybeFinish will wait until both directions are complete, then print out +// stats. +func (bd *bidi) maybeFinish() { + switch { + case bd.a == nil: + log.Fatalf("[%v] a should always be non-nil, since it's set when bidis are created", bd.key) + case !bd.a.done: + log.Printf("[%v] still waiting on first stream", bd.key) + case bd.b == nil: + log.Printf("[%v] no second stream yet", bd.key) + case !bd.b.done: + log.Printf("[%v] still waiting on second stream", bd.key) + default: + log.Printf("[%v] FINISHED, bytes: %d tx, %d rx", bd.key, bd.a.bytes, bd.b.bytes) + } +} + +func main() { + defer util.Run()() + log.Printf("starting capture on interface %q", *iface) + // Set up pcap packet capture + handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) + if err != nil { + panic(err) + } + if err := handle.SetBPFFilter(*filter); err != nil { + panic(err) + } + + // Set up assembly + streamFactory := &myFactory{bidiMap: make(map[key]*bidi)} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + + log.Println("reading in packets") + // Read in packets, pass to assembler. + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packets := packetSource.Packets() + ticker := time.Tick(timeout / 4) + for { + select { + case packet := <-packets: + if *logAllPackets { + log.Println(packet) + } + if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { + log.Println("Unusable packet") + continue + } + tcp := packet.TransportLayer().(*layers.TCP) + assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) + + case <-ticker: + // Every minute, flush connections that haven't seen activity in the past minute. + log.Println("---- FLUSHING ----") + assembler.FlushOlderThan(time.Now().Add(-timeout)) + streamFactory.collectOldStreams() + } + } +} diff --git a/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png Binary files differnew file mode 100644 index 0000000..5aa3c8a --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/bytediff/bytediff.png diff --git a/vendor/github.com/google/gopacket/examples/bytediff/main.go b/vendor/github.com/google/gopacket/examples/bytediff/main.go new file mode 100644 index 0000000..2a4c11b --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/bytediff/main.go @@ -0,0 +1,96 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary shows how to display byte differences to users via the bytediff +// library. +package main + +import ( + "fmt" + "github.com/google/gopacket/bytediff" +) + +var sliceA = []byte{ + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, + 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, + 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, + 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, + 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, + 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, + 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, + 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, + 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, + 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, + 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, + 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, + 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, + 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, + 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x2e, 0x31, + 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, + 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, + 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, + 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, + 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, + 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, + 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2d, 0x38, 0x38, 0x35, 0x39, + 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, + 0x0d, 0x0a, +} +var sliceB = []byte{ + 0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x20, 0xbc, 0x30, 0x5b, 0xe8, 0xd3, 0x49, + 0x08, 0x00, 0x45, 0x00, 0x01, 0xa4, 0x39, 0xdf, 0x40, 0x00, 0x40, 0x06, + 0x55, 0x5a, 0xac, 0x11, 0x51, 0x49, 0xad, 0xde, 0xfe, 0xe1, 0xc5, 0xf7, + 0x00, 0x50, 0xc5, 0x7e, 0x0e, 0x48, 0x49, 0x07, 0x42, 0x32, 0x80, 0x18, + 0x00, 0x73, 0x9a, 0x8f, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x03, 0x77, + 0x37, 0x9c, 0x42, 0x77, 0x5e, 0x3a, 0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, + 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, + 0x73, 0x74, 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x66, 0x69, 0x73, 0x68, + 0x2e, 0x63, 0x6f, 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x6c, 0x69, 0x76, 0x65, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, + 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x58, 0x31, 0x31, 0x3b, 0x20, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, + 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, + 0x74, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, 0x20, 0x28, 0x4b, 0x48, 0x54, + 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, + 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x31, + 0x35, 0x2e, 0x30, 0x2e, 0x38, 0x37, 0x34, 0x2e, 0x31, 0x32, 0x31, 0x20, + 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x35, 0x2e, 0x32, + 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, + 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, + 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, + 0x30, 0x2e, 0x39, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, + 0x38, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, 0x70, + 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, + 0x68, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x65, 0x6e, 0x2d, 0x55, + 0x53, 0x2c, 0x65, 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x43, 0x68, 0x61, 0x72, 0x73, + 0x65, 0x74, 0x3a, 0x20, 0x49, 0x53, 0x4f, 0x2e, 0x39, 0x55, 0x35, 0x39, + 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x37, 0x2c, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x33, 0x0d, 0x0a, + 0x0d, 0x0a, +} + +func main() { + fmt.Println(bytediff.BashOutput.String(bytediff.Diff(sliceA, sliceB))) +} diff --git a/vendor/github.com/google/gopacket/examples/httpassembly/main.go b/vendor/github.com/google/gopacket/examples/httpassembly/main.go new file mode 100644 index 0000000..02af21e --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/httpassembly/main.go @@ -0,0 +1,127 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides sample code for using the gopacket TCP assembler and TCP +// stream reader. It reads packets off the wire and reconstructs HTTP requests +// it sees, logging them. +package main + +import ( + "bufio" + "flag" + "io" + "log" + "net/http" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "github.com/google/gopacket/tcpassembly/tcpreader" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp and dst port 80", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail") + +// Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces + +// httpStreamFactory implements tcpassembly.StreamFactory +type httpStreamFactory struct{} + +// httpStream will handle the actual decoding of http requests. +type httpStream struct { + net, transport gopacket.Flow + r tcpreader.ReaderStream +} + +func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { + hstream := &httpStream{ + net: net, + transport: transport, + r: tcpreader.NewReaderStream(), + } + go hstream.run() // Important... we must guarantee that data from the reader stream is read. + + // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. + return &hstream.r +} + +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") + } + } +} + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + + // Set up pcap packet capture + if *fname != "" { + log.Printf("Reading from pcap dump %q", *fname) + handle, err = pcap.OpenOffline(*fname) + } else { + log.Printf("Starting capture on interface %q", *iface) + handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever) + } + if err != nil { + log.Fatal(err) + } + + if err := handle.SetBPFFilter(*filter); err != nil { + log.Fatal(err) + } + + // Set up assembly + streamFactory := &httpStreamFactory{} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + + log.Println("reading in packets") + // Read in packets, pass to assembler. + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packets := packetSource.Packets() + ticker := time.Tick(time.Minute) + for { + select { + case packet := <-packets: + // A nil packet indicates the end of a pcap file. + if packet == nil { + return + } + if *logAllPackets { + log.Println(packet) + } + if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP { + log.Println("Unusable packet") + continue + } + tcp := packet.TransportLayer().(*layers.TCP) + assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp) + + case <-ticker: + // Every minute, flush connections that haven't seen activity in the past 2 minutes. + assembler.FlushOlderThan(time.Now().Add(time.Minute * -2)) + } + } +} diff --git a/vendor/github.com/google/gopacket/examples/pcapdump/main.go b/vendor/github.com/google/gopacket/examples/pcapdump/main.go new file mode 100644 index 0000000..373dee2 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pcapdump/main.go @@ -0,0 +1,73 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcapdump binary implements a tcpdump-like command line tool with gopacket +// using pcap as a backend data collection mechanism. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket/dumpcommand" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pcap" + "log" + "os" + "strings" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to read packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var tstype = flag.String("timestamp_type", "", "Type of timestamps to use") +var promisc = flag.Bool("promisc", true, "Set promiscuous mode") + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + if *fname != "" { + if handle, err = pcap.OpenOffline(*fname); err != nil { + log.Fatal("PCAP OpenOffline error:", err) + } + } else { + // This is a little complicated because we want to allow all possible options + // for creating the packet capture handle... instead of all this you can + // just call pcap.OpenLive if you want a simple handle. + inactive, err := pcap.NewInactiveHandle(*iface) + if err != nil { + log.Fatalf("could not create: %v", err) + } + defer inactive.CleanUp() + if err = inactive.SetSnapLen(*snaplen); err != nil { + log.Fatalf("could not set snap length: %v", err) + } else if err = inactive.SetPromisc(*promisc); err != nil { + log.Fatalf("could not set promisc mode: %v", err) + } else if err = inactive.SetTimeout(time.Second); err != nil { + log.Fatalf("could not set timeout: %v", err) + } + if *tstype != "" { + if t, err := pcap.TimestampSourceFromString(*tstype); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } else if err := inactive.SetTimestampSource(t); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } + } + if handle, err = inactive.Activate(); err != nil { + log.Fatal("PCAP Activate error:", err) + } + defer handle.Close() + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = handle.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + dumpcommand.Run(handle) +} diff --git a/vendor/github.com/google/gopacket/examples/pcaplay/main.go b/vendor/github.com/google/gopacket/examples/pcaplay/main.go new file mode 100644 index 0000000..d36d860 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pcaplay/main.go @@ -0,0 +1,163 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcaplay binary load an offline capture (pcap file) and replay +// it on the select interface, with an emphasis on packet timing +package main + +import ( + "flag" + "fmt" + "io" + "log" + "os" + "strings" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pcap" +) + +var iface = flag.String("i", "eth0", "Interface to write packets to") +var fname = flag.String("r", "", "Filename to read from") +var fast = flag.Bool("f", false, "Send each packets as fast as possible") + +var lastTS time.Time +var lastSend time.Time + +var start time.Time +var bytesSent int + +func writePacketDelayed(handle *pcap.Handle, buf []byte, ci gopacket.CaptureInfo) { + if ci.CaptureLength != ci.Length { + // do not write truncated packets + return + } + + intervalInCapture := ci.Timestamp.Sub(lastTS) + elapsedTime := time.Since(lastSend) + + if (intervalInCapture > elapsedTime) && !lastSend.IsZero() { + time.Sleep(intervalInCapture - elapsedTime) + } + + lastSend = time.Now() + writePacket(handle, buf) + lastTS = ci.Timestamp +} + +func writePacket(handle *pcap.Handle, buf []byte) error { + if err := handle.WritePacketData(buf); err != nil { + log.Printf("Failed to send packet: %s\n", err) + return err + } + return nil +} + +func pcapInfo(filename string) (start time.Time, end time.Time, packets int, size int) { + handleRead, err := pcap.OpenOffline(*fname) + if err != nil { + log.Fatal("PCAP OpenOffline error (handle to read packet):", err) + } + + var previousTs time.Time + var deltaTotal time.Duration + + for { + data, ci, err := handleRead.ReadPacketData() + if err != nil && err != io.EOF { + log.Fatal(err) + } else if err == io.EOF { + break + } else { + + if start.IsZero() { + start = ci.Timestamp + } + end = ci.Timestamp + packets++ + size += len(data) + + if previousTs.IsZero() { + previousTs = ci.Timestamp + } else { + deltaTotal += ci.Timestamp.Sub(previousTs) + previousTs = ci.Timestamp + } + } + } + sec := int(deltaTotal.Seconds()) + if sec == 0 { + sec = 1 + } + fmt.Printf("Avg packet rate %d/s\n", packets/sec) + return start, end, packets, size +} + +func main() { + defer util.Run()() + + // Sanity checks + if *fname == "" { + log.Fatal("Need a input file") + } + + // Open PCAP file + handle potential BPF Filter + handleRead, err := pcap.OpenOffline(*fname) + if err != nil { + log.Fatal("PCAP OpenOffline error (handle to read packet):", err) + } + defer handleRead.Close() + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = handleRead.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + // Open up a second pcap handle for packet writes. + handleWrite, err := pcap.OpenLive(*iface, 65536, true, pcap.BlockForever) + if err != nil { + log.Fatal("PCAP OpenLive error (handle to write packet):", err) + } + defer handleWrite.Close() + + start = time.Now() + pkt := 0 + tsStart, tsEnd, packets, size := pcapInfo(*fname) + + // Loop over packets and write them + for { + data, ci, err := handleRead.ReadPacketData() + switch { + case err == io.EOF: + fmt.Printf("\nFinished in %s", time.Since(start)) + return + case err != nil: + log.Printf("Failed to read packet %d: %s\n", pkt, err) + default: + if *fast { + writePacket(handleWrite, data) + } else { + writePacketDelayed(handleWrite, data, ci) + } + + bytesSent += len(data) + duration := time.Since(start) + pkt++ + + if duration > time.Second { + rate := bytesSent / int(duration.Seconds()) + remainingTime := tsEnd.Sub(tsStart) - duration + fmt.Printf("\rrate %d kB/sec - sent %d/%d kB - %d/%d packets - remaining time %s", + rate/1000, bytesSent/1000, size/1000, + pkt, packets, remainingTime) + } + } + } + +} diff --git a/vendor/github.com/google/gopacket/examples/pfdump/main.go b/vendor/github.com/google/gopacket/examples/pfdump/main.go new file mode 100644 index 0000000..4b3ace6 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/pfdump/main.go @@ -0,0 +1,52 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pfdump binary implements a tcpdump-like command line tool with gopacket +// using pfring as a backend data collection mechanism. +package main + +import ( + "flag" + "fmt" + "github.com/google/gopacket/dumpcommand" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/pfring" + "log" + "os" + "strings" +) + +var iface = flag.String("i", "eth0", "Interface to read packets from") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var cluster = flag.Int("cluster", -1, "If >= 0, sets the pfring cluster to this value") +var clustertype = flag.Int("clustertype", int(pfring.ClusterPerFlow), "Cluster type") + +func main() { + defer util.Run()() + var ring *pfring.Ring + var err error + if ring, err = pfring.NewRing(*iface, uint32(*snaplen), pfring.FlagPromisc); err != nil { + log.Fatalln("pfring ring creation error:", err) + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + fmt.Fprintf(os.Stderr, "Using BPF filter %q\n", bpffilter) + if err = ring.SetBPFFilter(bpffilter); err != nil { + log.Fatalln("BPF filter error:", err) + } + } + if *cluster >= 0 { + if err = ring.SetCluster(*cluster, pfring.ClusterType(*clustertype)); err != nil { + log.Fatalln("pfring SetCluster error:", err) + } + } + if err = ring.SetSocketMode(pfring.ReadOnly); err != nil { + log.Fatalln("pfring SetSocketMode error:", err) + } else if err = ring.Enable(); err != nil { + log.Fatalln("pfring Enable error:", err) + } + dumpcommand.Run(ring) +} diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh new file mode 100755 index 0000000..671d29f --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/reassemblydump/compare.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Limitations: if the number extracted files in too big, finding identical +# files might fail due to '*' in cmdline +# This would require to split sha256sum symlinks in xx/yyyyy + +usage() +{ + echo "Usage: $0 <file.pcap> <output-dir>" + echo "Compares tcpreassembly against tcpflow" + echo "" + echo "$@" + exit 1 +} + +debug() { + return # comment me for debug + echo "$@" +} + +die() +{ + ( + echo "$@" + echo + ) >&2 + exit 1 +} + +rename() +{ + local path="$1" + local filter="$2" + find "$path" -type f -name "$filter" -print0 | + while IFS= read -r -d $'\0' f; do + local sha256="$(sha256sum "$f" | cut -d ' ' -f 1)" + local target="$(dirname $f)/../sha256/$sha256" + debug "$target → $f" + mkdir -p "$(dirname "$target")" || return 1 + if [ ! -f "$target" ]; then + ln -sr "$f" "$target" || return 1 + fi + done + return $? +} + +main() +{ + local src="$1" + local out="$2" + + # TODO: make options + local extra="" + extra="$extra -debug" + extra="$extra -cpuprofile "$out/gopacket/cpu.prof"" + extra="$extra -memprofile "$out/gopacket/mem.prof"" + + [ ! -f "$src" ] && usage "Missing pcap" + [ ! -d "$out" ] && ( mkdir "$out" || die "Failed to create $out" ) + + mkdir -p "$out/gopacket/all" || die "Failed to create $out/gopacket/all" + mkdir -p "$out/tcpflow/all" || die "Faield to create $out/tcpflow/all" + + echo " * Running go reassembly" + time ./reassemblydump -r "$src" $debug -output "$out/gopacket/all" $extra -writeincomplete -ignorefsmerr -nooptcheck -allowmissinginit port 80 &> "$out/gopacket.txt" || die "Failed to run reassmbly. Check $out/gopacket.txt" + echo " * Running tcpflow" + time tcpflow -e http -r "$src" -o "$out/tcpflow/all" port 80 &> "$out/tcpflow.txt" || die "Failed to run tcpflow. Check $out/tcpflow.txt" + + echo " * Creating sha256sum symlinks for gopacket" + rename "$out/gopacket/all" '*' || die "Failed to rename in $out/gopacket" + echo " * Creating sha256sum symlinks for tcpflow" + rename "$out/tcpflow/all" '*HTTPBODY*' || die "Failed to rename in $out/tcpflow" + + # Remove identical files + echo " * Finding identical files" + local nb=0 + mkdir -p "$out/gopacket/sha256-equal" + mkdir -p "$out/tcpflow/sha256-equal" + for f in "$out/gopacket/sha256/"*; do + local f="$(basename "$f")" + [ -f "$out/tcpflow/sha256/$f" ] && { + debug " $f" + mv "$out/gopacket/sha256/$f" "$out/gopacket/sha256-equal" + mv "$out/tcpflow/sha256/$f" "$out/tcpflow/sha256-equal" + nb=$((nb+1)) + } + done + echo " → found $nb files" + + echo " * Diffing {gopacket,tcpflow}/sha256" + local rc=0 + for p in "gopacket" "tcpflow"; do + local nb=$(ls -1 "$out/$p/sha256/" | wc -l) + if [ $nb -ne 0 ]; then + rc=$((rc+1)) + echo " → $nb files in $out/$p/sha256" + fi + done + return $rc +} + +main "$@" +exit $? diff --git a/vendor/github.com/google/gopacket/examples/reassemblydump/main.go b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go new file mode 100644 index 0000000..9fc3791 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/reassemblydump/main.go @@ -0,0 +1,650 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// The pcapdump binary implements a tcpdump-like command line tool with gopacket +// using pcap as a backend data collection mechanism. +package main + +import ( + "bufio" + "bytes" + "compress/gzip" + "encoding/binary" + "encoding/hex" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "os/signal" + "path" + "runtime/pprof" + "strings" + "sync" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/ip4defrag" + "github.com/google/gopacket/layers" // pulls in all layers decoders + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/reassembly" +) + +var maxcount = flag.Int("c", -1, "Only grab this many packets, then exit") +var decoder = flag.String("decoder", "", "Name of the decoder to use (default: guess from capture)") +var statsevery = flag.Int("stats", 1000, "Output statistics every N packets") +var lazy = flag.Bool("lazy", false, "If true, do lazy decoding") +var nodefrag = flag.Bool("nodefrag", false, "If true, do not do IPv4 defrag") +var checksum = flag.Bool("checksum", false, "Check TCP checksum") +var nooptcheck = flag.Bool("nooptcheck", false, "Do not check TCP options (useful to ignore MSS on captures with TSO)") +var ignorefsmerr = flag.Bool("ignorefsmerr", false, "Ignore TCP FSM errors") +var allowmissinginit = flag.Bool("allowmissinginit", false, "Support streams without SYN/SYN+ACK/ACK sequence") +var verbose = flag.Bool("verbose", false, "Be verbose") +var debug = flag.Bool("debug", false, "Display debug information") +var quiet = flag.Bool("quiet", false, "Be quiet regarding errors") + +// http +var nohttp = flag.Bool("nohttp", false, "Disable HTTP parsing") +var output = flag.String("output", "", "Path to create file for HTTP 200 OK responses") +var writeincomplete = flag.Bool("writeincomplete", false, "Write incomplete response") + +var hexdump = flag.Bool("dump", false, "Dump HTTP request/response as hex") +var hexdumppkt = flag.Bool("dumppkt", false, "Dump packet as hex") + +// capture +var iface = flag.String("i", "eth0", "Interface to read packets from") +var fname = flag.String("r", "", "Filename to read from, overrides -i") +var snaplen = flag.Int("s", 65536, "Snap length (number of bytes max to read per packet") +var tstype = flag.String("timestamp_type", "", "Type of timestamps to use") +var promisc = flag.Bool("promisc", true, "Set promiscuous mode") + +var memprofile = flag.String("memprofile", "", "Write memory profile") + +var stats struct { + ipdefrag int + missedBytes int + pkt int + sz int + totalsz int + rejectFsm int + rejectOpt int + rejectConnFsm int + reassembled int + outOfOrderBytes int + outOfOrderPackets int + biggestChunkBytes int + biggestChunkPackets int + overlapBytes int + overlapPackets int +} + +const closeTimeout time.Duration = time.Hour * 24 // Closing inactive: TODO: from CLI +const timeout time.Duration = time.Minute * 5 // Pending bytes: TODO: from CLI + +/* + * HTTP part + */ + +type httpReader struct { + ident string + isClient bool + bytes chan []byte + data []byte + hexdump bool + parent *tcpStream +} + +func (h *httpReader) Read(p []byte) (int, error) { + ok := true + for ok && len(h.data) == 0 { + h.data, ok = <-h.bytes + } + if !ok || len(h.data) == 0 { + return 0, io.EOF + } + + l := copy(p, h.data) + h.data = h.data[l:] + return l, nil +} + +var outputLevel int +var errorsMap map[string]uint +var errors uint + +// Too bad for perf that a... is evaluated +func Error(t string, s string, a ...interface{}) { + errors++ + nb, _ := errorsMap[t] + errorsMap[t] = nb + 1 + if outputLevel >= 0 { + fmt.Printf(s, a...) + } +} +func Info(s string, a ...interface{}) { + if outputLevel >= 1 { + fmt.Printf(s, a...) + } +} +func Debug(s string, a ...interface{}) { + if outputLevel >= 2 { + fmt.Printf(s, a...) + } +} + +func (h *httpReader) run(wg *sync.WaitGroup) { + defer wg.Done() + b := bufio.NewReader(h) + for true { + if h.isClient { + req, err := http.ReadRequest(b) + if err == io.EOF || err == io.ErrUnexpectedEOF { + break + } else if err != nil { + Error("HTTP-request", "HTTP/%s Request error: %s (%v,%+v)\n", h.ident, err, err, err) + continue + } + body, err := ioutil.ReadAll(req.Body) + s := len(body) + if err != nil { + Error("HTTP-request-body", "Got body err: %s\n", err) + } else if h.hexdump { + Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body)) + } + req.Body.Close() + Info("HTTP/%s Request: %s %s (body:%d)\n", h.ident, req.Method, req.URL, s) + h.parent.urls = append(h.parent.urls, req.URL.String()) + } else { + res, err := http.ReadResponse(b, nil) + var req string + if len(h.parent.urls) == 0 { + req = fmt.Sprintf("<no-request-seen>") + } else { + req, h.parent.urls = h.parent.urls[0], h.parent.urls[1:] + } + if err == io.EOF || err == io.ErrUnexpectedEOF { + break + } else if err != nil { + Error("HTTP-response", "HTTP/%s Response error: %s (%v,%+v)\n", h.ident, err, err, err) + continue + } + body, err := ioutil.ReadAll(res.Body) + s := len(body) + if err != nil { + Error("HTTP-response-body", "HTTP/%s: failed to get body(parsed len:%d): %s\n", h.ident, s, err) + } + if h.hexdump { + Info("Body(%d/0x%x)\n%s\n", len(body), len(body), hex.Dump(body)) + } + res.Body.Close() + sym := "," + if res.ContentLength > 0 && res.ContentLength != int64(s) { + sym = "!=" + } + contentType, ok := res.Header["Content-Type"] + if !ok { + contentType = []string{http.DetectContentType(body)} + } + encoding := res.Header["Content-Encoding"] + Info("HTTP/%s Response: %s URL:%s (%d%s%d%s) -> %s\n", h.ident, res.Status, req, res.ContentLength, sym, s, contentType, encoding) + if (err == nil || *writeincomplete) && *output != "" { + base := url.QueryEscape(path.Base(req)) + if err != nil { + base = "incomplete-" + base + } + base = path.Join(*output, base) + if len(base) > 250 { + base = base[:250] + "..." + } + if base == *output { + base = path.Join(*output, "noname") + } + target := base + n := 0 + for true { + _, err := os.Stat(target) + //if os.IsNotExist(err) != nil { + if err != nil { + break + } + target = fmt.Sprintf("%s-%d", base, n) + n++ + } + f, err := os.Create(target) + if err != nil { + Error("HTTP-create", "Cannot create %s: %s\n", target, err) + continue + } + var r io.Reader + r = bytes.NewBuffer(body) + if len(encoding) > 0 && (encoding[0] == "gzip" || encoding[0] == "deflate") { + r, err = gzip.NewReader(r) + if err != nil { + Error("HTTP-gunzip", "Failed to gzip decode: %s", err) + } + } + if err == nil { + w, err := io.Copy(f, r) + if _, ok := r.(*gzip.Reader); ok { + r.(*gzip.Reader).Close() + } + f.Close() + if err != nil { + Error("HTTP-save", "%s: failed to save %s (l:%d): %s\n", h.ident, target, w, err) + } else { + Info("%s: Saved %s (l:%d)\n", h.ident, target, w) + } + } + } + } + } +} + +/* + * The TCP factory: returns a new Stream + */ +type tcpStreamFactory struct { + wg sync.WaitGroup + doHTTP bool +} + +func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.TCP, ac reassembly.AssemblerContext) reassembly.Stream { + Debug("* NEW: %s %s\n", net, transport) + fsmOptions := reassembly.TCPSimpleFSMOptions{ + SupportMissingEstablishment: *allowmissinginit, + } + stream := &tcpStream{ + net: net, + transport: transport, + isDNS: tcp.SrcPort == 53 || tcp.DstPort == 53, + isHTTP: (tcp.SrcPort == 80 || tcp.DstPort == 80) && factory.doHTTP, + reversed: tcp.SrcPort == 80, + tcpstate: reassembly.NewTCPSimpleFSM(fsmOptions), + ident: fmt.Sprintf("%s:%s", net, transport), + optchecker: reassembly.NewTCPOptionCheck(), + } + if stream.isHTTP { + stream.client = httpReader{ + bytes: make(chan []byte), + ident: fmt.Sprintf("%s %s", net, transport), + hexdump: *hexdump, + parent: stream, + isClient: true, + } + stream.server = httpReader{ + bytes: make(chan []byte), + ident: fmt.Sprintf("%s %s", net.Reverse(), transport.Reverse()), + hexdump: *hexdump, + parent: stream, + } + factory.wg.Add(2) + go stream.client.run(&factory.wg) + go stream.server.run(&factory.wg) + } + return stream +} + +func (factory *tcpStreamFactory) WaitGoRoutines() { + factory.wg.Wait() +} + +/* + * The assembler context + */ +type Context struct { + CaptureInfo gopacket.CaptureInfo +} + +func (c *Context) GetCaptureInfo() gopacket.CaptureInfo { + return c.CaptureInfo +} + +/* + * TCP stream + */ + +/* It's a connection (bidirectional) */ +type tcpStream struct { + tcpstate *reassembly.TCPSimpleFSM + fsmerr bool + optchecker reassembly.TCPOptionCheck + net, transport gopacket.Flow + isDNS bool + isHTTP bool + reversed bool + client httpReader + server httpReader + urls []string + ident string +} + +func (t *tcpStream) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, acked reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool { + // FSM + if !t.tcpstate.CheckState(tcp, dir) { + Error("FSM", "%s: Packet rejected by FSM (state:%s)\n", t.ident, t.tcpstate.String()) + stats.rejectFsm++ + if !t.fsmerr { + t.fsmerr = true + stats.rejectConnFsm++ + } + if !*ignorefsmerr { + return false + } + } + // Options + err := t.optchecker.Accept(tcp, ci, dir, acked, start) + if err != nil { + Error("OptionChecker", "%s: Packet rejected by OptionChecker: %s\n", t.ident, err) + stats.rejectOpt++ + if !*nooptcheck { + return false + } + } + // Checksum + accept := true + if *checksum { + c, err := tcp.ComputeChecksum() + if err != nil { + Error("ChecksumCompute", "%s: Got error computing checksum: %s\n", t.ident, err) + accept = false + } else if c != 0x0 { + Error("Checksum", "%s: Invalid checksum: 0x%x\n", t.ident, c) + accept = false + } + } + if !accept { + stats.rejectOpt++ + } + return accept +} + +func (t *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext) { + dir, start, end, skip := sg.Info() + length, saved := sg.Lengths() + // update stats + sgStats := sg.Stats() + if skip > 0 { + stats.missedBytes += skip + } + stats.sz += length - saved + stats.pkt += sgStats.Packets + if sgStats.Chunks > 1 { + stats.reassembled++ + } + stats.outOfOrderPackets += sgStats.QueuedPackets + stats.outOfOrderBytes += sgStats.QueuedBytes + if length > stats.biggestChunkBytes { + stats.biggestChunkBytes = length + } + if sgStats.Packets > stats.biggestChunkPackets { + stats.biggestChunkPackets = sgStats.Packets + } + if sgStats.OverlapBytes != 0 && sgStats.OverlapPackets == 0 { + fmt.Printf("bytes:%d, pkts:%d\n", sgStats.OverlapBytes, sgStats.OverlapPackets) + panic("Invalid overlap") + } + stats.overlapBytes += sgStats.OverlapBytes + stats.overlapPackets += sgStats.OverlapPackets + + var ident string + if dir == reassembly.TCPDirClientToServer { + ident = fmt.Sprintf("%v %v(%s): ", t.net, t.transport, dir) + } else { + ident = fmt.Sprintf("%v %v(%s): ", t.net.Reverse(), t.transport.Reverse(), dir) + } + Debug("%s: SG reassembled packet with %d bytes (start:%v,end:%v,skip:%d,saved:%d,nb:%d,%d,overlap:%d,%d)\n", ident, length, start, end, skip, saved, sgStats.Packets, sgStats.Chunks, sgStats.OverlapBytes, sgStats.OverlapPackets) + if skip == -1 && *allowmissinginit { + // this is allowed + } else if skip != 0 { + // Missing bytes in stream: do not even try to parse it + return + } + data := sg.Fetch(length) + if t.isDNS { + dns := &layers.DNS{} + var decoded []gopacket.LayerType + if len(data) < 2 { + if len(data) > 0 { + sg.KeepFrom(0) + } + return + } + dnsSize := binary.BigEndian.Uint16(data[:2]) + missing := int(dnsSize) - len(data[2:]) + Debug("dnsSize: %d, missing: %d\n", dnsSize, missing) + if missing > 0 { + Info("Missing some bytes: %d\n", missing) + sg.KeepFrom(0) + return + } + p := gopacket.NewDecodingLayerParser(layers.LayerTypeDNS, dns) + err := p.DecodeLayers(data[2:], &decoded) + if err != nil { + Error("DNS-parser", "Failed to decode DNS: %v\n", err) + } else { + Debug("DNS: %s\n", gopacket.LayerDump(dns)) + } + if len(data) > 2+int(dnsSize) { + sg.KeepFrom(2 + int(dnsSize)) + } + } else if t.isHTTP { + if length > 0 { + if *hexdump { + Debug("Feeding http with:\n%s", hex.Dump(data)) + } + if dir == reassembly.TCPDirClientToServer && !t.reversed { + t.client.bytes <- data + } else { + t.server.bytes <- data + } + } + } +} + +func (t *tcpStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool { + Debug("%s: Connection closed\n", t.ident) + if t.isHTTP { + close(t.client.bytes) + close(t.server.bytes) + } + // do not remove the connection to allow last ACK + return false +} + +func main() { + defer util.Run()() + var handle *pcap.Handle + var err error + if *debug { + outputLevel = 2 + } else if *verbose { + outputLevel = 1 + } else if *quiet { + outputLevel = -1 + } + errorsMap = make(map[string]uint) + if *fname != "" { + if handle, err = pcap.OpenOffline(*fname); err != nil { + log.Fatal("PCAP OpenOffline error:", err) + } + } else { + // This is a little complicated because we want to allow all possible options + // for creating the packet capture handle... instead of all this you can + // just call pcap.OpenLive if you want a simple handle. + inactive, err := pcap.NewInactiveHandle(*iface) + if err != nil { + log.Fatal("could not create: %v", err) + } + defer inactive.CleanUp() + if err = inactive.SetSnapLen(*snaplen); err != nil { + log.Fatal("could not set snap length: %v", err) + } else if err = inactive.SetPromisc(*promisc); err != nil { + log.Fatal("could not set promisc mode: %v", err) + } else if err = inactive.SetTimeout(time.Second); err != nil { + log.Fatal("could not set timeout: %v", err) + } + if *tstype != "" { + if t, err := pcap.TimestampSourceFromString(*tstype); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } else if err := inactive.SetTimestampSource(t); err != nil { + log.Fatalf("Supported timestamp types: %v", inactive.SupportedTimestamps()) + } + } + if handle, err = inactive.Activate(); err != nil { + log.Fatal("PCAP Activate error:", err) + } + defer handle.Close() + } + if len(flag.Args()) > 0 { + bpffilter := strings.Join(flag.Args(), " ") + Info("Using BPF filter %q\n", bpffilter) + if err = handle.SetBPFFilter(bpffilter); err != nil { + log.Fatal("BPF filter error:", err) + } + } + + var dec gopacket.Decoder + var ok bool + decoder_name := *decoder + if decoder_name == "" { + decoder_name = fmt.Sprintf("%s", handle.LinkType()) + } + if dec, ok = gopacket.DecodersByLayerName[decoder_name]; !ok { + log.Fatalln("No decoder named", decoder_name) + } + source := gopacket.NewPacketSource(handle, dec) + source.Lazy = *lazy + source.NoCopy = true + Info("Starting to read packets\n") + count := 0 + bytes := int64(0) + start := time.Now() + defragger := ip4defrag.NewIPv4Defragmenter() + + streamFactory := &tcpStreamFactory{doHTTP: !*nohttp} + streamPool := reassembly.NewStreamPool(streamFactory) + assembler := reassembly.NewAssembler(streamPool) + + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt) + + for packet := range source.Packets() { + count++ + Debug("PACKET #%d\n", count) + data := packet.Data() + bytes += int64(len(data)) + if *hexdumppkt { + Debug("Packet content (%d/0x%x)\n%s\n", len(data), len(data), hex.Dump(data)) + } + + // defrag the IPv4 packet if required + if !*nodefrag { + ip4Layer := packet.Layer(layers.LayerTypeIPv4) + if ip4Layer == nil { + continue + } + ip4 := ip4Layer.(*layers.IPv4) + l := ip4.Length + newip4, err := defragger.DefragIPv4(ip4) + if err != nil { + log.Fatalln("Error while de-fragmenting", err) + } else if newip4 == nil { + Debug("Fragment...\n") + continue // packet fragment, we don't have whole packet yet. + } + if newip4.Length != l { + stats.ipdefrag++ + Debug("Decoding re-assembled packet: %s\n", newip4.NextLayerType()) + pb, ok := packet.(gopacket.PacketBuilder) + if !ok { + panic("Not a PacketBuilder") + } + nextDecoder := newip4.NextLayerType() + nextDecoder.Decode(newip4.Payload, pb) + } + } + + tcp := packet.Layer(layers.LayerTypeTCP) + if tcp != nil { + tcp := tcp.(*layers.TCP) + if *checksum { + err := tcp.SetNetworkLayerForChecksum(packet.NetworkLayer()) + if err != nil { + log.Fatalf("Failed to set network layer for checksum: %s\n", err) + } + } + c := Context{ + CaptureInfo: packet.Metadata().CaptureInfo, + } + stats.totalsz += len(tcp.Payload) + assembler.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c) + } + if count%*statsevery == 0 { + ref := packet.Metadata().CaptureInfo.Timestamp + flushed, closed := assembler.FlushWithOptions(reassembly.FlushOptions{T: ref.Add(-timeout), TC: ref.Add(-closeTimeout)}) + Debug("Forced flush: %d flushed, %d closed (%s)", flushed, closed, ref) + } + + done := *maxcount > 0 && count >= *maxcount + if count%*statsevery == 0 || done { + fmt.Fprintf(os.Stderr, "Processed %v packets (%v bytes) in %v (errors: %v, type:%v)\n", count, bytes, time.Since(start), errors, len(errorsMap)) + } + select { + case <-signalChan: + fmt.Fprintf(os.Stderr, "\nCaught SIGINT: aborting\n") + done = true + default: + // NOP: continue + } + if done { + break + } + } + + closed := assembler.FlushAll() + Debug("Final flush: %d closed", closed) + if outputLevel >= 2 { + streamPool.Dump() + } + + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal(err) + } + pprof.WriteHeapProfile(f) + f.Close() + } + + streamFactory.WaitGoRoutines() + Debug("%s\n", assembler.Dump()) + if !*nodefrag { + fmt.Printf("IPdefrag:\t\t%d\n", stats.ipdefrag) + } + fmt.Printf("TCP stats:\n") + fmt.Printf(" missed bytes:\t\t%d\n", stats.missedBytes) + fmt.Printf(" total packets:\t\t%d\n", stats.pkt) + fmt.Printf(" rejected FSM:\t\t%d\n", stats.rejectFsm) + fmt.Printf(" rejected Options:\t%d\n", stats.rejectOpt) + fmt.Printf(" reassembled bytes:\t%d\n", stats.sz) + fmt.Printf(" total TCP bytes:\t%d\n", stats.totalsz) + fmt.Printf(" conn rejected FSM:\t%d\n", stats.rejectConnFsm) + fmt.Printf(" reassembled chunks:\t%d\n", stats.reassembled) + fmt.Printf(" out-of-order packets:\t%d\n", stats.outOfOrderPackets) + fmt.Printf(" out-of-order bytes:\t%d\n", stats.outOfOrderBytes) + fmt.Printf(" biggest-chunk packets:\t%d\n", stats.biggestChunkPackets) + fmt.Printf(" biggest-chunk bytes:\t%d\n", stats.biggestChunkBytes) + fmt.Printf(" overlap packets:\t%d\n", stats.overlapPackets) + fmt.Printf(" overlap bytes:\t\t%d\n", stats.overlapBytes) + fmt.Printf("Errors: %d\n", errors) + for e, _ := range errorsMap { + fmt.Printf(" %s:\t\t%d\n", e, errorsMap[e]) + } +} diff --git a/vendor/github.com/google/gopacket/examples/statsassembly/main.go b/vendor/github.com/google/gopacket/examples/statsassembly/main.go new file mode 100644 index 0000000..36da011 --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/statsassembly/main.go @@ -0,0 +1,211 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// This binary provides sample code for using the gopacket TCP assembler raw, +// without the help of the tcpreader library. It watches TCP streams and +// reports statistics on completed streams. +// +// It also uses gopacket.DecodingLayerParser instead of the normal +// gopacket.PacketSource, to highlight the methods, pros, and cons of this +// approach. +package main + +import ( + "flag" + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/tcpassembly" + "log" + "time" +) + +var iface = flag.String("i", "eth0", "Interface to get packets from") +var snaplen = flag.Int("s", 65536, "SnapLen for pcap packet capture") +var filter = flag.String("f", "tcp", "BPF filter for pcap") +var logAllPackets = flag.Bool("v", false, "Log whenever we see a packet") +var bufferedPerConnection = flag.Int("connection_max_buffer", 0, ` +Max packets to buffer for a single connection before skipping over a gap in data +and continuing to stream the connection after the buffer. If zero or less, this +is infinite.`) +var bufferedTotal = flag.Int("total_max_buffer", 0, ` +Max packets to buffer total before skipping over gaps in connections and +continuing to stream connection data. If zero or less, this is infinite`) +var flushAfter = flag.String("flush_after", "2m", ` +Connections which have buffered packets (they've gotten packets out of order and +are waiting for old packets to fill the gaps) are flushed after they're this old +(their oldest gap is skipped). Any string parsed by time.ParseDuration is +acceptable here`) +var packetCount = flag.Int("c", -1, ` +Quit after processing this many packets, flushing all currently buffered +connections. If negative, this is infinite`) + +// simpleStreamFactory implements tcpassembly.StreamFactory +type statsStreamFactory struct{} + +// statsStream will handle the actual decoding of stats requests. +type statsStream struct { + net, transport gopacket.Flow + bytes, packets, outOfOrder, skipped int64 + start, end time.Time + sawStart, sawEnd bool +} + +// New creates a new stream. It's called whenever the assembler sees a stream +// it isn't currently following. +func (factory *statsStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream { + log.Printf("new stream %v:%v started", net, transport) + s := &statsStream{ + net: net, + transport: transport, + start: time.Now(), + } + s.end = s.start + // ReaderStream implements tcpassembly.Stream, so we can return a pointer to it. + return s +} + +// Reassembled is called whenever new packet data is available for reading. +// Reassembly objects contain stream data IN ORDER. +func (s *statsStream) Reassembled(reassemblies []tcpassembly.Reassembly) { + for _, reassembly := range reassemblies { + if reassembly.Seen.Before(s.end) { + s.outOfOrder++ + } else { + s.end = reassembly.Seen + } + s.bytes += int64(len(reassembly.Bytes)) + s.packets += 1 + if reassembly.Skip > 0 { + s.skipped += int64(reassembly.Skip) + } + s.sawStart = s.sawStart || reassembly.Start + s.sawEnd = s.sawEnd || reassembly.End + } +} + +// ReassemblyComplete is called when the TCP assembler believes a stream has +// finished. +func (s *statsStream) ReassemblyComplete() { + diffSecs := float64(s.end.Sub(s.start)) / float64(time.Second) + log.Printf("Reassembly of stream %v:%v complete - start:%v end:%v bytes:%v packets:%v ooo:%v bps:%v pps:%v skipped:%v", + s.net, s.transport, s.start, s.end, s.bytes, s.packets, s.outOfOrder, + float64(s.bytes)/diffSecs, float64(s.packets)/diffSecs, s.skipped) +} + +func main() { + defer util.Run()() + + flushDuration, err := time.ParseDuration(*flushAfter) + if err != nil { + log.Fatal("invalid flush duration: ", *flushAfter) + } + + log.Printf("starting capture on interface %q", *iface) + // Set up pcap packet capture + handle, err := pcap.OpenLive(*iface, int32(*snaplen), true, flushDuration/2) + if err != nil { + log.Fatal("error opening pcap handle: ", err) + } + if err := handle.SetBPFFilter(*filter); err != nil { + log.Fatal("error setting BPF filter: ", err) + } + + // Set up assembly + streamFactory := &statsStreamFactory{} + streamPool := tcpassembly.NewStreamPool(streamFactory) + assembler := tcpassembly.NewAssembler(streamPool) + assembler.MaxBufferedPagesPerConnection = *bufferedPerConnection + assembler.MaxBufferedPagesTotal = *bufferedTotal + + log.Println("reading in packets") + + // We use a DecodingLayerParser here instead of a simpler PacketSource. + // This approach should be measurably faster, but is also more rigid. + // PacketSource will handle any known type of packet safely and easily, + // but DecodingLayerParser will only handle those packet types we + // specifically pass in. This trade-off can be quite useful, though, in + // high-throughput situations. + var eth layers.Ethernet + var dot1q layers.Dot1Q + var ip4 layers.IPv4 + var ip6 layers.IPv6 + var ip6extensions layers.IPv6ExtensionSkipper + var tcp layers.TCP + var payload gopacket.Payload + parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, + ð, &dot1q, &ip4, &ip6, &ip6extensions, &tcp, &payload) + decoded := make([]gopacket.LayerType, 0, 4) + + nextFlush := time.Now().Add(flushDuration / 2) + + var byteCount int64 + start := time.Now() + +loop: + for ; *packetCount != 0; *packetCount-- { + // Check to see if we should flush the streams we have + // that haven't seen any new data in a while. Note we set a + // timeout on our PCAP handle, so this should happen even if we + // never see packet data. + if time.Now().After(nextFlush) { + stats, _ := handle.Stats() + log.Printf("flushing all streams that haven't seen packets in the last 2 minutes, pcap stats: %+v", stats) + assembler.FlushOlderThan(time.Now().Add(flushDuration)) + nextFlush = time.Now().Add(flushDuration / 2) + } + + // To speed things up, we're also using the ZeroCopy method for + // reading packet data. This method is faster than the normal + // ReadPacketData, but the returned bytes in 'data' are + // invalidated by any subsequent ZeroCopyReadPacketData call. + // Note that tcpassembly is entirely compatible with this packet + // reading method. This is another trade-off which might be + // appropriate for high-throughput sniffing: it avoids a packet + // copy, but its cost is much more careful handling of the + // resulting byte slice. + data, ci, err := handle.ZeroCopyReadPacketData() + + if err != nil { + log.Printf("error getting packet: %v", err) + continue + } + err = parser.DecodeLayers(data, &decoded) + if err != nil { + log.Printf("error decoding packet: %v", err) + continue + } + if *logAllPackets { + log.Printf("decoded the following layers: %v", decoded) + } + byteCount += int64(len(data)) + // Find either the IPv4 or IPv6 address to use as our network + // layer. + foundNetLayer := false + var netFlow gopacket.Flow + for _, typ := range decoded { + switch typ { + case layers.LayerTypeIPv4: + netFlow = ip4.NetworkFlow() + foundNetLayer = true + case layers.LayerTypeIPv6: + netFlow = ip6.NetworkFlow() + foundNetLayer = true + case layers.LayerTypeTCP: + if foundNetLayer { + assembler.AssembleWithTimestamp(netFlow, &tcp, ci.Timestamp) + } else { + log.Println("could not find IPv4 or IPv6 layer, inoring") + } + continue loop + } + } + log.Println("could not find TCP layer") + } + assembler.FlushAll() + log.Printf("processed %d bytes in %v", byteCount, time.Since(start)) +} diff --git a/vendor/github.com/google/gopacket/examples/synscan/main.go b/vendor/github.com/google/gopacket/examples/synscan/main.go new file mode 100644 index 0000000..7a2345f --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/synscan/main.go @@ -0,0 +1,259 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// synscan implements a TCP syn scanner on top of pcap. +// It's more complicated than arpscan, since it has to handle sending packets +// outside the local network, requiring some routing and ARP work. +// +// Since this is just an example program, it aims for simplicity over +// performance. It doesn't handle sending packets very quickly, it scans IPs +// serially instead of in parallel, and uses gopacket.Packet instead of +// gopacket.DecodingLayerParser for packet processing. We also make use of very +// simple timeout logic with time.Since. +// +// Making it blazingly fast is left as an exercise to the reader. +package main + +import ( + "errors" + "flag" + "log" + "net" + "time" + + "github.com/google/gopacket" + "github.com/google/gopacket/examples/util" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/google/gopacket/routing" +) + +// scanner handles scanning a single IP address. +type scanner struct { + // iface is the interface to send packets on. + iface *net.Interface + // destination, gateway (if applicable), and source IP addresses to use. + dst, gw, src net.IP + + handle *pcap.Handle + + // opts and buf allow us to easily serialize packets in the send() + // method. + opts gopacket.SerializeOptions + buf gopacket.SerializeBuffer +} + +// newScanner creates a new scanner for a given destination IP address, using +// router to determine how to route packets to that IP. +func newScanner(ip net.IP, router routing.Router) (*scanner, error) { + s := &scanner{ + dst: ip, + opts: gopacket.SerializeOptions{ + FixLengths: true, + ComputeChecksums: true, + }, + buf: gopacket.NewSerializeBuffer(), + } + // Figure out the route to the IP. + iface, gw, src, err := router.Route(ip) + if err != nil { + return nil, err + } + log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src) + s.gw, s.src, s.iface = gw, src, iface + + // Open the handle for reading/writing. + // Note we could very easily add some BPF filtering here to greatly + // decrease the number of packets we have to look at when getting back + // scan results. + handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) + if err != nil { + return nil, err + } + s.handle = handle + return s, nil +} + +// close cleans up the handle. +func (s *scanner) close() { + s.handle.Close() +} + +// getHwAddr is a hacky but effective way to get the destination hardware +// address for our packets. It does an ARP request for our gateway (if there is +// one) or destination IP (if no gateway is necessary), then waits for an ARP +// reply. This is pretty slow right now, since it blocks on the ARP +// request/reply. +func (s *scanner) getHwAddr() (net.HardwareAddr, error) { + start := time.Now() + arpDst := s.dst + if s.gw != nil { + arpDst = s.gw + } + // Prepare the layers to send for an ARP request. + eth := layers.Ethernet{ + SrcMAC: s.iface.HardwareAddr, + DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + EthernetType: layers.EthernetTypeARP, + } + arp := layers.ARP{ + AddrType: layers.LinkTypeEthernet, + Protocol: layers.EthernetTypeIPv4, + HwAddressSize: 6, + ProtAddressSize: 4, + Operation: layers.ARPRequest, + SourceHwAddress: []byte(s.iface.HardwareAddr), + SourceProtAddress: []byte(s.src), + DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, + DstProtAddress: []byte(arpDst), + } + // Send a single ARP request packet (we never retry a send, since this + // is just an example ;) + if err := s.send(ð, &arp); err != nil { + return nil, err + } + // Wait 3 seconds for an ARP reply. + for { + if time.Since(start) > time.Second*3 { + return nil, errors.New("timeout getting ARP reply") + } + data, _, err := s.handle.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + return nil, err + } + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { + arp := arpLayer.(*layers.ARP) + if net.IP(arp.SourceProtAddress).Equal(net.IP(arpDst)) { + return net.HardwareAddr(arp.SourceHwAddress), nil + } + } + } +} + +// scan scans the dst IP address of this scanner. +func (s *scanner) scan() error { + // First off, get the MAC address we should be sending packets to. + hwaddr, err := s.getHwAddr() + if err != nil { + return err + } + // Construct all the network layers we need. + eth := layers.Ethernet{ + SrcMAC: s.iface.HardwareAddr, + DstMAC: hwaddr, + EthernetType: layers.EthernetTypeIPv4, + } + ip4 := layers.IPv4{ + SrcIP: s.src, + DstIP: s.dst, + Version: 4, + TTL: 64, + Protocol: layers.IPProtocolTCP, + } + tcp := layers.TCP{ + SrcPort: 54321, + DstPort: 0, // will be incremented during the scan + SYN: true, + } + tcp.SetNetworkLayerForChecksum(&ip4) + + // Create the flow we expect returning packets to have, so we can check + // against it and discard useless packets. + ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src) + start := time.Now() + for { + // Send one packet per loop iteration until we've sent packets + // to all of ports [1, 65535]. + if tcp.DstPort < 65535 { + start = time.Now() + tcp.DstPort++ + if err := s.send(ð, &ip4, &tcp); err != nil { + log.Printf("error sending to port %v: %v", tcp.DstPort, err) + } + } + // Time out 5 seconds after the last packet we sent. + if time.Since(start) > time.Second*5 { + log.Printf("timed out for %v, assuming we've seen all we can", s.dst) + return nil + } + + // Read in the next packet. + data, _, err := s.handle.ReadPacketData() + if err == pcap.NextErrorTimeoutExpired { + continue + } else if err != nil { + log.Printf("error reading packet: %v", err) + continue + } + + // Parse the packet. We'd use DecodingLayerParser here if we + // wanted to be really fast. + packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy) + + // Find the packets we care about, and print out logging + // information about them. All others are ignored. + if net := packet.NetworkLayer(); net == nil { + // log.Printf("packet has no network layer") + } else if net.NetworkFlow() != ipFlow { + // log.Printf("packet does not match our ip src/dst") + } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil { + // log.Printf("packet has not tcp layer") + } else if tcp, ok := tcpLayer.(*layers.TCP); !ok { + // We panic here because this is guaranteed to never + // happen. + panic("tcp layer is not tcp layer :-/") + } else if tcp.DstPort != 54321 { + // log.Printf("dst port %v does not match", tcp.DstPort) + } else if tcp.RST { + log.Printf(" port %v closed", tcp.SrcPort) + } else if tcp.SYN && tcp.ACK { + log.Printf(" port %v open", tcp.SrcPort) + } else { + // log.Printf("ignoring useless packet") + } + } +} + +// send sends the given layers as a single packet on the network. +func (s *scanner) send(l ...gopacket.SerializableLayer) error { + if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil { + return err + } + return s.handle.WritePacketData(s.buf.Bytes()) +} + +func main() { + defer util.Run()() + router, err := routing.New() + if err != nil { + log.Fatal("routing error:", err) + } + for _, arg := range flag.Args() { + var ip net.IP + if ip = net.ParseIP(arg); ip == nil { + log.Printf("non-ip target: %q", arg) + continue + } else if ip = ip.To4(); ip == nil { + log.Printf("non-ipv4 target: %q", arg) + continue + } + // Note: newScanner creates and closes a pcap Handle once for + // every scan target. We could do much better, were this not an + // example ;) + s, err := newScanner(ip, router) + if err != nil { + log.Printf("unable to create scanner for %v: %v", ip, err) + continue + } + if err := s.scan(); err != nil { + log.Printf("unable to scan %v: %v", ip, err) + } + s.close() + } +} diff --git a/vendor/github.com/google/gopacket/examples/util/util.go b/vendor/github.com/google/gopacket/examples/util/util.go new file mode 100644 index 0000000..0f698fb --- /dev/null +++ b/vendor/github.com/google/gopacket/examples/util/util.go @@ -0,0 +1,40 @@ +// Copyright 2012 Google, Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file in the root of the source +// tree. + +// Package util provides shared utilities for all gopacket examples. +package util + +import ( + "flag" + "log" + "os" + "runtime/pprof" +) + +var cpuprofile = flag.String("cpuprofile", "", "Where to write CPU profile") + +// Run starts up stuff at the beginning of a main function, and returns a +// function to defer until the function completes. It should be used like this: +// +// func main() { +// defer util.Run()() +// ... stuff ... +// } +func Run() func() { + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatalf("could not open cpu profile file %q", *cpuprofile) + } + pprof.StartCPUProfile(f) + return func() { + pprof.StopCPUProfile() + f.Close() + } + } + return func() {} +} |