class Connection(object): direction = 'undefined' identifier = 0 def __init__(self, afi, peer, local): # peer and local are strings of the IP # If the OS tells us we have data on the socket, we should never have to wait more than read_timeout to be able to read it. # However real life says that on some OS we do ... So let the user control this value try: self.read_timeout = environment.settings().tcp.timeout self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.read_timeout = 1 self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self._reading = None self._writing = None self._buffer = '' self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__(self): if self.io: self.logger.network( "%s connection to %s closed" % (self.name(), self.peer), 'info') self.close() def name(self): return "session %d %s" % (self.id, self.direction) def close(self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(), self.local, self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt, e: raise e except:
class Connection (object): direction = 'undefined' identifier = 0 def __init__ (self,afi,peer,local): # peer and local are strings of the IP # If the OS tells us we have data on the socket, we should never have to wait more than read_timeout to be able to read it. # However real life says that on some OS we do ... So let the user control this value try: self.read_timeout = environment.settings().tcp.timeout self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.read_timeout = 1 self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self._reading = None self._writing = None self._buffer = '' self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__ (self): if self.io: self.logger.network("%s connection to %s closed" % (self.name(),self.peer),'info') self.close() def name (self): return "session %d %s" % (self.id,self.direction) def close (self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(),self.local,self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt,e: raise e except:
class Connection(object): direction = 'undefined' identifier = 0 def __init__(self, afi, peer, local): # peer and local are strings of the IP try: self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self._buffer = '' self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__(self): if self.io: self.logger.network( "%s connection to %s closed" % (self.name(), self.peer), 'info') self.close() def name(self): return "session %d %s" % (self.id, self.direction) def close(self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(), self.local, self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt, exc: raise exc except:
class Connection (object): direction = 'undefined' identifier = 0 def __init__ (self, afi, peer, local): # peer and local are strings of the IP try: self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self._buffer = '' self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__ (self): if self.io: self.logger.network("%s connection to %s closed" % (self.name(),self.peer),'info') self.close() def name (self): return "session %d %s" % (self.id,self.direction) def close (self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(),self.local,self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt,exc: raise exc except:
class Connection(object): direction = 'undefined' identifier = 0 def __init__(self, afi, peer, local): # peer and local are strings of the IP try: self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__(self): if self.io: self.logger.network( "%s connection to %s closed" % (self.name(), self.peer), 'info') self.close() def name(self): return "session %d %s" % (self.id, self.direction) def close(self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(), self.local, self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt as exc: raise exc except Exception: pass def reading(self): while True: try: r, _, _ = select.select([ self.io, ], [], [], 0) except select.error as exc: if exc.args[0] not in error.block: self.close() self.logger.wire( "%s %s errno %s on socket" % (self.name(), self.peer, errno.errorcode[exc.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return r != [] def writing(self): while True: try: _, w, _ = select.select([], [ self.io, ], [], 0) except select.error as exc: if exc.args[0] not in error.block: self.close() self.logger.wire( "%s %s errno %s on socket" % (self.name(), self.peer, errno.errorcode[exc.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return w != [] def _reader(self, number): # The function must not be called if it does not return with no data with a smaller size as parameter if not self.io: self.close() raise NotConnected('Trying to read on a closed TCP connection') if number == 0: yield b'' return while not self.reading(): yield b'' data = b'' reported = '' while True: try: while True: if self.defensive and random.randint(0, 2): raise socket.error(errno.EAGAIN, 'raising network error on purpose') read = self.io.recv(number) if not read: self.close() self.logger.wire("%s %s lost TCP session with peer" % (self.name(), self.peer)) raise LostConnection( 'the TCP connection was closed by the remote end') data += read number -= len(read) if not number: self.logger.wire( LazyFormat( "%s %-32s RECEIVED " % (self.name(), '%s / %s' % (self.local, self.peer)), read)) yield data return yield b'' except socket.timeout as exc: self.close() self.logger.wire("%s %s peer is too slow" % (self.name(), self.peer)) raise TooSlowError( 'Timeout while reading data from the network (%s)' % errstr(exc)) except socket.error as exc: if exc.args[0] in error.block: message = "%s %s blocking io problem mid-way through reading a message %s, trying to complete" % ( self.name(), self.peer, errstr(exc)) if message != reported: reported = message self.logger.wire(message, 'debug') yield b'' elif exc.args[0] in error.fatal: self.close() raise LostConnection('issue reading on the socket: %s' % errstr(exc)) # what error could it be ! else: self.logger.wire( "%s %s undefined error reading on socket" % (self.name(), self.peer)) raise NetworkError( 'Problem while reading data from the network (%s)' % errstr(exc)) def writer(self, data): if not self.io: # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session yield True return while not self.writing(): yield False self.logger.wire( LazyFormat( "%s %-32s SENDING " % (self.name(), '%s / %s' % (self.local, self.peer)), data)) # The first while is here to setup the try/catch block once as it is very expensive while True: try: while True: if self.defensive and random.randint(0, 2): raise socket.error(errno.EAGAIN, 'raising network error on purpose') # we can not use sendall as in case of network buffer filling # it does raise and does not let you know how much was sent number = self.io.send(data) if not number: self.close() self.logger.wire( "%s %s lost TCP connection with peer" % (self.name(), self.peer)) raise LostConnection('lost the TCP connection') data = data[number:] if not data: yield True return yield False except socket.error as exc: if exc.args[0] in error.block: self.logger.wire( "%s %s blocking io problem mid-way through writing a message %s, trying to complete" % (self.name(), self.peer, errstr(exc)), 'debug') yield False elif exc.errno == errno.EPIPE: # The TCP connection is gone. self.close() raise NetworkError('Broken TCP connection') elif exc.args[0] in error.fatal: self.close() self.logger.wire("%s %s problem sending message (%s)" % (self.name(), self.peer, errstr(exc))) raise NetworkError( 'Problem while writing data to the network (%s)' % errstr(exc)) # what error could it be ! else: self.logger.wire( "%s %s undefined error writing on socket" % (self.name(), self.peer)) yield False def reader(self): # _reader returns the whole number requested or nothing and then stops for header in self._reader(Message.HEADER_LEN): if not header: yield 0, 0, b'', b'', None if not header.startswith(Message.MARKER): report = 'The packet received does not contain a BGP marker' yield 0, 0, header, b'', NotifyError(1, 1, report) return msg = ordinal(header[18]) length = unpack('!H', header[16:18])[0] if length < Message.HEADER_LEN or length > Message.MAX_LEN: report = '%s has an invalid message length of %d' % ( Message.CODE.name(msg), length) yield length, 0, header, b'', NotifyError(1, 2, report) return validator = Message.Length.get(msg, lambda _: _ >= 19) if not validator(length): # MUST send the faulty length back report = '%s has an invalid message length of %d' % ( Message.CODE.name(msg), length) yield length, 0, header, b'', NotifyError(1, 2, report) return number = length - Message.HEADER_LEN if not number: yield length, msg, header, b'', None return for body in self._reader(number): if not body: yield 0, 0, b'', b'', None yield length, msg, header, body, None
class Connection (object): direction = 'undefined' identifier = 0 def __init__ (self, afi, peer, local): # peer and local are strings of the IP try: self.defensive = environment.settings().debug.defensive self.logger = Logger() except RuntimeError: self.defensive = True self.logger = FakeLogger() self.afi = afi self.peer = peer self.local = local self._buffer = '' self.io = None self.established = False self.identifier += 1 self.id = self.identifier # Just in case .. def __del__ (self): if self.io: self.logger.network("%s connection to %s closed" % (self.name(),self.peer),'info') self.close() def name (self): return "session %d %s" % (self.id,self.direction) def close (self): try: self.logger.wire("%s, closing connection from %s to %s" % (self.name(),self.local,self.peer)) if self.io: self.io.close() self.io = None except KeyboardInterrupt as exc: raise exc except: pass def reading (self): while True: try: r,_,_ = select.select([self.io,],[],[],0) except select.error as exc: if exc.args[0] not in error.block: self.close() self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return r != [] def writing (self): while True: try: _,w,_ = select.select([],[self.io,],[],0) except select.error as exc: if exc.args[0] not in error.block: self.close() self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]])) raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]]) return False return w != [] def _reader (self, number): # The function must not be called if it does not return with no data with a smaller size as parameter if not self.io: self.close() raise NotConnected('Trying to read on a closed TCP connection') if number == 0: yield '' return while not self.reading(): yield '' data = '' reported = '' while True: try: while True: if self.defensive and random.randint(0,2): raise socket.error(errno.EAGAIN,'raising network error on purpose') read = self.io.recv(number) if not read: self.close() self.logger.wire("%s %s lost TCP session with peer" % (self.name(),self.peer)) raise LostConnection('the TCP connection was closed by the remote end') data += read number -= len(read) if not number: self.logger.wire( LazyFormat( "%s %-32s RECEIVED " % ( self.name(), '%s / %s' % (self.local,self.peer) ), read ) ) yield data return yield '' except socket.timeout as exc: self.close() self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer)) raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(exc)) except socket.error as exc: if exc.args[0] in error.block: message = "%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(exc)) if message != reported: reported = message self.logger.wire(message,'debug') yield '' elif exc.args[0] in error.fatal: self.close() raise LostConnection('issue reading on the socket: %s' % errstr(exc)) # what error could it be ! else: self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer)) raise NetworkError('Problem while reading data from the network (%s)' % errstr(exc)) def writer (self, data): if not self.io: # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session yield True return while not self.writing(): yield False self.logger.wire(LazyFormat("%s %-32s SENDING " % (self.name(),'%s / %s' % (self.local,self.peer)),data)) # The first while is here to setup the try/catch block once as it is very expensive while True: try: while True: if self.defensive and random.randint(0,2): raise socket.error(errno.EAGAIN,'raising network error on purpose') # we can not use sendall as in case of network buffer filling # it does raise and does not let you know how much was sent number = self.io.send(data) if not number: self.close() self.logger.wire("%s %s lost TCP connection with peer" % (self.name(),self.peer)) raise LostConnection('lost the TCP connection') data = data[number:] if not data: yield True return yield False except socket.error as exc: if exc.args[0] in error.block: self.logger.wire( "%s %s blocking io problem mid-way through writing a message %s, trying to complete" % ( self.name(), self.peer, errstr(exc) ), 'debug' ) yield False elif exc.errno == errno.EPIPE: # The TCP connection is gone. self.close() raise NetworkError('Broken TCP connection') elif exc.args[0] in error.fatal: self.close() self.logger.wire("%s %s problem sending message (%s)" % (self.name(),self.peer,errstr(exc))) raise NetworkError('Problem while writing data to the network (%s)' % errstr(exc)) # what error could it be ! else: self.logger.wire("%s %s undefined error writing on socket" % (self.name(),self.peer)) yield False def reader (self): # _reader returns the whole number requested or nothing and then stops for header in self._reader(Message.HEADER_LEN): if not header: yield 0,0,'','',None if not header.startswith(Message.MARKER): report = 'The packet received does not contain a BGP marker' yield 0,0,header,'',NotifyError(1,1,report) return msg = ord(header[18]) length = unpack('!H',header[16:18])[0] if length < Message.HEADER_LEN or length > Message.MAX_LEN: report = '%s has an invalid message length of %d' % (Message.CODE.name(msg),length) yield length,0,header,'',NotifyError(1,2,report) return validator = Message.Length.get(msg,lambda _: _ >= 19) if not validator(length): # MUST send the faulty length back report = '%s has an invalid message length of %d' % (Message.CODE.name(msg),length) yield length,0,header,'',NotifyError(1,2,report) return number = length - Message.HEADER_LEN if not number: yield length,msg,header,'',None return for body in self._reader(number): if not body: yield 0,0,'','',None yield length,msg,header,body,None