def __init__(self, log=None, asmLog=None): self.log = log if type(self.log) == str: self.log = logging.getLogger(self.log) if type(asmLog) == str: asmLog = logging.getLogger(asmLog) self.realSockManager = AsyncSocketManager(log=asmLog) # handler for real sockets self.realSockStatus = RealSocketStatus(self.realSockManager) self.i2pSockActEvent = threading.Event() self.i2pSockStatus = I2PSocketStatus(self.i2pSockActEvent) self.i2pDests = {} # destination num => destination object self.uniqueDestId = 1 self.shouldStop = True self.lock = threading.Lock() # the main lock self.thread = None
class I2PSocketManager: def __init__(self, log=None, asmLog=None): self.log = log if type(self.log) == str: self.log = logging.getLogger(self.log) if type(asmLog) == str: asmLog = logging.getLogger(asmLog) self.realSockManager = AsyncSocketManager(log=asmLog) # handler for real sockets self.realSockStatus = RealSocketStatus(self.realSockManager) self.i2pSockActEvent = threading.Event() self.i2pSockStatus = I2PSocketStatus(self.i2pSockActEvent) self.i2pDests = {} # destination num => destination object self.uniqueDestId = 1 self.shouldStop = True self.lock = threading.Lock() # the main lock self.thread = None ##THREAD## def _start(self): assert self.shouldStop == True, "not stopped?! out of sync?" self.shouldStop = False if self.thread is None: # only start a thread if the old one already died self.thread = threading.Thread(target=self.run) self.thread.start() if self.log is not None: self.log.debug("Starting thread") def _stop(self): assert self.shouldStop == False, "stopped?! out of sync?" assert self.thread is not None, "no thread but stopping?!" assert self.i2pSockStatus.getConnAmount() == 0, "stopping but still i2p conns?!" self.shouldStop = True self.i2pSockActEvent.set() if self.log is not None: self.log.debug("Stopping thread") # for debugging assert len(self.i2pDests) == 0, "stopping thread but destinations left?!" ##INTERNAL - destination def _addDestination( self, ip, port, sessionName, sessionType, sessionDirection, sessionOptions, defaultInMaxQueueSize, defaultOutMaxQueueSize, defaultInRecvLimitThreshold, ): destId = self.uniqueDestId self.uniqueDestId += 1 if sessionType == "raw": destObj = SamDestination.SamRawDestination( destId, self.realSockManager, self.realSockStatus, self.log, ip, port, sessionName, sessionDirection, sessionOptions, self.i2pSockStatus, ) result = (destId, destObj.getI2PSocketId()) elif sessionType == "udp": destObj = SamDestination.SamUdpDestination( destId, self.realSockManager, self.realSockStatus, self.log, ip, port, sessionName, sessionDirection, sessionOptions, self.i2pSockStatus, ) result = (destId, destObj.getI2PSocketId()) elif sessionType == "tcp": destObj = SamDestination.SamTcpDestination( destId, self.realSockManager, self.realSockStatus, self.log, ip, port, sessionName, sessionDirection, sessionOptions, self.i2pSockStatus, defaultOutMaxQueueSize, defaultInMaxQueueSize, defaultInRecvLimitThreshold, ) result = destId self.i2pDests[destId] = {"obj": destObj, "type": sessionType} if len(self.i2pDests) == 1: # first destination, start threa self._start() return result def _removeDestination(self, destId): self.i2pDests[destId]["obj"].shutdown() del self.i2pDests[destId] if len(self.i2pDests) == 0: # just removed last destination, stop thread self._stop() def _removeAllDestinations(self): for destId in self.i2pDests.keys(): self._removeDestination(destId) ##EXTERNAL - DESTINATION## def addDestination( self, ip, port, sessionName, sessionType, sessionDirection, sessionOptions={}, defaultInMaxQueueSize=32768, defaultOutMaxQueueSize=32768, defaultInRecvLimitThreshold=None, ): self.lock.acquire() if defaultInRecvLimitThreshold is None: defaultInRecvLimitThreshold = defaultInMaxQueueSize / 2 result = self._addDestination( ip, port, sessionName, sessionType, sessionDirection, sessionOptions, defaultInMaxQueueSize, defaultOutMaxQueueSize, defaultInRecvLimitThreshold, ) self.lock.release() return result def removeDestination(self, destId): self.lock.acquire() self._removeDestination(destId) self.lock.release() def changeDefaultQueueSize(self, destId, defaultInMaxQueueSize=None, defaultOutMaxQueueSize=None): self.lock.acquire() if destId in self.i2pDests: destSet = self.i2pDests[destId] if destSet["type"] == "tcp": destSet["obj"].changeDefaultQueueSize(defaultInMaxQueueSize, defaultOutMaxQueueSize) self.lock.release() def changeDefaultInRecvLimitThreshold(self, destId, defaultInRecvLimitThreshold): self.lock.acquire() if destId in self.i2pDests: destSet = self.i2pDests[destId] if destSet["type"] == "tcp": self.changeDefaultInRecvLimitThreshold(defaultInRecvLimitThreshold) self.lock.release() def changeSessionAddress(self, destId, ip=None, port=None, reconnect=False): self.lock.acquire() if destId in self.i2pDests: self.i2pDests[destId]["obj"].changeSessionAddress(ip, port, reconnect) self.lock.release() def changeSessionName(self, destId, sessionName, reconnect=False): self.lock.acquire() if destId in self.i2pDests: self.i2pDests[destId]["obj"].changeSessionName(sessionName, reconnect) self.lock.release() def changeSessionOption(self, destId, option, value, reconnect=False): self.lock.acquire() if destId in self.i2pDests: self.i2pDests[destId]["obj"].changeSessionOption(option, value, reconnect) self.lock.release() def removeSessionOption(self, destId, option, reconnect=False): self.lock.acquire() if destId in self.i2pDests: self.i2pDests[destId]["obj"].removeSessionOption(option, reconnect) self.lock.release() def replaceSessionOptions(self, destId, sessionOptions, reconnect=False): self.lock.acquire() if destId in self.i2pDests: self.i2pDests[destId]["obj"].replaceSessionOptions(sessionOptions, reconnect) self.lock.release() ##EXTERNAL - SOCKETS## def connect(self, destId, remoteDest, inMaxQueueSize=None, outMaxQueueSize=None, inRecvLimitThreshold=None): self.lock.acquire() if inRecvLimitThreshold is None and inMaxQueueSize is not None: inRecvLimitThreshold = inMaxQueueSize / 2 i2pSocketId = self.i2pDests[destId]["obj"].connect( remoteDest, inMaxQueueSize, outMaxQueueSize, inRecvLimitThreshold ) self.lock.release() return i2pSocketId def listen(self, destId, addOld=False): self.lock.acquire() destSet = self.i2pDests[destId] i2pSocketId = None if destSet["type"] == "tcp": i2pSocketId = destSet["obj"].startListening(addOld) self.lock.release() return i2pSocketId def accept(self, i2pSocketId, max=-1): self.lock.acquire() newConns = [] if self.i2pSockStatus.getRecvable(i2pSocketId): # socket is valid and in the right state destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "tcpListen": newConns = self.i2pDests[destId]["obj"].accept(max) self.lock.release() return newConns def send(self, i2pSocketId, data, target=None): self.lock.acquire() bytesSend = 0 if self.i2pSockStatus.getSendable(i2pSocketId): # socket is valid and in the right state destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "udp" or connType == "raw": # udp-like transport, the referenced socket is the only socket of that destination, no buffer limits bytesSend = self.i2pDests[destId]["obj"].send(target, data) elif connType == "tcpOut" or connType == "tcpIn": # tcp-like transport bytesSend = self.i2pDests[destId]["obj"].send(i2pSocketId, data) self.lock.release() return bytesSend def recv(self, i2pSocketId, max=-1, peekOnly=False): self.lock.acquire() data = "" if self.i2pSockStatus.getRecvable(i2pSocketId): # socket is valid and in the right state destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "udp" or connType == "raw": data = self.i2pDests[destId]["obj"].recv(max, peekOnly) elif connType == "tcpOut" or connType == "tcpIn": data = self.i2pDests[destId]["obj"].recv(i2pSocketId, max, peekOnly) self.lock.release() return data def close(self, i2pSocketId, force=False): self.lock.acquire() if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "udp" or connType == "raw": # only a single socket, close that one and the dests goes down, too self._removeDestination(destId) elif connType == "tcpOut" or connType == "tcpIn": # tcp like self.i2pDests[destId]["obj"].close(i2pSocketId, force) elif connType == "tcpListen": # tcp listening socket self.i2pDests[destId]["obj"].stopListening(i2pSocketId) self.lock.release() def getOwnDestination(self, destId=None, i2pSocketId=None, timeout=None): self.lock.acquire() if destId is None: destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) startTime = time() destination = self.i2pDests[destId]["obj"].getOwnDestination() if timeout is not None: while destination is None and (timeout == -1 or startTime + timeout >= time()): self.lock.release() sleep(0.1) self.lock.acquire() destination = self.i2pDests[destId]["obj"].getOwnDestination() self.lock.release() return destination def getI2PSocketRemoteDestination(self, i2pSocketId): self.lock.acquire() remoteDest = "" if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "tcpOut" or connType == "tcpIn": remoteDest = self.i2pDests[destId]["obj"].getI2PSocketRemoteDestination(i2pSocketId) self.lock.release() return remoteDest def getI2PSocketUsedInBufferSpace(self, i2pSocketId): self.lock.acquire() usedSpace = 0 if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "tcpOut" or connType == "tcpIn": usedSpace = self.i2pDests[destId]["obj"].getI2PSocketUsedInBufferSpace(i2pSocketId) self.lock.release() return usedSpace def getI2PSocketFreeOutBufferSpace(self, i2pSocketId): self.lock.acquire() freeSpace = 0 if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "tcpOut" or connType == "tcpIn": freeSpace = self.i2pDests[destId]["obj"].getI2PSocketFreeOutBufferSpace(i2pSocketId) self.lock.release() return freeSpace def getI2PSocketType(self, i2pSocketId): self.lock.acquire() connType = "None" if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) self.lock.release() return connType def getI2PSocketErrorReason(self, i2pSocketId): self.lock.acquire() errorReason = "UNKNOWN_SOCKET" if self.i2pSockStatus.connExists(i2pSocketId): # socket is valid destId, connType = self.i2pSockStatus.getConnInfo(i2pSocketId) if connType == "tcpOut" or connType == "tcpIn": errorReason = self.i2pDests[destId]["obj"].getI2PSocketErrorReason(i2pSocketId) self.lock.release() return errorReason def select(self, recvInterest, sendInterest, errorInterest, timeout=None): self.lock.acquire() startTime = time() finished = False while finished == False: # generate sets recvable, sendable, errored = self.i2pSockStatus.select(recvInterest, sendInterest, errorInterest) # check if we should stop looping if len(recvable) > 0 or len(sendable) > 0 or len(errored) > 0: finished = True else: if timeout is not None: if time() - startTime > timeout: finished = True # check if we need to wait if not finished: self.lock.release() if timeout is None: self.i2pSockActEvent.wait() else: self.i2pSockActEvent.wait(timeout + startTime - time()) self.i2pSockActEvent.clear() self.lock.acquire() self.lock.release() return recvable, sendable, errored ##EXTERNAL - OTHER def shutdown(self): self.lock.acquire() thread = self.thread self._removeAllDestinations() self.lock.release() if thread is not None: # wait for thread to die thread.join() self.realSockManager.shutdown() ##MAIN LOOP## def run(self): self.lock.acquire() try: while self.shouldStop == False: self.lock.release() sockEvents = self.realSockStatus.getConnEvents() self.lock.acquire() for realSockId, eventmask in sockEvents: if eventmask & POLLRECV != 0: ##recv if self.realSockStatus.connExists(realSockId): destId = self.realSockStatus.getConnInfo(realSockId) self.i2pDests[destId]["obj"].recvEvent() if eventmask & POLLSEND != 0: ##send if self.realSockStatus.connExists(realSockId): destId = self.realSockStatus.getConnInfo(realSockId) self.i2pDests[destId]["obj"].sendEvent() if eventmask & POLLERROR != 0: ##error if self.realSockStatus.connExists(realSockId): destId = self.realSockStatus.getConnInfo(realSockId) self.i2pDests[destId]["obj"].errorEvent() # delete own reference self.thread = None except: # main loop crashed if self.log is not None: # ok, log traceback self.log.error("Error in main loop!\nTraceback:\n%s", getTraceback()) self.lock.release()