From 42d4672c9e21c4388fb581cc947aed7d25354931 Mon Sep 17 00:00:00 2001
From: gpotter2 <gabriel@potter.fr>
Date: Thu, 23 Feb 2017 19:58:57 +0100
Subject: [PATCH] Routing fixes

---
 scapy/arch/pcapdnet.py         | 54 +++++++++++++++++++++-------------
 scapy/arch/windows/__init__.py | 28 +++++++++++++-----
 scapy/arch/winpcapy.py         | 37 +++++++++++++----------
 scapy/config.py                |  1 +
 scapy/route.py                 | 17 +++++++----
 scapy/tools/UTscapy.py         |  2 +-
 6 files changed, 87 insertions(+), 52 deletions(-)

diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py
index a5a1e273..24f4081e 100644
--- a/scapy/arch/pcapdnet.py
+++ b/scapy/arch/pcapdnet.py
@@ -18,32 +18,44 @@ from scapy.utils import mac2str
 from scapy.supersocket import SuperSocket
 from scapy.error import Scapy_Exception, log_loading, warning
 import scapy.arch
+import scapy.consts
 
 if conf.use_winpcapy:
   #mostly code from https://github.com/phaethon/scapy translated to python2.X
   try:
-    from scapy.arch.winpcapy import *
-    def winpcapy_get_if_list():
-      err = create_string_buffer(PCAP_ERRBUF_SIZE)
-      devs = POINTER(pcap_if_t)()
-      ret = []
-      if pcap_findalldevs(byref(devs), err) < 0:
-        return ret
-      try:
-        p = devs
-        while p:
-          ret.append(p.contents.name.decode('ascii'))
-          p = p.contents.next
-        return ret
-      finally:
-        pcap_freealldevs(devs)
-
+      from scapy.arch.winpcapy import *
+      def winpcapy_get_if_list():
+          err = create_string_buffer(PCAP_ERRBUF_SIZE)
+          devs = POINTER(pcap_if_t)()
+          ret = []
+          if pcap_findalldevs(byref(devs), err) < 0:
+              return ret
+          try:
+              p = devs
+              while p:
+                  ret.append(p.contents.name.decode('ascii'))
+                  p = p.contents.next
+              return ret
+          except:
+              raise
+          finally:
+              pcap_freealldevs(devs)
+      # Detect Pcap version
+      version = pcap_lib_version()
+      if "winpcap" in version.lower():
+          if os.path.exists(os.environ["WINDIR"] + "\\System32\\Npcap\\wpcap.dll"):
+              warning("Winpcap is installed over Npcap. Will use Winpcap (see 'Winpcap/Npcap conflicts' in scapy's docs)", True)
+          else:
+              warning("WinPcap is now deprecated (not maintened). Please use Npcap instead", True)
+      elif "npcap" in version.lower():
+          conf.use_npcap = True
+          LOOPBACK_NAME = scapy.consts.LOOPBACK_NAME = "Npcap Loopback Adapter"
   except OSError as e:
-    def winpcapy_get_if_list():
-        return []
-    conf.use_winpcapy = False
-    if conf.interactive:
-      log_loading.warning("wpcap.dll is not installed. You won't be able to send/recieve packets. Visit the scapy's doc to install it")
+      def winpcapy_get_if_list():
+          return []
+      conf.use_winpcapy = False
+      if conf.interactive:
+          log_loading.warning("wpcap.dll is not installed. You won't be able to send/recieve packets. Visit the scapy's doc to install it")
 
   # From BSD net/bpf.h
   #BIOCIMMEDIATE=0x80044270
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
index 62256a78..24b6008e 100755
--- a/scapy/arch/windows/__init__.py
+++ b/scapy/arch/windows/__init__.py
@@ -6,8 +6,7 @@
 """
 Customizations needed to support Microsoft Windows.
 """
-
-import os,re,sys,socket,time, itertools
+import os, re, sys, socket, time, itertools, platform
 import subprocess as sp
 from glob import glob
 import tempfile
@@ -24,6 +23,7 @@ conf.use_dnet = False
 conf.use_winpcapy = True
 
 WINDOWS = (os.name == 'nt')
+NEW_RELEASE = False
 
 #hot-patching socket for missing variables on Windows
 import socket
@@ -37,6 +37,18 @@ if not hasattr(socket, 'IPPROTO_ESP'):
 from scapy.arch import pcapdnet
 from scapy.arch.pcapdnet import *
 
+def is_new_release():
+    release = platform.release()
+    try:
+        if float(release) >= 8:
+            return True
+    except ValueError:
+        if (release=="post2008Server"):
+            return True
+    return False
+
+NEW_RELEASE = is_new_release()
+
 def _exec_query_ps(cmd, fields):
     """Execute a PowerShell query"""
     if not WINDOWS:
@@ -221,7 +233,6 @@ def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
             break        
     return path
 
-
 def is_new_release(ignoreVBS=False):
     release = platform.release()
     if conf.prog.powershell is None and not ignoreVBS:
@@ -272,7 +283,6 @@ if conf.prog.sox == "sox":
 
 class PcapNameNotFoundError(Scapy_Exception):
     pass    
-import platform
 
 def is_interface_valid(iface):
     if "guid" in iface and iface["guid"]:
@@ -283,13 +293,13 @@ def is_interface_valid(iface):
     return False
 
 def get_windows_if_list():
-    if is_new_release():
+    """Returns windows interfaces"""
+    if NEW_RELEASE:
         # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
         # Careful: this is weird, but Get-NetAdaptater works like: (Name isn't the interface name)
         # Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
         # ----                      --------------------                    ------- ------       ----------             ---------
         # Ethernet                  Killer E2200 Gigabit Ethernet Contro...      13 Up           D0-50-99-56-DD-F9         1 Gbps
-        
         query = exec_query(['Get-NetAdapter'],
                            ['InterfaceDescription', 'InterfaceIndex', 'Name',
                             'InterfaceGuid', 'MacAddress']) # It is normal that it is in this order
@@ -346,6 +356,8 @@ class NetworkInterface(object):
                 self.ip=get_ip_from_name(data['name'])
         except (KeyError, AttributeError, NameError) as e:
             print e
+        if not self.ip and self.name == LOOPBACK_NAME:
+            self.ip = "127.0.0.1"
         try:
             self.mac = data['mac']
         except KeyError:
@@ -501,7 +513,7 @@ def read_routes():
     routes = []
     release = platform.release()
     try:
-        if is_new_release():
+        if NEW_RELEASE:
             routes = read_routes_post2008()
         elif release == "XP":
             routes = read_routes_xp()
@@ -513,7 +525,7 @@ def read_routes():
         if not routes:
             warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually", True)
     return routes
-       
+
 def read_routes_post2008():
     routes = []
     if_index = '(\d+)'
diff --git a/scapy/arch/winpcapy.py b/scapy/arch/winpcapy.py
index c1b7af52..9b51723c 100755
--- a/scapy/arch/winpcapy.py
+++ b/scapy/arch/winpcapy.py
@@ -1,3 +1,4 @@
+# Original license
 #-------------------------------------------------------------------------------
 # Name:        winpcapy.py
 #
@@ -7,31 +8,34 @@
 # Copyright:   (c) Massimo Ciani 2009
 #
 #-------------------------------------------------------------------------------
+# Modified for scapy's usage
 
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## This program is published under a GPLv2 license
 
 from ctypes import *
 from ctypes.util import find_library
-import sys
+import sys, os
+from scapy.consts import WINDOWS
 
-WIN32=False
 HAVE_REMOTE=False
 
-
-if sys.platform.startswith('win'):
-    WIN32=True
+if WINDOWS:
     HAVE_REMOTE=True
-
-if WIN32:
     SOCKET = c_uint
-    _lib=CDLL('wpcap.dll')
+    npcap_folder = os.environ["WINDIR"] + "\\System32\\Npcap"
+    if os.path.exists(npcap_folder):
+        # Try to load npcap
+        os.environ['PATH'] = npcap_folder + ";" + os.environ['PATH']
+    _lib=CDLL("wpcap.dll")
 else:
     SOCKET = c_int
-    _lib_name = find_library('pcap')
+    _lib_name = find_library("pcap")
     if not _lib_name:
-      raise OSError("Cannot fine libpcap.so library")
+        raise OSError("Cannot fine libpcap.so library")
     _lib=CDLL(_lib_name)
-
-
+    
 
 ##
 ## misc
@@ -70,7 +74,7 @@ timeval._fields_ = [('tv_sec', c_long),
 ## sockaddr is used by pcap_addr.
 ## For example if sa_family==socket.AF_INET then we need cast
 ## with sockaddr_in 
-if WIN32:
+if WINDOWS:
     class sockaddr(Structure):
         _fields_ = [("sa_family", c_ushort),
                     ("sa_data",c_ubyte * 14)]
@@ -501,8 +505,9 @@ pcap_dump_close = _lib.pcap_dump_close
 pcap_dump_close.restype = None
 pcap_dump_close.argtypes = [POINTER(pcap_dumper_t)]
 
-if not WIN32:
-
+if not WINDOWS:
+    #int pcap_get_selectable_fd(pcap_t, *p)
+    #   Returns, on UNIX, a file descriptor number for a file descriptor on which one can do a select(), poll(). -1 is returned if no such descriptor exists.
     pcap_get_selectable_fd = _lib.pcap_get_selectable_fd
     pcap_get_selectable_fd.restype = c_int    
     pcap_get_selectable_fd.argtypes = [POINTER(pcap_t)]
@@ -513,7 +518,7 @@ if not WIN32:
 ## (like remote packet capture, packet buffer size variation or high-precision packet injection).
 ## Howerver, at the moment they can be used only in Windows.
 ###########################################
-if WIN32:
+if WINDOWS:
     HANDLE = c_void_p
     
     ##############
diff --git a/scapy/config.py b/scapy/config.py
index b858065e..93537958 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -391,6 +391,7 @@ debug_tls:When 1, print some TLS session secrets when they are computed.
     use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
     use_bpf = False
     use_winpcapy = False
+    use_npcap = False
     ipv6_enabled = socket.has_ipv6
     ethertypes = ETHER_TYPES
     protocols = IP_PROTOS
diff --git a/scapy/route.py b/scapy/route.py
index 9c3ad897..017ac7b7 100644
--- a/scapy/route.py
+++ b/scapy/route.py
@@ -32,13 +32,18 @@ class Route:
         self.routes = read_routes()
 
     def __repr__(self):
-        rt = "Network         Netmask         Gateway         Iface           Output IP\n"
+        rtlst = [("Network", "Netmask", "Gateway", "Iface", "Output IP")]
+        
         for net,msk,gw,iface,addr in self.routes:
-            rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net),
-                                              ltoa(msk),
-                                              gw,
-                                              (iface.name if not isinstance(iface, basestring) else iface),
-                                              addr)
+	    rtlst.append((ltoa(net),
+                      ltoa(msk),
+                      gw,
+                      (iface.name if not isinstance(iface, basestring) else iface),
+                      addr))
+        
+        colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst))
+        fmt = "  ".join(map(lambda x: "%%-%ds"%x, colwidth))
+        rt = "\n".join(map(lambda x: fmt % x, rtlst))
         return rt
 
     def make_route(self, host=None, net=None, gw=None, dev=None):
diff --git a/scapy/tools/UTscapy.py b/scapy/tools/UTscapy.py
index 28b12461..46d52a93 100755
--- a/scapy/tools/UTscapy.py
+++ b/scapy/tools/UTscapy.py
@@ -362,7 +362,7 @@ def remove_empty_testsets(test_campaign):
 
 def run_campaign(test_campaign, get_interactive_session, verb=3):
     if WINDOWS:
-        # Add a route to 127.0.0.1
+        # Add a route to 127.0.0.1 and ::1
         from scapy.arch.windows import route_add_loopback
         route_add_loopback()
     passed=failed=0
-- 
GitLab