1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-04-19 09:49:01 -04:00

Updated APRSIS driver

This patch adds an override to _connect to set some more
socket keepalive options.
This commit is contained in:
Hemna 2025-01-30 10:03:14 -08:00
parent 3b57e7597d
commit 1606585d41

View File

@ -1,6 +1,7 @@
import datetime
import logging
import select
import socket
import threading
import aprslib
@ -18,7 +19,7 @@ from aprslib.exceptions import (
import aprsd
from aprsd.packets import core
LOG = logging.getLogger("APRSD")
LOG = logging.getLogger('APRSD')
class Aprsdis(aprslib.IS):
@ -31,7 +32,7 @@ class Aprsdis(aprslib.IS):
aprsd_keepalive = datetime.datetime.now()
# Which server we are connected to?
server_string = "None"
server_string = 'None'
# timeout in seconds
select_timeout = 1
@ -39,10 +40,10 @@ class Aprsdis(aprslib.IS):
def stop(self):
self.thread_stop = True
LOG.warning("Shutdown Aprsdis client.")
LOG.warning('Shutdown Aprsdis client.')
def close(self):
LOG.warning("Closing Aprsdis client.")
LOG.warning('Closing Aprsdis client.')
super().close()
@wrapt.synchronized(lock)
@ -54,6 +55,57 @@ class Aprsdis(aprslib.IS):
"""If the connection is alive or not."""
return self._connected
def _connect(self):
"""
Attemps connection to the server
"""
self.logger.info(
'Attempting connection to %s:%s', self.server[0], self.server[1]
)
try:
self._open_socket()
peer = self.sock.getpeername()
self.logger.info('Connected to %s', str(peer))
# 5 second timeout to receive server banner
self.sock.setblocking(1)
self.sock.settimeout(5)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# MACOS doesn't have TCP_KEEPIDLE
if hasattr(socket, 'TCP_KEEPIDLE'):
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
banner = self.sock.recv(512)
if is_py3:
banner = banner.decode('latin-1')
if banner[0] == '#':
self.logger.debug('Banner: %s', banner.rstrip())
else:
raise ConnectionError('invalid banner from server')
except ConnectionError as e:
self.logger.error(str(e))
self.close()
raise
except (socket.error, socket.timeout) as e:
self.close()
self.logger.error('Socket error: %s' % str(e))
if str(e) == 'timed out':
raise ConnectionError('no banner from server') from e
else:
raise ConnectionError(e) from e
self._connected = True
def _socket_readlines(self, blocking=False):
"""
Generator for complete lines, received from the server
@ -61,12 +113,12 @@ class Aprsdis(aprslib.IS):
try:
self.sock.setblocking(0)
except OSError as e:
self.logger.error(f"socket error when setblocking(0): {str(e)}")
raise aprslib.ConnectionDrop("connection dropped")
self.logger.error(f'socket error when setblocking(0): {str(e)}')
raise aprslib.ConnectionDrop('connection dropped') from e
while not self.thread_stop:
short_buf = b""
newline = b"\r\n"
short_buf = b''
newline = b'\r\n'
# set a select timeout, so we get a chance to exit
# when user hits CTRL-C
@ -91,11 +143,11 @@ class Aprsdis(aprslib.IS):
# We could just not be blocking, so empty is expected
continue
else:
self.logger.error("socket.recv(): returned empty")
raise aprslib.ConnectionDrop("connection dropped")
self.logger.error('socket.recv(): returned empty')
raise aprslib.ConnectionDrop('connection dropped')
except OSError as e:
# self.logger.error("socket error on recv(): %s" % str(e))
if "Resource temporarily unavailable" in str(e):
if 'Resource temporarily unavailable' in str(e):
if not blocking:
if len(self.buf) == 0:
break
@ -111,22 +163,22 @@ class Aprsdis(aprslib.IS):
"""
Sends login string to server
"""
login_str = "user {0} pass {1} vers github.com/craigerl/aprsd {3}{2}\r\n"
login_str = 'user {0} pass {1} vers Python-APRSD {3}{2}\r\n'
login_str = login_str.format(
self.callsign,
self.passwd,
(" filter " + self.filter) if self.filter != "" else "",
(' filter ' + self.filter) if self.filter != '' else '',
aprsd.__version__,
)
self.logger.debug("Sending login information")
self.logger.debug('Sending login information')
try:
self._sendall(login_str)
self.sock.settimeout(5)
test = self.sock.recv(len(login_str) + 100)
if is_py3:
test = test.decode("latin-1")
test = test.decode('latin-1')
test = test.rstrip()
self.logger.debug("Server: '%s'", test)
@ -134,26 +186,26 @@ class Aprsdis(aprslib.IS):
if not test:
raise LoginError(f"Server Response Empty: '{test}'")
_, _, callsign, status, e = test.split(" ", 4)
s = e.split(",")
_, _, callsign, status, e = test.split(' ', 4)
s = e.split(',')
if len(s):
server_string = s[0].replace("server ", "")
server_string = s[0].replace('server ', '')
else:
server_string = e.replace("server ", "")
server_string = e.replace('server ', '')
if callsign == "":
raise LoginError("Server responded with empty callsign???")
if callsign == '':
raise LoginError('Server responded with empty callsign???')
if callsign != self.callsign:
raise LoginError(f"Server: {test}")
if status != "verified," and self.passwd != "-1":
raise LoginError("Password is incorrect")
raise LoginError(f'Server: {test}')
if status != 'verified,' and self.passwd != '-1':
raise LoginError('Password is incorrect')
if self.passwd == "-1":
self.logger.info("Login successful (receive only)")
if self.passwd == '-1':
self.logger.info('Login successful (receive only)')
else:
self.logger.info("Login successful")
self.logger.info('Login successful')
self.logger.info(f"Connected to {server_string}")
self.logger.info(f'Connected to {server_string}')
self.server_string = server_string
except LoginError as e:
@ -164,7 +216,7 @@ class Aprsdis(aprslib.IS):
self.close()
self.logger.error(f"Failed to login '{e}'")
self.logger.exception(e)
raise LoginError("Failed to login")
raise LoginError('Failed to login') from e
def consumer(self, callback, blocking=True, immortal=False, raw=False):
"""
@ -180,21 +232,21 @@ class Aprsdis(aprslib.IS):
"""
if not self._connected:
raise ConnectionError("not connected to a server")
raise ConnectionError('not connected to a server')
line = b""
line = b''
while True and not self.thread_stop:
try:
for line in self._socket_readlines(blocking):
if line[0:1] != b"#":
if line[0:1] != b'#':
self.aprsd_keepalive = datetime.datetime.now()
if raw:
callback(line)
else:
callback(self._parse(line))
else:
self.logger.debug("Server: %s", line.decode("utf8"))
self.logger.debug('Server: %s', line.decode('utf8'))
self.aprsd_keepalive = datetime.datetime.now()
except ParseError as exp:
self.logger.log(
@ -211,7 +263,7 @@ class Aprsdis(aprslib.IS):
exp.packet,
)
except LoginError as exp:
self.logger.error("%s: %s", exp.__class__.__name__, exp)
self.logger.error('%s: %s', exp.__class__.__name__, exp)
except (KeyboardInterrupt, SystemExit):
raise
except (ConnectionDrop, ConnectionError):
@ -227,7 +279,7 @@ class Aprsdis(aprslib.IS):
except StopIteration:
break
except Exception:
self.logger.error("APRS Packet: %s", line)
self.logger.error('APRS Packet: %s', line)
raise
if not blocking: