def test_full_packet_parsing_l3_parse_error2(self):
        full_eii_packet_data = \
            [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00,
             0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 0x08, 0x00,
             0x41, 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]

        packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00')
        self.assertRaises(exceptions.PacketParsingException, packet.parse,
                          [pcap_packet.PCAPSLL])

        pmap = packet.layer_data
        emap = packet.extra_data

        self.assertEqual(1, len(emap['parse_errors.ip']))
        self.assertRegexpMatches(emap['parse_errors.ip'][0],
                                 'field must be at least')

        self.assertEqual('13:00', packet.timestamp)
        self.assertTrue('ethernet' in pmap)

        self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet']))

        ether_pmap = pmap['ethernet']
        """ :type: pcap_packet.PCAPEthernet """
        self.assertEqual('08:00:27:c6:25:01', ether_pmap.source_mac)
        self.assertEqual('00:00:00:00:00:00', ether_pmap.dest_mac)
        self.assertEqual(pcap_packet.PCAPIP4,
                         pmap['ethernet'].next_parse_recommendation)
    def test_full_packet_parsing_l3_bad_length(self):
        full_eii_packet_data = \
            [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00,
             0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 0x08, 0x00,
             0x45, 0x10, 0x00, 0x5c, 0x93, 0x06]

        packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00')
        self.assertRaises(exceptions.PacketParsingException, packet.parse,
                          [pcap_packet.PCAPSLL])

        pmap = packet.layer_data
        """ :type: dict[str, PCAPPacket.PCAPEncapsulatedLayer] """
        emap = packet.extra_data

        self.assertEqual(1, len(emap['parse_errors.ip']))
        self.assertRegexpMatches(emap['parse_errors.ip'][0],
                                 'must at least be')

        self.assertEqual('13:00', packet.timestamp)
        self.assertTrue('ethernet' in pmap)

        self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet']))

        ether_pmap = pmap['ethernet']
        """ :type: PCAPPacket.PCAPEthernet """
        self.assertEqual('08:00:27:c6:25:01', ether_pmap.source_mac)
        self.assertEqual('00:00:00:00:00:00', ether_pmap.dest_mac)
        self.assertEqual(pcap_packet.PCAPIP4,
                         pmap['ethernet'].next_parse_recommendation)
    def test_full_packet_parsing_l2_bad_length(self):
        full_eii_packet_data = \
            [0x52, 0x54, 0x00, 0x12, 0x35, 0x02, 0x08, 0x00,
             0x27, 0xc6]

        packet = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00')
        self.assertRaises(exceptions.PacketParsingException, packet.parse)

        emap = packet.extra_data

        self.assertEqual(1, len(emap['parse_errors.ethernet']))
        self.assertRegexpMatches(emap['parse_errors.ethernet'][0],
                                 'must at least be')

        self.assertEqual('13:00', packet.timestamp)
    def test_full_packet_parsing_set_partial_stack(self):
        full_eii_packet_data = \
            [0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x08, 0x00,
             0x27, 0xc6, 0x25, 0x01, 0x00, 0x00, 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 = pcap_packet.PCAPPacket(full_eii_packet_data, '13:00')
        pmap = packet.parse([pcap_packet.PCAPSLL])

        self.assertEqual('13:00', packet.timestamp)
        self.assertTrue('ethernet' in pmap)
        self.assertTrue('ip' in pmap)
        self.assertTrue('tcp' in pmap)

        self.assertEqual(pcap_packet.PCAPSLL, type(pmap['ethernet']))
        self.assertEqual(pcap_packet.PCAPIP4, type(pmap['ip']))
        self.assertEqual(pcap_packet.PCAPTCP, type(pmap['tcp']))

        self.assertEqual('08:00:27:c6:25:01', pmap['ethernet'].source_mac)
        self.assertEqual('00:00:00:00:00:00', pmap['ethernet'].dest_mac)
        self.assertEqual(pcap_packet.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(pcap_packet.IP4_PROTOCOL_TCP, pmap['ip'].protocol)
        self.assertEqual(pcap_packet.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(pcap_packet.TCP_PROTOCOL_FLAG_ACK))
        self.assertEqual(
            True, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_PUSH))
        self.assertEqual(
            False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_CWS))
        self.assertEqual(
            False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_ECE))
        self.assertEqual(
            False,
            pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_FINAL))
        self.assertEqual(
            False,
            pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_RESET))
        self.assertEqual(
            False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_NS))
        self.assertEqual(
            False, pmap['tcp'].is_flag_set(pcap_packet.TCP_PROTOCOL_FLAG_SYN))
        self.assertEqual(
            False,
            pmap['tcp'].is_flag_set(pcap_packet.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())
        tcp_processes = []
        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]

            cmd1 = ['tcpdump', '-n', '-xx', '-l']
            cmd1 += ['-c', str(count)] \
                if count > 0 else []
            cmd1 += ['-i', interface]
            cmd1 += ['-s', str(max_size)] \
                if max_size != 0 else []
            cmd1 += ['-T', packet_type] \
                if packet_type != '' else []
            cmd1 += [pcap_filter.to_str()] \
                if pcap_filter is not None else []

            cmd2 = ['tee', '-a', tmp_dump_filename]

            # FLAG STATE: ready[clear], stop[clear], finished[clear]
            with open(name=tmp_dump_filename, mode='w') as f:
                f.write("--START--\n")

            tcp_processes = cli.cmd(cmd_list=[cmd1, cmd2],
                                    blocking=False)
            tcp_piped_process = tcp_processes.process
            tcp_actual_process = tcp_processes.process_array[0]

            # set current p.stderr flags to NONBLOCK
            # Note that as stderr is NOT redirected through pipes, we
            # must listen on the actual tcpdump process's stderr
            # (not the tee process!)
            flags_se = fcntl(tcp_actual_process.stderr, F_GETFL)
            fcntl(tcp_actual_process.stderr, F_SETFL, flags_se | os.O_NONBLOCK)

            err_out = ''
            while not tcp_ready.is_set():
                try:
                    line = os.read(tcp_actual_process.stderr.fileno(), 256)
                    if line.find('listening on') != -1:
                        # TODO(micucci): Replace sleep after TCPDump s
                        # 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_piped_process.poll() is not None:
                            out, err = tcp_piped_process.communicate()
                            status_queue.put(
                                {'error': 'tcpdump exited abnormally',
                                 'returncode': tcp_piped_process.returncode,
                                 'stdout': out,
                                 'stderr': err_out})
                            tcp_error.set()

                            raise exceptions.SubprocessFailedException(
                                'tcpdump exited abnormally with status: ' +
                                str(tcp_piped_process.returncode) +
                                ', out: ' + out +
                                ', err: ' + err +
                                ', err_out: ' + err_out)
                        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
            # \t0x<addr>:  FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n
            # (eight quads of hexadecimal numbers representing 16
            # bytes or 4 32-bit words)
            #
            # hh:mm:ss.tick L3Proto <Proto-specific fields>\n
            # \t0x<addr>:  FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n
            # \t0x<addr>:  FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF\n
            # (Next packet)

            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_piped_process.returncode,
                         'stdout': '',
                         'stderr': ''})
                    tcp_error.set()
                    raise exceptions.ArgMismatchException(
                        'Expected --START-- tag at beginning of dumpfile')

                while True:
                    # 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_piped_process.poll() is not None \
                                or tcp_stop.is_set():
                            # 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 = pcap_packet.PCAPPacket(
                                    packet_data, timestamp)
                                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 = pcap_packet.PCAPPacket(
                                packet_data, timestamp)
                            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)
            tcp_processes.terminate()

        status_queue.put({'success': '',
                          'returncode': tcp_piped_process.returncode,
                          'stdout': tcp_piped_process.stdout,
                          'stderr': tcp_piped_process.stderr})

        # FLAG STATE: ready[set], stop[set], finished[clear]
        tcp_finished.set()

        # FLAG STATE: ready[set], stop[set], finished[set]
        return packet_queue