def add(self, pkt): ''' Merge the packet into the first chunk it overlaps with. If data was added to the end of a chunk, attempts to merge the next chunk (if there is one). This way, it is ensured that everything is as fully merged as it can be with the current data. Args: pkt = tcp.Packet ''' if self.finished: raise RuntimeError('tried to add packets to a finished tcp.Direction') # discard packets with no payload. we don't care about them here if pkt.data == '': return # attempt to merge packet with existing chunks merged = False for i, chunk in enumerate(self.chunks): overlapped, (front, back) = chunk.merge(pkt, self.create_merge_callback(pkt)) if overlapped: # check if this packet bridged the gap between two chunks if back and i < (len(self.chunks)-1): overlapped2, (front2, back2) = chunk.merge(self.chunks[i+1]) if overlapped2: # Used to be assert( (not front2) and back2), meaning # the merge was at the back of chunk. However, that # check seems incorrect if pkt completely # overlaps self.chunks[i+1]. In that case, pkt will # merge into the back of chunk, and chunk will # completely overlap self.chunks[i+1]. In that case, we # have (not front2) and (not back2). assert( (not front2) and ( back2 or (seq.lte(pkt.seq_start, self.chunks[i+1].seq_start) and seq.lte(self.chunks[i+1].seq_end, pkt.seq_end)) )) if not back2: logging.info('Overlapping chunk(%d-%d) consumed next ' 'chunk(%d-%d) in violation of old assert.' , chunk.seq_start, chunk.seq_end, self.chunks[i+1].seq_start, self.chunks[i+1].seq_end) del self.chunks[i+1] # if this is the main data chunk, calc final arrival if self.seq_start and chunk.seq_start == self.seq_start: if front: # packet was first in stream but just now arriving self.final_arrival_data.insert((self.seq_start, pkt.ts)) if back: # usual case self.final_arrival_data.insert((self.final_arrival_pointer, pkt.ts)) if not self.final_data_chunk: self.final_data_chunk = chunk self.final_arrival_pointer = self.final_data_chunk.seq_end merged = True break # skip further chunks if not merged: # nothing overlapped with the packet # we need a new chunk self.new_chunk(pkt)
def inner_merge(self, newseq, newdata, callback): ''' Internal implementation function for merging, very similar in interface to merge_pkt, but concentrates on the nitty-gritty logic of merging, as opposed to the high-level logic of merge(). Args: newseq = (seq_begin, seq_end) newdata = string, new data callback = see new_seq_callback in merge_pkt Returns: see merge_pkt ''' # setup overlapped = False added_front_data = False added_back_data = False # front data? if (seq.lt(newseq[0], self.seq_start) and seq.lte(self.seq_start, newseq[1])): new_data_length = seq.subtract(self.seq_start, newseq[0]) # slice out new data, stick it on the front self.data = newdata[:new_data_length] + self.data self.seq_start = newseq[0] # notifications overlapped = True added_front_data = True if callback: callback(newseq[0]) # back data? if seq.lte(newseq[0], self.seq_end) and seq.lt(self.seq_end, newseq[1]): new_data_length = seq.subtract(newseq[1], self.seq_end) self.data += newdata[-new_data_length:] self.seq_end += new_data_length # notifications overlapped = True added_back_data = True if callback: # the first seq number of new data in the back back_seq_start = newseq[1] - new_data_length callback(back_seq_start) # completely inside? if (seq.lte(self.seq_start, newseq[0]) and seq.lte(newseq[1], self.seq_end)): overlapped = True # done return (overlapped, (added_front_data, added_back_data))