OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 # | |
5 import errno, os | |
6 from twisted.python import log, reflect, components | |
7 from twisted.internet import base, fdesc, error | |
8 from twisted.pair import ethernet, ip | |
9 | |
10 """ | |
11 You need Eunuchs for twisted.pair.tuntap to work. | |
12 | |
13 Eunuchs is a library containing the missing manly parts of | |
14 UNIX API for Python. | |
15 | |
16 Eunuchs is a library of Python extension that complement the standard | |
17 libraries in parts where full support for the UNIX API (or the Linux | |
18 API) is missing. | |
19 | |
20 Most of the functions wrapped by Eunuchs are low-level, dirty, but | |
21 absolutely necessary functions for real systems programming. The aim is | |
22 to have the functions added to mainstream Python libraries. | |
23 | |
24 Current list of functions included: | |
25 | |
26 - fchdir(2) | |
27 - recvmsg(2) and sendmsg(2), including use of cmsg(3) | |
28 - socketpair(2) | |
29 - support for TUN/TAP virtual network interfaces | |
30 | |
31 Eunuchs doesn't have a proper web home right now, but you can fetch | |
32 the source from http://ftp.debian.org/debian/pool/main/e/eunuch | |
33 -- debian users can just use 'apt-get install python-eunuchs'. | |
34 | |
35 """ | |
36 from eunuchs.tuntap import opentuntap, TuntapPacketInfo, makePacketInfo | |
37 | |
38 class TuntapPort(base.BasePort): | |
39 """A Port that reads and writes packets from/to a TUN/TAP-device. | |
40 | |
41 TODO: Share general start/stop etc implementation details with | |
42 twisted.internet.udp.Port. | |
43 """ | |
44 maxThroughput = 256 * 1024 # max bytes we read in one eventloop iteration | |
45 | |
46 def __init__(self, interface, proto, maxPacketSize=8192, reactor=None): | |
47 if components.implements(proto, ethernet.IEthernetProtocol): | |
48 self.ethernet = 1 | |
49 else: | |
50 self.ethernet = 0 | |
51 assert components.implements(proto, ip.IIPProtocol) # XXX: fix me | |
52 base.BasePort.__init__(self, reactor) | |
53 self.interface = interface | |
54 self.protocol = proto | |
55 self.maxPacketSize = maxPacketSize | |
56 self.setLogStr() | |
57 | |
58 def __repr__(self): | |
59 return "<%s on %s>" % (self.protocol.__class__, self.interface) | |
60 | |
61 def startListening(self): | |
62 """Create and bind my socket, and begin listening on it. | |
63 | |
64 This is called on unserialization, and must be called after creating a | |
65 server to begin listening on the specified port. | |
66 """ | |
67 self._bindSocket() | |
68 self._connectToProtocol() | |
69 | |
70 def _bindSocket(self): | |
71 log.msg("%s starting on %s"%(self.protocol.__class__, self.interface)) | |
72 try: | |
73 fd, name = opentuntap(name=self.interface, | |
74 ethernet=self.ethernet, | |
75 packetinfo=0) | |
76 except OSError, e: | |
77 raise error.CannotListenError, (None, self.interface, e) | |
78 fdesc.setNonBlocking(fd) | |
79 self.interface = name | |
80 self.connected = 1 | |
81 self.fd = fd | |
82 | |
83 def fileno(self): | |
84 return self.fd | |
85 | |
86 def _connectToProtocol(self): | |
87 self.protocol.makeConnection(self) | |
88 self.startReading() | |
89 | |
90 def doRead(self): | |
91 """Called when my socket is ready for reading.""" | |
92 read = 0 | |
93 while read < self.maxThroughput: | |
94 try: | |
95 data = os.read(self.fd, self.maxPacketSize) | |
96 read += len(data) | |
97 # pkt = TuntapPacketInfo(data) | |
98 self.protocol.datagramReceived(data, | |
99 partial=0 # pkt.isPartial(), | |
100 ) | |
101 except OSError, e: | |
102 if e.errno in (errno.EWOULDBLOCK,): | |
103 return | |
104 else: | |
105 raise | |
106 except IOError, e: | |
107 if e.errno in (errno.EAGAIN, errno.EINTR): | |
108 return | |
109 else: | |
110 raise | |
111 except: | |
112 log.deferr() | |
113 | |
114 def write(self, datagram): | |
115 """Write a datagram.""" | |
116 # header = makePacketInfo(0, 0) | |
117 try: | |
118 return os.write(self.fd, datagram) | |
119 except IOError, e: | |
120 if e.errno == errno.EINTR: | |
121 return self.write(datagram) | |
122 elif e.errno == errno.EMSGSIZE: | |
123 raise error.MessageLengthError, "message too long" | |
124 elif e.errno == errno.ECONNREFUSED: | |
125 raise error.ConnectionRefusedError | |
126 else: | |
127 raise | |
128 | |
129 def writeSequence(self, seq): | |
130 self.write("".join(seq)) | |
131 | |
132 def loseConnection(self): | |
133 """Stop accepting connections on this port. | |
134 | |
135 This will shut down my socket and call self.connectionLost(). | |
136 """ | |
137 self.stopReading() | |
138 if self.connected: | |
139 from twisted.internet import reactor | |
140 reactor.callLater(0, self.connectionLost) | |
141 | |
142 stopListening = loseConnection | |
143 | |
144 def connectionLost(self, reason=None): | |
145 """Cleans up my socket. | |
146 """ | |
147 log.msg('(Tuntap %s Closed)' % self.interface) | |
148 base.BasePort.connectionLost(self, reason) | |
149 if hasattr(self, "protocol"): | |
150 # we won't have attribute in ConnectedPort, in cases | |
151 # where there was an error in connection process | |
152 self.protocol.doStop() | |
153 self.connected = 0 | |
154 os.close(self.fd) | |
155 del self.fd | |
156 | |
157 def setLogStr(self): | |
158 self.logstr = reflect.qual(self.protocol.__class__) + " (TUNTAP)" | |
159 | |
160 def logPrefix(self): | |
161 """Returns the name of my class, to prefix log entries with. | |
162 """ | |
163 return self.logstr | |
164 | |
165 def getHost(self): | |
166 """ | |
167 Returns a tuple of ('TUNTAP', interface), indicating | |
168 the servers address | |
169 """ | |
170 return ('TUNTAP',)+self.interface | |
OLD | NEW |