Esempio n. 1
0
 def client_coro(loop, server_host, server_port):
     client = RadiusClient(loop,
                           default_server=server_host,
                           default_port=server_port)
     request = packet.AccountingStatusServer(SHARED_SECRET)
     future = yield from client.send_packet(request)
     response, response_time = yield from future
     self.assertTrue(response.code == packet.ACCOUNTING_RESPONSE)
     self.assertTrue(len(response.attributes) == 0)
     client.close()
Esempio n. 2
0
 def client_coro(loop, server_host, server_port):
     client = RadiusClient(loop,
                           default_server=server_host,
                           default_port=server_port)
     request = packet.StatusServer(SHARED_SECRET)
     request.attributes.extend(('NAS-Identifier', 'test_nas'), )
     future = yield from client.send_packet(request)
     response, response_time = yield from future
     self.assertTrue(response.code == packet.ACCESS_ACCEPT)
     self.assertTrue(len(response.attributes) == 0)
     client.close()
Esempio n. 3
0
 def client_coro(loop, server_host, server_port):
     client = RadiusClient(loop,
                           default_server=server_host,
                           default_port=server_port)
     request = packet.AccessRequest(SHARED_SECRET)
     request.attributes = (
         ('User-Name', 'username'),
         ('User-Password', 'password'),
     )
     future = yield from client.send_packet(request)
     response, response_time = yield from future
     self.assertTrue(response.code == packet.ACCESS_REJECT)
     future = yield from client.send_packet(request, identifier=0)
     response, response_time = yield from future
     self.assertTrue(response.code == packet.ACCESS_REJECT)
     client.close()
Esempio n. 4
0
 def client_coro(loop, server_host, server_port, server):
     client = RadiusClient(loop)
     request = packet.AccountingRequest(SHARED_SECRET)
     request.attributes.extend(('User-Name', 'username'),
                               ('Acct-Session-Id', 'session-test'),
                               ('Acct-Status-Type', 'Interim-Update'))
     future = yield from client.send_packet(request,
                                            remote_host=server_host,
                                            remote_port=server_port,
                                            retries=3,
                                            timeout=1)
     with self.assertRaises(asyncio.TimeoutError):
         response, response_time = yield from future
     self.assertIsInstance(server.last_exc, RadiusResponseError)
     self.assertEqual(client.stat.get('requests'), 1)
     self.assertEqual(client.stat.get('responses'), 0)
     self.assertEqual(client.stat.get('no_responses'), 1)
     self.assertEqual(client.stat.get('timeouts'), 1)
     client.close()
Esempio n. 5
0
        def client_coro(loop, server_host, server_port, server):
            client = RadiusClient(loop, client_identifier='test-client')
            request = packet.AccountingRequest(SHARED_SECRET)
            request.attributes.extend(('User-Name', 'username'),
                                      ('Acct-Session-Id', 'session-test'),
                                      ('Acct-Status-Type', 'Start'))
            future = yield from client.send_packet(request,
                                                   remote_host=server_host,
                                                   remote_port=server_port)
            response, response_time = yield from future
            self.assertEqual(request.attributes.get('NAS-Identifier'),
                             'test-client')
            self.assertTrue(response.code == packet.ACCOUNTING_RESPONSE)
            self.assertIn('session-test', server.acct_sessions)

            request = packet.AccountingRequest(SHARED_SECRET)
            request.attributes.extend(('User-Name', 'username'),
                                      ('Acct-Session-Id', 'session-test'),
                                      ('Acct-Status-Type', 'Stop'))
            future = yield from client.send_packet(request,
                                                   remote_host=server_host,
                                                   remote_port=server_port)
            response, response_time = yield from future
            self.assertTrue(response.code == packet.ACCOUNTING_RESPONSE)
            self.assertNotIn('session-test', server.acct_sessions)

            request = packet.AccountingRequest(SHARED_SECRET)
            request.attributes.extend(('User-Name', 'username'),
                                      ('Acct-Session-Id', 'session-test'),
                                      ('Acct-Status-Type', 'Stop'))
            #with self.assertRaises(asyncio.TimeoutError):
            future = yield from client.send_packet(request,
                                                   remote_host=server_host,
                                                   remote_port=server_port)
            with self.assertRaises(asyncio.TimeoutError):
                response, response_time = yield from future
            self.assertIsInstance(server.last_exc, RadiusResponseError)
            client.close()
Esempio n. 6
0
class AbstractRadiusServer(RadiusService):
    def __init__(self,
                 host=DEFAULT_HOST,
                 auth_port=DEFAULT_AUTH_PORT,
                 acc_port=DEFAULT_ACC_PORT,
                 loop=None,
                 init_dac_client=True):
        """
        Create class instance or raise RadiusServerError if cant create it
        """
        # Verifying param `host`, it must be IP-address or hostname
        # Raise ServerError if host is not IP-address and not hostname
        self.logger = logging.getLogger(self.__class__.__name__)

        self.loop = loop or asyncio.new_event_loop()

        self.host = self.__validate_host(host)
        self.auth_port = self.__validate_port(auth_port)
        self.acc_port = self.__validate_port(acc_port)

        self.process_id = os.getpid()
        self.parent_process_id = os.getppid()

        self.__is_stoped = False
        self.__start_time = 0
        self.__stop_time = 0

        self.logger.debug("Used {} as event_loop".format(
            class_fullname(self.loop)))

        # Get methods decorated with @periodic_task and create for it PeriodicTask instances
        self.periodic_tasks = []
        for attr_name in dir(self):
            attr = getattr(self, attr_name)
            if inspect.ismethod(attr) and hasattr(
                    attr, 'is_periodic_task') and attr.is_periodic_task:
                self.periodic_tasks.append(
                    PeriodicTask(self.loop, attr, delay=attr.delay))

        self.__auth_transport = None
        self.__auth_proto = None

        self.__acc_transport = None
        self.__acc_proto = None

        if init_dac_client:
            self.__dac = RadiusClient(loop=self.loop)
        else:
            self.__dac = None

    @staticmethod
    def __validate_host(host):
        if host == '0.0.0.0':
            validated_host = host
        try:
            validated_host = IPv4Address(host)
        except ValueError:
            try:
                validated_host = socket.gethostbyname(host)
            except:
                raise RadiusServerError(
                    "Bad value of host '{}', use IPv4 address or hostname".
                    format(host))
        return str(validated_host)

    @staticmethod
    def __validate_port(port):
        if port is None:
            return None
        try:
            validated_port = int(port)
        except (ValueError, TypeError):
            raise RadiusServerError("Bad value of UDP port '{}'".format(port))
        return validated_port

    @property
    def uptime(self):
        return self.__stop_time - self.__start_time

    def get_event_loop(self):
        return self.loop

    def __initialize_udp_endpoints(self):
        try:
            if asyncio.iscoroutinefunction(self.on_auth_packet):
                self.loop.run_until_complete(self.on_auth_packet(None))
            else:
                self.on_auth_packet(None)
        except NotImplementedError:
            self.logger.warning(
                "Method `on_auth_packet` is not implemented, auth port wouldn't be listen"
            )
            self.auth_port = None
        except Exception:
            pass
        try:
            if asyncio.iscoroutinefunction(self.on_acct_packet):
                self.loop.run_until_complete(self.on_acct_packet(None))
            else:
                self.on_acct_packet(None)
        except NotImplementedError:
            self.logger.warning(
                "Method `on_acct_packet` is not implemented, acct port wouldn't be listen"
            )
            self.acc_port = None
        except Exception:
            pass

        any_port_is_listen = False
        if self.auth_port is not None:
            # Listen UDP socket for auth
            try:
                self.__auth_transport, self.__auth_proto = self.loop.run_until_complete(
                    self.loop.create_datagram_endpoint(
                        lambda: RadiusAuthProtocol(self),
                        local_addr=(self.host, self.auth_port)))
                any_port_is_listen = True
            except OSError as e:
                raise RadiusServerError(
                    "Cant listen UDP socket for auth on port {}, {!s}".format(
                        self.auth_port, e))

        if self.acc_port is not None:
            # Listen UDP socket for acc
            try:
                self.__acc_transport, self.__acc_proto = self.loop.run_until_complete(
                    self.loop.create_datagram_endpoint(
                        lambda: RadiusAccountingProtocol(self),
                        local_addr=(self.host, self.acc_port)))
                any_port_is_listen = True
            except OSError as e:
                raise RadiusServerError(
                    "Cant listen UDP socket for acc on port {}, {!s}".format(
                        self.acc_port, e))
        if not any_port_is_listen:
            raise RadiusServerError("Server is not listen any port")

    def __close_udp_endpoints(self):
        # Close and wait_for_close UDP socket for auth
        if self.__auth_transport is not None:
            self.__auth_transport.close()
            self.__auth_proto.wait_for_close()

        # Close and wait_for_close UDP socket for acc
        if self.__acc_transport is not None:
            self.__acc_transport.close()
            self.__acc_proto.wait_for_close()

    def __schedule_periodic_task(self):
        # Starting periodic tasks
        for item in self.periodic_tasks:
            item.start()

    def __unschedule_periodic_task(self):
        # Stop periodic tasks
        for item in self.periodic_tasks:
            item.stop()

    def shutdown(self):
        self.__is_stoped = True

    @asyncio.coroutine
    def __serve(self, future=None):
        try:
            if future is not None:
                yield from future
            else:
                while not self.__is_stoped:
                    yield from asyncio.sleep(1)
        except asyncio.CancelledError:
            pass
        except Exception as exc:
            self.register_exception(exc)
        finally:
            pass
            async_cancel_tasks(self.loop)

    @asyncio.coroutine
    def on_startup(self):
        return

    @asyncio.coroutine
    def on_shutdown(self, *args, **kwargs):
        return

    def close_dac(self):
        # Close DAC client
        if isinstance(self.__dac, RadiusClient):
            self.__dac.close()

    def run(self, future=None):
        # Log server process ID
        self.logger.info("Starting '{}' with PID {}(parent ID: {})".format(
            self.__class__.__name__, self.process_id, self.parent_process_id))
        self.__start_time = self.loop.time()
        # Event loop is not running
        self.loop.run_until_complete(self.on_startup())
        if not self.__is_stoped:

            self.__initialize_udp_endpoints()
            self.__schedule_periodic_task()

            self.loop.add_signal_handler(signal.SIGTERM, self.shutdown)

            if future is not None:
                if asyncio.iscoroutine(future):
                    future = asyncio.ensure_future(future, loop=self.loop)

            # Start server event loop
            try:
                self.loop.run_until_complete(self.__serve(future))
            except KeyboardInterrupt:
                self.logger.info("Server '{}' process was interrupted".format(
                    self.__class__.__name__))
                if future is not None:
                    future.cancel()
                self.shutdown()
            except asyncio.CancelledError:
                self.logger.info("Server '{}' process was interrupted".format(
                    self.__class__.__name__))
                if future is not None:
                    future.cancel()
                self.shutdown()

        self.loop.remove_signal_handler(signal.SIGTERM)
        cancel_tasks(self.loop)

        self.loop.run_until_complete(self.on_shutdown())
        self.close_dac()
        self.__unschedule_periodic_task()
        self.__close_udp_endpoints()

        self.__stop_time = self.loop.time()
        # Stop event loop
        if not self.loop.is_running():
            self.loop.stop()

        if not self.loop.is_closed():
            self.loop.close()
        self.logger.info("'{}' was stopped, uptime - {:.2f} sec".format(
            self.__class__.__name__, self.uptime))

    async def send_to_das(self,
                          request,
                          das_host,
                          das_port=DEFAULT_DAS_PORT,
                          **kwargs):
        """ Async Send Disconnect-Request or CoA-Request to DAS and async return response from DAS """
        if not isinstance(request,
                          (packet.DisconnectRequest, packet.CoARequest)):
            raise RadiusServerError(
                "You can send to DAS packet only of type DisconnectRequest or CoaRequest"
            )

        if 'NAS-IP-Address' not in request.attributes and 'NAS-Identifier' not in request.attributes:
            request.attributes.extend(('NAS-IP-Address', str(das_host)), )

        # TODO: Разобраться с генерацией authenticator-а
        bytes(request)
        response_future = await self.__dac.send_packet(request, das_host,
                                                       das_port, **kwargs)
        self.logger.info("Sent '{0!s}' to '{1}:{2}'".format(
            request, das_host, das_port))
        self.logger.debug(packet.packet_view(request))

        response, response_time = await response_future
        self.logger.info(
            "Got '{0!s}' from '{1}:{2}' (processed by {3:.4f} ms)".format(
                response, das_host, das_port, response_time))
        self.logger.debug(packet.packet_view(response))

        return response