def test_full_packet_parsing_default_stack(self): full_eii_packet_data = \ [0x52, 0x54, 0x00, 0x12, 0x35, 0x02, 0x08, 0x00, 0x27, 0xc6, 0x25, 0x01, 0x08, 0x00, 0x45, 0x10, 0x00, 0x5c, 0x93, 0x06, 0x40, 0x00, 0x40, 0x06, 0x8f, 0x75, 0x0a, 0x00, 0x02, 0x0f, 0x0a, 0x00, 0x02, 0x02, 0x00, 0x16, 0xd1, 0xf4, 0x52, 0x1a, 0x58, 0x7c, 0x58, 0x25, 0x2e, 0x9b, 0x50, 0x18, 0x9f, 0xb0, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xb0, 0x2c, 0x08, 0xab, 0x7f, 0x98, 0x3c, 0x58, 0x54, 0xeb, 0xde, 0x01, 0xd1, 0xc7, 0xbe, 0xf8, 0x85, 0xba, 0xe4, 0xb6, 0xea, 0x06, 0x98, 0xf9, 0xc0, 0xd2, 0xac, 0x17, 0x19, 0x85, 0x54, 0xdb, 0xe1, 0x3b, 0x3e, 0x85, 0x61, 0xee, 0x31, 0xaf, 0x36, 0xa6, 0x04, 0xd3, 0x51, 0x21, 0x09, 0x20] packet = PCAPPacket(full_eii_packet_data, '13:00') pmap = packet.parse() self.assertEqual('13:00', packet.timestamp) self.assertTrue('ethernet' in pmap) self.assertTrue('ip' in pmap) self.assertTrue('tcp' in pmap) self.assertEqual(PCAPEthernet, type(pmap['ethernet'])) self.assertEqual(PCAPIP4, type(pmap['ip'])) self.assertEqual(PCAPTCP, type(pmap['tcp'])) self.assertEqual('08:00:27:c6:25:01', pmap['ethernet'].source_mac) self.assertEqual('52:54:00:12:35:02', pmap['ethernet'].dest_mac) self.assertEqual(PCAPIP4, pmap['ethernet'].next_parse_recommendation) self.assertEqual('10.0.2.15', pmap['ip'].source_ip) self.assertEqual('10.0.2.2', pmap['ip'].dest_ip) self.assertEqual(5, pmap['ip'].header_length) self.assertEqual(4, pmap['ip'].version) self.assertEqual(IP4_PROTOCOL_TCP, pmap['ip'].protocol) self.assertEqual(PCAPTCP, pmap['ip'].next_parse_recommendation) self.assertEqual(22, pmap['tcp'].source_port) self.assertEqual(53748, pmap['tcp'].dest_port) self.assertEqual(1377458300, pmap['tcp'].seq) self.assertEqual(1478831771, pmap['tcp'].ack) self.assertEqual(5, pmap['tcp'].data_offset) self.assertEqual(True, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_ACK)) self.assertEqual(True, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_PUSH)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_CWS)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_ECE)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_FINAL)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_RESET)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_NS)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_SYN)) self.assertEqual(False, pmap['tcp'].is_flag_set(TCP_PROTOCOL_FLAG_URGENT)) self.assertEqual(40880, pmap['tcp'].window_size) self.assertEqual(None, pmap['tcp'].next_parse_recommendation)
def _read_packet(cli=LinuxCLI(), flag_set=None, interface='any', count=1, packet_type='', pcap_filter=None, max_size=0, packet_queues=None, callback=None, callback_args=None, save_dump_file=False, save_dump_filename=None): tmp_dump_filename = './.tcpdump.out.' + str(time.time()) try: # If flag set provided, use them instead, for synch with external functions tcp_ready = threading.Event() if flag_set is None else flag_set[0] tcp_error = threading.Event() if flag_set is None else flag_set[1] tcp_stop = threading.Event() if flag_set is None else flag_set[2] tcp_finished = threading.Event() if flag_set is None else flag_set[3] # If queue set provided, use them instead for synch with external functions packet_queue = Queue.Queue() if packet_queues is None else packet_queues[0] status_queue = Queue.Queue() if packet_queues is None else packet_queues[1] count_str = ('-c ' + str(count) if count > 0 else '') iface_str = '-i ' + interface max_size_str = '-s ' + max_size if max_size != 0 else '' type_str = '-T ' + packet_type if packet_type != '' else '' cmd_str = 'tcpdump -n -xx -l ' + \ count_str + ' ' + iface_str + ' ' + \ max_size_str + ' ' + type_str + \ "'" + (pcap_filter.to_str() if pcap_filter is not None else '') + "'" + \ ' >> ' + tmp_dump_filename #cli.log_cmd = True # FLAG STATE: ready[clear], stop[clear], finished[clear] with open(tmp_dump_filename, 'w') as f: f.write("--START--\n") tcp_process = cli.cmd(cmd_line=cmd_str, blocking=False) flags_se = fcntl(tcp_process.stderr, F_GETFL) # get current p.stderr flags fcntl(tcp_process.stderr, F_SETFL, flags_se | os.O_NONBLOCK) err_out = '' while tcp_ready.is_set() is False: try: line = os.read(tcp_process.stderr.fileno(), 256) if line.find('listening on') != -1: # TODO: Replace sleep after TCPDump starts with a real check # This is dangerous, and might not actually be enough to signal the # tcpdump is actually running. Instead, let's create a Cython module that # passes calls through to libpcap (there are 0 good libpcap implementations # for Python that are maintained, documented, and simple). time.sleep(1) tcp_ready.set() else: err_out += line if tcp_process.poll() is not None: out, err = tcp_process.communicate() status_queue.put({'error': 'tcpdump exited abnormally', 'returncode': tcp_process.returncode, 'stdout': out, 'stderr': err_out}) print 'item on status queue' tcp_error.set() raise SubprocessFailedException('tcpdump exited abnormally with status: ' + str(tcp_process.returncode)) time.sleep(0) except OSError: pass # FLAG STATE: ready[set], stop[clear], finished[clear] # tcpdump return output format: # hh:mm:ss.tick L3Proto <Proto-specific fields>\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n <- eight quads of hexadecimal numbers # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n representing 16 bytes or 4 32-bit words # hh:mm:ss.tick L3Proto <Proto-specific fields>\n <- Next packet # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n # \t0x<addr>: FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n packet_data = [] timestamp = '' with open(tmp_dump_filename, 'r+') as f: # Prepare for the first packet by reading the file until the first # packet's lines arrive (or until stopped by a stop_capture call) if f.readline().rstrip() != '--START--': status_queue.put({'error': 'Expected --START-- tag at beginning of dumpfile', 'returncode': tcp_process.returncode, 'stdout': '', 'stderr': ''}) tcp_error.set() raise ArgMismatchException('Expected --START-- tag at beginning of dumpfile') while True: # Read the lines and either append data, start a new packet, or finish # Read the lines and either append data, start a new packet, or finish line = f.readline() if line == '': #EOF # Is the tcpdump process finished or signaled to finish? if tcp_process.poll() is not None or tcp_stop.is_set(): if tcp_process.poll() is None: # Kill the TCP subprocess if it is still running common.Utils.terminate_process(tcp_process) # If we finished with packet data buffered up, append that packet to the queue if len(packet_data) > 0: # Create and parse the packet and push it onto the return list, calling # the callback function if one is set. packet = PCAPPacket(packet_data, timestamp) packet.parse() packet_queue.put(packet) if callback is not None: callback(packet, *(callback_args if callback_args is not None else ())) # Stop packet collection and exit break # Otherwise, we need to wait for data time.sleep(0) elif line.startswith('\t'): # Normal packet data: buffer into current packet packet_data += parse_line_to_byte_array(line) else: # We hit the end of the packet and will start a new packet # Only run if we had packet data buffered if len(packet_data) > 0: # Create and parse the packet and push it onto the return list, calling # the callback function if one is set. packet = PCAPPacket(packet_data, timestamp) packet.parse() packet_queue.put(packet) if callback is not None: callback(packet, *(callback_args if callback_args is not None else [])) packet_data = [] # Start the new packet by reading the timestamp timestamp = line.split(' ', 2)[0] finally: # Save the tcpdump output (if requested) and delete the temporary file if save_dump_file is True: LinuxCLI().copy_file(tmp_dump_filename, save_dump_filename if save_dump_filename is not None else 'tcp.out.' + str(time.time())) LinuxCLI().rm(tmp_dump_filename) status_queue.put({'success': '', 'returncode' : tcp_process.returncode, 'stdout': tcp_process.stdout, 'stderr': tcp_process.stderr}) # FLAG STATE: ready[set], stop[set], finished[clear] tcp_finished.set() # FLAG STATE: ready[set], stop[set], finished[set] return packet_queue