Exemplo n.º 1
0
def ready(io):
    logger = Logger()
    warned = False
    start = time.time()

    while True:
        try:
            _, w, _ = select.select([], [
                io,
            ], [], 0)
            if not w:
                if not warned and time.time() - start > 1.0:
                    logger.network('attempting to establish connection',
                                   'warning')
                    warned = True
                yield False
                continue
            err = io.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
            if not err:
                if warned:
                    logger.network('connection established', 'warning')
                yield True
                return
            elif err in error.block:
                logger.network(
                    'connect attempt failed, retrying, reason %s' %
                    errno.errorcode[err], 'warning')
                yield False
            else:
                yield False
                return
        except select.error:
            yield False
            return
Exemplo n.º 2
0
class Listener(object):
    _family_AFI_map = {
        socket.AF_INET: AFI.ipv4,
        socket.AF_INET6: AFI.ipv6,
    }

    def __init__(self, backlog=200):
        self._backlog = backlog
        self.serving = False
        self._sockets = {}

        self.logger = Logger()

    def _new_socket(self, ip):
        if ip.afi == AFI.ipv6:
            return socket.socket(socket.AF_INET6, socket.SOCK_STREAM,
                                 socket.IPPROTO_TCP)
        if ip.afi == AFI.ipv4:
            return socket.socket(socket.AF_INET, socket.SOCK_STREAM,
                                 socket.IPPROTO_TCP)
        raise NetworkError(
            'Can not create socket for listening, family of IP %s is unknown' %
            ip)

    def listen(self, local_ip, peer_ip, local_port, md5, ttl_in):
        self.serving = True

        for sock, (local, port, peer, md) in self._sockets.items():
            if local_ip.top() != local:
                continue
            if local_port != port:
                continue
            if md5:
                MD5(sock, peer_ip.top(), 0, md5)
            if ttl_in:
                MIN_TTL(sock, ttl_in)
            return

        try:
            sock = self._new_socket(local_ip)
            if md5:
                # MD5 must match the peer side of the TCP, not the local one
                MD5(sock, peer_ip.top(), 0, md5)
            try:
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            except (socket.error, AttributeError):
                pass
            sock.setblocking(0)
            # s.settimeout(0.0)
            sock.bind((local_ip.top(), local_port))
            sock.listen(self._backlog)
            self._sockets[sock] = (local_ip.top(), local_port, peer_ip.top(),
                                   md5)
        except socket.error as exc:
            if exc.args[0] == errno.EADDRINUSE:
                raise BindingError(
                    'could not listen on %s:%d, the port already in use by another application'
                    % (local_ip, local_port))
            elif exc.args[0] == errno.EADDRNOTAVAIL:
                raise BindingError(
                    'could not listen on %s:%d, this is an invalid address' %
                    (local_ip, local_port))
            raise NetworkError(str(exc))
        except NetworkError as exc:
            self.logger.network(str(exc), 'critical')
            raise exc

    # @each
    def connected(self):
        if not self.serving:
            return

        try:
            for sock in self._sockets:
                try:
                    io, _ = sock.accept()
                except socket.error as exc:
                    if exc.errno in error.block:
                        continue
                    raise AcceptError(
                        'could not accept a new connection (%s)' % errstr(exc))

                try:
                    if sock.family == socket.AF_INET:
                        local_ip = io.getpeername()[0]  # local_ip,local_port
                        remote_ip = io.getsockname()[
                            0]  # remote_ip,remote_port
                    elif sock.family == socket.AF_INET6:
                        local_ip = io.getpeername()[
                            0]  # local_ip,local_port,local_flow,local_scope
                        remote_ip = io.getsockname(
                        )[0]  # remote_ip,remote_port,remote_flow,remote_scope
                    else:
                        raise AcceptError('unexpected address family (%d)' %
                                          sock.family)
                    fam = self._family_AFI_map[sock.family]
                    yield Incoming(fam, remote_ip, local_ip, io)
                except socket.error as exc:
                    raise AcceptError('could not setup a new connection (%s)' %
                                      errstr(exc))
        except NetworkError as exc:
            self.logger.network(str(exc), 'critical')

    def stop(self):
        if not self.serving:
            return

        for sock, (ip, port, _, _) in self._sockets.items():
            sock.close()
            self.logger.network('stopped listening on %s:%d' % (ip, port),
                                'info')

        self._sockets = {}
        self.serving = False
Exemplo n.º 3
0
class Listener (object):
	_family_AFI_map = {
		socket.AF_INET: AFI.ipv4,
		socket.AF_INET6: AFI.ipv6,
	}

	def __init__ (self, backlog=200):
		self._backlog = backlog
		self.serving = False
		self._sockets = {}

		self.logger = Logger()

	def _new_socket (self, ip):
		if ip.afi == AFI.ipv6:
			return socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
		if ip.afi == AFI.ipv4:
			return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
		raise NetworkError('Can not create socket for listening, family of IP %s is unknown' % ip)

	def listen (self, local_ip, peer_ip, local_port, md5, ttl_in):
		self.serving = True

		for sock,(local,port,peer,md) in self._sockets.items():
			if local_ip.top() != local:
				continue
			if local_port != port:
				continue
			if md5:
				MD5(sock,peer_ip.top(),0,md5)
			if ttl_in:
				MIN_TTL(sock,ttl_in)
			return

		try:
			sock = self._new_socket(local_ip)
			if md5:
				# MD5 must match the peer side of the TCP, not the local one
				MD5(sock,peer_ip.top(),0,md5)
			try:
				sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			except (socket.error,AttributeError):
				pass
			sock.setblocking(0)
			# s.settimeout(0.0)
			sock.bind((local_ip.top(),local_port))
			sock.listen(self._backlog)
			self._sockets[sock] = (local_ip.top(),local_port,peer_ip.top(),md5)
		except socket.error as exc:
			if exc.args[0] == errno.EADDRINUSE:
				raise BindingError('could not listen on %s:%d, the port may already be in use by another application' % (local_ip,local_port))
			elif exc.args[0] == errno.EADDRNOTAVAIL:
				raise BindingError('could not listen on %s:%d, this is an invalid address' % (local_ip,local_port))
			raise NetworkError(str(exc))
		except NetworkError as exc:
			self.logger.network(str(exc),'critical')
			raise exc

	# @each
	def connected (self):
		if not self.serving:
			return

		try:
			for sock in self._sockets:
				try:
					io, _ = sock.accept()
				except socket.error as exc:
					if exc.errno in error.block:
						continue
					raise AcceptError('could not accept a new connection (%s)' % errstr(exc))

				try:
					if sock.family == socket.AF_INET:
						local_ip  = io.getpeername()[0]  # local_ip,local_port
						remote_ip = io.getsockname()[0]  # remote_ip,remote_port
					elif sock.family == socket.AF_INET6:
						local_ip  = io.getpeername()[0]  # local_ip,local_port,local_flow,local_scope
						remote_ip = io.getsockname()[0]  # remote_ip,remote_port,remote_flow,remote_scope
					else:
						raise AcceptError('unexpected address family (%d)' % sock.family)
					fam = self._family_AFI_map[sock.family]
					yield Incoming(fam,remote_ip,local_ip,io)
				except socket.error as exc:
					raise AcceptError('could not setup a new connection (%s)' % errstr(exc))
		except NetworkError as exc:
			self.logger.network(str(exc),'critical')

	def stop (self):
		if not self.serving:
			return

		for sock,(ip,port,_,_) in self._sockets.items():
			sock.close()
			self.logger.network('stopped listening on %s:%d' % (ip,port),'info')

		self._sockets = {}
		self.serving = False
Exemplo n.º 4
0
class Listener (object):
	_family_AFI_map = {
		socket.AF_INET: AFI.ipv4,
		socket.AF_INET6: AFI.ipv6,
	}

	def __init__ (self, reactor, backlog=200):
		self.serving = False
		self.logger = Logger()

		self._reactor = reactor
		self._backlog = backlog
		self._sockets = {}
		self._accepted = {}
		self._pending = 0

	def _new_socket (self, ip):
		if ip.afi == AFI.ipv6:
			return socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
		if ip.afi == AFI.ipv4:
			return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
		raise NetworkError('Can not create socket for listening, family of IP %s is unknown' % ip)

	def _listen (self, local_ip, peer_ip, local_port, md5, md5_base64, ttl_in):
		self.serving = True

		for sock,(local,port,peer,md) in self._sockets.items():
			if local_ip.top() != local:
				continue
			if local_port != port:
				continue
			MD5(sock,peer_ip.top(),0,md5,md5_base64)
			if ttl_in:
				MIN_TTL(sock,peer_ip,ttl_in)
			return

		try:
			sock = self._new_socket(local_ip)
			# MD5 must match the peer side of the TCP, not the local one
			MD5(sock,peer_ip.top(),0,md5,md5_base64)
			if ttl_in:
				MIN_TTL(sock,peer_ip,ttl_in)
			try:
				sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
				if local_ip.ipv6():
					sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
			except (socket.error,AttributeError):
				pass
			sock.setblocking(0)
			# s.settimeout(0.0)
			sock.bind((local_ip.top(),local_port))
			sock.listen(self._backlog)
			self._sockets[sock] = (local_ip.top(),local_port,peer_ip.top(),md5)
		except socket.error as exc:
			if exc.args[0] == errno.EADDRINUSE:
				raise BindingError('could not listen on %s:%d, the port may already be in use by another application' % (local_ip,local_port))
			elif exc.args[0] == errno.EADDRNOTAVAIL:
				raise BindingError('could not listen on %s:%d, this is an invalid address' % (local_ip,local_port))
			raise NetworkError(str(exc))
		except NetworkError as exc:
			self.logger.network(str(exc),'critical')
			raise exc

	def listen_on (self, local_addr, remote_addr, port, md5_password, md5_base64, ttl_in):
		try:
			if not remote_addr:
				remote_addr = IP.create('0.0.0.0') if local_addr.ipv4() else IP.create('::')
			self._listen(local_addr, remote_addr, port, md5_password, md5_base64, ttl_in)
			self.logger.network('Listening for BGP session(s) on %s:%d%s' % (local_addr, port,' with MD5' if md5_password else ''))
			return True
		except NetworkError as exc:
			if os.geteuid() != 0 and port <= 1024:
				self.logger.network('Can not bind to %s:%d, you may need to run ExaBGP as root' % (local_addr, port),'critical')
			else:
				self.logger.network('Can not bind to %s:%d (%s)' % (local_addr, port,str(exc)),'critical')
			self.logger.network('unset exabgp.tcp.bind if you do not want listen for incoming connections','critical')
			self.logger.network('and check that no other daemon is already binding to port %d' % port,'critical')
			return False

	def incoming (self):
		if not self.serving:
			return False

		for sock in self._sockets:
			if sock in self._accepted:
				continue
			try:
				io, _ = sock.accept()
				self._accepted[sock] = io
				self._pending += 1
			except socket.error as exc:
				if exc.errno in error.block:
					continue
				self.logger.network(str(exc),'critical')
		if self._pending:
			self._pending -= 1
			return True
		return False

	def _connected (self):
		try:
			for sock,io in list(self._accepted.items()):
				del self._accepted[sock]
				if sock.family == socket.AF_INET:
					local_ip  = io.getpeername()[0]  # local_ip,local_port
					remote_ip = io.getsockname()[0]  # remote_ip,remote_port
				elif sock.family == socket.AF_INET6:
					local_ip  = io.getpeername()[0]  # local_ip,local_port,local_flow,local_scope
					remote_ip = io.getsockname()[0]  # remote_ip,remote_port,remote_flow,remote_scope
				else:
					raise AcceptError('unexpected address family (%d)' % sock.family)
				fam = self._family_AFI_map[sock.family]
				yield Incoming(fam,remote_ip,local_ip,io)
		except NetworkError as exc:
			self.logger.network(str(exc),'critical')

	def new_connections (self):
		if not self.serving:
			return
		yield None

		reactor = self._reactor
		ranged_neighbor = []

		for connection in self._connected():
			for key in reactor.peers:
				peer = reactor.peers[key]
				neighbor = peer.neighbor

				connection_local = IP.create(connection.local).address()
				neighbor_peer_start = neighbor.peer_address.address()
				neighbor_peer_next = neighbor_peer_start + neighbor.range_size

				if not neighbor_peer_start <= connection_local < neighbor_peer_next:
					continue

				connection_peer = IP.create(connection.peer).address()
				neighbor_local = neighbor.local_address.address()

				if connection_peer != neighbor_local:
					if not neighbor.auto_discovery:
						continue

				# we found a range matching for this connection
				# but the peer may already have connected, so
				# we need to iterate all individual peers before
				# handling "range" peers
				if neighbor.range_size > 1:
					ranged_neighbor.append(peer.neighbor)
					continue

				denied = peer.handle_connection(connection)
				if denied:
					self.logger.network('refused connection from %s due to the state machine' % connection.name())
					break
				self.logger.network('accepted connection from %s' % connection.name())
				break
			else:
				# we did not break (and nothign was found/done or we have group match)
				matched = len(ranged_neighbor)
				if matched > 1:
					self.logger.network('could not accept connection from %s (more than one neighbor match)' % connection.name())
					reactor.async.schedule(str(uuid.uuid1()),'sending notification (6,5)',connection.notification(6,5,b'could not accept the connection (more than one neighbor match)'))
					return
				if not matched:
					self.logger.network('no session configured for %s' % connection.name())
					reactor.async.schedule(str(uuid.uuid1()),'sending notification (6,3)',connection.notification(6,3,b'no session configured for the peer'))
					return

				new_neighbor = copy.copy(ranged_neighbor[0])
				new_neighbor.range_size = 1
				new_neighbor.generated = True
				new_neighbor.local_address = IP.create(connection.peer)
				new_neighbor.peer_address = IP.create(connection.local)

				new_peer = Peer(new_neighbor,self)
				denied = new_peer.handle_connection(connection)
				if denied:
					self.logger.network('refused connection from %s due to the state machine' % connection.name())
					return

				reactor.peers[new_neighbor.name()] = new_peer
				return

	def stop (self):
		if not self.serving:
			return

		for sock,(ip,port,_,_) in self._sockets.items():
			sock.close()
			self.logger.network('stopped listening on %s:%d' % (ip,port),'info')

		self._sockets = {}
		self.serving = False