2025-12-25 upload
This commit is contained in:
35
venv/Lib/site-packages/pydivert/__init__.py
Normal file
35
venv/Lib/site-packages/pydivert/__init__.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import sys as _sys
|
||||
|
||||
from .consts import Layer, Flag, Param, CalcChecksumsOption, Direction, Protocol
|
||||
from .packet import Packet
|
||||
from .windivert import WinDivert
|
||||
|
||||
__author__ = 'fabio'
|
||||
__version__ = '2.1.0'
|
||||
|
||||
if _sys.version_info < (3, 4):
|
||||
# add socket.inet_pton on Python < 3.4
|
||||
import win_inet_pton as _win_inet_pton
|
||||
|
||||
assert _win_inet_pton
|
||||
|
||||
__all__ = [
|
||||
"WinDivert",
|
||||
"Packet",
|
||||
"Layer", "Flag", "Param", "CalcChecksumsOption", "Direction", "Protocol",
|
||||
]
|
||||
94
venv/Lib/site-packages/pydivert/consts.py
Normal file
94
venv/Lib/site-packages/pydivert/consts.py
Normal file
@@ -0,0 +1,94 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
# Divert layers.
|
||||
class Layer(IntEnum):
|
||||
"""
|
||||
See https://reqrypt.org/windivert-doc.html#divert_open
|
||||
"""
|
||||
NETWORK = 0
|
||||
NETWORK_FORWARD = 1
|
||||
|
||||
|
||||
# Divert Flag.
|
||||
class Flag(IntEnum):
|
||||
"""
|
||||
See https://reqrypt.org/windivert-doc.html#divert_open
|
||||
"""
|
||||
DEFAULT = 0
|
||||
SNIFF = 1
|
||||
DROP = 2
|
||||
NO_CHECKSUM = 1024 # Deprecated since Windivert 1.2
|
||||
|
||||
|
||||
# Divert parameters.
|
||||
class Param(IntEnum):
|
||||
"""
|
||||
See https://reqrypt.org/windivert-doc.html#divert_set_param
|
||||
"""
|
||||
QUEUE_LEN = 0 # Packet queue length 1 < default 512 (actually 1024) < 8192
|
||||
QUEUE_TIME = 1 # Packet queue time 128 < default 512 < 2048
|
||||
QUEUE_SIZE = 2 # Packet queue size (bytes) 4096 (4KB) < default 4194304 (4MB) < 33554432 (32MB)
|
||||
|
||||
|
||||
# Direction outbound/inbound
|
||||
class Direction(IntEnum):
|
||||
"""
|
||||
See https://reqrypt.org/windivert-doc.html#divert_address
|
||||
"""
|
||||
OUTBOUND = 0
|
||||
INBOUND = 1
|
||||
|
||||
|
||||
# Checksums
|
||||
class CalcChecksumsOption(IntEnum):
|
||||
"""
|
||||
See https://reqrypt.org/windivert-doc.html#divert_helper_calc_checksums
|
||||
"""
|
||||
NO_IP_CHECKSUM = 1
|
||||
NO_ICMP_CHECKSUM = 2
|
||||
NO_ICMPV6_CHECKSUM = 4
|
||||
NO_TCP_CHECKSUM = 8
|
||||
NO_UDP_CHECKSUM = 16
|
||||
NO_REPLACE = 2048
|
||||
|
||||
|
||||
class Protocol(IntEnum):
|
||||
"""
|
||||
Transport protocol values define the layout of the header that will immediately follow the IPv4 or IPv6 header.
|
||||
See http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
|
||||
"""
|
||||
HOPOPT = 0
|
||||
ICMP = 1
|
||||
TCP = 6
|
||||
UDP = 17
|
||||
ROUTING = 43
|
||||
FRAGMENT = 44
|
||||
AH = 51
|
||||
ICMPV6 = 58
|
||||
NONE = 59
|
||||
DSTOPTS = 60
|
||||
|
||||
|
||||
IPV6_EXT_HEADERS = {
|
||||
Protocol.HOPOPT,
|
||||
Protocol.ROUTING,
|
||||
Protocol.FRAGMENT,
|
||||
Protocol.DSTOPTS,
|
||||
Protocol.AH,
|
||||
}
|
||||
350
venv/Lib/site-packages/pydivert/packet/__init__.py
Normal file
350
venv/Lib/site-packages/pydivert/packet/__init__.py
Normal file
@@ -0,0 +1,350 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ctypes
|
||||
import pprint
|
||||
import socket
|
||||
|
||||
from pydivert import windivert_dll
|
||||
from pydivert.consts import Direction, IPV6_EXT_HEADERS, Protocol, Layer
|
||||
from pydivert.packet.header import Header
|
||||
from pydivert.packet.icmp import ICMPv4Header, ICMPv6Header
|
||||
from pydivert.packet.ip import IPv4Header, IPv6Header
|
||||
from pydivert.packet.tcp import TCPHeader
|
||||
from pydivert.packet.udp import UDPHeader
|
||||
from pydivert.util import cached_property, indexbyte as i, PY2
|
||||
|
||||
|
||||
class Packet(object):
|
||||
"""
|
||||
A single packet, possibly including an IP header, a TCP/UDP header and a payload.
|
||||
Creation of packets is cheap, parsing is done on first attribute access.
|
||||
"""
|
||||
|
||||
def __init__(self, raw, interface, direction):
|
||||
if isinstance(raw, bytes):
|
||||
raw = memoryview(bytearray(raw))
|
||||
self.raw = raw # type: memoryview
|
||||
self.interface = interface
|
||||
self.direction = direction
|
||||
|
||||
def __repr__(self):
|
||||
def dump(x):
|
||||
if isinstance(x, Header) or isinstance(x, Packet):
|
||||
d = {}
|
||||
for k in dir(x):
|
||||
v = getattr(x, k)
|
||||
if k.startswith("_") or callable(v):
|
||||
continue
|
||||
if k in {"address_family", "protocol", "ip", "icmp"}:
|
||||
continue
|
||||
if k == "payload" and v and len(v) > 20:
|
||||
v = v[:20] + b"..."
|
||||
d[k] = dump(v)
|
||||
if isinstance(x, Packet):
|
||||
return pprint.pformat(d)
|
||||
return d
|
||||
return x
|
||||
|
||||
return "Packet({})".format(dump(self))
|
||||
|
||||
@property
|
||||
def is_outbound(self):
|
||||
"""
|
||||
Indicates if the packet is outbound.
|
||||
Convenience method for ``.direction``.
|
||||
"""
|
||||
return self.direction == Direction.OUTBOUND
|
||||
|
||||
@property
|
||||
def is_inbound(self):
|
||||
"""
|
||||
Indicates if the packet is inbound.
|
||||
Convenience method for ``.direction``.
|
||||
"""
|
||||
return self.direction == Direction.INBOUND
|
||||
|
||||
@property
|
||||
def is_loopback(self):
|
||||
"""
|
||||
- True, if the packet is on the loopback interface.
|
||||
- False, otherwise.
|
||||
"""
|
||||
return self.interface[0] == 1
|
||||
|
||||
@cached_property
|
||||
def address_family(self):
|
||||
"""
|
||||
The packet address family:
|
||||
- socket.AF_INET, if IPv4
|
||||
- socket.AF_INET6, if IPv6
|
||||
- None, otherwise.
|
||||
"""
|
||||
if len(self.raw) >= 20:
|
||||
v = i(self.raw[0]) >> 4
|
||||
if v == 4:
|
||||
return socket.AF_INET
|
||||
if v == 6:
|
||||
return socket.AF_INET6
|
||||
|
||||
@cached_property
|
||||
def protocol(self):
|
||||
"""
|
||||
- | A (ipproto, proto_start) tuple.
|
||||
| ``ipproto`` is the IP protocol in use, e.g. Protocol.TCP or Protocol.UDP.
|
||||
| ``proto_start`` denotes the beginning of the protocol data.
|
||||
| If the packet does not match our expectations, both ipproto and proto_start are None.
|
||||
"""
|
||||
if self.address_family == socket.AF_INET:
|
||||
proto = i(self.raw[9])
|
||||
start = (i(self.raw[0]) & 0b1111) * 4
|
||||
elif self.address_family == socket.AF_INET6:
|
||||
proto = i(self.raw[6])
|
||||
|
||||
# skip over well-known ipv6 headers
|
||||
start = 40
|
||||
while proto in IPV6_EXT_HEADERS:
|
||||
if start >= len(self.raw):
|
||||
# less than two bytes left
|
||||
start = None
|
||||
proto = None
|
||||
break
|
||||
if proto == Protocol.FRAGMENT:
|
||||
hdrlen = 8
|
||||
elif proto == Protocol.AH:
|
||||
hdrlen = (i(self.raw[start + 1]) + 2) * 4
|
||||
else:
|
||||
# Protocol.HOPOPT, Protocol.DSTOPTS, Protocol.ROUTING
|
||||
hdrlen = (i(self.raw[start + 1]) + 1) * 8
|
||||
proto = i(self.raw[start])
|
||||
start += hdrlen
|
||||
else:
|
||||
start = None
|
||||
proto = None
|
||||
|
||||
out_of_bounds = (
|
||||
(proto == Protocol.TCP and start + 20 > len(self.raw)) or
|
||||
(proto == Protocol.UDP and start + 8 > len(self.raw)) or
|
||||
(proto in {Protocol.ICMP, Protocol.ICMPV6} and start + 4 > len(self.raw))
|
||||
)
|
||||
if out_of_bounds:
|
||||
# special-case tcp/udp so that we can rely on .protocol for the port properties.
|
||||
start = None
|
||||
proto = None
|
||||
|
||||
return proto, start
|
||||
|
||||
@cached_property
|
||||
def ipv4(self):
|
||||
"""
|
||||
- An IPv4Header instance, if the packet is valid IPv4.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self.address_family == socket.AF_INET:
|
||||
return IPv4Header(self)
|
||||
|
||||
@cached_property
|
||||
def ipv6(self):
|
||||
"""
|
||||
- An IPv6Header instance, if the packet is valid IPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self.address_family == socket.AF_INET6:
|
||||
return IPv6Header(self)
|
||||
|
||||
@cached_property
|
||||
def ip(self):
|
||||
"""
|
||||
- An IPHeader instance, if the packet is valid IPv4 or IPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
return self.ipv4 or self.ipv6
|
||||
|
||||
@cached_property
|
||||
def icmpv4(self):
|
||||
"""
|
||||
- An ICMPv4Header instance, if the packet is valid ICMPv4.
|
||||
- None, otherwise.
|
||||
"""
|
||||
ipproto, proto_start = self.protocol
|
||||
if ipproto == Protocol.ICMP:
|
||||
return ICMPv4Header(self, proto_start)
|
||||
|
||||
@cached_property
|
||||
def icmpv6(self):
|
||||
"""
|
||||
- An ICMPv6Header instance, if the packet is valid ICMPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
ipproto, proto_start = self.protocol
|
||||
if ipproto == Protocol.ICMPV6:
|
||||
return ICMPv6Header(self, proto_start)
|
||||
|
||||
@cached_property
|
||||
def icmp(self):
|
||||
"""
|
||||
- An ICMPHeader instance, if the packet is valid ICMPv4 or ICMPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
return self.icmpv4 or self.icmpv6
|
||||
|
||||
@cached_property
|
||||
def tcp(self):
|
||||
"""
|
||||
- An TCPHeader instance, if the packet is valid TCP.
|
||||
- None, otherwise.
|
||||
"""
|
||||
ipproto, proto_start = self.protocol
|
||||
if ipproto == Protocol.TCP:
|
||||
return TCPHeader(self, proto_start)
|
||||
|
||||
@cached_property
|
||||
def udp(self):
|
||||
"""
|
||||
- An TCPHeader instance, if the packet is valid UDP.
|
||||
- None, otherwise.
|
||||
"""
|
||||
ipproto, proto_start = self.protocol
|
||||
if ipproto == Protocol.UDP:
|
||||
return UDPHeader(self, proto_start)
|
||||
|
||||
@cached_property
|
||||
def _port(self):
|
||||
"""header that implements PortMixin"""
|
||||
return self.tcp or self.udp
|
||||
|
||||
@cached_property
|
||||
def _payload(self):
|
||||
"""header that implements PayloadMixin"""
|
||||
return self.tcp or self.udp or self.icmpv4 or self.icmpv6
|
||||
|
||||
@property
|
||||
def src_addr(self):
|
||||
"""
|
||||
- The source address, if the packet is valid IPv4 or IPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self.ip:
|
||||
return self.ip.src_addr
|
||||
|
||||
@src_addr.setter
|
||||
def src_addr(self, val):
|
||||
self.ip.src_addr = val
|
||||
|
||||
@property
|
||||
def dst_addr(self):
|
||||
"""
|
||||
- The destination address, if the packet is valid IPv4 or IPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self.ip:
|
||||
return self.ip.dst_addr
|
||||
|
||||
@dst_addr.setter
|
||||
def dst_addr(self, val):
|
||||
self.ip.dst_addr = val
|
||||
|
||||
@property
|
||||
def src_port(self):
|
||||
"""
|
||||
- The source port, if the packet is valid TCP or UDP.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self._port:
|
||||
return self._port.src_port
|
||||
|
||||
@src_port.setter
|
||||
def src_port(self, val):
|
||||
self._port.src_port = val
|
||||
|
||||
@property
|
||||
def dst_port(self):
|
||||
"""
|
||||
- The destination port, if the packet is valid TCP or UDP.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self._port:
|
||||
return self._port.dst_port
|
||||
|
||||
@dst_port.setter
|
||||
def dst_port(self, val):
|
||||
self._port.dst_port = val
|
||||
|
||||
@property
|
||||
def payload(self):
|
||||
"""
|
||||
- The payload, if the packet is valid TCP, UDP, ICMP or ICMPv6.
|
||||
- None, otherwise.
|
||||
"""
|
||||
if self._payload:
|
||||
return self._payload.payload
|
||||
|
||||
@payload.setter
|
||||
def payload(self, val):
|
||||
self._payload.payload = val
|
||||
|
||||
def recalculate_checksums(self, flags=0):
|
||||
"""
|
||||
(Re)calculates the checksum for any IPv4/ICMP/ICMPv6/TCP/UDP checksum present in the given packet.
|
||||
Individual checksum calculations may be disabled via the appropriate flag.
|
||||
Typically this function should be invoked on a modified packet before it is injected with WinDivert.send().
|
||||
Returns the number of checksums calculated.
|
||||
|
||||
See: https://reqrypt.org/windivert-doc.html#divert_helper_calc_checksums
|
||||
"""
|
||||
buff, buff_ = self.__to_buffers()
|
||||
num = windivert_dll.WinDivertHelperCalcChecksums(ctypes.byref(buff_), len(self.raw), flags)
|
||||
if PY2:
|
||||
self.raw = memoryview(buff)[:len(self.raw)]
|
||||
return num
|
||||
|
||||
def __to_buffers(self):
|
||||
buff = bytearray(self.raw.tobytes()) if PY2 else self.raw.obj
|
||||
return buff, (ctypes.c_char * len(self.raw)).from_buffer(buff)
|
||||
|
||||
@property
|
||||
def wd_addr(self):
|
||||
"""
|
||||
Gets the interface and direction as a `WINDIVERT_ADDRESS` structure.
|
||||
:return: The `WINDIVERT_ADDRESS` structure.
|
||||
"""
|
||||
address = windivert_dll.WinDivertAddress()
|
||||
address.IfIdx, address.SubIfIdx = self.interface
|
||||
address.Direction = self.direction
|
||||
return address
|
||||
|
||||
def matches(self, filter, layer=Layer.NETWORK):
|
||||
"""
|
||||
Evaluates the packet against the given packet filter string.
|
||||
|
||||
The remapped function is::
|
||||
|
||||
BOOL WinDivertHelperEvalFilter(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in PWINDIVERT_ADDRESS pAddr
|
||||
);
|
||||
|
||||
See: https://reqrypt.org/windivert-doc.html#divert_helper_eval_filter
|
||||
|
||||
:param filter: The filter string.
|
||||
:param layer: The network layer.
|
||||
:return: True if the packet matches, and False otherwise.
|
||||
"""
|
||||
buff, buff_ = self.__to_buffers()
|
||||
return windivert_dll.WinDivertHelperEvalFilter(filter.encode(), layer, ctypes.byref(buff_), len(self.raw),
|
||||
ctypes.byref(self.wd_addr))
|
||||
91
venv/Lib/site-packages/pydivert/packet/header.py
Normal file
91
venv/Lib/site-packages/pydivert/packet/header.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
|
||||
|
||||
class Header(object):
|
||||
def __init__(self, packet, start=0):
|
||||
self._packet = packet # type: "pydivert.Packet"
|
||||
self._start = start
|
||||
|
||||
@property
|
||||
def raw(self):
|
||||
"""
|
||||
The raw header, possibly including payload.
|
||||
"""
|
||||
return self._packet.raw[self._start:]
|
||||
|
||||
@raw.setter
|
||||
def raw(self, val):
|
||||
if len(val) == len(self.raw):
|
||||
self.raw[:] = val
|
||||
else:
|
||||
self._packet.raw = memoryview(bytearray(
|
||||
self._packet.raw[:self._start].tobytes() + val
|
||||
))
|
||||
self._packet.ip.packet_len = len(self._packet.raw)
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key in dir(self) or key in {"_packet", "_start"}:
|
||||
return super(Header, self).__setattr__(key, value)
|
||||
raise AttributeError("AttributeError: '{}' object has no attribute '{}'".format(
|
||||
type(self).__name__,
|
||||
key
|
||||
))
|
||||
|
||||
|
||||
class PayloadMixin(object):
|
||||
@property
|
||||
def header_len(self):
|
||||
raise NotImplementedError() # pragma: no cover
|
||||
|
||||
@property
|
||||
def payload(self):
|
||||
"""
|
||||
The packet payload data.
|
||||
"""
|
||||
return self.raw[self.header_len:].tobytes()
|
||||
|
||||
@payload.setter
|
||||
def payload(self, val):
|
||||
if len(val) == len(self.raw) - self.header_len:
|
||||
self.raw[self.header_len:] = val
|
||||
else:
|
||||
self.raw = self.raw[:self.header_len].tobytes() + val
|
||||
|
||||
|
||||
class PortMixin(object):
|
||||
@property
|
||||
def src_port(self):
|
||||
"""
|
||||
The source port.
|
||||
"""
|
||||
return struct.unpack_from("!H", self.raw, 0)[0]
|
||||
|
||||
@property
|
||||
def dst_port(self):
|
||||
"""
|
||||
The destination port.
|
||||
"""
|
||||
return struct.unpack_from("!H", self.raw, 2)[0]
|
||||
|
||||
@src_port.setter
|
||||
def src_port(self, val):
|
||||
self.raw[0:2] = struct.pack("!H", val)
|
||||
|
||||
@dst_port.setter
|
||||
def dst_port(self, val):
|
||||
self.raw[2:4] = struct.pack("!H", val)
|
||||
53
venv/Lib/site-packages/pydivert/packet/icmp.py
Normal file
53
venv/Lib/site-packages/pydivert/packet/icmp.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from pydivert.packet.header import Header, PayloadMixin
|
||||
from pydivert.util import indexbyte as i, raw_property
|
||||
|
||||
|
||||
class ICMPHeader(Header, PayloadMixin):
|
||||
header_len = 4
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
The ICMP message type.
|
||||
"""
|
||||
return i(self.raw[0])
|
||||
|
||||
@type.setter
|
||||
def type(self, val):
|
||||
self.raw[0] = i(val)
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
"""
|
||||
The ICMP message code.
|
||||
"""
|
||||
return i(self.raw[1])
|
||||
|
||||
@code.setter
|
||||
def code(self, val):
|
||||
self.raw[1] = i(val)
|
||||
|
||||
cksum = raw_property('!H', 2, docs='The ICMP header checksum field.')
|
||||
|
||||
|
||||
class ICMPv4Header(ICMPHeader):
|
||||
pass
|
||||
|
||||
|
||||
class ICMPv6Header(ICMPHeader):
|
||||
pass
|
||||
216
venv/Lib/site-packages/pydivert/packet/ip.py
Normal file
216
venv/Lib/site-packages/pydivert/packet/ip.py
Normal file
@@ -0,0 +1,216 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import socket
|
||||
import struct
|
||||
|
||||
from pydivert.packet.header import Header
|
||||
from pydivert.util import PY2, PY34, flag_property, indexbyte as i, raw_property
|
||||
|
||||
|
||||
class IPHeader(Header):
|
||||
_src_addr = slice(0, 0)
|
||||
_dst_addr = slice(0, 0)
|
||||
_af = None
|
||||
|
||||
@property
|
||||
def src_addr(self):
|
||||
"""
|
||||
The packet source address.
|
||||
"""
|
||||
try:
|
||||
return socket.inet_ntop(self._af, self.raw[self._src_addr].tobytes())
|
||||
except (ValueError, socket.error):
|
||||
pass
|
||||
|
||||
@src_addr.setter
|
||||
def src_addr(self, val):
|
||||
self.raw[self._src_addr] = socket.inet_pton(self._af, val)
|
||||
|
||||
@property
|
||||
def dst_addr(self):
|
||||
"""
|
||||
The packet destination address.
|
||||
"""
|
||||
try:
|
||||
return socket.inet_ntop(self._af, self.raw[self._dst_addr].tobytes())
|
||||
except (ValueError, socket.error):
|
||||
pass
|
||||
|
||||
@dst_addr.setter
|
||||
def dst_addr(self, val):
|
||||
self.raw[self._dst_addr] = socket.inet_pton(self._af, val)
|
||||
|
||||
@property
|
||||
def packet_len(self):
|
||||
"""
|
||||
The total packet length, including *all* headers, as reported by the IP header.
|
||||
"""
|
||||
raise NotImplementedError() # pragma: no cover
|
||||
|
||||
@packet_len.setter
|
||||
def packet_len(self, val):
|
||||
raise NotImplementedError() # pragma: no cover
|
||||
|
||||
|
||||
class IPv4Header(IPHeader):
|
||||
_src_addr = slice(12, 16)
|
||||
_dst_addr = slice(16, 20)
|
||||
_af = socket.AF_INET
|
||||
|
||||
@property
|
||||
def header_len(self):
|
||||
"""
|
||||
The IP header length in bytes.
|
||||
"""
|
||||
return self.hdr_len * 4
|
||||
|
||||
@property
|
||||
def hdr_len(self):
|
||||
"""
|
||||
The header length in words of 32bit.
|
||||
"""
|
||||
return i(self.raw[0]) & 0x0F
|
||||
|
||||
@hdr_len.setter
|
||||
def hdr_len(self, val):
|
||||
if val < 5:
|
||||
raise ValueError("IP header length must be greater or equal than 5.")
|
||||
struct.pack_into('!B', self.raw, 0, 0x40 | val)
|
||||
|
||||
packet_len = raw_property('!H', 2, docs=IPHeader.packet_len.__doc__)
|
||||
tos = raw_property('!B', 1, docs='The Type Of Service field (six-bit DiffServ field and a two-bit ECN field).')
|
||||
ident = raw_property('!H', 4, docs='The Identification field.')
|
||||
|
||||
reserved = flag_property('reserved', 6, 0b10000000)
|
||||
evil = flag_property('evil', 6, 0b10000000, docs='Just an april\'s fool joke for the RESERVED flag.')
|
||||
df = flag_property('df', 6, 0b01000000)
|
||||
mf = flag_property('mf', 6, 0b00100000)
|
||||
|
||||
ttl = raw_property('!B', 8, docs='The Time To Live field.')
|
||||
protocol = raw_property('!B', 9, docs='The Protocol field.')
|
||||
cksum = raw_property('!H', 10, docs='The IP header Checksum field.')
|
||||
|
||||
@property
|
||||
def flags(self):
|
||||
"""
|
||||
The flags field: RESERVED (the evil bit), DF (don't fragment), MF (more fragments).
|
||||
"""
|
||||
return i(self.raw[6]) >> 5
|
||||
|
||||
@flags.setter
|
||||
def flags(self, val):
|
||||
struct.pack_into('!B', self.raw, 6, (val << 5) | (self.frag_offset & 0xFF00))
|
||||
|
||||
@property
|
||||
def frag_offset(self):
|
||||
"""
|
||||
The Fragment Offset field in blocks of 8 bytes.
|
||||
"""
|
||||
return struct.unpack_from("!H", self.raw, 6)[0] & 0x1FFF
|
||||
|
||||
@frag_offset.setter
|
||||
def frag_offset(self, val):
|
||||
self.raw[6:8] = struct.pack("!H", (self.flags << 13) | (val & 0x1FFF))
|
||||
|
||||
@property
|
||||
def dscp(self):
|
||||
"""
|
||||
The Differentiated Services Code Point field (originally defined as Type of Service) also known as DiffServ.
|
||||
"""
|
||||
return (i(self.raw[1]) >> 2) & 0x3F
|
||||
|
||||
@dscp.setter
|
||||
def dscp(self, val):
|
||||
struct.pack_into('!B', self.raw, 1, (val << 2) | self.ecn)
|
||||
|
||||
diff_serv = dscp
|
||||
|
||||
@property
|
||||
def ecn(self):
|
||||
"""
|
||||
The Explicit Congestion Notification field.
|
||||
"""
|
||||
return i(self.raw[1]) & 0x03
|
||||
|
||||
@ecn.setter
|
||||
def ecn(self, val):
|
||||
struct.pack_into('!B', self.raw, 1, (self.dscp << 2) | (val & 0x03))
|
||||
|
||||
|
||||
class IPv6Header(IPHeader):
|
||||
_src_addr = slice(8, 24)
|
||||
_dst_addr = slice(24, 40)
|
||||
_af = socket.AF_INET6
|
||||
header_len = 40
|
||||
|
||||
payload_len = raw_property('!H', 4, docs='The Payload Length field.')
|
||||
next_hdr = raw_property('!B', 6, docs='The Next Header field. Replaces the Protocol field in IPv4.')
|
||||
hop_limit = raw_property('!B', 7, docs='The Hop Limit field. Replaces the TTL field in IPv4.')
|
||||
|
||||
@property
|
||||
def packet_len(self):
|
||||
return self.payload_len + self.header_len
|
||||
|
||||
@packet_len.setter
|
||||
def packet_len(self, val):
|
||||
self.payload_len = val - self.header_len
|
||||
|
||||
@property
|
||||
def traffic_class(self):
|
||||
"""
|
||||
The Traffic Class field (six-bit DiffServ field and a two-bit ECN field).
|
||||
"""
|
||||
return (struct.unpack_from('!H', self.raw, 0)[0] >> 4) & 0x00FF
|
||||
|
||||
@traffic_class.setter
|
||||
def traffic_class(self, val):
|
||||
struct.pack_into('!H', self.raw, 0, 0x6000 | (val << 4) | (self.flow_label & 0x000F0000))
|
||||
|
||||
@property
|
||||
def flow_label(self):
|
||||
"""
|
||||
The Flow Label field.
|
||||
"""
|
||||
return struct.unpack_from('!I', self.raw, 0)[0] & 0x000FFFFF
|
||||
|
||||
@flow_label.setter
|
||||
def flow_label(self, val):
|
||||
struct.pack_into('!I', self.raw, 0, 0x60000000 | (self.traffic_class << 20) | (val & 0x000FFFFF))
|
||||
|
||||
@property
|
||||
def diff_serv(self):
|
||||
"""
|
||||
The DiffServ field.
|
||||
"""
|
||||
return (self.traffic_class & 0xFC) >> 2
|
||||
|
||||
@diff_serv.setter
|
||||
def diff_serv(self, val):
|
||||
self.traffic_class = self.ecn | (val << 2)
|
||||
|
||||
@property
|
||||
def ecn(self):
|
||||
"""
|
||||
The Explicit Congestion Notification field.
|
||||
"""
|
||||
return self.traffic_class & 0x03
|
||||
|
||||
@ecn.setter
|
||||
def ecn(self, val):
|
||||
self.traffic_class = (self.diff_serv << 2) | val
|
||||
|
||||
if not PY2 and not PY34:
|
||||
packet_len.__doc__ = IPHeader.packet_len.__doc__
|
||||
82
venv/Lib/site-packages/pydivert/packet/tcp.py
Normal file
82
venv/Lib/site-packages/pydivert/packet/tcp.py
Normal file
@@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
|
||||
from pydivert.packet.header import Header, PayloadMixin, PortMixin
|
||||
from pydivert.util import indexbyte as i, flag_property, raw_property
|
||||
|
||||
|
||||
class TCPHeader(Header, PayloadMixin, PortMixin):
|
||||
ns = flag_property("ns", 12, 0b00000001)
|
||||
|
||||
cwr = flag_property("cwr", 13, 0b10000000)
|
||||
ece = flag_property("ece", 13, 0b01000000)
|
||||
|
||||
urg = flag_property("syn", 13, 0b00100000)
|
||||
ack = flag_property("ack", 13, 0b00010000)
|
||||
psh = flag_property("psh", 13, 0b00001000)
|
||||
rst = flag_property("rst", 13, 0b00000100)
|
||||
syn = flag_property("syn", 13, 0b00000010)
|
||||
fin = flag_property("fin", 13, 0b00000001)
|
||||
|
||||
@property
|
||||
def header_len(self):
|
||||
"""
|
||||
The TCP header length.
|
||||
"""
|
||||
return self.data_offset * 4
|
||||
|
||||
seq_num = raw_property('!I', 4, docs='The sequence number field.')
|
||||
ack_num = raw_property('!I', 8, docs='The acknowledgement number field.')
|
||||
|
||||
window_size = raw_property('!H', 14, docs='The size of the receive window in bytes.')
|
||||
cksum = raw_property('!H', 16, docs='The TCP header checksum field.')
|
||||
urg_ptr = raw_property('!H', 18, docs='The Urgent Pointer field.')
|
||||
|
||||
@property
|
||||
def data_offset(self):
|
||||
"""
|
||||
The size of TCP header in 32bit words.
|
||||
"""
|
||||
return i(self.raw[12]) >> 4
|
||||
|
||||
@data_offset.setter
|
||||
def data_offset(self, val):
|
||||
if val < 5 or val > 15:
|
||||
raise ValueError("TCP data offset must be greater or equal than 5 and less than 15.")
|
||||
struct.pack_into('!B', self.raw, 12, (val << 4) | (self.reserved << 1) | self.ns)
|
||||
|
||||
@property
|
||||
def reserved(self):
|
||||
"""
|
||||
The reserved field.
|
||||
"""
|
||||
return (i(self.raw[12]) >> 1) & 0x07
|
||||
|
||||
@reserved.setter
|
||||
def reserved(self, val):
|
||||
struct.pack_into('!B', self.raw, 12, (self.data_offset << 4) | (val << 1) | self.ns)
|
||||
|
||||
@property
|
||||
def control_bits(self):
|
||||
"""
|
||||
The Control Bits field.
|
||||
"""
|
||||
return struct.unpack_from('!H', self.raw, 12)[0] & 0x01FF
|
||||
|
||||
@control_bits.setter
|
||||
def control_bits(self, val):
|
||||
struct.pack_into('!H', self.raw, 12, (self.data_offset << 12) | (self.reserved << 9) | (val & 0x01FF))
|
||||
45
venv/Lib/site-packages/pydivert/packet/udp.py
Normal file
45
venv/Lib/site-packages/pydivert/packet/udp.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
|
||||
from pydivert.packet.header import Header, PayloadMixin, PortMixin
|
||||
from pydivert.util import PY2, PY34, raw_property
|
||||
|
||||
|
||||
class UDPHeader(Header, PayloadMixin, PortMixin):
|
||||
header_len = 8
|
||||
|
||||
@property
|
||||
def payload(self):
|
||||
return PayloadMixin.payload.fget(self)
|
||||
|
||||
@payload.setter
|
||||
def payload(self, val):
|
||||
PayloadMixin.payload.fset(self, val)
|
||||
self.payload_len = len(val)
|
||||
|
||||
if not PY2 and not PY34:
|
||||
payload.__doc__ = PayloadMixin.payload.__doc__
|
||||
|
||||
@property
|
||||
def payload_len(self):
|
||||
return struct.unpack_from("!H", self.raw, 4)[0] - 8
|
||||
|
||||
@payload_len.setter
|
||||
def payload_len(self, val):
|
||||
self.raw[4:6] = struct.pack("!H", val + 8)
|
||||
|
||||
cksum = raw_property('!H', 6, docs='The UDP header checksum field.')
|
||||
15
venv/Lib/site-packages/pydivert/tests/__init__.py
Normal file
15
venv/Lib/site-packages/pydivert/tests/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
98
venv/Lib/site-packages/pydivert/tests/fixtures.py
Normal file
98
venv/Lib/site-packages/pydivert/tests/fixtures.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import itertools
|
||||
import socket
|
||||
import threading
|
||||
|
||||
import pydivert
|
||||
import pytest
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def windivert_handle():
|
||||
with pydivert.WinDivert("false") as w:
|
||||
yield w
|
||||
|
||||
|
||||
@pytest.fixture(params=list(itertools.product(
|
||||
("ipv4", "ipv6"),
|
||||
("tcp", "udp"),
|
||||
)), ids=lambda x: ",".join(x))
|
||||
def scenario(request):
|
||||
ip_version, proto = request.param
|
||||
|
||||
if ip_version == "ipv4":
|
||||
atype = socket.AF_INET
|
||||
host = "127.0.0.1"
|
||||
else:
|
||||
atype = socket.AF_INET6
|
||||
host = "::1"
|
||||
if proto == "tcp":
|
||||
stype = socket.SOCK_STREAM
|
||||
else:
|
||||
stype = socket.SOCK_DGRAM
|
||||
|
||||
server = socket.socket(atype, stype)
|
||||
server.bind((host, 0))
|
||||
client = socket.socket(atype, stype)
|
||||
client.bind((host, 0))
|
||||
|
||||
reply = Queue()
|
||||
|
||||
if proto == "tcp":
|
||||
def server_echo():
|
||||
server.listen(1)
|
||||
conn, addr = server.accept()
|
||||
conn.sendall(conn.recv(4096).upper())
|
||||
conn.close()
|
||||
|
||||
def send(addr, data):
|
||||
client.connect(addr)
|
||||
client.sendall(data)
|
||||
reply.put(client.recv(4096))
|
||||
else:
|
||||
def server_echo():
|
||||
data, addr = server.recvfrom(4096)
|
||||
server.sendto(data.upper(), addr)
|
||||
|
||||
def send(addr, data):
|
||||
client.sendto(data, addr)
|
||||
data, recv_addr = client.recvfrom(4096)
|
||||
assert addr[:2] == recv_addr[:2] # only accept responses from the same host
|
||||
reply.put(data)
|
||||
|
||||
server_thread = threading.Thread(target=server_echo)
|
||||
server_thread.start()
|
||||
|
||||
filt = "{proto}.SrcPort == {c_port} or {proto}.SrcPort == {s_port}".format(
|
||||
proto=proto,
|
||||
c_port=client.getsockname()[1],
|
||||
s_port=server.getsockname()[1]
|
||||
)
|
||||
|
||||
def send_thread(*args, **kwargs):
|
||||
threading.Thread(target=send, args=args, kwargs=kwargs).start()
|
||||
return reply
|
||||
|
||||
with pydivert.WinDivert(filt) as w:
|
||||
yield client.getsockname(), server.getsockname(), w, send_thread
|
||||
client.close()
|
||||
server.close()
|
||||
517
venv/Lib/site-packages/pydivert/tests/test_packet.py
Normal file
517
venv/Lib/site-packages/pydivert/tests/test_packet.py
Normal file
@@ -0,0 +1,517 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import socket
|
||||
|
||||
import pydivert
|
||||
import pytest
|
||||
from hypothesis import given, example
|
||||
from hypothesis.strategies import binary
|
||||
from pydivert import util
|
||||
from pydivert.consts import Protocol, Direction
|
||||
|
||||
|
||||
def p(raw):
|
||||
return pydivert.Packet(raw, (0, 0), Direction.OUTBOUND)
|
||||
|
||||
|
||||
ipv4_hdr = util.fromhex("45200028fa8d40002906368b345ad4f0c0a856a4")
|
||||
ipv6_hdr = util.fromhex("600d684a00280640fc000002000000020000000000000001fc000002000000010000000000000001")
|
||||
|
||||
|
||||
@given(raw=binary(0, 500, 1600))
|
||||
@example(raw=b'`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
@example(raw=b'E\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
def test_fuzz(raw):
|
||||
assert repr(p(raw))
|
||||
assert repr(p(ipv4_hdr + raw))
|
||||
assert repr(p(ipv6_hdr + raw))
|
||||
|
||||
|
||||
def test_ipv6_tcp():
|
||||
raw = util.fromhex("600d684a007d0640fc000002000000020000000000000001fc000002000000010000000000000001a9a01f90021b638"
|
||||
"dba311e8e801800cfc92e00000101080a801da522801da522474554202f68656c6c6f2e74787420485454502f312e31"
|
||||
"0d0a557365722d4167656e743a206375726c2f372e33382e300d0a486f73743a205b666330303a323a303a313a3a315"
|
||||
"d3a383038300d0a4163636570743a202a2f2a0d0a0d0a")
|
||||
x = p(raw)
|
||||
assert x.address_family == socket.AF_INET6
|
||||
assert x.protocol[0] == Protocol.TCP
|
||||
assert x.src_addr == "fc00:2:0:2::1"
|
||||
assert x.dst_addr == "fc00:2:0:1::1"
|
||||
assert x.src_port == 43424
|
||||
assert x.dst_port == 8080
|
||||
assert not x.ipv4
|
||||
assert x.ipv6
|
||||
assert x.tcp
|
||||
assert not x.udp
|
||||
assert not x.icmp
|
||||
assert x.payload == (
|
||||
b"GET /hello.txt HTTP/1.1\r\n"
|
||||
b"User-Agent: curl/7.38.0\r\n"
|
||||
b"Host: [fc00:2:0:1::1]:8080\r\n"
|
||||
b"Accept: */*\r\n\r\n"
|
||||
)
|
||||
assert x.ip.packet_len == 165
|
||||
assert repr(x)
|
||||
|
||||
|
||||
def test_ipv4_udp():
|
||||
raw = util.fromhex("4500004281bf000040112191c0a82b09c0a82b01c9dd0035002ef268528e01000001000000000000013801380138013"
|
||||
"807696e2d61646472046172706100000c0001")
|
||||
x = p(raw)
|
||||
assert x.address_family == socket.AF_INET
|
||||
assert x.protocol[0] == Protocol.UDP
|
||||
assert x.src_addr == "192.168.43.9"
|
||||
assert x.dst_addr == "192.168.43.1"
|
||||
assert x.src_port == 51677
|
||||
assert x.dst_port == 53
|
||||
assert x.ipv4
|
||||
assert not x.ipv6
|
||||
assert not x.tcp
|
||||
assert x.udp
|
||||
assert not x.icmp
|
||||
assert x.payload == util.fromhex("528e01000001000000000000013801380138013807696e2d61646472046172706100000c0001")
|
||||
assert x.udp.payload_len == 38
|
||||
assert repr(x)
|
||||
|
||||
|
||||
def test_icmp_ping():
|
||||
raw = util.fromhex("4500005426ef0000400157f9c0a82b09080808080800bbb3d73b000051a7d67d000451e408090a0b0c0d0e0f1011121"
|
||||
"31415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637")
|
||||
x = p(raw)
|
||||
assert x.address_family == socket.AF_INET
|
||||
assert x.protocol[0] == Protocol.ICMP
|
||||
assert x.src_addr == "192.168.43.9"
|
||||
assert x.dst_addr == "8.8.8.8"
|
||||
assert x.src_port is None
|
||||
assert x.dst_port is None
|
||||
assert x.icmp.type == 8
|
||||
assert x.icmp.code == 0
|
||||
assert x.ipv4
|
||||
assert not x.ipv6
|
||||
assert not x.tcp
|
||||
assert not x.udp
|
||||
assert x.icmpv4
|
||||
assert not x.icmpv6
|
||||
assert x.payload == util.fromhex("d73b000051a7d67d000451e408090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232"
|
||||
"425262728292a2b2c2d2e2f3031323334353637")
|
||||
assert repr(x)
|
||||
|
||||
|
||||
def test_icmpv6_unreachable():
|
||||
raw = util.fromhex("6000000000443a3d3ffe05010410000002c0dffffe47033e3ffe050700000001020086fffe0580da010413520000000"
|
||||
"060000000001411013ffe050700000001020086fffe0580da3ffe05010410000002c0dffffe47033ea07582a40014cf"
|
||||
"470a040000f9c8e7369d250b00")
|
||||
x = p(raw)
|
||||
assert x.address_family == socket.AF_INET6
|
||||
assert x.protocol[0] == Protocol.ICMPV6
|
||||
assert x.src_addr == "3ffe:501:410:0:2c0:dfff:fe47:33e"
|
||||
assert x.dst_addr == "3ffe:507:0:1:200:86ff:fe05:80da"
|
||||
assert x.src_port is None
|
||||
assert x.dst_port is None
|
||||
assert x.icmp.type == 1
|
||||
assert x.icmp.code == 4
|
||||
assert not x.ipv4
|
||||
assert x.ipv6
|
||||
assert not x.tcp
|
||||
assert not x.udp
|
||||
assert not x.icmpv4
|
||||
assert x.icmpv6
|
||||
assert x.payload == util.fromhex("0000000060000000001411013ffe050700000001020086fffe0580da3ffe05010410000002c0dffff"
|
||||
"e47033ea07582a40014cf470a040000f9c8e7369d250b00")
|
||||
assert repr(x)
|
||||
|
||||
|
||||
def test_ipv4_tcp_modify():
|
||||
raw = util.fromhex("45000051476040008006f005c0a856a936f274fdd84201bb0876cfd0c19f9320501800ff8dba0000170303002400000"
|
||||
"00000000c2f53831a37ed3c3a632f47440594cab95283b558bf82cb7784344c3314")
|
||||
x = p(raw)
|
||||
assert x.protocol[0] == Protocol.TCP
|
||||
|
||||
# src_addr
|
||||
x.src_addr = "1.2.3.4"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = "::1"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = 42
|
||||
assert x.src_addr == "1.2.3.4"
|
||||
|
||||
# dst_addr
|
||||
x.dst_addr = "4.3.2.1"
|
||||
with pytest.raises(Exception):
|
||||
x.dst_addr = "::1"
|
||||
assert x.dst_addr == "4.3.2.1"
|
||||
|
||||
# src_port
|
||||
x.src_port = 42
|
||||
with pytest.raises(Exception):
|
||||
x.src_port = "bogus"
|
||||
assert x.src_port == 42
|
||||
|
||||
# dst_port
|
||||
x.dst_port = 43
|
||||
with pytest.raises(Exception):
|
||||
x.dst_port = "bogus"
|
||||
assert x.dst_port == 43
|
||||
|
||||
# tcp_ack (others follow trivially)
|
||||
x.tcp.ack = False
|
||||
assert x.tcp.ack is False
|
||||
x.tcp.ack = True
|
||||
assert x.tcp.ack is True
|
||||
|
||||
# payload
|
||||
x.payload = b"test"
|
||||
with pytest.raises(Exception):
|
||||
x.payload = 42
|
||||
assert x.payload == b"test"
|
||||
|
||||
# checksum
|
||||
a = x.raw.tobytes()
|
||||
assert x.recalculate_checksums(
|
||||
pydivert.CalcChecksumsOption.NO_IP_CHECKSUM |
|
||||
pydivert.CalcChecksumsOption.NO_TCP_CHECKSUM
|
||||
) == 0
|
||||
assert x.raw.tobytes() == a
|
||||
|
||||
assert x.recalculate_checksums() == 2
|
||||
assert x.raw.tobytes() != a
|
||||
|
||||
# test same length raw replace.
|
||||
x.tcp.raw = x.tcp.raw.tobytes().replace(b"test", b"abcd")
|
||||
|
||||
# catch typo in headers
|
||||
with pytest.raises(AttributeError):
|
||||
x.tcp.typo = 42
|
||||
|
||||
|
||||
def test_ipv6_udp_modify():
|
||||
raw = util.fromhex("60000000002711403ffe050700000001020086fffe0580da3ffe0501481900000000000000000042095d0035002746b"
|
||||
"700060100000100000000000003777777057961686f6f03636f6d00000f0001")
|
||||
x = p(raw)
|
||||
assert x.protocol[0] == Protocol.UDP
|
||||
|
||||
# src_addr
|
||||
x.src_addr = "::1"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = "127.0.0.1"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = 42
|
||||
assert x.src_addr == "::1"
|
||||
|
||||
# dst_addr
|
||||
x.dst_addr = "::2"
|
||||
with pytest.raises(Exception):
|
||||
x.dst_addr = "bogus"
|
||||
assert x.dst_addr == "::2"
|
||||
|
||||
# src_port
|
||||
x.src_port = 42
|
||||
with pytest.raises(Exception):
|
||||
x.src_port = "bogus"
|
||||
assert x.src_port == 42
|
||||
|
||||
# dst_port
|
||||
x.dst_port = 43
|
||||
with pytest.raises(Exception):
|
||||
x.dst_port = "bogus"
|
||||
assert x.dst_port == 43
|
||||
|
||||
# payload
|
||||
x.payload = b"test"
|
||||
with pytest.raises(Exception):
|
||||
x.payload = 42
|
||||
assert x.payload == b"test"
|
||||
|
||||
# checksum
|
||||
a = x.raw.tobytes()
|
||||
assert x.recalculate_checksums(
|
||||
pydivert.CalcChecksumsOption.NO_IP_CHECKSUM |
|
||||
pydivert.CalcChecksumsOption.NO_UDP_CHECKSUM
|
||||
) == 0
|
||||
assert x.raw.tobytes() == a
|
||||
|
||||
assert x.recalculate_checksums() == 1
|
||||
assert x.raw.tobytes() != a
|
||||
|
||||
|
||||
def test_icmp_modify():
|
||||
raw = util.fromhex("4500005426ef0000400157f9c0a82b09080808080800bbb3d73b000051a7d67d000451e408090a0b0c0d0e0f1011121"
|
||||
"31415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637")
|
||||
x = p(raw)
|
||||
assert x.protocol[0] == Protocol.ICMP
|
||||
|
||||
# src_addr
|
||||
x.src_addr = "1.2.3.4"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = "::1"
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = 42
|
||||
assert x.src_addr == "1.2.3.4"
|
||||
|
||||
# dst_addr
|
||||
x.dst_addr = "4.3.2.1"
|
||||
with pytest.raises(Exception):
|
||||
x.dst_addr = "::1"
|
||||
assert x.dst_addr == "4.3.2.1"
|
||||
|
||||
# payload
|
||||
x.payload = b"test"
|
||||
with pytest.raises(Exception):
|
||||
x.payload = 42
|
||||
assert x.payload == b"test"
|
||||
|
||||
# icmp
|
||||
x.icmp.type = 42
|
||||
with pytest.raises(Exception):
|
||||
x.icmp.type = "bogus"
|
||||
assert x.icmp.type == 42
|
||||
x.icmp.code = 42
|
||||
with pytest.raises(Exception):
|
||||
x.icmp.code = "bogus"
|
||||
assert x.icmp.code == 42
|
||||
|
||||
# checksum
|
||||
a = x.raw.tobytes()
|
||||
assert x.recalculate_checksums(
|
||||
pydivert.CalcChecksumsOption.NO_IP_CHECKSUM |
|
||||
pydivert.CalcChecksumsOption.NO_ICMP_CHECKSUM
|
||||
) == 0
|
||||
assert x.raw.tobytes() == a
|
||||
|
||||
assert x.recalculate_checksums() == 2
|
||||
assert x.raw.tobytes() != a
|
||||
|
||||
|
||||
def test_meta():
|
||||
p = pydivert.Packet(b"", (1, 1), Direction.OUTBOUND)
|
||||
assert p.is_outbound
|
||||
assert not p.is_inbound
|
||||
assert p.is_loopback
|
||||
|
||||
p2 = pydivert.Packet(b"", (2, 2), Direction.INBOUND)
|
||||
assert not p2.is_outbound
|
||||
assert p2.is_inbound
|
||||
assert not p2.is_loopback
|
||||
|
||||
|
||||
def test_bogus():
|
||||
x = p(b"")
|
||||
with pytest.raises(Exception):
|
||||
x.src_addr = "127.0.0.1"
|
||||
with pytest.raises(Exception):
|
||||
x.dst_addr = "127.0.0.1"
|
||||
with pytest.raises(Exception):
|
||||
x.src_port = 80
|
||||
with pytest.raises(Exception):
|
||||
x.dst_port = 80
|
||||
with pytest.raises(Exception):
|
||||
x.payload = b""
|
||||
with pytest.raises(Exception):
|
||||
x.icmp.code = 42
|
||||
with pytest.raises(Exception):
|
||||
x.tcp.ack = True
|
||||
with pytest.raises(Exception):
|
||||
x.tcp.unknown_attr = True
|
||||
assert x.recalculate_checksums() == 0
|
||||
|
||||
|
||||
def test_ipv6_extension_headers():
|
||||
# AH Header
|
||||
raw = util.fromhex("6e000000003c3301fe800000000000000000000000000001ff020000000000000000000000000005590400000000010"
|
||||
"00000001321d3a95c5ffd4d184622b9f8030100240101010100000001fb8600000000000501000013000a0028000000"
|
||||
"0000000000")
|
||||
assert p(raw).protocol[0] == 89
|
||||
|
||||
# Fragmented...
|
||||
raw = util.fromhex("6000000005b02c80fe8000000000000002105afffeaa20a2fe800000000000000250dafffed8c1533a0000010000000"
|
||||
"580009e9d0000000d6162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70"
|
||||
"717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717"
|
||||
"273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273"
|
||||
"747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747"
|
||||
"576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576"
|
||||
"776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776"
|
||||
"162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162"
|
||||
"636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636"
|
||||
"465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465"
|
||||
"666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666"
|
||||
"768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768"
|
||||
"696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696"
|
||||
"a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b"
|
||||
"6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6"
|
||||
"d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e"
|
||||
"6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7"
|
||||
"0717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071"
|
||||
"7273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727"
|
||||
"3747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374"
|
||||
"7576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757"
|
||||
"6776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677"
|
||||
"6162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616"
|
||||
"2636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616263"
|
||||
"6465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616263646"
|
||||
"5666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616263646566"
|
||||
"6768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616263646566676"
|
||||
"8696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f7071727374757677616263646566676869"
|
||||
"6a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6"
|
||||
"b6c6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c"
|
||||
"6d6e6f70717273747576776162636465666768696a6b6c6d6e6f70717273747576776162636465666768696a6b6c6d6"
|
||||
"e6f70717273747576776162636465666768696a6b6c6d6e")
|
||||
assert p(raw).protocol[0] == Protocol.ICMPV6
|
||||
|
||||
# HOPOPTS
|
||||
raw = util.fromhex("600000000020000100000000000000000000000000000000ff0200000000000000000000000000013a0005020000000"
|
||||
"082007ac103e8000000000000000000000000000000000000")
|
||||
assert p(raw).protocol[0] == Protocol.ICMPV6
|
||||
|
||||
|
||||
def test_ipv4_fields():
|
||||
raw = util.fromhex("4500005426ef0000400157f9c0a82b09080808080800bbb3d73b000051a7d67d000451e408090a0b0c0d0e0f1011121"
|
||||
"31415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637")
|
||||
ip = p(raw).ipv4
|
||||
|
||||
assert not ip.df
|
||||
ip.df = True
|
||||
assert ip.df
|
||||
assert ip.flags == 2
|
||||
assert ip.frag_offset == 0
|
||||
ip.flags = 3
|
||||
assert ip.flags == 3
|
||||
assert ip.mf
|
||||
assert ip.df
|
||||
assert ip.frag_offset == 0
|
||||
ip.ecn = 3
|
||||
assert ip.ecn == 3
|
||||
ip.dscp = 18
|
||||
assert ip.dscp == 18
|
||||
assert ip.diff_serv == ip.dscp
|
||||
assert ip.ecn == 3
|
||||
assert ip.tos == 75
|
||||
ip.tos = 1
|
||||
assert ip.tos == 1
|
||||
assert ip.ecn == 1
|
||||
assert ip.dscp == 0
|
||||
ip.flags = 1
|
||||
assert ip.mf
|
||||
ip.mf = False
|
||||
assert not ip.mf
|
||||
assert ip.flags == 0
|
||||
ip.frag_offset = 65
|
||||
assert ip.frag_offset == 65
|
||||
assert ip.flags == 0
|
||||
ip.flags = 7
|
||||
assert ip.frag_offset == 65
|
||||
assert ip.evil
|
||||
assert ip.reserved == ip.evil
|
||||
ip.evil = False
|
||||
assert not ip.evil
|
||||
assert ip.reserved == ip.evil
|
||||
assert ip.flags == 3
|
||||
ip.ident = 257
|
||||
assert ip.ident == 257
|
||||
assert ip.hdr_len == 5
|
||||
ip.cksum = 514
|
||||
assert ip.cksum == 514
|
||||
ip.hdr_len = 6
|
||||
assert ip.hdr_len == 6
|
||||
assert ip.header_len == 6 * 4
|
||||
ip.ttl = 4
|
||||
assert ip.ttl == 4
|
||||
ip.protocol = Protocol.FRAGMENT
|
||||
assert ip.protocol == Protocol.FRAGMENT
|
||||
with pytest.raises(ValueError):
|
||||
ip.hdr_len = 4
|
||||
|
||||
|
||||
def test_ipv6_fields():
|
||||
raw = util.fromhex("6e000000003c3301fe800000000000000000000000000001ff020000000000000000000000000005590400000000010"
|
||||
"00000001321d3a95c5ffd4d184622b9f8030100240101010100000001fb8600000000000501000013000a0028000000"
|
||||
"0000000000")
|
||||
ip = p(raw).ipv6
|
||||
|
||||
ip.traffic_class = 3
|
||||
assert ip.traffic_class == 3
|
||||
assert ip.ecn == 3
|
||||
ip.ecn = 0
|
||||
assert ip.ecn == 0
|
||||
assert ip.traffic_class == 0
|
||||
ip.diff_serv = 8
|
||||
assert ip.diff_serv == 8
|
||||
assert ip.traffic_class == 32
|
||||
ip.flow_label = 17
|
||||
assert ip.flow_label == 17
|
||||
assert ip.traffic_class == 32
|
||||
|
||||
|
||||
def test_icmp_fields():
|
||||
raw = util.fromhex("4500005426ef0000400157f9c0a82b09080808080800bbb3d73b000051a7d67d000451e408090a0b0c0d0e0f1011121"
|
||||
"31415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637")
|
||||
icmp = p(raw).icmp
|
||||
|
||||
icmp.cksum = 11
|
||||
assert icmp.cksum == 11
|
||||
|
||||
|
||||
def test_tcp_fields():
|
||||
raw = util.fromhex("45000051476040008006f005c0a856a936f274fdd84201bb0876cfd0c19f9320501800ff8dba0000170303002400000"
|
||||
"00000000c2f53831a37ed3c3a632f47440594cab95283b558bf82cb7784344c3314")
|
||||
tcp = p(raw).tcp
|
||||
|
||||
assert tcp.reserved == 0
|
||||
tcp.reserved = 7
|
||||
assert tcp.reserved == 7
|
||||
assert not tcp.ns
|
||||
tcp.ns = True
|
||||
assert tcp.ns
|
||||
assert tcp.reserved == 0b111
|
||||
assert tcp.header_len == tcp.data_offset * 4
|
||||
tcp.data_offset = 5
|
||||
assert tcp.data_offset == 5
|
||||
with pytest.raises(ValueError):
|
||||
tcp.data_offset = 4
|
||||
with pytest.raises(ValueError):
|
||||
tcp.data_offset = 16
|
||||
|
||||
tcp.cwr = True
|
||||
assert tcp.cwr
|
||||
tcp.ece = True
|
||||
assert tcp.ece
|
||||
tcp.syn = True
|
||||
tcp.control_bits = 0x01F0
|
||||
assert not tcp.fin
|
||||
assert not tcp.syn
|
||||
assert tcp.control_bits == 0x01F0
|
||||
assert tcp.ece
|
||||
assert tcp.ns
|
||||
tcp.ns = False
|
||||
assert tcp.control_bits == 0x00F0
|
||||
|
||||
|
||||
def test_udp_fields():
|
||||
raw = util.fromhex("4500004281bf000040112191c0a82b09c0a82b01c9dd0035002ef268528e01000001000000000000013801380138013"
|
||||
"807696e2d61646472046172706100000c0001")
|
||||
udp = p(raw).udp
|
||||
|
||||
udp.cksum = 0xAAAA
|
||||
assert udp.cksum == 0xAAAA
|
||||
|
||||
|
||||
def test_filter_match():
|
||||
raw = util.fromhex("4500004281bf000040112191c0a82b09c0a82b01c9dd0035002ef268528e01000001000000000000013801380138013"
|
||||
"807696e2d61646472046172706100000c0001")
|
||||
p = pydivert.Packet(raw, (1, 1), Direction.OUTBOUND)
|
||||
|
||||
assert p.matches("true")
|
||||
assert p.matches("udp and outbound")
|
||||
assert not p.matches("tcp")
|
||||
204
venv/Lib/site-packages/pydivert/tests/test_windivert.py
Normal file
204
venv/Lib/site-packages/pydivert/tests/test_windivert.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from pydivert.consts import Param
|
||||
from pydivert.windivert import WinDivert
|
||||
|
||||
from .fixtures import scenario, windivert_handle as w
|
||||
|
||||
assert scenario, w # keep fixtures
|
||||
|
||||
|
||||
def test_open():
|
||||
w = WinDivert("false")
|
||||
w.open()
|
||||
assert w.is_open
|
||||
w.close()
|
||||
assert not w.is_open
|
||||
|
||||
with w:
|
||||
# open a second one.
|
||||
with WinDivert("false") as w2:
|
||||
assert w2.is_open
|
||||
|
||||
assert w.is_open
|
||||
assert "open" in repr(w)
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
w.open()
|
||||
|
||||
assert not w.is_open
|
||||
assert "closed" in repr(w)
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
w.recv()
|
||||
with pytest.raises(RuntimeError):
|
||||
w.close()
|
||||
|
||||
|
||||
def test_register():
|
||||
if WinDivert.is_registered():
|
||||
WinDivert.unregister()
|
||||
while WinDivert.is_registered():
|
||||
time.sleep(0.01) # pragma: no cover
|
||||
assert not WinDivert.is_registered()
|
||||
WinDivert.register()
|
||||
assert WinDivert.is_registered()
|
||||
|
||||
|
||||
def test_unregister():
|
||||
w = WinDivert("false")
|
||||
w.open()
|
||||
WinDivert.unregister()
|
||||
time.sleep(0.1)
|
||||
assert WinDivert.is_registered()
|
||||
w.close()
|
||||
# may not trigger immediately.
|
||||
while WinDivert.is_registered():
|
||||
time.sleep(0.01) # pragma: no cover
|
||||
|
||||
|
||||
class TestParams(object):
|
||||
def test_queue_time_range(self, w):
|
||||
"""
|
||||
Tests setting the minimum value for queue time.
|
||||
From docs: 128 < default 512 < 2048
|
||||
"""
|
||||
def_range = (128, 512, 2048)
|
||||
for value in def_range:
|
||||
w.set_param(Param.QUEUE_TIME, value)
|
||||
assert value == w.get_param(Param.QUEUE_TIME)
|
||||
|
||||
def test_queue_len_range(self, w):
|
||||
"""
|
||||
Tests setting the minimum value for queue length.
|
||||
From docs: 1< default 512 <8192
|
||||
"""
|
||||
for value in (1, 512, 8192):
|
||||
w.set_param(Param.QUEUE_LEN, value)
|
||||
assert value == w.get_param(Param.QUEUE_LEN)
|
||||
|
||||
def test_invalid_set(self, w):
|
||||
with pytest.raises(Exception):
|
||||
w.set_param(42, 43)
|
||||
|
||||
def test_invalid_get(self, w):
|
||||
with pytest.raises(Exception):
|
||||
w.get_param(42)
|
||||
|
||||
|
||||
def test_echo(scenario):
|
||||
client_addr, server_addr, w, send = scenario
|
||||
w = w # type: WinDivert
|
||||
reply = send(server_addr, b"echo")
|
||||
|
||||
for p in w:
|
||||
assert p.is_loopback
|
||||
assert p.is_outbound
|
||||
w.send(p)
|
||||
done = (
|
||||
p.udp and p.dst_port == client_addr[1]
|
||||
or
|
||||
p.tcp and p.tcp.fin
|
||||
)
|
||||
if done:
|
||||
break
|
||||
|
||||
assert reply.get() == b"ECHO"
|
||||
|
||||
|
||||
def test_divert(scenario):
|
||||
client_addr, server_addr, w, send = scenario
|
||||
w = w # type: WinDivert
|
||||
target = (server_addr[0], 80)
|
||||
reply = send(target, b"echo")
|
||||
for p in w:
|
||||
if p.src_port == client_addr[1]:
|
||||
p.dst_port = server_addr[1]
|
||||
if p.src_port == server_addr[1]:
|
||||
p.src_port = target[1]
|
||||
w.send(p)
|
||||
|
||||
done = (
|
||||
p.udp and p.dst_port == client_addr[1]
|
||||
or
|
||||
p.tcp and p.tcp.fin
|
||||
)
|
||||
if done:
|
||||
break
|
||||
|
||||
assert reply.get() == b"ECHO"
|
||||
|
||||
|
||||
def test_modify_payload(scenario):
|
||||
client_addr, server_addr, w, send = scenario
|
||||
w = w # type: WinDivert
|
||||
reply = send(server_addr, b"echo")
|
||||
|
||||
for p in w:
|
||||
p.payload = p.payload.replace(b"echo", b"test").replace(b"TEST", b"ECHO")
|
||||
w.send(p)
|
||||
|
||||
done = (
|
||||
p.udp and p.dst_port == client_addr[1]
|
||||
or
|
||||
p.tcp and p.tcp.fin
|
||||
)
|
||||
if done:
|
||||
break
|
||||
assert reply.get() == b"ECHO"
|
||||
|
||||
|
||||
def test_packet_cutoff(scenario):
|
||||
client_addr, server_addr, w, send = scenario
|
||||
w = w # type: WinDivert
|
||||
reply = send(server_addr, b"a" * 1000)
|
||||
|
||||
cutoff = None
|
||||
while True:
|
||||
p = w.recv(500)
|
||||
if p.ip.packet_len != len(p.raw):
|
||||
assert cutoff is None
|
||||
cutoff = p.ip.packet_len - len(p.raw)
|
||||
p.ip.packet_len = len(p.raw) # fix length
|
||||
if p.udp:
|
||||
p.udp.payload_len = len(p.payload)
|
||||
w.send(p)
|
||||
done = (
|
||||
p.udp and p.dst_port == client_addr[1]
|
||||
or
|
||||
p.tcp and p.tcp.fin
|
||||
)
|
||||
if done:
|
||||
break
|
||||
assert cutoff
|
||||
assert reply.get() == b"A" * (1000 - cutoff)
|
||||
|
||||
def test_check_filter():
|
||||
|
||||
res, pos, msg = WinDivert.check_filter('true')
|
||||
assert res
|
||||
assert pos == 0
|
||||
assert msg is not None
|
||||
res, pos, msg = WinDivert.check_filter('something wrong here')
|
||||
assert not res
|
||||
assert pos == 0
|
||||
assert msg is not None
|
||||
res, pos, msg = WinDivert.check_filter('outbound and something wrong here')
|
||||
assert not res
|
||||
assert pos == 13
|
||||
90
venv/Lib/site-packages/pydivert/util.py
Normal file
90
venv/Lib/site-packages/pydivert/util.py
Normal file
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
class cached_property(object):
|
||||
"""
|
||||
A property that is only computed once per instance and then replaces itself
|
||||
with an ordinary attribute. Deleting the attribute resets the property.
|
||||
Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
|
||||
"""
|
||||
|
||||
def __init__(self, func):
|
||||
self.__doc__ = getattr(func, '__doc__')
|
||||
self.func = func
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
if obj is None: # pragma: no cover
|
||||
return self
|
||||
value = obj.__dict__[self.func.__name__] = self.func(obj)
|
||||
return value
|
||||
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
# python 3's byte indexing: b"AAA"[1] == 65
|
||||
indexbyte = lambda x: chr(x) if isinstance(x, int) else ord(x)
|
||||
# python 3's bytes.fromhex()
|
||||
fromhex = lambda x: x.decode("hex")
|
||||
PY2 = True
|
||||
PY34 = False
|
||||
else:
|
||||
indexbyte = lambda x: x
|
||||
fromhex = lambda x: bytes.fromhex(x)
|
||||
PY2 = False
|
||||
if sys.version_info < (3, 5):
|
||||
# __doc__ attribute is only writable from 3.5.
|
||||
PY34 = True
|
||||
else:
|
||||
PY34 = False
|
||||
|
||||
|
||||
def flag_property(name, offset, bit, docs=None):
|
||||
@property
|
||||
def flag(self):
|
||||
return bool(indexbyte(self.raw[offset]) & bit)
|
||||
|
||||
@flag.setter
|
||||
def flag(self, val):
|
||||
flags = indexbyte(self.raw[offset])
|
||||
if val:
|
||||
flags |= bit
|
||||
else:
|
||||
flags &= ~bit
|
||||
self.raw[offset] = indexbyte(flags)
|
||||
|
||||
if not PY2 and not PY34:
|
||||
flag.__doc__ = """
|
||||
Indicates if the {} flag is set.
|
||||
""".format(name.upper()) if not docs else docs
|
||||
|
||||
return flag
|
||||
|
||||
|
||||
def raw_property(fmt, offset, docs=None):
|
||||
@property
|
||||
def rprop(self):
|
||||
return struct.unpack_from(fmt, self.raw, offset)[0]
|
||||
|
||||
@rprop.setter
|
||||
def rprop(self, val):
|
||||
struct.pack_into(fmt, self.raw, offset, val)
|
||||
|
||||
if docs and not PY2 and not PY34:
|
||||
rprop.__doc__ = docs
|
||||
|
||||
return rprop
|
||||
274
venv/Lib/site-packages/pydivert/windivert.py
Normal file
274
venv/Lib/site-packages/pydivert/windivert.py
Normal file
@@ -0,0 +1,274 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import subprocess
|
||||
import sys
|
||||
from ctypes import byref, c_uint64, c_uint, c_char, c_char_p
|
||||
|
||||
from pydivert import windivert_dll
|
||||
from pydivert.consts import Layer, Direction, Flag
|
||||
from pydivert.packet import Packet
|
||||
from pydivert.util import PY2
|
||||
|
||||
DEFAULT_PACKET_BUFFER_SIZE = 1500
|
||||
|
||||
|
||||
class WinDivert(object):
|
||||
"""
|
||||
A WinDivert handle that can be used to capture packets.
|
||||
The main methods are `.open()`, `.recv()`, `.send()` and `.close()`.
|
||||
|
||||
Use it like so::
|
||||
|
||||
with pydivert.WinDivert() as w:
|
||||
for packet in w:
|
||||
print(packet)
|
||||
w.send(packet)
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filter="true", layer=Layer.NETWORK, priority=0, flags=Flag.DEFAULT):
|
||||
self._handle = None
|
||||
self._filter = filter.encode()
|
||||
self._layer = layer
|
||||
self._priority = priority
|
||||
self._flags = flags
|
||||
|
||||
def __repr__(self):
|
||||
return '<WinDivert state="{}" filter="{}" layer="{}" priority="{}" flags="{}" />'.format(
|
||||
"open" if self._handle is not None else "closed",
|
||||
self._filter.decode(),
|
||||
self._layer,
|
||||
self._priority,
|
||||
self._flags
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
self.open()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.recv()
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
next = __next__
|
||||
|
||||
@staticmethod
|
||||
def register():
|
||||
"""
|
||||
An utility method to register the service the first time.
|
||||
It is usually not required to call this function, as WinDivert will register itself when opening a handle.
|
||||
"""
|
||||
with WinDivert("false"):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def is_registered():
|
||||
"""
|
||||
Check if the WinDivert service is currently installed on the system.
|
||||
"""
|
||||
return subprocess.call("sc query WinDivert1.3", stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE) == 0
|
||||
|
||||
@staticmethod
|
||||
def unregister():
|
||||
"""
|
||||
Unregisters the WinDivert service.
|
||||
This function only requests a service stop, which may not be processed immediately if there are still open
|
||||
handles.
|
||||
"""
|
||||
subprocess.check_call("sc stop WinDivert1.3", stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
@staticmethod
|
||||
def check_filter(filter, layer=Layer.NETWORK):
|
||||
"""
|
||||
Checks if the given packet filter string is valid with respect to the filter language.
|
||||
|
||||
The remapped function is WinDivertHelperCheckFilter::
|
||||
|
||||
BOOL WinDivertHelperCheckFilter(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__out_opt const char **errorStr,
|
||||
__out_opt UINT *errorPos
|
||||
);
|
||||
|
||||
See: https://reqrypt.org/windivert-doc.html#divert_helper_check_filter
|
||||
|
||||
:return: A tuple (res, pos, msg) with check result in 'res' human readable description of the error in 'msg' and the error's position in 'pos'.
|
||||
"""
|
||||
res, pos, msg = False, c_uint(), c_char_p()
|
||||
try:
|
||||
res = windivert_dll.WinDivertHelperCheckFilter(filter.encode(), layer, byref(msg), byref(pos))
|
||||
except OSError:
|
||||
pass
|
||||
return res, pos.value, msg.value.decode()
|
||||
|
||||
def open(self):
|
||||
"""
|
||||
Opens a WinDivert handle for the given filter.
|
||||
Unless otherwise specified by flags, any packet that matches the filter will be diverted to the handle.
|
||||
Diverted packets can be read by the application with receive().
|
||||
|
||||
The remapped function is WinDivertOpen::
|
||||
|
||||
HANDLE WinDivertOpen(
|
||||
__in const char *filter,
|
||||
__in WINDIVERT_LAYER layer,
|
||||
__in INT16 priority,
|
||||
__in UINT64 flags
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_open
|
||||
"""
|
||||
if self.is_open:
|
||||
raise RuntimeError("WinDivert handle is already open.")
|
||||
self._handle = windivert_dll.WinDivertOpen(self._filter, self._layer, self._priority,
|
||||
self._flags)
|
||||
|
||||
@property
|
||||
def is_open(self):
|
||||
"""
|
||||
Indicates if there is currently an open handle.
|
||||
"""
|
||||
return bool(self._handle)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Closes the handle opened by open().
|
||||
|
||||
The remapped function is WinDivertClose::
|
||||
|
||||
BOOL WinDivertClose(
|
||||
__in HANDLE handle
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_close
|
||||
"""
|
||||
if not self.is_open:
|
||||
raise RuntimeError("WinDivert handle is not open.")
|
||||
windivert_dll.WinDivertClose(self._handle)
|
||||
self._handle = None
|
||||
|
||||
def recv(self, bufsize=DEFAULT_PACKET_BUFFER_SIZE):
|
||||
"""
|
||||
Receives a diverted packet that matched the filter.
|
||||
|
||||
The remapped function is WinDivertRecv::
|
||||
|
||||
BOOL WinDivertRecv(
|
||||
__in HANDLE handle,
|
||||
__out PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__out_opt PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *recvLen
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_recv
|
||||
|
||||
:return: The return value is a `pydivert.Packet`.
|
||||
"""
|
||||
if self._handle is None:
|
||||
raise RuntimeError("WinDivert handle is not open")
|
||||
|
||||
packet = bytearray(bufsize)
|
||||
packet_ = (c_char * bufsize).from_buffer(packet)
|
||||
address = windivert_dll.WinDivertAddress()
|
||||
recv_len = c_uint(0)
|
||||
windivert_dll.WinDivertRecv(self._handle, packet_, bufsize, byref(address), byref(recv_len))
|
||||
return Packet(
|
||||
memoryview(packet)[:recv_len.value],
|
||||
(address.IfIdx, address.SubIfIdx),
|
||||
Direction(address.Direction)
|
||||
)
|
||||
|
||||
def send(self, packet, recalculate_checksum=True):
|
||||
"""
|
||||
Injects a packet into the network stack.
|
||||
Recalculates the checksum before sending unless recalculate_checksum=False is passed.
|
||||
|
||||
The injected packet may be one received from recv(), or a modified version, or a completely new packet.
|
||||
Injected packets can be captured and diverted again by other WinDivert handles with lower priorities.
|
||||
|
||||
The remapped function is WinDivertSend::
|
||||
|
||||
BOOL WinDivertSend(
|
||||
__in HANDLE handle,
|
||||
__in PVOID pPacket,
|
||||
__in UINT packetLen,
|
||||
__in PWINDIVERT_ADDRESS pAddr,
|
||||
__out_opt UINT *sendLen
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_send
|
||||
|
||||
:return: The return value is the number of bytes actually sent.
|
||||
"""
|
||||
if recalculate_checksum:
|
||||
packet.recalculate_checksums()
|
||||
|
||||
send_len = c_uint(0)
|
||||
if PY2:
|
||||
# .from_buffer(memoryview) does not work on PY2
|
||||
buff = bytearray(packet.raw)
|
||||
else:
|
||||
buff = packet.raw
|
||||
buff = (c_char * len(packet.raw)).from_buffer(buff)
|
||||
windivert_dll.WinDivertSend(self._handle, buff, len(packet.raw), byref(packet.wd_addr),
|
||||
byref(send_len))
|
||||
return send_len
|
||||
|
||||
def get_param(self, name):
|
||||
"""
|
||||
Get a WinDivert parameter. See pydivert.Param for the list of parameters.
|
||||
|
||||
The remapped function is WinDivertGetParam::
|
||||
|
||||
BOOL WinDivertGetParam(
|
||||
__in HANDLE handle,
|
||||
__in WINDIVERT_PARAM param,
|
||||
__out UINT64 *pValue
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_get_param
|
||||
|
||||
:return: The parameter value.
|
||||
"""
|
||||
value = c_uint64(0)
|
||||
windivert_dll.WinDivertGetParam(self._handle, name, byref(value))
|
||||
return value.value
|
||||
|
||||
def set_param(self, name, value):
|
||||
"""
|
||||
Set a WinDivert parameter. See pydivert.Param for the list of parameters.
|
||||
|
||||
The remapped function is DivertSetParam::
|
||||
|
||||
BOOL WinDivertSetParam(
|
||||
__in HANDLE handle,
|
||||
__in WINDIVERT_PARAM param,
|
||||
__in UINT64 value
|
||||
);
|
||||
|
||||
For more info on the C call visit: http://reqrypt.org/windivert-doc.html#divert_set_param
|
||||
"""
|
||||
return windivert_dll.WinDivertSetParam(self._handle, name, value)
|
||||
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert32.dll
Normal file
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert32.dll
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert32.sys
Normal file
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert32.sys
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert64.dll
Normal file
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert64.dll
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert64.sys
Normal file
BIN
venv/Lib/site-packages/pydivert/windivert_dll/WinDivert64.sys
Normal file
Binary file not shown.
123
venv/Lib/site-packages/pydivert/windivert_dll/__init__.py
Normal file
123
venv/Lib/site-packages/pydivert/windivert_dll/__init__.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
pydivert bundles the WinDivert binaries from
|
||||
https://reqrypt.org/download/WinDivert-1.3.0-WDDK.zip
|
||||
"""
|
||||
import functools
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
from ctypes import (
|
||||
POINTER, GetLastError, WinError, c_uint, c_void_p, c_uint32, c_char_p, ARRAY, c_uint64, c_int16, c_int, WinDLL,
|
||||
c_uint8, windll)
|
||||
from ctypes.wintypes import HANDLE
|
||||
|
||||
from .structs import WinDivertAddress
|
||||
|
||||
ERROR_IO_PENDING = 997
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
if platform.architecture()[0] == "64bit":
|
||||
DLL_PATH = os.path.join(here, "WinDivert64.dll")
|
||||
else:
|
||||
DLL_PATH = os.path.join(here, "WinDivert32.dll")
|
||||
|
||||
|
||||
def raise_on_error(f):
|
||||
"""
|
||||
This decorator throws a WinError whenever GetLastError() returns an error.
|
||||
As as special case, ERROR_IO_PENDING is ignored.
|
||||
"""
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
result = f(*args, **kwargs)
|
||||
retcode = GetLastError()
|
||||
if retcode and retcode != ERROR_IO_PENDING:
|
||||
err = WinError(code=retcode)
|
||||
windll.kernel32.SetLastError(0) # clear error code so that we don't raise twice.
|
||||
raise err
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
WINDIVERT_FUNCTIONS = {
|
||||
"WinDivertHelperParsePacket": [HANDLE, c_uint, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p,
|
||||
c_void_p, POINTER(c_uint)],
|
||||
"WinDivertHelperParseIPv4Address": [c_char_p, POINTER(c_uint32)],
|
||||
"WinDivertHelperParseIPv6Address": [c_char_p, POINTER(ARRAY(c_uint8, 16))],
|
||||
"WinDivertHelperCalcChecksums": [c_void_p, c_uint, c_uint64],
|
||||
"WinDivertHelperCheckFilter": [c_char_p, c_int, POINTER(c_char_p), POINTER(c_uint)],
|
||||
"WinDivertHelperEvalFilter": [c_char_p, c_int, c_void_p, c_uint, c_void_p],
|
||||
"WinDivertOpen": [c_char_p, c_int, c_int16, c_uint64],
|
||||
"WinDivertRecv": [HANDLE, c_void_p, c_uint, c_void_p, c_void_p],
|
||||
"WinDivertSend": [HANDLE, c_void_p, c_uint, c_void_p, c_void_p],
|
||||
"WinDivertRecvEx": [HANDLE, c_void_p, c_uint, c_uint64, c_void_p, c_void_p, c_void_p],
|
||||
"WinDivertSendEx": [HANDLE, c_void_p, c_uint, c_uint64, c_void_p, c_void_p, c_void_p],
|
||||
"WinDivertClose": [HANDLE],
|
||||
"WinDivertGetParam": [HANDLE, c_int, POINTER(c_uint64)],
|
||||
"WinDivertSetParam": [HANDLE, c_int, c_uint64],
|
||||
}
|
||||
|
||||
_instance = None
|
||||
|
||||
|
||||
def instance():
|
||||
global _instance
|
||||
if _instance is None:
|
||||
_instance = WinDLL(DLL_PATH)
|
||||
for funcname, argtypes in WINDIVERT_FUNCTIONS.items():
|
||||
func = getattr(_instance, funcname)
|
||||
func.argtypes = argtypes
|
||||
return _instance
|
||||
|
||||
|
||||
# Dark magic happens below.
|
||||
# On init, windivert_dll.WinDivertOpen is a proxy function that loads the DLL on the first invocation
|
||||
# and then replaces all existing proxy function with direct handles to the DLL's functions.
|
||||
|
||||
|
||||
_module = sys.modules[__name__]
|
||||
|
||||
|
||||
def _init():
|
||||
"""
|
||||
Lazy-load DLL, replace proxy functions with actual ones.
|
||||
"""
|
||||
i = instance()
|
||||
for funcname in WINDIVERT_FUNCTIONS:
|
||||
func = getattr(i, funcname)
|
||||
func = raise_on_error(func)
|
||||
setattr(_module, funcname, func)
|
||||
|
||||
|
||||
def _mkprox(funcname):
|
||||
"""
|
||||
Make lazy-init proxy function.
|
||||
"""
|
||||
|
||||
def prox(*args, **kwargs):
|
||||
_init()
|
||||
return getattr(_module, funcname)(*args, **kwargs)
|
||||
|
||||
return prox
|
||||
|
||||
|
||||
for funcname in WINDIVERT_FUNCTIONS:
|
||||
setattr(_module, funcname, _mkprox(funcname))
|
||||
45
venv/Lib/site-packages/pydivert/windivert_dll/structs.py
Normal file
45
venv/Lib/site-packages/pydivert/windivert_dll/structs.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Fabio Falcinelli, Maximilian Hils
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ctypes
|
||||
|
||||
|
||||
class WinDivertAddress(ctypes.Structure):
|
||||
"""
|
||||
Ctypes Structure for WINDIVERT_ADDRESS.
|
||||
The WINDIVERT_ADDRESS structure represents the "address" of a captured or injected packet.
|
||||
The address includes the packet's network interfaces and the packet direction.
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT32 IfIdx;
|
||||
UINT32 SubIfIdx;
|
||||
UINT8 Direction;
|
||||
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;
|
||||
|
||||
Fields:
|
||||
|
||||
- IfIdx: The interface index on which the packet arrived (for inbound packets),
|
||||
or is to be sent (for outbound packets).
|
||||
- SubIfIdx: The sub-interface index for IfIdx.
|
||||
- Direction: The packet's direction. The possible values are
|
||||
- WINDIVERT_DIRECTION_OUTBOUND with value 0 for outbound packets.
|
||||
- WINDIVERT_DIRECTION_INBOUND with value 1 for inbound packets.
|
||||
"""
|
||||
_fields_ = [
|
||||
("IfIdx", ctypes.c_uint32),
|
||||
("SubIfIdx", ctypes.c_uint32),
|
||||
("Direction", ctypes.c_uint8),
|
||||
]
|
||||
Reference in New Issue
Block a user