Files
pyrad3/pyrad3/new_client.py
Istvan Ruzman 6b639f3aaf Initial commit
2020-07-02 16:01:29 +02:00

97 lines
2.7 KiB
Python

# Copyright 2020 Istvan Ruzman
# SPDX-License-Identifier: MIT OR Apache-2.0
import select
import socket
import time
from pyrad3 import new_packet as P
from pyrad3 import new_host
SUPPORTED_SEND_TYPES = [
P.AccessRequest,
P.AccountingRequest,
P.CoARequest,
]
PACKET_TYPE_PORT_MAPPING = {
P.AccessRequest: 'authport',
P.AccountingRequest: 'acctport',
P.CoARequest: 'coaport',
}
class Timeout(Exception):
pass
class UnsupportedPacketType(Exception):
pass
class Client(new_host.Host):
def __init__(self, server, secret, radius_dictionary, **kwargs):
super().__init__(secret, radius_dictionary, **kwargs)
self.server = server
self._socket = None
self._poll = None
def bind(self, addr):
self._socket_close()
self._socket_open()
self._socket.bind(addr)
def _socket_open(self):
if self._socket is not None:
return
try:
family = socket.getaddrinfo(self.server, 'www')[0][0]
except socket.gaierror:
family = socket.AF_INET
self._socket = socket.socket(family, socket.SOCK_DGRAM)
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._poll = select.poll()
self._poll.register(self._socket, select.POLLIN)
def _socket_close(self):
if self._socket is not None:
self._poll.unregister(self._socket)
self._socket.close()
self._socket = None
def _select_port(self, packet):
try:
port_type = PACKET_TYPE_PORT_MAPPING[packet.code]
return getattr(self, port_type)
except (AttributeError, KeyError):
pass
raise UnsupportedPacketType(f"The packet type {packet.code} by Client")
def _send_packet(self, packet):
port = self._select_port(packet)
raw_packet = packet.serialize()
for _attempt in range(self.retries):
now = time.time()
waitto = now + self.timeout
self._socket.sendto(raw_packet, (self.server, port))
while now < waitto:
if not self._poll.poll((waitto - now) * 1000):
# socket is not ready for some reason
now = time.time()
continue
rawreply = self._socket.recv(4096)
try:
return packet.verify_reply(rawreply)
except P.PacketError:
pass
# timed out: try the next attempt after increasing the acct delay time
if packet.code == packet.AccountingRequest:
packet.increase_acct_delay_time(self.timeout)
raw_packet = packet.serialize()
raise Timeout