aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/pair
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/pair
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/pair')
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/__init__.py13
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/ethernet.py56
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/ip.py71
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/raw.py40
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/rawudp.py59
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/testing.py572
-rw-r--r--contrib/python/Twisted/py2/twisted/pair/tuntap.py433
7 files changed, 1244 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/pair/__init__.py b/contrib/python/Twisted/py2/twisted/pair/__init__.py
new file mode 100644
index 0000000000..09fb3dc85d
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/__init__.py
@@ -0,0 +1,13 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Twisted Pair: The framework of your ethernet.
+
+Low-level networking transports and utilities.
+
+See also twisted.protocols.ethernet, twisted.protocols.ip,
+twisted.protocols.raw and twisted.protocols.rawudp.
+
+Maintainer: Tommi Virtanen
+"""
diff --git a/contrib/python/Twisted/py2/twisted/pair/ethernet.py b/contrib/python/Twisted/py2/twisted/pair/ethernet.py
new file mode 100644
index 0000000000..7c7f9a8996
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/ethernet.py
@@ -0,0 +1,56 @@
+# -*- test-case-name: twisted.pair.test.test_ethernet -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+#
+
+
+"""Support for working directly with ethernet frames"""
+
+import struct
+
+
+from twisted.internet import protocol
+from twisted.pair import raw
+from zope.interface import implementer, Interface
+
+
+class IEthernetProtocol(Interface):
+ """An interface for protocols that handle Ethernet frames"""
+ def addProto():
+ """Add an IRawPacketProtocol protocol"""
+
+ def datagramReceived():
+ """An Ethernet frame has been received"""
+
+class EthernetHeader:
+ def __init__(self, data):
+
+ (self.dest, self.source, self.proto) \
+ = struct.unpack("!6s6sH", data[:6+6+2])
+
+
+
+@implementer(IEthernetProtocol)
+class EthernetProtocol(protocol.AbstractDatagramProtocol):
+ def __init__(self):
+ self.etherProtos = {}
+
+ def addProto(self, num, proto):
+ proto = raw.IRawPacketProtocol(proto)
+ if num < 0:
+ raise TypeError('Added protocol must be positive or zero')
+ if num >= 2**16:
+ raise TypeError('Added protocol must fit in 16 bits')
+ if num not in self.etherProtos:
+ self.etherProtos[num] = []
+ self.etherProtos[num].append(proto)
+
+ def datagramReceived(self, data, partial=0):
+ header = EthernetHeader(data[:14])
+ for proto in self.etherProtos.get(header.proto, ()):
+ proto.datagramReceived(data=data[14:],
+ partial=partial,
+ dest=header.dest,
+ source=header.source,
+ protocol=header.proto)
diff --git a/contrib/python/Twisted/py2/twisted/pair/ip.py b/contrib/python/Twisted/py2/twisted/pair/ip.py
new file mode 100644
index 0000000000..47db7075d7
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/ip.py
@@ -0,0 +1,71 @@
+# -*- test-case-name: twisted.pair.test.test_ip -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+#
+
+
+"""Support for working directly with IP packets"""
+
+import struct
+import socket
+
+from twisted.internet import protocol
+from twisted.pair import raw
+from zope.interface import implementer
+
+
+class IPHeader:
+ def __init__(self, data):
+
+ (ihlversion, self.tos, self.tot_len, self.fragment_id, frag_off,
+ self.ttl, self.protocol, self.check, saddr, daddr) \
+ = struct.unpack("!BBHHHBBH4s4s", data[:20])
+ self.saddr = socket.inet_ntoa(saddr)
+ self.daddr = socket.inet_ntoa(daddr)
+ self.version = ihlversion & 0x0F
+ self.ihl = ((ihlversion & 0xF0) >> 4) << 2
+ self.fragment_offset = frag_off & 0x1FFF
+ self.dont_fragment = (frag_off & 0x4000 != 0)
+ self.more_fragments = (frag_off & 0x2000 != 0)
+
+MAX_SIZE = 2**32
+
+@implementer(raw.IRawPacketProtocol)
+class IPProtocol(protocol.AbstractDatagramProtocol):
+ def __init__(self):
+ self.ipProtos = {}
+
+ def addProto(self, num, proto):
+ proto = raw.IRawDatagramProtocol(proto)
+ if num < 0:
+ raise TypeError('Added protocol must be positive or zero')
+ if num >= MAX_SIZE:
+ raise TypeError('Added protocol must fit in 32 bits')
+ if num not in self.ipProtos:
+ self.ipProtos[num] = []
+ self.ipProtos[num].append(proto)
+
+ def datagramReceived(self,
+ data,
+ partial,
+ dest,
+ source,
+ protocol):
+ header = IPHeader(data)
+ for proto in self.ipProtos.get(header.protocol, ()):
+ proto.datagramReceived(data=data[20:],
+ partial=partial,
+ source=header.saddr,
+ dest=header.daddr,
+ protocol=header.protocol,
+ version=header.version,
+ ihl=header.ihl,
+ tos=header.tos,
+ tot_len=header.tot_len,
+ fragment_id=header.fragment_id,
+ fragment_offset=header.fragment_offset,
+ dont_fragment=header.dont_fragment,
+ more_fragments=header.more_fragments,
+ ttl=header.ttl,
+ )
diff --git a/contrib/python/Twisted/py2/twisted/pair/raw.py b/contrib/python/Twisted/py2/twisted/pair/raw.py
new file mode 100644
index 0000000000..ed859db957
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/raw.py
@@ -0,0 +1,40 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+"""
+Interface definitions for working with raw packets
+"""
+
+from zope.interface import Interface
+
+
+class IRawDatagramProtocol(Interface):
+ """
+ An interface for protocols such as UDP, ICMP and TCP.
+ """
+
+ def addProto():
+ """
+ Add a protocol on top of this one.
+ """
+
+ def datagramReceived():
+ """
+ An IP datagram has been received. Parse and process it.
+ """
+
+
+
+class IRawPacketProtocol(Interface):
+ """
+ An interface for low-level protocols such as IP and ARP.
+ """
+
+ def addProto():
+ """
+ Add a protocol on top of this one.
+ """
+
+ def datagramReceived():
+ """
+ An IP datagram has been received. Parse and process it.
+ """
diff --git a/contrib/python/Twisted/py2/twisted/pair/rawudp.py b/contrib/python/Twisted/py2/twisted/pair/rawudp.py
new file mode 100644
index 0000000000..f52e417425
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/rawudp.py
@@ -0,0 +1,59 @@
+# -*- test-case-name: twisted.pair.test.test_rawudp -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Implementation of raw packet interfaces for UDP
+"""
+
+import struct
+
+from twisted.internet import protocol
+from twisted.pair import raw
+from zope.interface import implementer
+
+class UDPHeader:
+ def __init__(self, data):
+
+ (self.source, self.dest, self.len, self.check) \
+ = struct.unpack("!HHHH", data[:8])
+
+
+
+@implementer(raw.IRawDatagramProtocol)
+class RawUDPProtocol(protocol.AbstractDatagramProtocol):
+ def __init__(self):
+ self.udpProtos = {}
+
+
+ def addProto(self, num, proto):
+ if not isinstance(proto, protocol.DatagramProtocol):
+ raise TypeError('Added protocol must be an instance of DatagramProtocol')
+ if num < 0:
+ raise TypeError('Added protocol must be positive or zero')
+ if num >= 2**16:
+ raise TypeError('Added protocol must fit in 16 bits')
+ if num not in self.udpProtos:
+ self.udpProtos[num] = []
+ self.udpProtos[num].append(proto)
+
+
+ def datagramReceived(self,
+ data,
+ partial,
+ source,
+ dest,
+ protocol,
+ version,
+ ihl,
+ tos,
+ tot_len,
+ fragment_id,
+ fragment_offset,
+ dont_fragment,
+ more_fragments,
+ ttl):
+ header = UDPHeader(data)
+ for proto in self.udpProtos.get(header.dest, ()):
+ proto.datagramReceived(data[8:],
+ (source, header.source))
diff --git a/contrib/python/Twisted/py2/twisted/pair/testing.py b/contrib/python/Twisted/py2/twisted/pair/testing.py
new file mode 100644
index 0000000000..b20136bdd1
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/testing.py
@@ -0,0 +1,572 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Tools for automated testing of L{twisted.pair}-based applications.
+"""
+
+import struct
+import socket
+from errno import (
+ EPERM, EAGAIN, EWOULDBLOCK, ENOSYS, EBADF, EINVAL, EINTR, ENOBUFS)
+from collections import deque
+from functools import wraps
+
+from zope.interface import implementer
+
+from twisted.internet.protocol import DatagramProtocol
+from twisted.pair.ethernet import EthernetProtocol
+from twisted.pair.rawudp import RawUDPProtocol
+from twisted.pair.ip import IPProtocol
+from twisted.pair.tuntap import (
+ _IFNAMSIZ, _TUNSETIFF, _IInputOutputSystem, TunnelFlags)
+from twisted.python.compat import nativeString
+
+
+# The number of bytes in the "protocol information" header that may be present
+# on datagrams read from a tunnel device. This is two bytes of flags followed
+# by two bytes of protocol identification. All this code does with this
+# information is use it to discard the header.
+_PI_SIZE = 4
+
+
+def _H(n):
+ """
+ Pack an integer into a network-order two-byte string.
+
+ @param n: The integer to pack. Only values that fit into 16 bits are
+ supported.
+
+ @return: The packed representation of the integer.
+ @rtype: L{bytes}
+ """
+ return struct.pack('>H', n)
+
+
+_IPv4 = 0x0800
+
+
+def _ethernet(src, dst, protocol, payload):
+ """
+ Construct an ethernet frame.
+
+ @param src: The source ethernet address, encoded.
+ @type src: L{bytes}
+
+ @param dst: The destination ethernet address, encoded.
+ @type dst: L{bytes}
+
+ @param protocol: The protocol number of the payload of this datagram.
+ @type protocol: L{int}
+
+ @param payload: The content of the ethernet frame (such as an IP datagram).
+ @type payload: L{bytes}
+
+ @return: The full ethernet frame.
+ @rtype: L{bytes}
+ """
+ return dst + src + _H(protocol) + payload
+
+
+
+def _ip(src, dst, payload):
+ """
+ Construct an IP datagram with the given source, destination, and
+ application payload.
+
+ @param src: The source IPv4 address as a dotted-quad string.
+ @type src: L{bytes}
+
+ @param dst: The destination IPv4 address as a dotted-quad string.
+ @type dst: L{bytes}
+
+ @param payload: The content of the IP datagram (such as a UDP datagram).
+ @type payload: L{bytes}
+
+ @return: An IP datagram header and payload.
+ @rtype: L{bytes}
+ """
+ ipHeader = (
+ # Version and header length, 4 bits each
+ b'\x45'
+ # Differentiated services field
+ b'\x00'
+ # Total length
+ + _H(20 + len(payload))
+ + b'\x00\x01\x00\x00\x40\x11'
+ # Checksum
+ + _H(0)
+ # Source address
+ + socket.inet_pton(socket.AF_INET, nativeString(src))
+ # Destination address
+ + socket.inet_pton(socket.AF_INET, nativeString(dst)))
+
+ # Total all of the 16-bit integers in the header
+ checksumStep1 = sum(struct.unpack('!10H', ipHeader))
+ # Pull off the carry
+ carry = checksumStep1 >> 16
+ # And add it to what was left over
+ checksumStep2 = (checksumStep1 & 0xFFFF) + carry
+ # Compute the one's complement sum
+ checksumStep3 = checksumStep2 ^ 0xFFFF
+
+ # Reconstruct the IP header including the correct checksum so the platform
+ # IP stack, if there is one involved in this test, doesn't drop it on the
+ # floor as garbage.
+ ipHeader = (
+ ipHeader[:10] +
+ struct.pack('!H', checksumStep3) +
+ ipHeader[12:])
+
+ return ipHeader + payload
+
+
+
+def _udp(src, dst, payload):
+ """
+ Construct a UDP datagram with the given source, destination, and
+ application payload.
+
+ @param src: The source port number.
+ @type src: L{int}
+
+ @param dst: The destination port number.
+ @type dst: L{int}
+
+ @param payload: The content of the UDP datagram.
+ @type payload: L{bytes}
+
+ @return: A UDP datagram header and payload.
+ @rtype: L{bytes}
+ """
+ udpHeader = (
+ # Source port
+ _H(src)
+ # Destination port
+ + _H(dst)
+ # Length
+ + _H(len(payload) + 8)
+ # Checksum
+ + _H(0))
+ return udpHeader + payload
+
+
+
+class Tunnel(object):
+ """
+ An in-memory implementation of a tun or tap device.
+
+ @cvar _DEVICE_NAME: A string representing the conventional filesystem entry
+ for the tunnel factory character special device.
+ @type _DEVICE_NAME: C{bytes}
+ """
+ _DEVICE_NAME = b"/dev/net/tun"
+
+ # Between POSIX and Python, there are 4 combinations. Here are two, at
+ # least.
+ EAGAIN_STYLE = IOError(EAGAIN, "Resource temporarily unavailable")
+ EWOULDBLOCK_STYLE = OSError(EWOULDBLOCK, "Operation would block")
+
+ # Oh yea, and then there's the case where maybe we would've read, but
+ # someone sent us a signal instead.
+ EINTR_STYLE = IOError(EINTR, "Interrupted function call")
+
+ nonBlockingExceptionStyle = EAGAIN_STYLE
+
+ SEND_BUFFER_SIZE = 1024
+
+ def __init__(self, system, openFlags, fileMode):
+ """
+ @param system: An L{_IInputOutputSystem} provider to use to perform I/O.
+
+ @param openFlags: Any flags to apply when opening the tunnel device.
+ See C{os.O_*}.
+
+ @type openFlags: L{int}
+
+ @param fileMode: ignored
+ """
+ self.system = system
+
+ # Drop fileMode on the floor - evidence and logic suggest it is
+ # irrelevant with respect to /dev/net/tun
+ self.openFlags = openFlags
+ self.tunnelMode = None
+ self.requestedName = None
+ self.name = None
+ self.readBuffer = deque()
+ self.writeBuffer = deque()
+ self.pendingSignals = deque()
+
+
+ @property
+ def blocking(self):
+ """
+ If the file descriptor for this tunnel is open in blocking mode,
+ C{True}. C{False} otherwise.
+ """
+ return not (self.openFlags & self.system.O_NONBLOCK)
+
+
+ @property
+ def closeOnExec(self):
+ """
+ If the file descriptor for this tunnel is marked as close-on-exec,
+ C{True}. C{False} otherwise.
+ """
+ return bool(self.openFlags & self.system.O_CLOEXEC)
+
+
+ def addToReadBuffer(self, datagram):
+ """
+ Deliver a datagram to this tunnel's read buffer. This makes it
+ available to be read later using the C{read} method.
+
+ @param datagram: The IPv4 datagram to deliver. If the mode of this
+ tunnel is TAP then ethernet framing will be added automatically.
+ @type datagram: L{bytes}
+ """
+ # TAP devices also include ethernet framing.
+ if self.tunnelMode & TunnelFlags.IFF_TAP.value:
+ datagram = _ethernet(
+ src=b'\x00' * 6, dst=b'\xff' * 6, protocol=_IPv4,
+ payload=datagram)
+
+ self.readBuffer.append(datagram)
+
+
+ def read(self, limit):
+ """
+ Read a datagram out of this tunnel.
+
+ @param limit: The maximum number of bytes from the datagram to return.
+ If the next datagram is larger than this, extra bytes are dropped
+ and lost forever.
+ @type limit: L{int}
+
+ @raise OSError: Any of the usual I/O problems can result in this
+ exception being raised with some particular error number set.
+
+ @raise IOError: Any of the usual I/O problems can result in this
+ exception being raised with some particular error number set.
+
+ @return: The datagram which was read from the tunnel. If the tunnel
+ mode does not include L{TunnelFlags.IFF_NO_PI} then the datagram is
+ prefixed with a 4 byte PI header.
+ @rtype: L{bytes}
+ """
+ if self.readBuffer:
+ if self.tunnelMode & TunnelFlags.IFF_NO_PI.value:
+ header = b""
+ else:
+ # Synthesize a PI header to include in the result. Nothing in
+ # twisted.pair uses the PI information yet so we can synthesize
+ # something incredibly boring (ie 32 bits of 0).
+ header = b"\x00" * _PI_SIZE
+ limit -= 4
+ return header + self.readBuffer.popleft()[:limit]
+ elif self.blocking:
+ raise NotImplementedError()
+ else:
+ raise self.nonBlockingExceptionStyle
+
+
+ def write(self, datagram):
+ """
+ Write a datagram into this tunnel.
+
+ @param datagram: The datagram to write.
+ @type datagram: L{bytes}
+
+ @raise IOError: Any of the usual I/O problems can result in this
+ exception being raised with some particular error number set.
+
+ @return: The number of bytes of the datagram which were written.
+ @rtype: L{int}
+ """
+ if self.pendingSignals:
+ self.pendingSignals.popleft()
+ raise IOError(EINTR, "Interrupted system call")
+
+ if len(datagram) > self.SEND_BUFFER_SIZE:
+ raise IOError(ENOBUFS, "No buffer space available")
+
+ self.writeBuffer.append(datagram)
+ return len(datagram)
+
+
+
+def _privileged(original):
+ """
+ Wrap a L{MemoryIOSystem} method with permission-checking logic. The
+ returned function will check C{self.permissions} and raise L{IOError} with
+ L{errno.EPERM} if the function name is not listed as an available
+ permission.
+
+ @param original: The L{MemoryIOSystem} instance to wrap.
+
+ @return: A wrapper around C{original} that applies permission checks.
+ """
+ @wraps(original)
+ def permissionChecker(self, *args, **kwargs):
+ if original.__name__ not in self.permissions:
+ raise IOError(EPERM, "Operation not permitted")
+ return original(self, *args, **kwargs)
+ return permissionChecker
+
+
+
+@implementer(_IInputOutputSystem)
+class MemoryIOSystem(object):
+ """
+ An in-memory implementation of basic I/O primitives, useful in the context
+ of unit testing as a drop-in replacement for parts of the C{os} module.
+
+ @ivar _devices:
+ @ivar _openFiles:
+ @ivar permissions:
+
+ @ivar _counter:
+ """
+ _counter = 8192
+
+ O_RDWR = 1 << 0
+ O_NONBLOCK = 1 << 1
+ O_CLOEXEC = 1 << 2
+
+ def __init__(self):
+ self._devices = {}
+ self._openFiles = {}
+ self.permissions = set(['open', 'ioctl'])
+
+
+ def getTunnel(self, port):
+ """
+ Get the L{Tunnel} object associated with the given L{TuntapPort}.
+
+ @param port: A L{TuntapPort} previously initialized using this
+ L{MemoryIOSystem}.
+
+ @return: The tunnel object created by a prior use of C{open} on this
+ object on the tunnel special device file.
+ @rtype: L{Tunnel}
+ """
+ return self._openFiles[port.fileno()]
+
+
+ def registerSpecialDevice(self, name, cls):
+ """
+ Specify a class which will be used to handle I/O to a device of a
+ particular name.
+
+ @param name: The filesystem path name of the device.
+ @type name: L{bytes}
+
+ @param cls: A class (like L{Tunnel}) to instantiated whenever this
+ device is opened.
+ """
+ self._devices[name] = cls
+
+
+ @_privileged
+ def open(self, name, flags, mode=None):
+ """
+ A replacement for C{os.open}. This initializes state in this
+ L{MemoryIOSystem} which will be reflected in the behavior of the other
+ file descriptor-related methods (eg L{MemoryIOSystem.read},
+ L{MemoryIOSystem.write}, etc).
+
+ @param name: A string giving the name of the file to open.
+ @type name: C{bytes}
+
+ @param flags: The flags with which to open the file.
+ @type flags: C{int}
+
+ @param mode: The mode with which to open the file.
+ @type mode: C{int}
+
+ @raise OSError: With C{ENOSYS} if the file is not a recognized special
+ device file.
+
+ @return: A file descriptor associated with the newly opened file
+ description.
+ @rtype: L{int}
+ """
+ if name in self._devices:
+ fd = self._counter
+ self._counter += 1
+ self._openFiles[fd] = self._devices[name](self, flags, mode)
+ return fd
+ raise OSError(ENOSYS, "Function not implemented")
+
+
+ def read(self, fd, limit):
+ """
+ Try to read some bytes out of one of the in-memory buffers which may
+ previously have been populated by C{write}.
+
+ @see: L{os.read}
+ """
+ try:
+ return self._openFiles[fd].read(limit)
+ except KeyError:
+ raise OSError(EBADF, "Bad file descriptor")
+
+
+ def write(self, fd, data):
+ """
+ Try to add some bytes to one of the in-memory buffers to be accessed by
+ a later C{read} call.
+
+ @see: L{os.write}
+ """
+ try:
+ return self._openFiles[fd].write(data)
+ except KeyError:
+ raise OSError(EBADF, "Bad file descriptor")
+
+
+ def close(self, fd):
+ """
+ Discard the in-memory buffer and other in-memory state for the given
+ file descriptor.
+
+ @see: L{os.close}
+ """
+ try:
+ del self._openFiles[fd]
+ except KeyError:
+ raise OSError(EBADF, "Bad file descriptor")
+
+
+ @_privileged
+ def ioctl(self, fd, request, args):
+ """
+ Perform some configuration change to the in-memory state for the given
+ file descriptor.
+
+ @see: L{fcntl.ioctl}
+ """
+ try:
+ tunnel = self._openFiles[fd]
+ except KeyError:
+ raise IOError(EBADF, "Bad file descriptor")
+
+ if request != _TUNSETIFF:
+ raise IOError(EINVAL, "Request or args is not valid.")
+
+ name, mode = struct.unpack('%dsH' % (_IFNAMSIZ,), args)
+ tunnel.tunnelMode = mode
+ tunnel.requestedName = name
+ tunnel.name = name[:_IFNAMSIZ - 3] + b"123"
+
+ return struct.pack('%dsH' % (_IFNAMSIZ,), tunnel.name, mode)
+
+
+ def sendUDP(self, datagram, address):
+ """
+ Write an ethernet frame containing an ip datagram containing a udp
+ datagram containing the given payload, addressed to the given address,
+ to a tunnel device previously opened on this I/O system.
+
+ @param datagram: A UDP datagram payload to send.
+ @type datagram: L{bytes}
+
+ @param address: The destination to which to send the datagram.
+ @type address: L{tuple} of (L{bytes}, L{int})
+
+ @return: A two-tuple giving the address from which gives the address
+ from which the datagram was sent.
+ @rtype: L{tuple} of (L{bytes}, L{int})
+ """
+ # Just make up some random thing
+ srcIP = '10.1.2.3'
+ srcPort = 21345
+
+ serialized = _ip(
+ src=srcIP, dst=address[0], payload=_udp(
+ src=srcPort, dst=address[1], payload=datagram))
+
+ openFiles = list(self._openFiles.values())
+ openFiles[0].addToReadBuffer(serialized)
+
+ return (srcIP, srcPort)
+
+
+ def receiveUDP(self, fileno, host, port):
+ """
+ Get a socket-like object which can be used to receive a datagram sent
+ from the given address.
+
+ @param fileno: A file descriptor representing a tunnel device which the
+ datagram will be received via.
+ @type fileno: L{int}
+
+ @param host: The IPv4 address to which the datagram was sent.
+ @type host: L{bytes}
+
+ @param port: The UDP port number to which the datagram was sent.
+ received.
+ @type port: L{int}
+
+ @return: A L{socket.socket}-like object which can be used to receive
+ the specified datagram.
+ """
+ return _FakePort(self, fileno)
+
+
+
+class _FakePort(object):
+ """
+ A socket-like object which can be used to read UDP datagrams from
+ tunnel-like file descriptors managed by a L{MemoryIOSystem}.
+ """
+ def __init__(self, system, fileno):
+ self._system = system
+ self._fileno = fileno
+
+
+ def recv(self, nbytes):
+ """
+ Receive a datagram sent to this port using the L{MemoryIOSystem} which
+ created this object.
+
+ This behaves like L{socket.socket.recv} but the data being I{sent} and
+ I{received} only passes through various memory buffers managed by this
+ object and L{MemoryIOSystem}.
+
+ @see: L{socket.socket.recv}
+ """
+ data = self._system._openFiles[self._fileno].writeBuffer.popleft()
+
+ datagrams = []
+ receiver = DatagramProtocol()
+
+ def capture(datagram, address):
+ datagrams.append(datagram)
+
+ receiver.datagramReceived = capture
+
+ udp = RawUDPProtocol()
+ udp.addProto(12345, receiver)
+
+ ip = IPProtocol()
+ ip.addProto(17, udp)
+
+ mode = self._system._openFiles[self._fileno].tunnelMode
+ if (mode & TunnelFlags.IFF_TAP.value):
+ ether = EthernetProtocol()
+ ether.addProto(0x800, ip)
+ datagramReceived = ether.datagramReceived
+ else:
+ datagramReceived = lambda data: ip.datagramReceived(
+ data, None, None, None, None)
+
+ dataHasPI = not (mode & TunnelFlags.IFF_NO_PI.value)
+
+ if dataHasPI:
+ # datagramReceived can't handle the PI, get rid of it.
+ data = data[_PI_SIZE:]
+
+ datagramReceived(data)
+ return datagrams[0][:nbytes]
diff --git a/contrib/python/Twisted/py2/twisted/pair/tuntap.py b/contrib/python/Twisted/py2/twisted/pair/tuntap.py
new file mode 100644
index 0000000000..69b3f9bfff
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/pair/tuntap.py
@@ -0,0 +1,433 @@
+# -*- test-case-name: twisted.pair.test.test_tuntap -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Support for Linux ethernet and IP tunnel devices.
+
+@see: U{https://en.wikipedia.org/wiki/TUN/TAP}
+"""
+
+import os
+import fcntl
+import errno
+import struct
+import warnings
+
+from collections import namedtuple
+from constantly import Flags, FlagConstant
+from zope.interface import Attribute, Interface, implementer
+
+from twisted.python.util import FancyEqMixin, FancyStrMixin
+from incremental import Version
+from twisted.python.reflect import fullyQualifiedName
+from twisted.python.deprecate import deprecated
+from twisted.python import log
+from twisted.internet import abstract, error, task, interfaces, defer
+from twisted.pair import ethernet, raw
+
+__all__ = [
+ "TunnelFlags", "TunnelAddress", "TuntapPort",
+ ]
+
+
+_IFNAMSIZ = 16
+_TUNSETIFF = 0x400454ca
+_TUNGETIFF = 0x800454d2
+_TUN_KO_PATH = b"/dev/net/tun"
+
+
+class TunnelFlags(Flags):
+ """
+ L{TunnelFlags} defines more flags which are used to configure the behavior
+ of a tunnel device.
+
+ @cvar IFF_TUN: This indicates a I{tun}-type device. This type of tunnel
+ carries IP datagrams. This flag is mutually exclusive with C{IFF_TAP}.
+
+ @cvar IFF_TAP: This indicates a I{tap}-type device. This type of tunnel
+ carries ethernet frames. This flag is mutually exclusive with C{IFF_TUN}.
+
+ @cvar IFF_NO_PI: This indicates the I{protocol information} header will
+ B{not} be included in data read from the tunnel.
+
+ @see: U{https://www.kernel.org/doc/Documentation/networking/tuntap.txt}
+ """
+ IFF_TUN = FlagConstant(0x0001)
+ IFF_TAP = FlagConstant(0x0002)
+
+ TUN_FASYNC = FlagConstant(0x0010)
+ TUN_NOCHECKSUM = FlagConstant(0x0020)
+ TUN_NO_PI = FlagConstant(0x0040)
+ TUN_ONE_QUEUE = FlagConstant(0x0080)
+ TUN_PERSIST = FlagConstant(0x0100)
+ TUN_VNET_HDR = FlagConstant(0x0200)
+
+ IFF_NO_PI = FlagConstant(0x1000)
+ IFF_ONE_QUEUE = FlagConstant(0x2000)
+ IFF_VNET_HDR = FlagConstant(0x4000)
+ IFF_TUN_EXCL = FlagConstant(0x8000)
+
+
+
+@implementer(interfaces.IAddress)
+class TunnelAddress(FancyStrMixin, FancyEqMixin, object):
+ """
+ A L{TunnelAddress} represents the tunnel to which a L{TuntapPort} is bound.
+ """
+ compareAttributes = ("_typeValue", "name")
+ showAttributes = (("type", lambda flag: flag.name), "name")
+
+ @property
+ def _typeValue(self):
+ """
+ Return the integer value of the C{type} attribute. Used to produce
+ correct results in the equality implementation.
+ """
+ # Work-around for https://twistedmatrix.com/trac/ticket/6878
+ return self.type.value
+
+
+ def __init__(self, type, name):
+ """
+ @param type: Either L{TunnelFlags.IFF_TUN} or L{TunnelFlags.IFF_TAP},
+ representing the type of this tunnel.
+
+ @param name: The system name of the tunnel.
+ @type name: L{bytes}
+ """
+ self.type = type
+ self.name = name
+
+
+ def __getitem__(self, index):
+ """
+ Deprecated accessor for the tunnel name. Use attributes instead.
+ """
+ warnings.warn(
+ "TunnelAddress.__getitem__ is deprecated since Twisted 14.0.0 "
+ "Use attributes instead.", category=DeprecationWarning,
+ stacklevel=2)
+ return ('TUNTAP', self.name)[index]
+
+
+
+class _TunnelDescription(namedtuple("_TunnelDescription", "fileno name")):
+ """
+ Describe an existing tunnel.
+
+ @ivar fileno: the file descriptor associated with the tunnel
+ @type fileno: L{int}
+
+ @ivar name: the name of the tunnel
+ @type name: L{bytes}
+ """
+
+
+
+class _IInputOutputSystem(Interface):
+ """
+ An interface for performing some basic kinds of I/O (particularly that I/O
+ which might be useful for L{twisted.pair.tuntap}-using code).
+ """
+ O_RDWR = Attribute("@see: L{os.O_RDWR}")
+ O_NONBLOCK = Attribute("@see: L{os.O_NONBLOCK}")
+ O_CLOEXEC = Attribute("@see: L{os.O_CLOEXEC}")
+
+ def open(filename, flag, mode=0o777):
+ """
+ @see: L{os.open}
+ """
+
+
+ def ioctl(fd, opt, arg=None, mutate_flag=None):
+ """
+ @see: L{fcntl.ioctl}
+ """
+
+
+ def read(fd, limit):
+ """
+ @see: L{os.read}
+ """
+
+
+ def write(fd, data):
+ """
+ @see: L{os.write}
+ """
+
+
+ def close(fd):
+ """
+ @see: L{os.close}
+ """
+
+
+ def sendUDP(datagram, address):
+ """
+ Send a datagram to a certain address.
+
+ @param datagram: The payload of a UDP datagram to send.
+ @type datagram: L{bytes}
+
+ @param address: The destination to which to send the datagram.
+ @type address: L{tuple} of (L{bytes}, L{int})
+
+ @return: The local address from which the datagram was sent.
+ @rtype: L{tuple} of (L{bytes}, L{int})
+ """
+
+
+ def receiveUDP(fileno, host, port):
+ """
+ Return a socket which can be used to receive datagrams sent to the
+ given address.
+
+ @param fileno: A file descriptor representing a tunnel device which the
+ datagram was either sent via or will be received via.
+ @type fileno: L{int}
+
+ @param host: The IPv4 address at which the datagram will be received.
+ @type host: L{bytes}
+
+ @param port: The UDP port number at which the datagram will be
+ received.
+ @type port: L{int}
+
+ @return: A L{socket.socket} which can be used to receive the specified
+ datagram.
+ """
+
+
+
+class _RealSystem(object):
+ """
+ An interface to the parts of the operating system which L{TuntapPort}
+ relies on. This is most of an implementation of L{_IInputOutputSystem}.
+ """
+ open = staticmethod(os.open)
+ read = staticmethod(os.read)
+ write = staticmethod(os.write)
+ close = staticmethod(os.close)
+ ioctl = staticmethod(fcntl.ioctl)
+
+ O_RDWR = os.O_RDWR
+ O_NONBLOCK = os.O_NONBLOCK
+ # Introduced in Python 3.x
+ # Ubuntu 12.04, /usr/include/x86_64-linux-gnu/bits/fcntl.h
+ O_CLOEXEC = getattr(os, "O_CLOEXEC", 0o2000000)
+
+
+
+@implementer(interfaces.IListeningPort)
+class TuntapPort(abstract.FileDescriptor):
+ """
+ A Port that reads and writes packets from/to a TUN/TAP-device.
+ """
+ maxThroughput = 256 * 1024 # Max bytes we read in one eventloop iteration
+
+ def __init__(self, interface, proto, maxPacketSize=8192, reactor=None,
+ system=None):
+ if ethernet.IEthernetProtocol.providedBy(proto):
+ self.ethernet = 1
+ self._mode = TunnelFlags.IFF_TAP
+ else:
+ self.ethernet = 0
+ self._mode = TunnelFlags.IFF_TUN
+ assert raw.IRawPacketProtocol.providedBy(proto)
+
+ if system is None:
+ system = _RealSystem()
+ self._system = system
+
+ abstract.FileDescriptor.__init__(self, reactor)
+ self.interface = interface
+ self.protocol = proto
+ self.maxPacketSize = maxPacketSize
+
+ logPrefix = self._getLogPrefix(self.protocol)
+ self.logstr = "%s (%s)" % (logPrefix, self._mode.name)
+
+
+ def __repr__(self):
+ args = (fullyQualifiedName(self.protocol.__class__),)
+ if self.connected:
+ args = args + ("",)
+ else:
+ args = args + ("not ",)
+ args = args + (self._mode.name, self.interface)
+ return "<%s %slistening on %s/%s>" % args
+
+
+ def startListening(self):
+ """
+ Create and bind my socket, and begin listening on it.
+
+ This must be called after creating a server to begin listening on the
+ specified tunnel.
+ """
+ self._bindSocket()
+ self.protocol.makeConnection(self)
+ self.startReading()
+
+
+ def _openTunnel(self, name, mode):
+ """
+ Open the named tunnel using the given mode.
+
+ @param name: The name of the tunnel to open.
+ @type name: L{bytes}
+
+ @param mode: Flags from L{TunnelFlags} with exactly one of
+ L{TunnelFlags.IFF_TUN} or L{TunnelFlags.IFF_TAP} set.
+
+ @return: A L{_TunnelDescription} representing the newly opened tunnel.
+ """
+ flags = (
+ self._system.O_RDWR | self._system.O_CLOEXEC |
+ self._system.O_NONBLOCK)
+ config = struct.pack("%dsH" % (_IFNAMSIZ,), name, mode.value)
+ fileno = self._system.open(_TUN_KO_PATH, flags)
+ result = self._system.ioctl(fileno, _TUNSETIFF, config)
+ return _TunnelDescription(fileno, result[:_IFNAMSIZ].strip(b'\x00'))
+
+
+ def _bindSocket(self):
+ """
+ Open the tunnel.
+ """
+ log.msg(
+ format="%(protocol)s starting on %(interface)s",
+ protocol=self.protocol.__class__,
+ interface=self.interface)
+ try:
+ fileno, interface = self._openTunnel(
+ self.interface, self._mode | TunnelFlags.IFF_NO_PI)
+ except (IOError, OSError) as e:
+ raise error.CannotListenError(None, self.interface, e)
+
+ self.interface = interface
+ self._fileno = fileno
+
+ self.connected = 1
+
+
+ def fileno(self):
+ return self._fileno
+
+
+ def doRead(self):
+ """
+ Called when my socket is ready for reading.
+ """
+ read = 0
+ while read < self.maxThroughput:
+ try:
+ data = self._system.read(self._fileno, self.maxPacketSize)
+ except EnvironmentError as e:
+ if e.errno in (errno.EWOULDBLOCK, errno.EAGAIN, errno.EINTR):
+ return
+ else:
+ raise
+ except:
+ raise
+ read += len(data)
+ # TODO pkt.isPartial()?
+ try:
+ self.protocol.datagramReceived(data, partial=0)
+ except:
+ cls = fullyQualifiedName(self.protocol.__class__)
+ log.err(
+ None,
+ "Unhandled exception from %s.datagramReceived" % (cls,))
+
+
+ def write(self, datagram):
+ """
+ Write the given data as a single datagram.
+
+ @param datagram: The data that will make up the complete datagram to be
+ written.
+ @type datagram: L{bytes}
+ """
+ try:
+ return self._system.write(self._fileno, datagram)
+ except IOError as e:
+ if e.errno == errno.EINTR:
+ return self.write(datagram)
+ raise
+
+
+ def writeSequence(self, seq):
+ """
+ Write a datagram constructed from a L{list} of L{bytes}.
+
+ @param datagram: The data that will make up the complete datagram to be
+ written.
+ @type seq: L{list} of L{bytes}
+ """
+ self.write(b"".join(seq))
+
+
+ def stopListening(self):
+ """
+ Stop accepting connections on this port.
+
+ This will shut down my socket and call self.connectionLost().
+
+ @return: A L{Deferred} that fires when this port has stopped.
+ """
+ self.stopReading()
+ if self.disconnecting:
+ return self._stoppedDeferred
+ elif self.connected:
+ self._stoppedDeferred = task.deferLater(
+ self.reactor, 0, self.connectionLost)
+ self.disconnecting = True
+ return self._stoppedDeferred
+ else:
+ return defer.succeed(None)
+
+
+ def loseConnection(self):
+ """
+ Close this tunnel. Use L{TuntapPort.stopListening} instead.
+ """
+ self.stopListening().addErrback(log.err)
+
+
+ def connectionLost(self, reason=None):
+ """
+ Cleans up my socket.
+
+ @param reason: Ignored. Do not use this.
+ """
+ log.msg('(Tuntap %s Closed)' % self.interface)
+ abstract.FileDescriptor.connectionLost(self, reason)
+ self.protocol.doStop()
+ self.connected = 0
+ self._system.close(self._fileno)
+ self._fileno = -1
+
+
+ def logPrefix(self):
+ """
+ Returns the name of my class, to prefix log entries with.
+ """
+ return self.logstr
+
+
+ def getHost(self):
+ """
+ Get the local address of this L{TuntapPort}.
+
+ @return: A L{TunnelAddress} which describes the tunnel device to which
+ this object is bound.
+ @rtype: L{TunnelAddress}
+ """
+ return TunnelAddress(self._mode, self.interface)
+
+TuntapPort.loseConnection = deprecated(
+ Version("Twisted", 14, 0, 0),
+ TuntapPort.stopListening)(TuntapPort.loseConnection)