diff --git a/.travis/pylibpcap.rb b/.travis/pylibpcap.rb deleted file mode 100644 index c1104c84c69..00000000000 --- a/.travis/pylibpcap.rb +++ /dev/null @@ -1,10 +0,0 @@ -class Pylibpcap `_. @@ -233,6 +240,9 @@ Install using Homebrew $ sudo brew install --with-python libdnet $ sudo brew install https://raw.githubusercontent.com/secdev/scapy/master/.travis/pylibpcap.rb +In Scapy:: + + conf.use_pcap = True Install using MacPorts ^^^^^^^^^^^^^^^^^^^^^^ @@ -249,18 +259,12 @@ Install using MacPorts OpenBSD ------- -In a similar manner, to install Scapy on OpenBSD 5.9+, you will need to install the libpcap/libdnet bindings: +In a similar manner, to install Scapy on OpenBSD 5.9+, you **may** want to install the libpcap/libdnet bindings: .. code-block:: text $ doas pkg_add py-libpcap py-libdnet tcpdump -An OpenBSD install may be lacking the ``/etc/ethertypes`` file. You may install it with - -.. code-block:: text - - # wget http://git.netfilter.org/ebtables/plain/ethertypes -O /etc/ethertypes - Then install Scapy via ``pip`` or ``pkg_add`` (bundled under ``python-scapy``) All dependencies may be installed either via the platform-specific installer, or via PyPI. See `Optional Dependencies <#optional-dependencies>`_ for more information. diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py index 736fd5618e1..6c176b23337 100644 --- a/scapy/arch/pcapdnet.py +++ b/scapy/arch/pcapdnet.py @@ -76,22 +76,27 @@ def nonblock_recv(self): def select(sockets, remain=None): return _select_nonblock(sockets, remain=None) +########## +# PCAP # +########## -################### -# WINPCAP/NPCAP # -################### -if conf.use_winpcapy: +if conf.use_pcap: + if WINDOWS: + # Windows specific + NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap" + from scapy.modules.winpcapy import pcap_setmintocopy + else: + from scapy.modules.winpcapy import pcap_get_selectable_fd from ctypes import POINTER, byref, create_string_buffer, c_ubyte, cast - NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap" # Part of the Winpcapy integration was inspired by phaethon/scapy # but he destroyed the commit history, so there is no link to that try: from scapy.modules.winpcapy import PCAP_ERRBUF_SIZE, pcap_if_t, \ sockaddr_in, sockaddr_in6, pcap_findalldevs, pcap_freealldevs, \ pcap_lib_version, pcap_close, \ - pcap_open_live, pcap_setmintocopy, pcap_pkthdr, \ + pcap_open_live, pcap_pkthdr, \ pcap_next_ex, pcap_datalink, \ pcap_compile, pcap_setfilter, pcap_setnonblock, pcap_sendpacket, \ bpf_program as winpcapy_bpf_program @@ -143,35 +148,41 @@ def load_winpcapy(): raise finally: pcap_freealldevs(devs) - # Detect Pcap version - version = pcap_lib_version() - if b"winpcap" in version.lower(): - if os.path.exists(NPCAP_PATH + "\\wpcap.dll"): - warning("Winpcap is installed over Npcap. " - "Will use Winpcap (see 'Winpcap/Npcap conflicts' " - "in Scapy's docs)") - elif platform.release() != "XP": - warning("WinPcap is now deprecated (not maintained). " - "Please use Npcap instead") - elif b"npcap" in version.lower(): - conf.use_npcap = True - LOOPBACK_NAME = scapy.consts.LOOPBACK_NAME = "Npcap Loopback Adapter" # noqa: E501 except OSError: - conf.use_winpcapy = False - if conf.interactive: - log_loading.critical( - "Npcap/Winpcap is not installed ! See " - "https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501 - ) - - if conf.use_winpcapy: - def get_if_list(): - """Returns all pcap names""" - if not conf.cache_iflist: - load_winpcapy() - return list(conf.cache_iflist) + conf.use_pcap = False + if WINDOWS: + if conf.interactive: + log_loading.critical( + "Npcap/Winpcap is not installed ! See " + "https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501 + ) + else: + if conf.interactive: + log_loading.critical( + "Libpcap is not installed!" + ) else: - get_if_list = lambda: [] + if WINDOWS: + # Detect Pcap version: check for Npcap + version = pcap_lib_version() + if b"winpcap" in version.lower(): + if os.path.exists(NPCAP_PATH + "\\wpcap.dll"): + warning("Winpcap is installed over Npcap. " + "Will use Winpcap (see 'Winpcap/Npcap conflicts' " + "in Scapy's docs)") + elif platform.release() != "XP": + warning("WinPcap is now deprecated (not maintained). " + "Please use Npcap instead") + elif b"npcap" in version.lower(): + conf.use_npcap = True + LOOPBACK_NAME = scapy.consts.LOOPBACK_NAME = "Npcap Loopback Adapter" # noqa: E501 + +if conf.use_pcap: + def get_if_list(): + """Returns all pcap names""" + if not conf.cache_iflist: + load_winpcapy() + return list(conf.cache_iflist) class _PcapWrapper_winpcap: # noqa: F811 """Wrapper for the WinPcap calls""" @@ -180,8 +191,8 @@ def __init__(self, device, snaplen, promisc, to_ms, monitor=None): self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) self.iface = create_string_buffer(device.encode("utf8")) if monitor: - if not conf.use_npcap: - raise OSError("This feature requires NPcap !") + if WINDOWS and not conf.use_npcap: + raise OSError("On Windows, this feature requires NPcap !") # Npcap-only functions from scapy.modules.winpcapy import pcap_create, \ pcap_set_snaplen, pcap_set_promisc, \ @@ -199,9 +210,10 @@ def __init__(self, device, snaplen, promisc, to_ms, monitor=None): snaplen, promisc, to_ms, self.errbuf) - # Winpcap/Npcap exclusive: make every packet to be instantly - # returned, and not buffered within Winpcap/Npcap - pcap_setmintocopy(self.pcap, 0) + if WINDOWS: + # Winpcap/Npcap exclusive: make every packet to be instantly + # returned, and not buffered within Winpcap/Npcap + pcap_setmintocopy(self.pcap, 0) self.header = POINTER(pcap_pkthdr)() self.pkt_data = POINTER(c_ubyte)() @@ -225,7 +237,6 @@ def fileno(self): return 0 else: # This does not exist under Windows - from scapy.modules.winpcapy import pcap_get_selectable_fd return pcap_get_selectable_fd(self.pcap) def setfilter(self, f): @@ -247,182 +258,10 @@ def send(self, x): def close(self): pcap_close(self.pcap) - open_pcap = lambda *args, **kargs: _PcapWrapper_winpcap(*args, **kargs) -else: - NPCAP_PATH = "" - get_if_list = lambda: {} -################ -# PCAP/PCAPY # -################ + # pcap sockets - -if conf.use_pcap: - # We try from most to less tested/used - try: - import pcapy as pcap # python-pcapy - _PCAP_MODE = "pcapy" - except ImportError as e: - try: - import pcap # python-pypcap - _PCAP_MODE = "pypcap" - except ImportError as e2: - try: - # This is our last chance, but we don't really - # recommend it as very little tested - import libpcap as pcap # python-libpcap - _PCAP_MODE = "libpcap" - except ImportError: - if conf.interactive: - log_loading.error( - "Unable to import any of the pcap " - "modules: %s/%s", e, e2 - ) - conf.use_pcap = False - else: - raise - if conf.use_pcap: - if _PCAP_MODE == "pypcap": # python-pypcap - class _PcapWrapper_pypcap: # noqa: F811 - def __init__(self, device, snaplen, promisc, - to_ms, monitor=False): - try: - self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1, timeout_ms=to_ms, rfmon=monitor) # noqa: E501 - except TypeError: - try: - if monitor: - warning("Your pypcap version is too old to support monitor mode, Please use pypcap 1.2.1+ !") # noqa: E501 - self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1, timeout_ms=to_ms) # noqa: E501 - except TypeError: - # Even older pypcap versions do not support the timeout_ms argument # noqa: E501 - self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1) # noqa: E501 - - def __getattr__(self, attr): - return getattr(self.pcap, attr) - - def setnonblock(self, i): - self.pcap.setnonblock(i) - - def close(self): - try: - self.pcap.close() - except AttributeError: - warning("close(): don't know how to close the file " - "descriptor. Bugs ahead! Please use python-pypcap 1.2.1+") # noqa: E501 - - def send(self, x): - self.pcap.sendpacket(x) - - def next(self): - c = self.pcap.next() - if c is None: - return - ts, pkt = c - return ts, raw(pkt) - __next__ = next - open_pcap = lambda *args, **kargs: _PcapWrapper_pypcap(*args, **kargs) # noqa: E501 - elif _PCAP_MODE == "libpcap": # python-libpcap - class _PcapWrapper_libpcap: - def __init__(self, device, snaplen, promisc, to_ms, monitor=False): # noqa: E501 - self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE) - if monitor: - self.pcap = pcap.pcap_create(device, self.errbuf) - pcap.pcap_set_snaplen(self.pcap, snaplen) - pcap.pcap_set_promisc(self.pcap, promisc) - pcap.pcap_set_timeout(self.pcap, to_ms) - if pcap.pcap_set_rfmon(self.pcap, 1) != 0: - warning("Could not set monitor mode") - if pcap.pcap_activate(self.pcap) != 0: - raise OSError("Could not activate the pcap handler") # noqa: E501 - else: - self.pcap = pcap.open_live(device, snaplen, promisc, to_ms) # noqa: E501 - - def setfilter(self, filter): - self.pcap.setfilter(filter, 0, 0) - - def next(self): - c = self.pcap.next() - if c is None: - return - l, pkt, ts = c - return ts, pkt - __next__ = next - - def setnonblock(self, i): - pcap.pcap_setnonblock(self.pcap, i, self.errbuf) - - def __getattr__(self, attr): - return getattr(self.pcap, attr) - - def send(self, x): - pcap.pcap_sendpacket(self.pcap, x, len(x)) - - def close(self): - pcap.close(self.pcap) - open_pcap = lambda *args, **kargs: _PcapWrapper_libpcap(*args, **kargs) # noqa: E501 - elif _PCAP_MODE == "pcapy": # python-pcapy - class _PcapWrapper_pcapy: - def __init__(self, device, snaplen, promisc, to_ms, monitor=False): # noqa: E501 - if monitor: - try: - self.pcap = pcap.create(device) - self.pcap.set_snaplen(snaplen) - self.pcap.set_promisc(promisc) - self.pcap.set_timeout(to_ms) - if self.pcap.set_rfmon(1) != 0: - warning("Could not set monitor mode") - if self.pcap.activate() != 0: - raise OSError("Could not activate the pcap handler") # noqa: E501 - except AttributeError: - raise OSError("Your pcapy version does not support" - "monitor mode ! Use pcapy 0.11.4+") - else: - self.pcap = pcap.open_live(device, snaplen, promisc, to_ms) # noqa: E501 - - def next(self): - try: - c = self.pcap.next() - except pcap.PcapError: - return None - else: - h, p = c - if h is None: - return - s, us = h.getts() - return (s + 0.000001 * us), p - __next__ = next - - def fileno(self): - try: - return self.pcap.getfd() - except AttributeError: - warning("fileno: getfd() does not exist. Please use " - "pcapy 0.11.3+ !") - - def setnonblock(self, i): - self.pcap.setnonblock(i) - - def __getattr__(self, attr): - return getattr(self.pcap, attr) - - def send(self, x): - self.pcap.sendpacket(x) - - def close(self): - try: - self.pcap.close() - except AttributeError: - warning("close(): don't know how to close the file " - "descriptor. Bugs ahead! Please update pcapy!") - open_pcap = lambda *args, **kargs: _PcapWrapper_pcapy(*args, **kargs) # noqa: E501 - - -################# -# PCAP/WINPCAPY # -################# - -if conf.use_pcap or conf.use_winpcapy: class L2pcapListenSocket(_L2pcapdnetSocket): desc = "read packets at layer 2 using libpcap" @@ -527,7 +366,11 @@ def send(self, x): if hasattr(x, "sent_time"): x.sent_time = time.time() return self.ins.send(sx) - +else: + # No libpcap installed + get_if_list = lambda: [] + if WINDOWS: + NPCAP_PATH = "" ########## # DNET # diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py index b169c96af45..ac5f971762c 100755 --- a/scapy/arch/windows/__init__.py +++ b/scapy/arch/windows/__init__.py @@ -33,8 +33,7 @@ from scapy.compat import plain_str from scapy.supersocket import SuperSocket -conf.use_winpcapy = True -conf.use_pcap = False +conf.use_pcap = True conf.use_dnet = False # These import must appear after setting conf.use_* variables @@ -599,7 +598,7 @@ class NetworkInterfaceDict(UserDict): @classmethod def _pcap_check(cls): """Performs checks/restart pcap adapter""" - if not conf.use_winpcapy: + if not conf.use_pcap: # Winpcap/Npcap isn't installed return @@ -723,7 +722,7 @@ def reload(self): """Reload interface list""" self.restarted_adapter = False self.data.clear() - if conf.use_winpcapy: + if conf.use_pcap: # Reload from Winpcapy from scapy.arch.pcapdnet import load_winpcapy load_winpcapy() @@ -790,33 +789,33 @@ def show_interfaces(resolve_mac=True): return IFACES.show(resolve_mac) -if conf.use_winpcapy: +if conf.use_pcap: _orig_open_pcap = pcapdnet.open_pcap - -def open_pcap(iface, *args, **kargs): - """open_pcap: Windows routine for creating a pcap from an interface. - This function is also responsible for detecting monitor mode. - """ - iface_pcap_name = pcapname(iface) - if not isinstance(iface, NetworkInterface) and iface_pcap_name is not None: - iface = IFACES.dev_from_name(iface) - if iface.is_invalid(): - raise Scapy_Exception("Interface is invalid (no pcap match found) !") - # Only check monitor mode when manually specified. - # Checking/setting for monitor mode will slow down the process, and the - # common is case is not to use monitor mode - kw_monitor = kargs.get("monitor", None) - if conf.use_npcap and kw_monitor is not None and iface is not None: - monitored = iface.ismonitor() - if kw_monitor is not monitored: - # The monitor param is specified, and not matching the current - # interface state - iface.setmonitor(kw_monitor) - return _orig_open_pcap(iface_pcap_name, *args, **kargs) - - -pcapdnet.open_pcap = open_pcap + def open_pcap(iface, *args, **kargs): + """open_pcap: Windows routine for creating a pcap from an interface. + This function is also responsible for detecting monitor mode. + """ + iface_pcap_name = pcapname(iface) + if not isinstance(iface, NetworkInterface) and \ + iface_pcap_name is not None: + iface = IFACES.dev_from_name(iface) + if iface.is_invalid(): + raise Scapy_Exception( + "Interface is invalid (no pcap match found) !" + ) + # Only check monitor mode when manually specified. + # Checking/setting for monitor mode will slow down the process, and the + # common is case is not to use monitor mode + kw_monitor = kargs.get("monitor", None) + if conf.use_npcap and kw_monitor is not None and iface is not None: + monitored = iface.ismonitor() + if kw_monitor is not monitored: + # The monitor param is specified, and not matching the current + # interface state + iface.setmonitor(kw_monitor) + return _orig_open_pcap(iface_pcap_name, *args, **kargs) + pcapdnet.open_pcap = open_pcap get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: ( # noqa: E501 ARPHDR_ETHER, mac2str(IFACES.dev_from_pcapname(pcapname(iface)).mac) diff --git a/scapy/arch/windows/native.py b/scapy/arch/windows/native.py index e5c197a0d3a..82b800d698d 100644 --- a/scapy/arch/windows/native.py +++ b/scapy/arch/windows/native.py @@ -29,7 +29,7 @@ provide in this module the get_actual_icmp_seq() function. Example: - >>> conf.use_winpcapy = False + >>> conf.use_pcap = False >>> a = conf.L3socket() # This will (most likely) work: >>> current = get_current_icmp_seq() diff --git a/scapy/config.py b/scapy/config.py index 40af34830dc..01ee7569d14 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -436,26 +436,25 @@ def _set_conf_sockets(): """Populate the conf.L2Socket and conf.L3Socket according to the various use_* parameters """ + from scapy.main import _load if conf.use_bpf and not BSD: Interceptor.set_from_hook(conf, "use_bpf", False) raise ScapyInvalidPlatformException("BSD-like (OSX, *BSD...) only !") - if conf.use_winpcapy and not WINDOWS: - Interceptor.set_from_hook(conf, "use_winpcapy", False) - raise ScapyInvalidPlatformException("Windows only !") # we are already in an Interceptor hook, use Interceptor.set_from_hook - if conf.use_pcap or conf.use_dnet or conf.use_winpcapy: + if conf.use_pcap or conf.use_dnet: try: from scapy.arch.pcapdnet import L2pcapListenSocket, L2pcapSocket, \ L3pcapSocket - except ImportError: - warning("No pcap provider available ! pcap won't be used") - Interceptor.set_from_hook(conf, "use_winpcapy", False) + except (OSError, ImportError): + warning("No libpcap provider available ! pcap won't be used") Interceptor.set_from_hook(conf, "use_pcap", False) else: conf.L3socket = L3pcapSocket conf.L3socket6 = functools.partial(L3pcapSocket, filter="ip6") conf.L2socket = L2pcapSocket conf.L2listen = L2pcapListenSocket + # Update globals + _load("scapy.arch.pcapdnet") return if conf.use_bpf: from scapy.arch.bpf.supersocket import L2bpfListenSocket, \ @@ -464,6 +463,8 @@ def _set_conf_sockets(): conf.L3socket6 = functools.partial(L3bpfSocket, filter="ip6") conf.L2socket = L2bpfSocket conf.L2listen = L2bpfListenSocket + # Update globals + _load("scapy.arch.bpf") return if LINUX: from scapy.arch.linux import L3PacketSocket, L2Socket, L2ListenSocket @@ -471,6 +472,8 @@ def _set_conf_sockets(): conf.L3socket6 = functools.partial(L3PacketSocket, filter="ip6") conf.L2socket = L2Socket conf.L2listen = L2ListenSocket + # Update globals + _load("scapy.arch.linux") return if WINDOWS: from scapy.arch.windows import _NotAvailableSocket @@ -479,6 +482,7 @@ def _set_conf_sockets(): conf.L3socket6 = L3WinSocket6 conf.L2socket = _NotAvailableSocket conf.L2listen = _NotAvailableSocket + # No need to update globals on Windows return from scapy.supersocket import L3RawSocket from scapy.layers.inet6 import L3RawSocket6 @@ -490,9 +494,8 @@ def _socket_changer(attr, val): if not isinstance(val, bool): raise TypeError("This argument should be a boolean") dependencies = { # Things that will be turned off - "use_pcap": ["use_bpf", "use_winpcapy"], + "use_pcap": ["use_bpf"], "use_bpf": ["use_pcap"], - "use_winpcapy": ["use_pcap", "use_dnet"], } restore = {k: getattr(conf, k) for k in dependencies} del restore[attr] # This is handled directly by _set_conf_sockets @@ -606,7 +609,6 @@ class Conf(ConfClass): # XXX use_dnet is deprecated use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y") use_bpf = Interceptor("use_bpf", False, _socket_changer) - use_winpcapy = Interceptor("use_winpcapy", False, _socket_changer) use_npcap = False ipv6_enabled = socket.has_ipv6 extensions_paths = "." diff --git a/scapy/modules/winpcapy.py b/scapy/modules/winpcapy.py index 11854ee1933..4a3513e2ac0 100644 --- a/scapy/modules/winpcapy.py +++ b/scapy/modules/winpcapy.py @@ -19,9 +19,10 @@ import sys, os from scapy.consts import WINDOWS -HAVE_REMOTE=False +HAVE_REMOTE = False if WINDOWS: + # Try to load Npcap, or Winpcap HAVE_REMOTE=True SOCKET = c_uint npcap_folder = os.environ["WINDIR"] + "\\System32\\Npcap" @@ -37,6 +38,7 @@ _lib=CDLL("wpcap.dll") del npcap_folder else: + # Try to load libpcap SOCKET = c_int _lib_name = find_library("pcap") if not _lib_name: diff --git a/test/regression.uts b/test/regression.uts index d9a91fe9062..7b18bf503e4 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -86,19 +86,12 @@ conf.debug_dissector = True = Configuration conf.use_* LINUX ~ linux -try: - conf.use_winpcapy = True - assert False -except: - True - try: conf.use_bpf = True assert False except: True -assert not conf.use_winpcapy assert not conf.use_bpf = Configuration conf.use_* WINDOWS @@ -112,17 +105,6 @@ except: assert not conf.use_bpf -= Configuration conf.use_* OSX -~ osx - -try: - conf.use_winpcapy = True - assert False -except: - True - -assert not conf.use_winpcapy - ########### ########### diff --git a/test/windows.uts b/test/windows.uts index e5a9574b1c1..b4298cf9aa2 100644 --- a/test/windows.uts +++ b/test/windows.uts @@ -82,8 +82,8 @@ finally: = Set up native mode -conf.use_winpcapy = False -assert conf.use_winpcapy == False +conf.use_pcap = False +assert conf.use_pcap == False = Prepare ping: open firewall & get current seq number ~ netaccess needs_root @@ -122,5 +122,5 @@ retry_test(_test) = Leave native mode -conf.use_winpcapy = True -assert conf.use_winpcapy == True +conf.use_pcap = True +assert conf.use_pcap == True