##################################################################### # -*- coding: iso-8859-1 -*- # # # # Frets on Fire X (FoFiX) # # Copyright (C) 2009 myfingershurt # # 2009 John Stumpo # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # # as published by the Free Software Foundation; either version 2 # # of the License, or (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### """Drop-in replacements for socket and asyncore that implement loopback connections, sans the loopback connections. Only the necessary functionality to replace FoFiX's loopback connection is implemented. Limitations: - TCP only - Address connected to/listened to is ignored. - EOF doesn't get passed around correctly, but the rest of the code seems to be able to deal with it. - socket.errors are raised with strings rather than tuples. - All sockets are non-blocking. - No support for select(). - Probably rough around the edges in many other ways. """ __version__ = '$Id: FakeNetworking.py 914 2009-02-05 01:18:15Z john.stumpo $' # Keep track of what ports are being listened to. class FakeTcpStack(object): def __init__(self): self.openPorts = {} def registerOpenPort(self, port, obj): if self.openPorts.has_key(port): raise socket.error, 'Address already in use.' self.openPorts[port] = obj def releaseOpenPort(self, port): del self.openPorts[port] def connectSocket(self, port, sock): if not self.openPorts.has_key(port): raise socket.error, 'Connection refused.' self.openPorts[port].backlog.append(sock) tcpStack = FakeTcpStack() # singleton instance del FakeTcpStack # Reimplementation of socket. class socket(object): AF_INET = None SOCK_STREAM = None class error(Exception): pass def __init__(self, af, socktype, proto): self.bindaddr = None self.pairedSocket = None self.listening = False self.buf = '' def __del__(self): self.close() @staticmethod def socket(af, socktype, proto): return socket(af, socktype, proto) def bind(self, addr): if self.listening or (self.pairedSocket is not None) or (self.bindaddr is not None): raise socket.error, 'Socket already bound.' self.bindaddr = addr def listen(self, backlog): self.backlog = [] self.listening = True tcpStack.registerOpenPort(self.bindaddr[1], self) def accept(self): if not self.listening: raise socket.error, 'Invalid argument.' if not len(self.backlog): raise socket.error, 'The socket operation could not complete without blocking.' s = self.backlog[0] del self.backlog[0] return s, ('127.0.0.1', 1024) def connect(self, addr): self.pairedSocket = socket.socket(None, None, None) self.pairedSocket.pairedSocket = self tcpStack.connectSocket(addr[1], self.pairedSocket) def close(self): if self.pairedSocket is not None: self.pairedSocket = None if self.listening: try: tcpStack.releaseOpenPort(self.bindaddr[1]) except: pass del self.backlog self.listening = False self.bindaddr = None def send(self, buf, length=None): if self.pairedSocket is None: raise socket.error, 'Socket is not connected.' try: self.pairedSocket.buf += buf return len(buf) except: self.close() raise socket.error, 'Connection reset by peer.' sendall = send def recv(self, length=4096): if self.pairedSocket is None: raise socket.error, 'Socket is not connected.' try: if len(self.buf) == 0: raise socket.error, 'The socket operation could not complete without blocking.' if len(self.buf) < length: buf = self.buf self.buf = '' return buf buf = self.buf[:length] self.buf = self.buf[length:] return buf except socket.error: raise except: self.close() raise socket.error, 'Connection reset by peer.' def readable(self): return (len(self.buf) > 0) def writable(self): return (self.pairedSocket is not None) def acceptable(self): return (self.listening and len(self.backlog) > 0) # Reimplementation of asyncore atop the above. class asyncore(object): socket_map = [] class dispatcher(object): def __init__(self, sock=None): self._socket = sock self.connected = False asyncore.socket_map.append(self) def __del__(self): self.close() def create_socket(self, af, socktype): self._socket = socket.socket(af, socktype, 0) def set_reuse_addr(self): pass def bind(self, addr): if self._socket is None: raise socket.error, 'Bad file descriptor' self._socket.bind(addr) def listen(self, backlog): if self._socket is None: raise socket.error, 'Bad file descriptor' self._socket.listen(backlog) def accept(self): if self._socket is None: raise socket.error, 'Bad file descriptor' return self._socket.accept() def close(self): if self._socket is not None: self._socket.close() self._socket = None try: asyncore.socket_map.remove(self) except ValueError: pass def send(self, data): if self._socket is None: raise socket.error, 'Bad file descriptor' return self._socket.send(data) def recv(self, length=4096): if self._socket is None: raise socket.error, 'Bad file descriptor' return self._socket.recv(length) def connect(self, addr): if self._socket is None: raise socket.error, 'Bad file descriptor' self._socket.connect(addr) self.connected = True def readable(self): if self._socket is None: return False return self._socket.readable() def writable(self): if self._socket is None: return False return self._socket.writable() def acceptable(self): if self._socket is None: return False return self._socket.acceptable() @classmethod def poll(self, arg, lst): for d in lst: if d.readable(): if hasattr(d, 'handle_read'): d.handle_read() if d.writable(): if hasattr(d, 'handle_write'): d.handle_write() if d.acceptable(): if hasattr(d, 'handle_accept'): d.handle_accept() @classmethod def close_all(self): pass