% Regression tests on Windows only for Scapy

# More information at http://www.secdev.org/projects/UTscapy/

############
############
+ Networking tests

= Automaton - SelectableSelector system timeout

class TimeOutSelector(SelectableObject):
    def check_recv(self):
        return False

assert select_objects([TimeOutSelector()], 0) == []
assert select_objects([TimeOutSelector()], 1) == []

############
############
+ Windows Networking tests

= Mocked read_routes6() calls with a few missing InterfaceMetric

import mock
from scapy.tools.UTscapy import Bunch
from scapy.arch.windows import _read_routes6_post2008

def check_mandatory_ipv6_routes(routes6):
    """Ensure that mandatory IPv6 routes are present."""
    if len([r for r in routes6 if r[0] == "::" and r[4] == ["::1"]]) < 1:
        return False
    if len([r for r in routes6 if r[0] == "fe80::" and (r[1] == 64 or r[1] == 32)]) < 1:
        return False
    if len([r for r in routes6 if in6_islladdr(r[0]) and r[1] == 128]) < 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._get_metrics")
@mock.patch("scapy.arch.windows.construct_source_candidate_set")
@mock.patch("scapy.arch.windows.get_if_list")
@mock.patch("scapy.arch.windows.dev_from_index")
@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
def test_read_routes6_windows(mock_comm, mock_dev_from_index, mock_winpcapylist, mock_utils6cset, mock_get_metrics):
    """Test read_routes6() on Windows"""
    # 'Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop'
    get_net_route_output = """
ifIndex           : 3
DestinationPrefix : ff00::/8
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 16
DestinationPrefix : ff00::/8
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 13
DestinationPrefix : ff00::/8
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 1
DestinationPrefix : ff00::/8
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 13
DestinationPrefix : fe80::dc1d:24e8:af00:125e/128
NextHop           : ::
RouteMetric       : 20
InterfaceMetric   : 256

ifIndex           : 3
DestinationPrefix : fe80::9402:5804:cb16:fb3b/128
NextHop           : ::
RouteMetric       : 1
InterfaceMetric   : 0

ifIndex           : 16
DestinationPrefix : fe80::100:7f:fffe/128
NextHop           : ::
RouteMetric       : 1
InterfaceMetric   : 0

ifIndex           : 3
DestinationPrefix : fe80::/64
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 16
DestinationPrefix : fe80::/64
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 13
DestinationPrefix : fe80::/64
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 1

ifIndex           : 13
DestinationPrefix : 2a01:e35:2f17:fe60:dc1d:24e8:af00:125e/128
NextHop           : ::
RouteMetric       : 20
InterfaceMetric   : 

ifIndex           : 13
DestinationPrefix : 2a01:e35:2f17:fe60::/64
NextHop           : ::
RouteMetric       : 30
InterfaceMetric   : 256

ifIndex           : 1
DestinationPrefix : ::1/128
NextHop           : ::
RouteMetric       : 0
InterfaceMetric   : 256

ifIndex           : 13
DestinationPrefix : ::/0
NextHop           : fe80::224:d4ff:fea0:a6d7
RouteMetric       : 0
InterfaceMetric   : 256
"""
    mock_comm.return_value = get_net_route_output.split("\n")
    mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
    mock_get_metrics.side_effect = lambda ipv6=True: {'16': 0, '13': 999}
    # Mocked in6_getifaddr() output
    mock_dev_from_index.side_effect = dev_from_index_custom
    # Random
    mock_utils6cset.side_effect = lambda x,y,z: ["::1"] if x=="::" else ["fdbb:d995:ddd8:51fc::"]
    # Test the function
    routes = _read_routes6_post2008()
    for r in routes:
        print(r)
    print(len(routes))
    assert(len(routes) == 9)
    assert(check_mandatory_ipv6_routes(routes))
    assert routes[6][5] == 1019
    assert routes[7][5] == 286

test_read_routes6_windows()

= Test _read_routes_post2008 with missing InterfaceMetric

from scapy.arch.windows import _read_routes_post2008

@mock.patch("scapy.arch.windows._get_metrics")
@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
@mock.patch("scapy.arch.windows.get_if_list")
@mock.patch("scapy.arch.windows.dev_from_index")
def test_missing_ifacemetric(mock_dev_from_index, mock_winpcapylist, mock_exec_query, mock_get_metrics):
    exc_query_output = """ifIndex           : 3
DestinationPrefix : 255.255.255.255/0
NextHop           : 192.168.103.1
RouteMetric       : 10
InterfaceMetric   : 256

ifIndex           : 13
DestinationPrefix : 255.255.255.255/32
NextHop           : 0.0.0.0
RouteMetric       : 20
InterfaceMetric   :
"""
    mock_exec_query.side_effect = lambda *args, **kargs: exc_query_output.split("\n")
    mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
    mock_dev_from_index.side_effect = dev_from_index_custom
    mock_get_metrics.side_effect = lambda ipv6=False: {'16': 0, '13': 123}
    routes = _read_routes_post2008()
    for r in routes:
        print(r)
    assert len(routes) == 2
    # Test if metrics were correctly read/guessed
    assert routes[0][5] == 266
    assert routes[1][5] == 143

test_missing_ifacemetric()

= Test _get_metrics with weird netsh length

from scapy.arch.windows import _get_metrics

@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
def test_get_metrics(mock_exec_query):
    exc_query_output = """Interface Loopback Pseudo-Interface 1 Parameters
-------------------------------
IfLuid : loopback_0
IfIndex : 1
State : connected
Metric : 75
Link MTU : 4294967295 byt
Reachable Time : 40500 ms
Base Reachable Time : 30000 ms
Retransmission Interval : 1000 ms
DAD Transmits : 0
Site Prefix Length : 64
Site Id : 1
Forwarding : disabled
Advertising : disabled
Neighbor Discovery : disabled
Neighbor Unreachability Detection : disabled
Router Discovery : dhcp
Managed Address Configuration : enabled
Other Stateful Configuration : enabled
Weak Host Sends : disabled
Weak Host Receives : disabled
Use Automatic Metric : enabled
Ignore Default Routes : disabled
Advertised Router Lifetime : 1800 seconds
Advertise Default Route : disabled
Current Hop Limit : 0
Force ARPND Wake up patterns : disabled
Directed MAC Wake up patterns : disabled
ECN capability : application

Interface Wi-Fi Parameters
-------------------------------
IfLuid : wireless_32768
IfIndex : 7
State : connected
Metric : 55
Link MTU : 1500 bytes
Reachable Time : 43500 ms
Base Reachable Time : 30000 ms
Retransmission Interval : 1000 ms
DAD Transmits : 3
Site Prefix Length : 64
Site Id : 1
Forwarding : disabled
Advertising : disabled
Neighbor Discovery : enabled
Neighbor Unreachability Detection : enabled
Router Discovery : dhcp
Managed Address Configuration : enabled
Other Stateful Configuration : enabled
Weak Host Sends : disabled
Weak Host Receives : disabled
Use Automatic Metric : enabled
Ignore Default Routes : disabled
Advertised Router Lifetime : 1800 seconds
Advertise Default Route : disabled
Current Hop Limit : 0
Force ARPND Wake up patterns : disabled
Directed MAC Wake up patterns : disabled
ECN capability : application
"""
    mock_exec_query.side_effect = lambda *args, **kargs: exc_query_output.split("\n")
    metrics = _get_metrics()
    print(metrics)
    assert metrics == {'1': 75, '7': 55}

test_get_metrics()

############
############
+ Windows arch unit tests

= Test PowerShell availability
from scapy.config import conf
assert conf.prog.powershell != None

= Store powershell results
import mock
from scapy.config import conf

ps_ip = get_ip_from_name(conf.iface.name)
ps_if_list = get_windows_if_list()
ps_read_routes = read_routes()

# Turn on VBS mode
conf.prog.powershell = None

= Test get_ip_from_name with VBS
ps_ip

assert get_ip_from_name(conf.iface.name) == ps_ip

= Test get_windows_if_list with VBS
~ appveyor_only

# This test is slightly unstable locally, especially on Windows 7

ps_if_list

def is_in_if_list(i, list):
    if not i["mac"]:
        return True
    for j in list:
        if j["guid"] == i["guid"] and j["name"] == i["name"]:
            return True
    return False

vbs_if_list = get_windows_if_list()
vbs_if_list
_correct = True
for i in vbs_if_list:
    if not is_in_if_list(i, ps_if_list):
        _correct = False
        break

assert _correct

= Test read_routes with VBS
ps_read_routes

def is_in_route_list(i, list):
    # Ignore all empty IP or macs
    if i[4] == '':
        return True
    if i[3].mac == '' or i[3].guid == '' or i[3].ip == '':
        return True
    for j in list:
        if j[2] == i[2] and j[4] == i[4] and j[3].guid == i[3].guid:
            return True
    return False

vbs_read_routes = read_routes()
vbs_read_routes
_correct = True
for i in vbs_read_routes:
    if not is_in_route_list(i, ps_read_routes):
        _correct = False
        break

assert _correct

conf.prog._reload()

= show_interfaces

from scapy.arch import show_interfaces

with ContextManagerCaptureOutput() as cmco:
    show_interfaces()
    lines = cmco.get_output().split("\n")[1:]
    for l in lines:
        if not l.strip():
            continue
        int(l[:2])

= dev_from_pcapname

from scapy.config import conf

assert dev_from_pcapname(conf.iface.pcap_name).guid == conf.iface.guid

= test pcap_service_status

status = pcap_service_status()
status
assert status[0] in ["npcap", "npf"]
assert status[2] == True

= test pcap_service_stop
~ appveyor_only require_gui

pcap_service_stop()
assert pcap_service_status()[2] == False

= test pcap_service_start
~ appveyor_only require_gui

pcap_service_start()
assert pcap_service_status()[2] == True

= Test auto-pcap start UI

old_ifaces = IFACES.data.copy()

@mock.patch("scapy.arch.windows.get_if_list")
def _test_autostart_ui(mocked_getiflist):
    mocked_getiflist.side_effect = lambda: []
    IFACES.reload()
    assert IFACES.data == {}

_test_autostart_ui()

IFACES.data = old_ifaces
