add Linux_i686
This commit is contained in:
parent
75f9a2fcbc
commit
95cd9b11f2
1644 changed files with 564260 additions and 0 deletions
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
# 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
|
||||
|
||||
"""
|
||||
|
||||
from twisted.pair._version import version
|
||||
__version__ = version.short()
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
# This is an auto-generated file. Do not edit it.
|
||||
|
||||
"""
|
||||
Provides Twisted version information.
|
||||
"""
|
||||
|
||||
from twisted.python import versions
|
||||
version = versions.Version('twisted.pair', 14, 0, 0)
|
||||
|
|
@ -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 implements, 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])
|
||||
|
||||
class EthernetProtocol(protocol.AbstractDatagramProtocol):
|
||||
|
||||
implements(IEthernetProtocol)
|
||||
|
||||
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)
|
||||
72
Linux_i686/lib/python2.7/site-packages/twisted/pair/ip.py
Normal file
72
Linux_i686/lib/python2.7/site-packages/twisted/pair/ip.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# -*- 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 implements
|
||||
|
||||
|
||||
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 = 2L**32
|
||||
|
||||
class IPProtocol(protocol.AbstractDatagramProtocol):
|
||||
implements(raw.IRawPacketProtocol)
|
||||
|
||||
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,
|
||||
)
|
||||
33
Linux_i686/lib/python2.7/site-packages/twisted/pair/raw.py
Normal file
33
Linux_i686/lib/python2.7/site-packages/twisted/pair/raw.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# 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.
|
||||
"""
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# -*- 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 implements
|
||||
|
||||
class UDPHeader:
|
||||
def __init__(self, data):
|
||||
|
||||
(self.source, self.dest, self.len, self.check) \
|
||||
= struct.unpack("!HHHH", data[:8])
|
||||
|
||||
class RawUDPProtocol(protocol.AbstractDatagramProtocol):
|
||||
implements(raw.IRawDatagramProtocol)
|
||||
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))
|
||||
|
|
@ -0,0 +1 @@
|
|||
'pair tests'
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.python import components
|
||||
from twisted.pair import ethernet, raw
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
class MyProtocol:
|
||||
implements(raw.IRawPacketProtocol)
|
||||
|
||||
def __init__(self, expecting):
|
||||
self.expecting = list(expecting)
|
||||
|
||||
def datagramReceived(self, data, **kw):
|
||||
assert self.expecting, 'Got a packet when not expecting anymore.'
|
||||
expect = self.expecting.pop(0)
|
||||
assert expect == (data, kw), \
|
||||
"Expected %r, got %r" % (
|
||||
expect, (data, kw),
|
||||
)
|
||||
|
||||
class EthernetTestCase(unittest.TestCase):
|
||||
def testPacketParsing(self):
|
||||
proto = ethernet.EthernetProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0800, p1)
|
||||
|
||||
proto.datagramReceived("123456987654\x08\x00foobar",
|
||||
partial=0)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
|
||||
def testMultiplePackets(self):
|
||||
proto = ethernet.EthernetProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': "012345",
|
||||
'source': "abcdef",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0800, p1)
|
||||
|
||||
proto.datagramReceived("123456987654\x08\x00foobar",
|
||||
partial=0)
|
||||
proto.datagramReceived("012345abcdef\x08\x00quux",
|
||||
partial=1)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
|
||||
def testMultipleSameProtos(self):
|
||||
proto = ethernet.EthernetProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
])
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
])
|
||||
|
||||
proto.addProto(0x0800, p1)
|
||||
proto.addProto(0x0800, p2)
|
||||
|
||||
proto.datagramReceived("123456987654\x08\x00foobar",
|
||||
partial=0)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testWrongProtoNotSeen(self):
|
||||
proto = ethernet.EthernetProtocol()
|
||||
p1 = MyProtocol([])
|
||||
proto.addProto(0x0801, p1)
|
||||
|
||||
proto.datagramReceived("123456987654\x08\x00foobar",
|
||||
partial=0)
|
||||
proto.datagramReceived("012345abcdef\x08\x00quux",
|
||||
partial=1)
|
||||
|
||||
def testDemuxing(self):
|
||||
proto = ethernet.EthernetProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': "012345",
|
||||
'source': "abcdef",
|
||||
'protocol': 0x0800,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0800, p1)
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': "012345",
|
||||
'source': "abcdef",
|
||||
'protocol': 0x0806,
|
||||
}),
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': "123456",
|
||||
'source': "987654",
|
||||
'protocol': 0x0806,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0806, p2)
|
||||
|
||||
proto.datagramReceived("123456987654\x08\x00foobar",
|
||||
partial=0)
|
||||
proto.datagramReceived("012345abcdef\x08\x06quux",
|
||||
partial=1)
|
||||
proto.datagramReceived("123456987654\x08\x06foobar",
|
||||
partial=0)
|
||||
proto.datagramReceived("012345abcdef\x08\x00quux",
|
||||
partial=1)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testAddingBadProtos_WrongLevel(self):
|
||||
"""Adding a wrong level protocol raises an exception."""
|
||||
e = ethernet.EthernetProtocol()
|
||||
try:
|
||||
e.addProto(42, "silliness")
|
||||
except components.CannotAdapt:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooSmall(self):
|
||||
"""Adding a protocol with a negative number raises an exception."""
|
||||
e = ethernet.EthernetProtocol()
|
||||
try:
|
||||
e.addProto(-1, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must be positive or zero',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooBig(self):
|
||||
"""Adding a protocol with a number >=2**16 raises an exception."""
|
||||
e = ethernet.EthernetProtocol()
|
||||
try:
|
||||
e.addProto(2**16, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 16 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
def testAddingBadProtos_TooBig2(self):
|
||||
"""Adding a protocol with a number >=2**16 raises an exception."""
|
||||
e = ethernet.EthernetProtocol()
|
||||
try:
|
||||
e.addProto(2**16+1, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 16 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
|
@ -0,0 +1,415 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.python import components
|
||||
from twisted.pair import ip, raw
|
||||
from zope import interface
|
||||
|
||||
|
||||
class MyProtocol:
|
||||
interface.implements(raw.IRawDatagramProtocol)
|
||||
|
||||
def __init__(self, expecting):
|
||||
self.expecting = list(expecting)
|
||||
|
||||
def datagramReceived(self, data, **kw):
|
||||
assert self.expecting, 'Got a packet when not expecting anymore.'
|
||||
expectData, expectKw = self.expecting.pop(0)
|
||||
|
||||
expectKwKeys = expectKw.keys(); expectKwKeys.sort()
|
||||
kwKeys = kw.keys(); kwKeys.sort()
|
||||
assert expectKwKeys == kwKeys, "Expected %r, got %r" % (expectKwKeys, kwKeys)
|
||||
|
||||
for k in expectKwKeys:
|
||||
assert expectKw[k] == kw[k], "Expected %s=%r, got %r" % (k, expectKw[k], kw[k])
|
||||
assert expectKw == kw, "Expected %r, got %r" % (expectKw, kw)
|
||||
assert expectData == data, "Expected %r, got %r" % (expectData, data)
|
||||
|
||||
class IPTestCase(unittest.TestCase):
|
||||
def testPacketParsing(self):
|
||||
proto = ip.IPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0F, p1)
|
||||
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
def testMultiplePackets(self):
|
||||
proto = ip.IPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': '5.4.3.2',
|
||||
'source': '6.7.8.9',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0F, p1)
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x06\x07\x08\x09" + "\x05\x04\x03\x02" + "quux",
|
||||
partial=1,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
|
||||
def testMultipleSameProtos(self):
|
||||
proto = ip.IPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
])
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
])
|
||||
|
||||
proto.addProto(0x0F, p1)
|
||||
proto.addProto(0x0F, p2)
|
||||
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testWrongProtoNotSeen(self):
|
||||
proto = ip.IPProtocol()
|
||||
p1 = MyProtocol([])
|
||||
proto.addProto(1, p1)
|
||||
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
|
||||
def testDemuxing(self):
|
||||
proto = ip.IPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': '5.4.3.2',
|
||||
'source': '6.7.8.9',
|
||||
'protocol': 0x0F,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
])
|
||||
proto.addProto(0x0F, p1)
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('quux', {
|
||||
'partial': 1,
|
||||
'dest': '5.4.3.2',
|
||||
'source': '6.7.8.9',
|
||||
'protocol': 0x0A,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
('foobar', {
|
||||
'partial': 0,
|
||||
'dest': '1.2.3.4',
|
||||
'source': '5.6.7.8',
|
||||
'protocol': 0x0A,
|
||||
'version': 4,
|
||||
'ihl': 20,
|
||||
'tos': 7,
|
||||
'tot_len': 20+6,
|
||||
'fragment_id': 0xDEAD,
|
||||
'fragment_offset': 0x1EEF,
|
||||
'dont_fragment': 0,
|
||||
'more_fragments': 1,
|
||||
'ttl': 0xC0,
|
||||
}),
|
||||
|
||||
|
||||
])
|
||||
proto.addProto(0x0A, p2)
|
||||
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0A" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x06\x07\x08\x09" + "\x05\x04\x03\x02" + "quux",
|
||||
partial=1,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0F" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x06\x07\x08\x09" + "\x05\x04\x03\x02" + "quux",
|
||||
partial=1,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x54" #ihl version
|
||||
+ "\x07" #tos
|
||||
+ "\x00\x1a" #tot_len
|
||||
+ "\xDE\xAD" #id
|
||||
+ "\xBE\xEF" #frag_off
|
||||
+ "\xC0" #ttl
|
||||
+ "\x0A" #protocol
|
||||
+ "FE" #checksum
|
||||
+ "\x05\x06\x07\x08" + "\x01\x02\x03\x04" + "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='dummy',
|
||||
protocol='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testAddingBadProtos_WrongLevel(self):
|
||||
"""Adding a wrong level protocol raises an exception."""
|
||||
e = ip.IPProtocol()
|
||||
try:
|
||||
e.addProto(42, "silliness")
|
||||
except components.CannotAdapt:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooSmall(self):
|
||||
"""Adding a protocol with a negative number raises an exception."""
|
||||
e = ip.IPProtocol()
|
||||
try:
|
||||
e.addProto(-1, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must be positive or zero',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooBig(self):
|
||||
"""Adding a protocol with a number >=2**32 raises an exception."""
|
||||
e = ip.IPProtocol()
|
||||
try:
|
||||
e.addProto(2L**32, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 32 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
def testAddingBadProtos_TooBig2(self):
|
||||
"""Adding a protocol with a number >=2**32 raises an exception."""
|
||||
e = ip.IPProtocol()
|
||||
try:
|
||||
e.addProto(2L**32+1, MyProtocol([]))
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 32 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
#
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.internet import protocol
|
||||
from twisted.pair import rawudp
|
||||
|
||||
class MyProtocol(protocol.DatagramProtocol):
|
||||
def __init__(self, expecting):
|
||||
self.expecting = list(expecting)
|
||||
|
||||
def datagramReceived(self, data, (host, port)):
|
||||
assert self.expecting, 'Got a packet when not expecting anymore.'
|
||||
expectData, expectHost, expectPort = self.expecting.pop(0)
|
||||
|
||||
assert expectData == data, "Expected data %r, got %r" % (expectData, data)
|
||||
assert expectHost == host, "Expected host %r, got %r" % (expectHost, host)
|
||||
assert expectPort == port, "Expected port %d=0x%04x, got %d=0x%04x" % (expectPort, expectPort, port, port)
|
||||
|
||||
class RawUDPTestCase(unittest.TestCase):
|
||||
def testPacketParsing(self):
|
||||
proto = rawudp.RawUDPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', 'testHost', 0x43A2),
|
||||
|
||||
])
|
||||
proto.addProto(0xF00F, p1)
|
||||
|
||||
proto.datagramReceived("\x43\xA2" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
def testMultiplePackets(self):
|
||||
proto = rawudp.RawUDPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', 'testHost', 0x43A2),
|
||||
('quux', 'otherHost', 0x33FE),
|
||||
|
||||
])
|
||||
proto.addProto(0xF00F, p1)
|
||||
proto.datagramReceived("\x43\xA2" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x33\xFE" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x05" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "quux",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='otherHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
|
||||
|
||||
def testMultipleSameProtos(self):
|
||||
proto = rawudp.RawUDPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', 'testHost', 0x43A2),
|
||||
|
||||
])
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('foobar', 'testHost', 0x43A2),
|
||||
|
||||
])
|
||||
|
||||
proto.addProto(0xF00F, p1)
|
||||
proto.addProto(0xF00F, p2)
|
||||
|
||||
proto.datagramReceived("\x43\xA2" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testWrongProtoNotSeen(self):
|
||||
proto = rawudp.RawUDPProtocol()
|
||||
p1 = MyProtocol([])
|
||||
proto.addProto(1, p1)
|
||||
|
||||
proto.datagramReceived("\x43\xA2" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
|
||||
def testDemuxing(self):
|
||||
proto = rawudp.RawUDPProtocol()
|
||||
p1 = MyProtocol([
|
||||
|
||||
('foobar', 'testHost', 0x43A2),
|
||||
('quux', 'otherHost', 0x33FE),
|
||||
|
||||
])
|
||||
proto.addProto(0xF00F, p1)
|
||||
|
||||
p2 = MyProtocol([
|
||||
|
||||
('quux', 'otherHost', 0xA401),
|
||||
('foobar', 'testHost', 0xA302),
|
||||
|
||||
])
|
||||
proto.addProto(0xB050, p2)
|
||||
|
||||
proto.datagramReceived("\xA4\x01" #source
|
||||
+ "\xB0\x50" #dest
|
||||
+ "\x00\x05" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "quux",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='otherHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x43\xA2" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
proto.datagramReceived("\x33\xFE" #source
|
||||
+ "\xf0\x0f" #dest
|
||||
+ "\x00\x05" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "quux",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='otherHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
proto.datagramReceived("\xA3\x02" #source
|
||||
+ "\xB0\x50" #dest
|
||||
+ "\x00\x06" #len
|
||||
+ "\xDE\xAD" #check
|
||||
+ "foobar",
|
||||
partial=0,
|
||||
dest='dummy',
|
||||
source='testHost',
|
||||
protocol='dummy',
|
||||
version='dummy',
|
||||
ihl='dummy',
|
||||
tos='dummy',
|
||||
tot_len='dummy',
|
||||
fragment_id='dummy',
|
||||
fragment_offset='dummy',
|
||||
dont_fragment='dummy',
|
||||
more_fragments='dummy',
|
||||
ttl='dummy',
|
||||
)
|
||||
|
||||
assert not p1.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p1.expecting
|
||||
assert not p2.expecting, \
|
||||
'Should not expect any more packets, but still want %r' % p2.expecting
|
||||
|
||||
def testAddingBadProtos_WrongLevel(self):
|
||||
"""Adding a wrong level protocol raises an exception."""
|
||||
e = rawudp.RawUDPProtocol()
|
||||
try:
|
||||
e.addProto(42, "silliness")
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must be an instance of DatagramProtocol',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooSmall(self):
|
||||
"""Adding a protocol with a negative number raises an exception."""
|
||||
e = rawudp.RawUDPProtocol()
|
||||
try:
|
||||
e.addProto(-1, protocol.DatagramProtocol())
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must be positive or zero',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
|
||||
def testAddingBadProtos_TooBig(self):
|
||||
"""Adding a protocol with a number >=2**16 raises an exception."""
|
||||
e = rawudp.RawUDPProtocol()
|
||||
try:
|
||||
e.addProto(2**16, protocol.DatagramProtocol())
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 16 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
|
||||
def testAddingBadProtos_TooBig2(self):
|
||||
"""Adding a protocol with a number >=2**16 raises an exception."""
|
||||
e = rawudp.RawUDPProtocol()
|
||||
try:
|
||||
e.addProto(2**16+1, protocol.DatagramProtocol())
|
||||
except TypeError, e:
|
||||
if e.args == ('Added protocol must fit in 16 bits',):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise AssertionError, 'addProto must raise an exception for bad protocols'
|
||||
File diff suppressed because it is too large
Load diff
570
Linux_i686/lib/python2.7/site-packages/twisted/pair/testing.py
Normal file
570
Linux_i686/lib/python2.7/site-packages/twisted/pair/testing.py
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
# 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)
|
||||
|
||||
|
||||
# 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
|
||||
'\x45'
|
||||
# Differentiated services field
|
||||
'\x00'
|
||||
# Total length
|
||||
+ _H(20 + len(payload))
|
||||
+ '\x00\x01\x00\x00\x40\x11'
|
||||
# Checksum
|
||||
+ _H(0)
|
||||
# Source address
|
||||
+ socket.inet_pton(socket.AF_INET, src)
|
||||
# Destination address
|
||||
+ socket.inet_pton(socket.AF_INET, 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 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='\x00' * 6, dst='\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.func_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] + "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))
|
||||
|
||||
self._openFiles.values()[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]
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
Twisted Pair 14.0.0 (2014-05-08)
|
||||
================================
|
||||
|
||||
Features
|
||||
--------
|
||||
- twisted.pair.tuntap now has complete test coverage, basic
|
||||
documentation, and works without the difficult-to-find system
|
||||
bindings it used to require. (#6169)
|
||||
|
||||
Other
|
||||
-----
|
||||
- #6898, #6931, #6993
|
||||
|
||||
|
||||
Twisted Pair 13.2.0 (2013-10-29)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 13.1.0 (2013-06-23)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 13.0.0 (2013-03-19)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 12.3.0 (2012-12-20)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 12.2.0 (2012-08-26)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 12.1.0 (2012-06-02)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 12.0.0 (2012-02-10)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 11.1.0 (2011-11-15)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 11.0.0 (2011-04-01)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 10.2.0 (2010-11-29)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 10.1.0 (2010-06-27)
|
||||
================================
|
||||
|
||||
No significant changes have been made for this release.
|
||||
|
||||
|
||||
Twisted Pair 10.0.0 (2010-03-01)
|
||||
================================
|
||||
|
||||
Other
|
||||
-----
|
||||
- #4170
|
||||
|
||||
|
||||
Twisted Pair 9.0.0 (2009-11-24)
|
||||
===============================
|
||||
|
||||
Other
|
||||
-----
|
||||
- #3540, #4050
|
||||
|
||||
|
||||
Pair 8.2.0 (2008-12-16)
|
||||
=======================
|
||||
|
||||
No interesting changes since Twisted 8.0.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Twisted Pair 14.0.0: Very low-level networking functionality
|
||||
|
||||
Twisted Pair includes parsers for several datagram formats, including ethernet,
|
||||
IP, and UDP. It also offers low-level networking integration on Linux via tun
|
||||
and tap devices.
|
||||
|
||||
Twisted Pair depends on Twisted Core.
|
||||
|
||||
To be able to run the Twisted Pair test suite as an unprivileged user, you need
|
||||
to create a tunnel device owned by that user. Likewise, to use Twisted Pair's
|
||||
tuntap support as an unprivileged user, you must do the same. See the
|
||||
configuration howto for details.
|
||||
434
Linux_i686/lib/python2.7/site-packages/twisted/pair/tuntap.py
Normal file
434
Linux_i686/lib/python2.7/site-packages/twisted/pair/tuntap.py
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
# -*- 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 zope.interface import Attribute, Interface, implementer
|
||||
|
||||
from twisted.python.util import FancyEqMixin, FancyStrMixin
|
||||
from twisted.python.versions import Version
|
||||
from twisted.python.reflect import fullyQualifiedName
|
||||
from twisted.python.deprecate import deprecated
|
||||
from twisted.python.constants import Flags, FlagConstant
|
||||
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, object, FancyEqMixin):
|
||||
"""
|
||||
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 = (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('\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)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue