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 }