github.com/gopacket/gopacket@v1.1.0/tcpassembly/tcpreader/reader.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  // Package tcpreader provides an implementation for tcpassembly.Stream which presents
     8  // the caller with an io.Reader for easy processing.
     9  //
    10  // The assembly package handles packet data reordering, but its output is
    11  // library-specific, thus not usable by the majority of external Go libraries.
    12  // The io.Reader interface, on the other hand, is used throughout much of Go
    13  // code as an easy mechanism for reading in data streams and decoding them.  For
    14  // example, the net/http package provides the ReadRequest function, which can
    15  // parse an HTTP request from a live data stream, just what we'd want when
    16  // sniffing HTTP traffic.  Using ReaderStream, this is relatively easy to set
    17  // up:
    18  //
    19  //	// Create our StreamFactory
    20  //	type httpStreamFactory struct {}
    21  //	func (f *httpStreamFactory) New(a, b gopacket.Flow) tcpassembly.Stream {
    22  //		r := tcpreader.NewReaderStream()
    23  //		go printRequests(&r, a, b)
    24  //		return &r
    25  //	}
    26  //	func printRequests(r io.Reader, a, b gopacket.Flow) {
    27  //		// Convert to bufio, since that's what ReadRequest wants.
    28  //		buf := bufio.NewReader(r)
    29  //		for {
    30  //			if req, err := http.ReadRequest(buf); err == io.EOF {
    31  //				return
    32  //			} else if err != nil {
    33  //				log.Println("Error parsing HTTP requests:", err)
    34  //			} else {
    35  //				fmt.Println(a, b)
    36  //				fmt.Println("HTTP REQUEST:", req)
    37  //				fmt.Println("Body contains", tcpreader.DiscardBytesToEOF(req.Body), "bytes")
    38  //			}
    39  //		}
    40  //	}
    41  //
    42  // Using just this code, we're able to reference a powerful, built-in library
    43  // for HTTP request parsing to do all the dirty-work of parsing requests from
    44  // the wire in real-time.  Pass this stream factory to an tcpassembly.StreamPool,
    45  // start up an tcpassembly.Assembler, and you're good to go!
    46  package tcpreader
    47  
    48  import (
    49  	"errors"
    50  	"io"
    51  
    52  	"github.com/gopacket/gopacket/tcpassembly"
    53  )
    54  
    55  var discardBuffer = make([]byte, 4096)
    56  
    57  // DiscardBytesToFirstError will read in all bytes up to the first error
    58  // reported by the given reader, then return the number of bytes discarded
    59  // and the error encountered.
    60  func DiscardBytesToFirstError(r io.Reader) (discarded int, err error) {
    61  	for {
    62  		n, e := r.Read(discardBuffer)
    63  		discarded += n
    64  		if e != nil {
    65  			return discarded, e
    66  		}
    67  	}
    68  }
    69  
    70  // DiscardBytesToEOF will read in all bytes from a Reader until it
    71  // encounters an io.EOF, then return the number of bytes.  Be careful
    72  // of this... if used on a Reader that returns a non-io.EOF error
    73  // consistently, this will loop forever discarding that error while
    74  // it waits for an EOF.
    75  func DiscardBytesToEOF(r io.Reader) (discarded int) {
    76  	for {
    77  		n, e := DiscardBytesToFirstError(r)
    78  		discarded += n
    79  		if e == io.EOF {
    80  			return
    81  		}
    82  	}
    83  }
    84  
    85  // ReaderStream implements both tcpassembly.Stream and io.Reader.  You can use it
    86  // as a building block to make simple, easy stream handlers.
    87  //
    88  // IMPORTANT:  If you use a ReaderStream, you MUST read ALL BYTES from it,
    89  // quickly.  Not reading available bytes will block TCP stream reassembly.  It's
    90  // a common pattern to do this by starting a goroutine in the factory's New
    91  // method:
    92  //
    93  //	type myStreamHandler struct {
    94  //		r ReaderStream
    95  //	}
    96  //	func (m *myStreamHandler) run() {
    97  //		// Do something here that reads all of the ReaderStream, or your assembly
    98  //		// will block.
    99  //		fmt.Println(tcpreader.DiscardBytesToEOF(&m.r))
   100  //	}
   101  //	func (f *myStreamFactory) New(a, b gopacket.Flow) tcpassembly.Stream {
   102  //		s := &myStreamHandler{}
   103  //		go s.run()
   104  //		// Return the ReaderStream as the stream that assembly should populate.
   105  //		return &s.r
   106  //	}
   107  type ReaderStream struct {
   108  	ReaderStreamOptions
   109  	reassembled  chan []tcpassembly.Reassembly
   110  	done         chan bool
   111  	current      []tcpassembly.Reassembly
   112  	closed       bool
   113  	lossReported bool
   114  	first        bool
   115  	initiated    bool
   116  }
   117  
   118  // ReaderStreamOptions provides user-resettable options for a ReaderStream.
   119  type ReaderStreamOptions struct {
   120  	// LossErrors determines whether this stream will return
   121  	// ReaderStreamDataLoss errors from its Read function whenever it
   122  	// determines data has been lost.
   123  	LossErrors bool
   124  }
   125  
   126  // NewReaderStream returns a new ReaderStream object.
   127  func NewReaderStream() ReaderStream {
   128  	r := ReaderStream{
   129  		reassembled: make(chan []tcpassembly.Reassembly),
   130  		done:        make(chan bool),
   131  		first:       true,
   132  		initiated:   true,
   133  	}
   134  	return r
   135  }
   136  
   137  // Reassembled implements tcpassembly.Stream's Reassembled function.
   138  func (r *ReaderStream) Reassembled(reassembly []tcpassembly.Reassembly) {
   139  	if !r.initiated {
   140  		panic("ReaderStream not created via NewReaderStream")
   141  	}
   142  	r.reassembled <- reassembly
   143  	<-r.done
   144  }
   145  
   146  // ReassemblyComplete implements tcpassembly.Stream's ReassemblyComplete function.
   147  func (r *ReaderStream) ReassemblyComplete() {
   148  	close(r.reassembled)
   149  	close(r.done)
   150  }
   151  
   152  // stripEmpty strips empty reassembly slices off the front of its current set of
   153  // slices.
   154  func (r *ReaderStream) stripEmpty() {
   155  	for len(r.current) > 0 && len(r.current[0].Bytes) == 0 {
   156  		r.current = r.current[1:]
   157  		r.lossReported = false
   158  	}
   159  }
   160  
   161  // DataLost is returned by the ReaderStream's Read function when it encounters
   162  // a Reassembly with Skip != 0.
   163  var DataLost = errors.New("lost data")
   164  
   165  // Read implements io.Reader's Read function.
   166  // Given a byte slice, it will either copy a non-zero number of bytes into
   167  // that slice and return the number of bytes and a nil error, or it will
   168  // leave slice p as is and return 0, io.EOF.
   169  func (r *ReaderStream) Read(p []byte) (int, error) {
   170  	if !r.initiated {
   171  		panic("ReaderStream not created via NewReaderStream")
   172  	}
   173  	var ok bool
   174  	r.stripEmpty()
   175  	for !r.closed && len(r.current) == 0 {
   176  		if r.first {
   177  			r.first = false
   178  		} else {
   179  			r.done <- true
   180  		}
   181  		if r.current, ok = <-r.reassembled; ok {
   182  			r.stripEmpty()
   183  		} else {
   184  			r.closed = true
   185  		}
   186  	}
   187  	if len(r.current) > 0 {
   188  		current := &r.current[0]
   189  		if r.LossErrors && !r.lossReported && current.Skip != 0 {
   190  			r.lossReported = true
   191  			return 0, DataLost
   192  		}
   193  		length := copy(p, current.Bytes)
   194  		current.Bytes = current.Bytes[length:]
   195  		return length, nil
   196  	}
   197  	return 0, io.EOF
   198  }
   199  
   200  // Close implements io.Closer's Close function, making ReaderStream a
   201  // io.ReadCloser.  It discards all remaining bytes in the reassembly in a
   202  // manner that's safe for the assembler (IE: it doesn't block).
   203  func (r *ReaderStream) Close() error {
   204  	r.current = nil
   205  	r.closed = true
   206  	for {
   207  		if _, ok := <-r.reassembled; !ok {
   208  			return nil
   209  		}
   210  		r.done <- true
   211  	}
   212  }