github.com/gopacket/gopacket@v1.1.0/examples/httpassembly/main.go (about)

     1  // Copyright 2012 Google, Inc. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the LICENSE file in the root of the source
     5  // tree.
     6  
     7  // This binary provides sample code for using the gopacket TCP assembler and TCP
     8  // stream reader.  It reads packets off the wire and reconstructs HTTP requests
     9  // it sees, logging them.
    10  package main
    11  
    12  import (
    13  	"bufio"
    14  	"flag"
    15  	"io"
    16  	"log"
    17  	"net/http"
    18  	"time"
    19  
    20  	"github.com/gopacket/gopacket"
    21  	"github.com/gopacket/gopacket/examples/util"
    22  	"github.com/gopacket/gopacket/layers"
    23  	"github.com/gopacket/gopacket/pcap"
    24  	"github.com/gopacket/gopacket/tcpassembly"
    25  	"github.com/gopacket/gopacket/tcpassembly/tcpreader"
    26  )
    27  
    28  var iface = flag.String("i", "eth0", "Interface to get packets from")
    29  var fname = flag.String("r", "", "Filename to read from, overrides -i")
    30  var snaplen = flag.Int("s", 1600, "SnapLen for pcap packet capture")
    31  var filter = flag.String("f", "tcp and dst port 80", "BPF filter for pcap")
    32  var logAllPackets = flag.Bool("v", false, "Logs every packet in great detail")
    33  
    34  // Build a simple HTTP request parser using tcpassembly.StreamFactory and tcpassembly.Stream interfaces
    35  
    36  // httpStreamFactory implements tcpassembly.StreamFactory
    37  type httpStreamFactory struct{}
    38  
    39  // httpStream will handle the actual decoding of http requests.
    40  type httpStream struct {
    41  	net, transport gopacket.Flow
    42  	r              tcpreader.ReaderStream
    43  }
    44  
    45  func (h *httpStreamFactory) New(net, transport gopacket.Flow) tcpassembly.Stream {
    46  	hstream := &httpStream{
    47  		net:       net,
    48  		transport: transport,
    49  		r:         tcpreader.NewReaderStream(),
    50  	}
    51  	go hstream.run() // Important... we must guarantee that data from the reader stream is read.
    52  
    53  	// ReaderStream implements tcpassembly.Stream, so we can return a pointer to it.
    54  	return &hstream.r
    55  }
    56  
    57  func (h *httpStream) run() {
    58  	buf := bufio.NewReader(&h.r)
    59  	for {
    60  		req, err := http.ReadRequest(buf)
    61  		if err == io.EOF {
    62  			// We must read until we see an EOF... very important!
    63  			return
    64  		} else if err != nil {
    65  			log.Println("Error reading stream", h.net, h.transport, ":", err)
    66  		} else {
    67  			bodyBytes := tcpreader.DiscardBytesToEOF(req.Body)
    68  			req.Body.Close()
    69  			log.Println("Received request from stream", h.net, h.transport, ":", req, "with", bodyBytes, "bytes in request body")
    70  		}
    71  	}
    72  }
    73  
    74  func main() {
    75  	defer util.Run()()
    76  	var handle *pcap.Handle
    77  	var err error
    78  
    79  	// Set up pcap packet capture
    80  	if *fname != "" {
    81  		log.Printf("Reading from pcap dump %q", *fname)
    82  		handle, err = pcap.OpenOffline(*fname)
    83  	} else {
    84  		log.Printf("Starting capture on interface %q", *iface)
    85  		handle, err = pcap.OpenLive(*iface, int32(*snaplen), true, pcap.BlockForever)
    86  	}
    87  	if err != nil {
    88  		log.Fatal(err)
    89  	}
    90  
    91  	if err := handle.SetBPFFilter(*filter); err != nil {
    92  		log.Fatal(err)
    93  	}
    94  
    95  	// Set up assembly
    96  	streamFactory := &httpStreamFactory{}
    97  	streamPool := tcpassembly.NewStreamPool(streamFactory)
    98  	assembler := tcpassembly.NewAssembler(streamPool)
    99  
   100  	log.Println("reading in packets")
   101  	// Read in packets, pass to assembler.
   102  	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
   103  	packets := packetSource.Packets()
   104  	ticker := time.Tick(time.Minute)
   105  	for {
   106  		select {
   107  		case packet := <-packets:
   108  			// A nil packet indicates the end of a pcap file.
   109  			if packet == nil {
   110  				return
   111  			}
   112  			if *logAllPackets {
   113  				log.Println(packet)
   114  			}
   115  			if packet.NetworkLayer() == nil || packet.TransportLayer() == nil || packet.TransportLayer().LayerType() != layers.LayerTypeTCP {
   116  				log.Println("Unusable packet")
   117  				continue
   118  			}
   119  			tcp := packet.TransportLayer().(*layers.TCP)
   120  			assembler.AssembleWithTimestamp(packet.NetworkLayer().NetworkFlow(), tcp, packet.Metadata().Timestamp)
   121  
   122  		case <-ticker:
   123  			// Every minute, flush connections that haven't seen activity in the past 2 minutes.
   124  			assembler.FlushOlderThan(time.Now().Add(time.Minute * -2))
   125  		}
   126  	}
   127  }