diff --git a/.travis/test.sh b/.travis/test.sh
index c8064ca8f484a40162e87acfaa19c97fb0aaeb53..9edadf7e6b57be98be8e676949fc59052d431b5b 100644
--- a/.travis/test.sh
+++ b/.travis/test.sh
@@ -65,8 +65,7 @@ fi
for f in *.uts
do
- if [ "$f" = "bpf.uts" ]
- then
+ if [ "$f" = "bpf.uts" ] || [ "$f" = "mock_windows.uts" ] ; then
continue
fi
$SCAPY_SUDO ./run_tests -q -F -t $f $UT_FLAGS || exit $?
diff --git a/appveyor.yml b/appveyor.yml
index d634de0a519f972df67cb014dc06197706456a3e..1686a008f68fad33439eef02974a0b255ffef83a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -19,7 +19,7 @@ install:
- refreshenv
# Install Python modules
- - "%PYTHON%\\python -m pip install ecdsa cryptography coverage"
+ - "%PYTHON%\\python -m pip install ecdsa cryptography coverage mock"
test_script:
# Set environment variables
diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py
index 0f815ca85bb46231ad5386c4b3ed0c4d4eece157..77e18b4720e073196263993b2b00b25cfc164e02 100644
--- a/scapy/arch/pcapdnet.py
+++ b/scapy/arch/pcapdnet.py
@@ -22,7 +22,7 @@ import scapy.arch
if conf.use_winpcapy:
#mostly code from https://github.com/phaethon/scapy translated to python2.X
try:
- from .winpcapy import *
+ from scapy.arch.winpcapy import *
def winpcapy_get_if_list():
err = create_string_buffer(PCAP_ERRBUF_SIZE)
devs = POINTER(pcap_if_t)()
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
index a9356f79c04c6a7360db41f16d373149dd5c3f0d..e60998bac12db0638d7d6ca99f0be894e148818a 100755
--- a/scapy/arch/windows/__init__.py
+++ b/scapy/arch/windows/__init__.py
@@ -6,7 +6,6 @@
"""
Customizations needed to support Microsoft Windows.
"""
-
import os,re,sys,socket,time, itertools
import subprocess as sp
from glob import glob
@@ -34,19 +33,20 @@ if not hasattr(socket, 'IPPROTO_AH'):
if not hasattr(socket, 'IPPROTO_ESP'):
socket.IPPROTO_ESP=50
-
from scapy.arch import pcapdnet
from scapy.arch.pcapdnet import *
def _exec_query_ps(cmd, fields):
"""Execute a PowerShell query"""
+ if not WINDOWS:
+ return
ps = sp.Popen([conf.prog.powershell] + cmd +
['|', 'select %s' % ', '.join(fields), '|', 'fl'],
stdout=sp.PIPE,
universal_newlines=True)
l=[]
for line in ps.stdout:
- if not line.strip(): #skip empty lines
+ if not line.strip(): # skip empty lines
continue
sl = line.split(':', 1)
if len(sl) == 1:
@@ -103,6 +103,8 @@ _VBS_WMI_OUTPUT = {
}
def _exec_query_vbs(cmd, fields):
+ if not WINDOWS:
+ return
"""Execute a query using VBS. Currently Get-WmiObject queries are
supported.
@@ -219,9 +221,10 @@ def get_windows_if_list():
# 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']) #Weird order, but normal
+ 'InterfaceGuid', 'MacAddress']) # It is normal that it is in this order
else:
query = exec_query(['Get-WmiObject', 'Win32_NetworkAdapter'],
['Name', 'InterfaceIndex', 'InterfaceDescription',
@@ -317,7 +320,6 @@ class NetworkInterfaceDict(UserDict):
def dev_from_name(self, name):
"""Return the first pcap device name for a given Windows
device name.
-
"""
for iface in self.itervalues():
if iface.name == name:
@@ -438,7 +440,7 @@ def read_routes():
else:
routes = read_routes_7()
except Exception as e:
- log_loading.warning("Error building scapy routing table : %s"%str(e))
+ log_loading.warning("Error building scapy routing table : %s" % str(e))
else:
if not routes:
log_loading.warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually")
@@ -474,8 +476,75 @@ def read_routes_post2008():
match.group(4), iface, iface.ip))
return routes
+############
+### IPv6 ###
+############
+
+def in6_getifaddr():
+ """
+ Returns all IPv6 addresses found on the computer
+ """
+ ret = []
+ ps = sp.Popen([conf.prog.powershell, 'Get-NetRoute', '-AddressFamily IPV6', '|', 'select ifIndex, DestinationPrefix'], stdout = sp.PIPE, universal_newlines = True)
+ stdout, stdin = ps.communicate()
+ netstat_line = '\s+'.join(['(\d+)', ''.join(['([A-z|0-9|:]+)', '(\/\d+)'])])
+ pattern = re.compile(netstat_line)
+ for l in stdout.split('\n'):
+ match = re.search(pattern,l)
+ if match:
+ try:
+ if_index = match.group(1)
+ iface = dev_from_index(if_index)
+ except:
+ continue
+ scope = scapy.utils6.in6_getscope(match.group(2))
+ ret.append((match.group(2), scope, iface)) # (addr,scope,iface)
+ continue
+ return ret
+
def read_routes6():
- return []
+ routes = []
+ ipv6_r = '([A-z|0-9|:]+)' #Hope it is a valid address...
+ # The correct IPv6 regex would be:
+ # ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?
+ # but is too big to be used (PyCParser): AssertionError: sorry, but this version only supports 100 named groups
+ netmask = '(\/\d+)?'
+ if_index = '(\d+)'
+ delim = '\s+' # The columns are separated by whitespace
+ netstat_line = delim.join([if_index, "".join([ipv6_r, netmask]), ipv6_r])
+ pattern = re.compile(netstat_line)
+ # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
+ # Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop
+ ps = sp.Popen([conf.prog.powershell, 'Get-NetRoute', '-AddressFamily IPV6', '|', 'select ifIndex, DestinationPrefix, NextHop'], stdout = sp.PIPE, universal_newlines = True)
+ stdout, stdin = ps.communicate()
+ lifaddr = in6_getifaddr()
+ for l in stdout.split('\n'):
+ match = re.search(pattern,l)
+ if match:
+ try:
+ if_index = match.group(1)
+ iface = dev_from_index(if_index)
+ except:
+ continue
+
+ d = match.group(2)
+ dp = int(match.group(3)[1:])
+ nh = match.group(4)
+
+ cset = [] # candidate set (possible source addresses)
+ if iface.name == LOOPBACK_NAME:
+ if d == '::':
+ continue
+ cset = ['::1']
+ else:
+ devaddrs = filter(lambda x: x[2] == iface, lifaddr)
+ cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs, LOOPBACK_NAME)
+ # APPEND (DESTINATION, NETMASK, NEXT HOP, IFACE, CANDIDATS)
+ routes.append((d, dp, nh, iface, cset))
+ return routes
+
+
+
if conf.interactive_shell != 'ipython':
try:
@@ -501,22 +570,43 @@ def get_working_if():
conf.iface = get_working_if()
-
-def route_add_loopback():
- """Add a route to 127.0.0.1 to simplify unit tests on Windows"""
-
+def route_add_loopback(routes=None, ipv6=False, iflist=None):
+ """Add a route to 127.0.0.1 and ::1 to simplify unit tests on Windows"""
+ # Add only if some adpaters already exist
+ if ipv6:
+ if len(conf.route6.routes) == 0:
+ return
+ else:
+ if len(conf.route.routes) == 0:
+ return
+ data = {}
+ data['name'] = LOOPBACK_NAME
+ data['description'] = "Loopback"
+ data['win_index'] = -1
+ data['guid'] = "{0XX00000-X000-0X0X-X00X-00XXXX000XXX}"
+ data['invalid'] = True
+ adapter = NetworkInterface(data)
+ if iflist:
+ iflist.append(unicode("\\Device\\NPF_" + adapter.guid))
+ return
# Build the packed network addresses
loop_net = struct.unpack("!I", socket.inet_aton("127.0.0.0"))[0]
loop_mask = struct.unpack("!I", socket.inet_aton("255.0.0.0"))[0]
-
- # Get the adapter from an existing route
- if len(conf.route.routes) == 0:
- return
- adapter = conf.route.routes[0][3]
-
- # Build and inject the fake route
+ # Build the fake routes
loopback_route = (loop_net, loop_mask, "0.0.0.0", adapter, "127.0.0.1")
- conf.route.routes.append(loopback_route)
-
- # Flush the cache
- conf.route.invalidate_cache()
+ loopback_route6 = ('::1', 128, '::', adapter, ["::1"])
+ loopback_route6_custom = ("fe80::", 128, "::", adapter, ["::1"])
+ if routes == None:
+ # Injection
+ conf.route6.routes.append(loopback_route6)
+ conf.route6.routes.append(loopback_route6_custom)
+ conf.route.routes.append(loopback_route)
+ # Flush the caches
+ conf.route6.invalidate_cache()
+ conf.route.invalidate_cache()
+ else:
+ if ipv6:
+ routes.append(loopback_route6)
+ routes.append(loopback_route6_custom)
+ else:
+ routes.append(loopback_route)
diff --git a/scapy/error.py b/scapy/error.py
index ac9bfa652bc9719661824902074e052cc30bb5d4..b2a3b4f8b0f2dca9f01ef77a5aca1f279fcb6d66 100644
--- a/scapy/error.py
+++ b/scapy/error.py
@@ -60,6 +60,17 @@ log_runtime.addFilter(ScapyFreqFilter())
log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions
log_loading = logging.getLogger("scapy.loading") # logs when loading Scapy
+def muteLogLoading(mute):
+ """
+ This mutes the log loading: used when a class is loaded several times,
+ to avoid the warning messages to be showed more than once.
+ :param mute: setting it to True will mute, False will un-mute
+ """
+ if not mute:
+ log_loading.setLevel(logging.WARNING)
+ else:
+ log_loading.setLevel(logging.CRITICAL)
+
def warning(x):
log_runtime.warning(x)
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
index b5cba9c71638ae3f4e479b4d429984e0e169412a..dd5929aa60f6c3de6da1fceb88877792896bff27 100644
--- a/scapy/layers/inet6.py
+++ b/scapy/layers/inet6.py
@@ -108,8 +108,12 @@ def getmacbyip6(ip6, chainCC=0):
iff,a,nh = conf.route6.route(ip6)
- if iff == LOOPBACK_NAME:
- return "ff:ff:ff:ff:ff:ff"
+ if isinstance(iff, basestring):
+ if iff == LOOPBACK_NAME:
+ return "ff:ff:ff:ff:ff:ff"
+ else:
+ if iff.name == LOOPBACK_NAME:
+ return "ff:ff:ff:ff:ff:ff"
if nh != '::':
ip6 = nh # Found next hop
diff --git a/scapy/route6.py b/scapy/route6.py
index e00edfc4757ba8d9e43dcbc17a96dd0ebfea70d5..4331e467f93d52abeb539cc196d88a385a19090b 100644
--- a/scapy/route6.py
+++ b/scapy/route6.py
@@ -49,7 +49,7 @@ class Route6:
rtlst = [('Destination', 'Next Hop', "iface", "src candidates")]
for net,msk,gw,iface,cset in self.routes:
- rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset)))
+ rtlst.append(('%s/%i'% (net,msk), gw, (iface if isinstance(iface, basestring) else iface.name), ", ".join(cset)))
colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst))
fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth))
@@ -200,7 +200,7 @@ class Route6:
# Deal with dev-specific request for cache search
k = dst
if dev is not None:
- k = dst + "%%" + dev
+ k = dst + "%%" + (dev if isinstance(dev, basestring) else dev.pcap_name)
if k in self.cache:
return self.cache[k]
@@ -265,7 +265,7 @@ class Route6:
# Fill the cache (including dev-specific request)
k = dst
if dev is not None:
- k = dst + "%%" + dev
+ k = dst + "%%" + (dev if isinstance(dev, basestring) else dev.pcap_name)
self.cache[k] = res[0][1]
return res[0][1]
diff --git a/test/mock_windows.uts b/test/mock_windows.uts
new file mode 100644
index 0000000000000000000000000000000000000000..25d495cb9ce713bf799b136e424cf3c5e5fe647a
--- /dev/null
+++ b/test/mock_windows.uts
@@ -0,0 +1,71 @@
+% Regression tests on Windows only for Scapy
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+############
+############
++ Mocked read_routes6() calls
+
+= Windows with fake IPv6 adapter
+
+import mock
+
+def check_mandatory_ipv6_routes_windows(routes6):
+ """Ensure that mandatory IPv6 routes are present. There is not always a loopback interface on windows !"""
+ if len(filter(lambda r: r[0] == "fe80::" and (r[1] == 64 or r[1] == 32), routes6)) < 1:
+ return False
+ if len(filter(lambda r: in6_islladdr(r[0]) and r[1] == 128, routes6)) < 1:
+ return False
+ return True
+
+def dev_from_index_custom(if_index):
+ if_list = [{'mac': 'D0-50-99-56-DD-F9', 'win_index': '13', 'guid': '{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}', 'name': 'Killer E2200 Gigabit Ethernet Controller', 'description': 'Ethernet'}, {'mac': '00-FF-0E-C7-25-37', 'win_index': '3', 'guid': '{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', 'name': 'TAP-Windows Adapter V9', 'description': 'Ethernet 2'}]
+ values = {}
+ for i in if_list:
+ try:
+ interface = NetworkInterface(i)
+ values[interface.guid] = interface
+ except (KeyError, PcapNameNotFoundError):
+ pass
+ for devname, iface in values.items():
+ if iface.win_index == str(if_index):
+ return iface
+ raise ValueError("Unknown network interface index %r" % if_index)
+
+@mock.patch("scapy.arch.windows.winpcapy_get_if_list")
+@mock.patch("scapy.arch.windows.dev_from_index")
+@mock.patch("scapy.arch.windows.sp.Popen")
+def test_read_routes6_windows(mock_comm, mock_dev_from_index, mock_winpcapylist):
+ """Test read_routes6() on Windows"""
+ # 'Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop'
+ get_net_route_output = """
+ifIndex DestinationPrefix NextHop
+------- ----------------- -------
+3 ff00::/8 ::
+16 ff00::/8 ::
+13 ff00::/8 ::
+1 ff00::/8 ::
+13 fe80::dc1d:24e8:af00:125e/128 ::
+3 fe80::9402:5804:cb16:fb3b/128 ::
+16 fe80::100:7f:fffe/128 ::
+3 fe80::/64 ::
+16 fe80::/64 ::
+13 fe80::/64 ::
+13 2a01:e35:2f17:fe60:dc1d:24e8:af00:125e/128 ::
+13 2a01:e35:2f17:fe60::/64 ::
+1 ::1/128 ::
+13 ::/0 fe80::224:d4ff:fea0:a6d7
+"""
+ mock_comm.return_value.communicate.return_value = (get_net_route_output, "")
+ mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
+ # Mocked in6_getifaddr() output
+ mock_dev_from_index.side_effect = dev_from_index_custom
+ # Test the function
+ routes = read_routes6()
+ for r in routes:
+ print r
+ assert(len(routes) == 9)
+ assert(check_mandatory_ipv6_routes_windows(routes))
+
+
+test_read_routes6_windows()
diff --git a/test/regression.uts b/test/regression.uts
index 678641f21abb48429fc62e3cc635ce42e653e7f9..5b917beb656913a9b33b638a70fc63e5b92fae52 100644
--- a/test/regression.uts
+++ b/test/regression.uts
@@ -1,7 +1,6 @@
% Regression tests for Scapy
# More informations at http://www.secdev.org/projects/UTscapy/
-# $Id: regression.uts,v 1.9 2008/07/29 15:30:29 pbi Exp pbi $
############
############
@@ -40,12 +39,17 @@ get_if_list()
= Test read_routes6() - default output
routes6 = read_routes6()
+if WINDOWS:
+ route_add_loopback(routes6, True)
+
# Expected results:
# - one route if there is only the loopback interface
# - three routes if there is a network interface
if len(routes6):
iflist = get_if_list()
+ if WINDOWS:
+ route_add_loopback(ipv6=True, iflist=iflist)
if iflist == [LOOPBACK_NAME]:
len(routes6) == 1
elif len(iflist) >= 2:
@@ -3971,6 +3975,9 @@ not a < b and a > b
+ Mobile Prefix Solicitation/Advertisement
= ICMPv6MPSol - build (default values)
+if WINDOWS:
+ route_add_loopback()
+
s = '`\x00\x00\x00\x00\x08:@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x92\x00m\xbb\x00\x00\x00\x00'
str(IPv6()/ICMPv6MPSol()) == s
@@ -5428,7 +5435,6 @@ ff02::%lo0/32 ::1 UC -
test_netbsd_7_0()
-
############
############
+ EAPOL class tests
diff --git a/test/run_tests.bat b/test/run_tests.bat
index 5c38d9ca756858786cfd29d7fd6b4296bbf072a3..8ed0b7f159ab43c2fadd9dd5a2587207a4e63752 100644
--- a/test/run_tests.bat
+++ b/test/run_tests.bat
@@ -2,8 +2,8 @@
set MYDIR=%cd%\..
set PYTHONPATH=%MYDIR%
if [%1]==[] (
- SET date=%DATE%
- python "%MYDIR%\scapy\tools\UTscapy.py" -t regression.uts -f html -o scapy_regression_test_%date:~6,4%_%date:~3,2%_%date:~0,2%.html
+ python "%MYDIR%\scapy\tools\UTscapy.py" -t regression.uts -K automaton -f html -o scapy_regression_test_%date:~6,4%_%date:~3,2%_%date:~0,2%.html
) else (
python "%MYDIR%\scapy\tools\UTscapy.py" %@
)
+PAUSE