Exemplo n.º 1
0
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
Exemplo n.º 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)
Exemplo n.º 3
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