| 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 |