Exemple #1
0
def _capture_all(
    device_names: typing.List[str], filter_expression: str
) -> typing.List[typing.Tuple[str, object, LinkLayerPacket.Decoder]]:
    """
    Begin capture on all devices in promiscuous mode.
    We can't use "any" because libpcap does not support promiscuous mode with it, as stated in the docs and here:
    https://github.com/the-tcpdump-group/libpcap/blob/bcca74d2713dc9c0a27992102c469f77bdd8dd1f/pcap-linux.c#L2522.
    It shouldn't be a problem because we have our filter expression that is expected to be highly efficient.
    Devices whose ifaces are down or that are not usable for other valid reasons will be silently filtered out here.
    """
    import libpcap as pcap

    codecs = LinkLayerPacket.get_codecs()
    caps: typing.List[typing.Tuple[str, object, LinkLayerPacket.Decoder]] = []
    try:
        for name in device_names:
            pd = _capture_single_device(name, filter_expression, list(codecs.keys()))
            if pd is None:
                _logger.info("Could not set up capture on %r", name)
                continue
            data_link_type = pcap.datalink(pd)
            try:
                _, dec = codecs[data_link_type]
            except LookupError:
                # This is where we filter out devices that certainly have no relevance, like CAN adapters.
                pcap.close(pd)
                _logger.info(
                    "Device %r will not be used for packet capture because its data link layer type=%r "
                    "is not supported by this library. Either the device is irrelevant, "
                    "or the library needs to be extended to support this link layer protocol.",
                    name,
                    data_link_type,
                )
            else:
                caps.append((name, pd, dec))
    except Exception:
        for _, c, _ in caps:
            pcap.close(c)
        raise
    _logger.info(
        "Capture sessions with filter %r have been set up on: %s", filter_expression, list(n for n, _, _ in caps)
    )
    return caps
Exemple #2
0
def _capture_single_device(
    device: str, filter_expression: str, data_link_hints: typing.Sequence[int]
) -> typing.Optional[object]:
    """
    Returns None if the interface managed by this device is not up or if it cannot be captured from for other reasons.
    On GNU/Linux, some virtual devices (like netfilter devices) can only be accessed by a superuser.

    The function will configure libpcap to use the first supported data link type from the list.
    If none of the specified data link types are supported, a log message is emitted but no error is raised.
    The available link types are listed in https://www.tcpdump.org/linktypes.html.
    """
    import libpcap as pcap

    def status_to_str(error_code: int) -> str:
        """
        Some libpcap-compatible libraries (e.g., WinPCap) do not have this function, so we have to define a fallback.
        """
        try:
            return str(pcap.statustostr(error_code).decode())
        except AttributeError:  # pragma: no cover
            return f"[error {error_code}]"

    # This is helpful: https://github.com/karpierz/libpcap/blob/master/tests/capturetest.py
    err_buf = ctypes.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)
    pd = pcap.create(device.encode(), err_buf)
    if pd is None:
        raise LinkLayerCaptureError(f"Could not instantiate pcap_t for {device!r}: {err_buf.value.decode()}")
    try:
        # Non-fatal errors are intentionally logged at a low severity level to not disturb the user unnecessarily.
        err = pcap.set_snaplen(pd, _SNAPSHOT_LENGTH)
        if err != 0:
            _logger.info("Could not set snapshot length for %r: %r", device, status_to_str(err))

        err = pcap.set_timeout(pd, int(_BUFFER_TIMEOUT * 1e3))
        if err != 0:
            _logger.info("Could not set timeout for %r: %r", device, status_to_str(err))

        err = pcap.set_promisc(pd, 1)
        if err != 0:
            _logger.info("Could not enable promiscuous mode for %r: %r", device, status_to_str(err))

        err = pcap.activate(pd)
        if err in (pcap.PCAP_ERROR_PERM_DENIED, pcap.PCAP_ERROR_PROMISC_PERM_DENIED):
            raise PermissionError(f"Capture is not permitted on {device!r}: {status_to_str(err)}")
        if err == pcap.PCAP_ERROR_IFACE_NOT_UP:
            _logger.debug("Device %r is not capturable because the iface is not up. %s", device, status_to_str(err))
            pcap.close(pd)
            return None
        if err < 0:
            _logger.info(
                "Could not activate capture on %r: %s; %s", device, status_to_str(err), pcap.geterr(pd).decode()
            )
            pcap.close(pd)
            return None
        if err > 0:
            _logger.info(
                "Capture on %r started successfully, but libpcap reported a warning: %s", device, status_to_str(err)
            )

        # https://www.tcpdump.org/manpages/pcap_set_datalink.3pcap.html
        for dlt in data_link_hints:
            err = pcap.set_datalink(pd, dlt)
            if err == 0:
                _logger.debug("Device %r is configured to use the data link type %r", device, dlt)
                break
        else:
            _logger.debug(
                "Device %r supports none of the following data link types: %r. Last error was: %s",
                device,
                list(data_link_hints),
                pcap.geterr(pd).decode(),
            )

        # https://www.tcpdump.org/manpages/pcap_compile.3pcap.html
        code = pcap.bpf_program()  # This memory needs to be freed when closed. Fix it later.
        err = pcap.compile(pd, ctypes.byref(code), filter_expression.encode(), 1, pcap.PCAP_NETMASK_UNKNOWN)
        if err != 0:
            raise LinkLayerCaptureError(
                f"Could not compile filter expression {filter_expression!r}: {status_to_str(err)}; "
                f"{pcap.geterr(pd).decode()}"
            )
        err = pcap.setfilter(pd, ctypes.byref(code))
        if err != 0:
            raise LinkLayerCaptureError(f"Could not install filter: {status_to_str(err)}; {pcap.geterr(pd).decode()}")
    except Exception:
        pcap.close(pd)
        raise
    return typing.cast(object, pd)
Exemple #3
0
    def _thread_worker(self, name: str, pd: object, decoder: LinkLayerPacket.Decoder) -> None:
        import libpcap as pcap

        assert isinstance(pd, ctypes.POINTER(pcap.pcap_t))
        try:
            _logger.debug("%r: Worker thread for %r is started: %s", self, name, threading.current_thread())

            # noinspection PyTypeChecker
            @pcap.pcap_handler  # type: ignore
            def proxy(_: object, header: ctypes.Structure, packet: typing.Any) -> None:
                # Parse the header, extract the timestamp and the packet length.
                header = header.contents
                ts_ns = (header.ts.tv_sec * 1_000_000 + header.ts.tv_usec) * 1000
                ts = Timestamp(system_ns=ts_ns, monotonic_ns=time.monotonic_ns())
                length, real_length = header.caplen, header.len
                _logger.debug("%r: CAPTURED PACKET ts=%s dev=%r len=%d bytes", self, ts, name, length)
                if real_length != length:
                    # In theory, this should never occur because we use a huge capture buffer.
                    # On Windows, however, when using Npcap v0.96, the captured length is (always?) reported to be
                    # 32 bytes shorter than the real length, despite the fact that the packet is not truncated.
                    _logger.debug(
                        "%r: Length mismatch in a packet captured from %r: real %r bytes, captured %r bytes",
                        self,
                        name,
                        real_length,
                        length,
                    )
                # Create a copy of the payload. This is required per the libpcap API contract -- it says that the
                # memory is invalidated upon return from the callback.
                packet = memoryview(ctypes.cast(packet, ctypes.POINTER(ctypes.c_ubyte * length))[0]).tobytes()
                llp = decoder(memoryview(packet))
                if llp is None:
                    if _logger.isEnabledFor(logging.INFO):
                        _logger.info(
                            "%r: Link-layer packet of %d bytes captured from %r at %s could not be parsed. "
                            "The header is: %s",
                            self,
                            len(packet),
                            name,
                            ts,
                            packet[:32].hex(),
                        )
                else:
                    self._callback(LinkLayerCapture(timestamp=ts, packet=llp, device_name=name))

            packets_per_batch = 100
            while self._keep_going:
                err = pcap.dispatch(pd, packets_per_batch, proxy, ctypes.POINTER(ctypes.c_ubyte)())
                if err < 0:  # Negative values represent errors, otherwise it's the number of packets processed.
                    if self._keep_going:
                        _logger.critical(
                            "%r: Worker thread for %r has failed with error %s; %s",
                            self,
                            name,
                            err,
                            pcap.geterr(pd).decode(),
                        )
                    else:
                        _logger.debug(
                            "%r: Error %r in worker thread for %r ignored because it is commanded to stop",
                            self,
                            err,
                            name,
                        )
                    break
        except Exception as ex:
            _logger.exception("%r: Unhandled exception in worker thread for %r; stopping: %r", self, name, ex)
        finally:
            # BEWARE: pcap_close() is not idempotent! Second close causes a heap corruption. *sigh*
            pcap.close(pd)
        _logger.debug("%r: Worker thread for %r is being terminated", self, name)
def main(argv):

    global program_name
    program_name = os.path.basename(argv[0])

    try:
        opts, args = getopt.getopt(argv[1:], "i:Ips:aB:")
    except getopt.GetoptError:
        usage()

    device = None
    dorfmon = False
    dopromisc = False
    snaplen = MAXIMUM_SNAPLEN
    bufsize = 0
    useactivate = False
    for opt, optarg in opts:
        if opt == '-i':
            device = optarg.encode("utf-8")
        elif opt == '-I':
            dorfmon = True
            useactivate = True  # required for rfmon
        elif opt == '-p':
            dopromisc = True
        elif opt == '-s':
            try:
                long_snaplen = int(optarg)
            except:
                error("invalid snaplen {}", optarg)
            if not (0 <= long_snaplen <= MAXIMUM_SNAPLEN):
                error("invalid snaplen {}", optarg)
            elif long_snaplen == 0:  # <AK> fix, was: snaplen == 0:
                snaplen = MAXIMUM_SNAPLEN
            else:
                snaplen = long_snaplen
        elif opt == '-B':
            try:
                bufsize = int(optarg) * 1024
            except:
                error("invalid packet buffer size {}", optarg)
            if bufsize <= 0:
                error("invalid packet buffer size {}", optarg)
            useactivate = True  # required for bufsize
        elif opt == '-a':
            useactivate = True
        else:
            usage()

    status = 0

    ebuf = ct.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)

    if device is None:
        devlist = ct.POINTER(pcap.pcap_if_t)()
        if pcap.findalldevs(ct.byref(devlist), ebuf) == -1:
            error("{!s}", ebuf.value.decode("utf-8", "ignore"))
        if not devlist:
            error("no interfaces available for capture")
        device = devlist[0].name
        pcap.freealldevs(devlist)

    if useactivate:

        pd = pcap.create(device, ebuf)
        if not pd:
            error("{!s}: pcap.create failed: {!s}",
                  device.decode("utf-8"),
                  ebuf.value.decode("utf-8", "ignore"))

        status = pcap.set_snaplen(pd, snaplen)
        if status != 0:
            error("{!s}: pcap.set_snaplen failed: {!s}",
                  device.decode("utf-8"),
                  statustostr(status).decode("utf-8", "ignore"))

        if dopromisc:
            status = pcap.set_promisc(pd, 1)
            if status != 0:
                error("{!s}: pcap.set_promisc failed: {!s}",
                      device.decode("utf-8"),
                      statustostr(status).decode("utf-8", "ignore"))

        if dorfmon:
            try:
                status = pcap.set_rfmon(pd, 1)
            except AttributeError:
                error("pcap.set_rfmon is not available on this platform")
            if status != 0:
                error("{!s}: pcap.set_rfmon failed: {!s}",
                      device.decode("utf-8"),
                      statustostr(status).decode("utf-8", "ignore"))

        status = pcap.set_timeout(pd, 1000)
        if status != 0:
            error("{!s}: pcap.set_timeout failed: {!s}",
                  device.decode("utf-8"),
                  statustostr(status).decode("utf-8", "ignore"))

        if bufsize != 0:
            status = pcap.set_buffer_size(pd, bufsize)
            if status != 0:
                error("{!s}: pcap.set_buffer_size failed: {!s}",
                      device.decode("utf-8"),
                      statustostr(status).decode("utf-8", "ignore"))

        status = pcap.activate(pd)
        if status < 0:
            # pcap.activate() failed.
            error("{!s}: {!s}\n({!s})",
                  device.decode("utf-8"),
                  statustostr(status).decode("utf-8", "ignore"),
                  pcap.geterr(pd).decode("utf-8", "ignore"))
        elif status > 0:
            # pcap.activate() succeeded, but it's warning us
            # of a problem it had.
            warning("{!s}: {!s}\n({!s})",
                    device.decode("utf-8"),
                    statustostr(status).decode("utf-8", "ignore"),
                    pcap.geterr(pd).decode("utf-8", "ignore"))
        else:
            print("{!s} opened successfully".format(device.decode("utf-8")))

        pcap.close(pd)

    else:

        ebuf.value = b""
        pd = pcap.open_live(device, 65535, 0, 1000, ebuf)
        if not pd:
            error("{!s}", ebuf.value.decode("utf-8", "ignore"))
        elif ebuf.value:
            warning("{!s}", ebuf.value.decode("utf-8", "ignore"))
        else:
            print("{!s} opened successfully".format(device.decode("utf-8")))

        pcap.close(pd)

    return 1 if status < 0 else 0
Exemple #5
0
def main(argv=sys.argv):

    global program_name
    program_name = os.path.basename(sys.argv[0])

    try:
        opts, args = getopt.getopt(argv[1:], "i:sptn")
    except getopt.GetoptError:
        usage()

    device = None
    doselect = False
    dopoll = False
    mechanism = None
    dotimeout = False
    dononblock = False
    for opt, optarg in opts:
        if opt == '-i':
            device = optarg.encode("utf-8")
        elif opt == '-s':
            doselect = True
            mechanism = "select() and pcap_dispatch()"
        elif opt == '-p':
            dopoll = True
            mechanism = "poll() and pcap_dispatch()"
        elif opt == '-t':
            dotimeout = True
        elif opt == '-n':
            dononblock = True
        else:
            usage()

    expression = args

    if doselect and dopoll:
        print("selpolltest: choose select (-s) or poll (-p), but not both",
              file=sys.stderr)
        return 1
    if dotimeout and not doselect and not dopoll:
        print("selpolltest: timeout (-t) requires select (-s) or poll (-p)",
              file=sys.stderr)
        return 1

    ebuf = ct.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)

    if device is None:
        devlist = ct.POINTER(pcap.pcap_if_t)()
        if pcap.findalldevs(ct.byref(devlist), ebuf) == -1:
            error("{!s}", ebuf.value.decode("utf-8", "ignore"))
        if not devlist:
            error("no interfaces available for capture")
        device = devlist[0].name
        pcap.freealldevs(devlist)

    ebuf.value = b""
    pd = pcap.open_live(device, 65535, 0, 1000, ebuf)
    if not pd:
        error("{!s}", ebuf.value.decode("utf-8", "ignore"))
    elif ebuf.value:
        warning("{!s}", ebuf.value.decode("utf-8", "ignore"))

    localnet = pcap.bpf_u_int32()
    netmask  = pcap.bpf_u_int32()
    if pcap.lookupnet(device, ct.byref(localnet), ct.byref(netmask), ebuf) < 0:
        localnet = pcap.bpf_u_int32(0)
        netmask  = pcap.bpf_u_int32(0)
        warning("{!s}", ebuf.value.decode("utf-8", "ignore"))

    fcode = pcap.bpf_program()
    cmdbuf = " ".join(expression).encode("utf-8")
    if pcap.compile(pd, ct.byref(fcode), cmdbuf, 1, netmask) < 0:
        error("{!s}", pcap.geterr(pd).decode("utf-8", "ignore"))

    if pcap.setfilter(pd, ct.byref(fcode)) < 0:
        error("{!s}", pcap.geterr(pd).decode("utf-8", "ignore"))

    if doselect or dopoll:
        # We need either an FD on which to do select()/poll()
        # or, if there isn't one, a timeout to use in select()/
        # poll().
        try:
            selectable_fd = pcap.get_selectable_fd(pd)
        except AttributeError:
            error("pcap.get_selectable_fd is not available on this platform")
        if selectable_fd == -1:
            print("Listening on {!s}, using {}, with a timeout".format(
                  device.decode("utf-8"), mechanism))
            try:
                required_timeout = pcap.get_required_select_timeout(pd)
            except AttributeError:
                error("pcap.get_required_select_timeout is not available "
                      "on this platform")
            if not required_timeout:
                error("select()/poll() isn't supported on {!s}, "
                      "even with a timeout", device.decode("utf-8"))
            required_timeout = required_timeout[0]
            # As we won't be notified by select() or poll()
            # that a read can be done, we'll have to periodically
            # try reading from the device every time the required
            # timeout expires, and we don't want those attempts
            # to block if nothing has arrived in that interval,
            # so we want to force non-blocking mode.
            dononblock = True
        else:
            print("Listening on {!s}, using {}".format(
                  device.decode("utf-8"), mechanism))
            required_timeout = None
    else:
        print("Listening on {!s}, using pcap_dispatch()".format(
              device.decode("utf-8")))

    if dononblock:
        if pcap.setnonblock(pd, 1, ebuf) == -1:
            error("pcap.setnonblock failed: {!s}",
                  ebuf.value.decode("utf-8", "ignore"))

    status = 0

    if doselect:
        while True:
            try:
                if dotimeout:
                    seltimeout = (0 + (required_timeout.tv_usec
                                       if required_timeout is not None and
                                          required_timeout.tv_usec < 1000
                                       else 1000) / 1000000.0)
                    rfds, wfds, efds = select.select([selectable_fd], [],
                                                     [selectable_fd], seltimeout)
                elif required_timeout is not None:
                    seltimeout = (required_timeout.tv_sec +
                                  required_timeout.tv_usec / 1000000.0)
                    rfds, wfds, efds = select.select([selectable_fd], [],
                                                     [selectable_fd], seltimeout)
                else:
                    rfds, wfds, efds = select.select([selectable_fd], [],
                                                     [selectable_fd])
            except select.error as exc:
                print("Select returns error ({})".format(exc.args[1]))
            else:
                if selectable_fd == -1:
                    if status != 0:
                        print("Select returned a descriptor")
                else:
                    print("Select timed out: "
                          if not rfds and not wfds and not efds else
                          "Select returned a descriptor: ", end="")
                    print("readable, "
                          if selectable_fd in rfds else
                          "not readable, ", end="")
                    print("exceptional condition"
                          if selectable_fd in efds else
                          "no exceptional condition", end="")
                    print()

                packet_count = ct.c_int(0)
                status = pcap.dispatch(pd, -1, countme,
                    ct.cast(ct.pointer(packet_count), ct.POINTER(ct.c_ubyte)))
                if status < 0:
                    break
                # Don't report this if we're using a
                # required timeout and we got no packets,
                # because that could be a very short timeout,
                # and we don't want to spam the user with
                # a ton of "no packets" reports.
                if (status != 0 or packet_count.value != 0 or
                    required_timeout is not None):
                    print("{:d} packets seen, {:d} packets counted after "
                          "select returns".format(status, packet_count.value))
    elif dopoll:
        while True:
            poller = select.poll()
            poller.register(selectable_fd, select.POLLIN)
            if dotimeout:
                polltimeout = 1
            elif (required_timeout is not None and
                  required_timeout.tv_usec >= 1000):
                polltimeout = required_timeout.tv_usec // 1000
            else:
                polltimeout = None
            try:
                events = poller.poll(polltimeout)
            except select.error as exc:
                print("Poll returns error ({})".format(exc.args[1]))
            else:
                if selectable_fd == -1:
                    if status != 0:
                        print("Poll returned a descriptor")
                else:
                    if not events:
                        print("Poll timed out")
                    else:
                        event = events[0][1]
                        print("Poll returned a descriptor: ", end="")
                        print("readable, "
                              if event & select.POLLIN else
                              "not readable, ", end="")
                        print("exceptional condition, "
                              if event & select.POLLERR else
                              "no exceptional condition, ", end="")
                        print("disconnect, "
                              if event & select.POLLHUP else
                              "no disconnect, ", end="")
                        print("invalid"
                              if event & select.POLLNVAL else
                              "not invalid", end="")
                        print()

                packet_count = ct.c_int(0)
                status = pcap.dispatch(pd, -1, countme,
                    ct.cast(ct.pointer(packet_count), ct.POINTER(ct.c_ubyte)))
                if status < 0:
                    break
                # Don't report this if we're using a
                # required timeout and we got no packets,
                # because that could be a very short timeout,
                # and we don't want to spam the user with
                # a ton of "no packets" reports.
                if (status != 0 or packet_count.value != 0 or
                    required_timeout is not None):
                    print("{:d} packets seen, {:d} packets counted after "
                          "poll returns".format(status, packet_count.value))
    else:
        while True:
            packet_count = ct.c_int(0)
            status = pcap.dispatch(pd, -1, countme,
                ct.cast(ct.pointer(packet_count), ct.POINTER(ct.c_ubyte)))
            if status < 0:
                break
            print("{:d} packets seen, {:d} packets counted after "
                  "pcap.dispatch returns".format(status, packet_count.value))

    if status == -2:
        # We got interrupted, so perhaps we didn't manage to finish a
        # line we were printing. Print an extra newline, just in case.
        print()
    sys.stdout.flush()
    if status == -1:
        # Error. Report it.
        print("{}: pcap.loop: {!s}".format(program_name,
              pcap.geterr(pd).decode("utf-8", "ignore")), file=sys.stderr)

    pcap.freecode(ct.byref(fcode))
    pcap.close(pd)

    return 1 if status == -1 else 0
Exemple #6
0
def main(argv):

    global program_name
    program_name = os.path.basename(sys.argv[0])

    try:
        opts, args = getopt.getopt(argv[1:], "i:mnt:")
    except getopt.GetoptError:
        usage()

    device = None
    immediate = False
    nonblock = 0
    timeout = 1000
    for opt, optarg in opts:
        if opt == '-i':
            device = optarg.encode("utf-8")
        elif opt == '-m':
            immediate = True
        elif opt == '-n':
            nonblock = 1
        elif opt == '-t':
            try:
                timeout = int(optarg)
            except:
                error('Timeout value "{}" is not a number', optarg)
            if timeout < 0:
                error("Timeout value {:d} is negative", timeout)
            if timeout > INT_MAX:
                error("Timeout value {:d} is too large (> {:d})",
                      timeout, INT_MAX)
        else:
            usage()

    expression = args

    ebuf = ct.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)

    if device is None:
        devlist = ct.POINTER(pcap.pcap_if_t)()
        if pcap.findalldevs(ct.byref(devlist), ebuf) == -1:
            error("{!s}", ebuf.value.decode("utf-8", "ignore"))
        if not devlist:
            error("no interfaces available for capture")
        device = devlist[0].name
        pcap.freealldevs(devlist)

    ebuf.value = b""
    pd = pcap.create(device, ebuf)
    if not pd:
        error("{!s}", ebuf.value.decode("utf-8", "ignore"))

    status = pcap.set_snaplen(pd, 65535)
    if status != 0:
        error("{!s}: pcap.set_snaplen failed: {!s}",
              device.decode("utf-8"),
              statustostr(status).decode("utf-8", "ignore"));
    if immediate:
        try:
            status = pcap.set_immediate_mode(pd, 1)
        except AttributeError:
            error("pcap.set_immediate_mode is not available on this platform")
        if status != 0:
            error("{!s}: pcap.set_immediate_mode failed: {!s}",
                  device.decode("utf-8"),
                  statustostr(status).decode("utf-8", "ignore"));
    status = pcap.set_timeout(pd, timeout)
    if status != 0:
        error("{!s}: pcap.set_timeout failed: {!s}",
              device.decode("utf-8"),
              statustostr(status).decode("utf-8", "ignore"));

    status = pcap.activate(pd)
    if status < 0:
        # pcap.activate() failed.
        error("{!s}: {!s}\n({!s})",
              device.decode("utf-8"),
              statustostr(status).decode("utf-8", "ignore"),
              pcap.geterr(pd).decode("utf-8", "ignore"))
    elif status > 0:
        # pcap.activate() succeeded, but it's warning us
        # of a problem it had.
        warning("{!s}: {!s}\n({!s})",
                device.decode("utf-8"),
                statustostr(status).decode("utf-8", "ignore"),
                pcap.geterr(pd).decode("utf-8", "ignore"))

    localnet = pcap.bpf_u_int32()
    netmask  = pcap.bpf_u_int32()
    if pcap.lookupnet(device, ct.byref(localnet), ct.byref(netmask), ebuf) < 0:
        localnet = pcap.bpf_u_int32(0)
        netmask  = pcap.bpf_u_int32(0)
        warning("{!s}", ebuf.value.decode("utf-8", "ignore"))

    fcode = pcap.bpf_program()
    cmdbuf = " ".join(expression).encode("utf-8")
    if pcap.compile(pd, ct.byref(fcode), cmdbuf, 1, netmask) < 0:
        error("{!s}", pcap.geterr(pd).decode("utf-8", "ignore"))

    if pcap.setfilter(pd, ct.byref(fcode)) < 0:
        error("{!s}", pcap.geterr(pd).decode("utf-8", "ignore"))
    if pcap.setnonblock(pd, nonblock, ebuf) == -1:
        error("pcap.setnonblock failed: {!s}",
              ebuf.value.decode("utf-8", "ignore"))

    print("Listening on {!s}".format(device.decode("utf-8")))

    while True:
        packet_count = ct.c_int(0)
        status = pcap.dispatch(pd, -1, countme,
                 ct.cast(ct.pointer(packet_count), ct.POINTER(ct.c_ubyte)))
        if status < 0:
            break
        if status != 0:
            print("{:d} packets seen, {:d} packets counted after "
                  "pcap.dispatch returns".format(status, packet_count.value))

    if status == -2:
        # We got interrupted, so perhaps we didn't manage to finish a
        # line we were printing. Print an extra newline, just in case.
        print()
    sys.stdout.flush()
    if status == -1:
        # Error. Report it.
        print("{}: pcap.loop: {!s}".format(program_name,
              pcap.geterr(pd).decode("utf-8", "ignore")), file=sys.stderr)

    pcap.freecode(ct.byref(fcode))
    pcap.close(pd)

    return 1 if status == -1 else 0
Exemple #7
0
def main(argv=sys.argv):

    global program_name
    program_name = os.path.basename(argv[0])

    try:
        opts, args = getopt.getopt(argv[1:], "dF:gm:Os:")
    except getopt.GetoptError:
        usage()

    if is_windows and hasattr(pcap, "wsockinit") and pcap.wsockinit() != 0:
        return 1

    have_fcode = False
    dflag = 1
    if defined("BDEBUG"):
        gflag = 0
    infile = None
    netmask = pcap.PCAP_NETMASK_UNKNOWN
    Oflag = 1
    snaplen = MAXIMUM_SNAPLEN
    for opt, optarg in opts:
        if opt == '-d':
            dflag += 1
        elif opt == 'g':
            if defined("BDEBUG"):
                gflag += 1
            else:
                error(
                    "libpcap and filtertest not built with optimizer debugging enabled"
                )
        elif opt == '-F':
            infile = optarg
        elif opt == '-O':
            Oflag = 0
        elif opt == '-m':  # !!!
            # try:
            #     addr = socket.inet_pton(socket.AF_INET, optarg)
            # except socket.error:
            #     if r == 0:
            #         error("invalid netmask {}", optarg)
            #     elif r == -1:
            #         error("invalid netmask {}: {}", optarg, pcap_strerror(errno))
            # else: # elif r == 1:
            #     addr = bpf_u_int32(addr)
            #     netmask = addr
            pass
        elif opt == '-s':
            try:
                long_snaplen = int(optarg)
            except:
                error("invalid snaplen {}", optarg)
            if not (0 <= long_snaplen <= MAXIMUM_SNAPLEN):
                error("invalid snaplen {}", optarg)
            elif long_snaplen == 0:  # <AK> fix, was: snaplen == 0:
                snaplen = MAXIMUM_SNAPLEN
            else:
                snaplen = long_snaplen
        else:
            usage()

    if not args:
        usage()

    dlt_name = args[0]
    expression = args[1:]

    dlt = pcap.datalink_name_to_val(dlt_name.encode("utf-8"))
    if dlt < 0:
        try:
            dlt = int(dlt_name)
        except:
            error("invalid data link type {!s}", dlt_name)

    if infile:
        cmdbuf = read_infile(infile)
    else:
        # concatenating arguments with spaces.
        cmdbuf = " ".join(expression).encode("utf-8")

    pd = pcap.open_dead(dlt, snaplen)
    if not pd:
        error("Can't open fake pcap_t")

    fcode = pcap.bpf_program()
    if pcap.compile(pd, ct.byref(fcode), cmdbuf, Oflag, netmask) < 0:
        error("{!s}", pcap.geterr(pd).decode("utf-8", "ignore"))
    have_fcode = True

    if not pcap.bpf_validate(fcode.bf_insns, fcode.bf_len):
        warning("Filter doesn't pass validation")

    if defined("BDEBUG"):
        if cmdbuf:
            # replace line feed with space
            mcodes = cmdbuf.decode("utf-8", "ignore")
            mcodes = mcodes.replace('\r', ' ').replace('\n', ' ')
            # only show machine code if BDEBUG defined, since dflag > 3
            print("machine codes for filter: {}".format(mcodes))
        else:
            print("machine codes for empty filter:")

    pcap.bpf_dump(ct.byref(fcode), dflag)
    del cmdbuf
    if have_fcode:
        pcap.freecode(ct.byref(fcode))
    pcap.close(pd)

    return 0