예제 #1
0
def _find_devices() -> typing.List[str]:
    """
    Returns a list of local network devices that can be captured from.
    Raises a PermissionError if the user is suspected to lack the privileges necessary for capture.

    We used to filter the devices by address family, but it turned out to be a dysfunctional solution because
    a device does not necessarily have to have an address in a particular family to be able to capture packets
    of that kind. For instance, on Windows, a virtual network adapter may have no addresses while still being
    able to capture packets.
    """
    import libpcap as pcap

    err_buf = ctypes.create_string_buffer(pcap.PCAP_ERRBUF_SIZE)
    devices = ctypes.POINTER(pcap.pcap_if_t)()
    if pcap.findalldevs(ctypes.byref(devices), err_buf) != 0:
        raise LinkLayerError(f"Could not list network devices: {err_buf.value.decode()}")
    if not devices:
        # This may seem odd, but libpcap returns an empty list if the user is not allowed to perform capture.
        # This is documented in the API docs as follows:
        #   Note that there may be network devices that cannot be opened by the process calling pcap_findalldevs(),
        #   because, for example, that process does not have sufficient privileges to open them for capturing;
        #   if so, those devices will not appear on the list.
        raise PermissionError("No capturable devices have been found. Do you have the required privileges?")
    dev_names: typing.List[str] = []
    d = typing.cast(ctypes.Structure, devices)
    while d:
        d = d.contents
        name = d.name.decode()
        if name != "any":
            dev_names.append(name)
        else:
            _logger.debug("Synthetic device %r does not support promiscuous mode, skipping", name)
        d = d.next
    pcap.freealldevs(devices)
    return dev_names
예제 #2
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
예제 #3
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
예제 #4
0
def main(argv=sys.argv):

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

    exit_status = 0

    alldevs = ct.POINTER(pcap.pcap_if_t)()
    errbuf = ct.create_string_buffer(pcap.PCAP_ERRBUF_SIZE + 1)

    #ifdef ENABLE_REMOTE
    if len(argv) >= 2:
        source = argv[1]
        if pcap.findalldevs_ex(source, NULL, ct.byref(alldevs), errbuf) == -1:

            # OK, try it with a user name and password.

            username = input("User name: ")
            if not username:
                exit_status = 1
                return exit_status
            password = getpass("Password: "******"Error in pcap.findalldevs: {!s}".format(
                    errbuf.value.decode("utf-8", "ignore")),
                      file=sys.stderr)
                exit_status = 1
                return exit_status
    else:
        #endif
        if pcap.findalldevs(ct.byref(alldevs), errbuf) == -1:
            print("Error in pcap.findalldevs: {!s}".format(
                errbuf.value.decode("utf-8", "ignore")),
                  file=sys.stderr)
            exit_status = 1
            return exit_status

    d = alldevs
    while d:
        d = d.contents
        if not ifprint(d):
            exit_status = 2
        d = d.next

    if alldevs:
        net = pcap.bpf_u_int32()
        mask = pcap.bpf_u_int32()
        if pcap.lookupnet(alldevs[0].name, ct.byref(net), ct.byref(mask),
                          errbuf) < 0:
            print("Error in pcap.lookupnet: {!s}".format(
                errbuf.value.decode("utf-8", "ignore")),
                  file=sys.stderr)
            exit_status = 2
        else:
            print("Preferred device is on network: {}/{}".format(
                iptos(net), iptos(mask)))

    pcap.freealldevs(alldevs)

    return exit_status
예제 #5
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