Skip to content

Commit c77a4d6

Browse files
committed
improve error handling
use exceptions to handle invalid data
1 parent 0e2c677 commit c77a4d6

File tree

1 file changed

+68
-55
lines changed

1 file changed

+68
-55
lines changed

jamulus.py

Lines changed: 68 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,17 @@ class JamulusConnector:
502502
def __init__(self, host="", port=DEFAULT_PORT, debug=False, log_audio=True):
503503
self.debug = debug
504504
self.log_audio = log_audio
505-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
506-
self.sock.bind((host, port))
507-
print("listening to port {}".format(port))
505+
self.host = host
506+
self.port = port
507+
if self.port is not None:
508+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
509+
print("listening to port {}".format(self.port))
510+
self.sock.bind((self.host, self.port))
511+
512+
def close(self):
513+
if self.port is not None:
514+
print("closing socket")
515+
self.sock.close()
508516

509517
def calc_crc(self, data):
510518
"""
@@ -559,29 +567,36 @@ def pack(self, format, values, mode="<"):
559567
"""
560568
data = b""
561569
for key, format_char in format:
562-
if format_char == "A":
563-
# A = 4 bytes / IPv4 address
564-
ip = socket.inet_aton(values[key])
565-
data += struct.pack("{}{}".format(mode, "L"), struct.unpack("!L", ip)[0])
566-
elif format_char in ["U", "V", "v"]:
567-
# U = 1 byte length n + n bytes UTF-8 string
568-
# V = 2 bytes length n + n bytes UTF-8 string
569-
# v = 2 bytes length n + n bytes data
570-
utf8_enc = True if format_char in ["U", "V"] else False
571-
value = values[key].encode() if utf8_enc else values[key]
572-
573-
length_format = "B" if format_char == "U" else "H"
574-
length = len(value)
575-
576-
data += struct.pack("{}{}{}{}".format(mode, length_format, length, "s"), length, value)
577-
elif format_char == "z":
578-
# z = all remaining data
570+
try:
579571
value = values[key]
580-
length = len(value)
581-
data += struct.pack("{}{}".format(length, "s"), value)
582-
else:
583-
# standard format characters
584-
data += struct.pack("{}{}".format(mode, format_char), values[key])
572+
except KeyError as error:
573+
raise ValueError("error packing '{}': missing key in values".format(key))
574+
575+
try:
576+
if format_char == "A":
577+
# A = 4 bytes / IPv4 address
578+
ip = socket.inet_aton(value)
579+
data += struct.pack("{}{}".format(mode, "L"), struct.unpack("!L", ip)[0])
580+
elif format_char in ["U", "V", "v"]:
581+
# U = 1 byte length n + n bytes UTF-8 string
582+
# V = 2 bytes length n + n bytes UTF-8 string
583+
# v = 2 bytes length n + n bytes data
584+
if format_char in ["U", "V"]:
585+
value = value.encode()
586+
587+
length_format = "B" if format_char == "U" else "H"
588+
length = len(value)
589+
590+
data += struct.pack("{}{}{}{}".format(mode, length_format, length, "s"), length, value)
591+
elif format_char == "z":
592+
# z = all remaining data
593+
length = len(value)
594+
data += struct.pack("{}{}".format(length, "s"), value)
595+
else:
596+
# standard format characters
597+
data += struct.pack("{}{}".format(mode, format_char), value)
598+
except struct.error as error:
599+
raise ValueError("error packing '{}': {}".format(key, error))
585600

586601
return data
587602

@@ -608,8 +623,8 @@ def unpack(self, format, data, offset=0, mode="<"):
608623
"""
609624
values = {}
610625

611-
try:
612-
for key, format_char in format:
626+
for key, format_char in format:
627+
try:
613628
if format_char == "A":
614629
# A = 4 bytes / IPv4 address
615630
ip = struct.pack("!L", *struct.unpack_from("{}{}".format(mode, "L"), data, offset))
@@ -638,8 +653,8 @@ def unpack(self, format, data, offset=0, mode="<"):
638653
(values[key],) = struct.unpack_from("{}{}".format(mode, format_char), data, offset)
639654
offset += struct.calcsize("{}{}".format(mode, format_char))
640655

641-
except struct.error:
642-
return {}, 0
656+
except struct.error as error:
657+
raise ValueError("error unpacking '{}': {}".format(key, error))
643658

644659
return values, offset
645660

@@ -699,8 +714,7 @@ def prot_unpack(self, format, data, repeat=False):
699714
values, offset = self.unpack(format, data, offset)
700715

701716
if offset != len(data):
702-
print("invalid message length ({}/{}) {}".format(offset, len(data), values))
703-
return None
717+
raise ValueError("invalid message length ({}/{}) {}".format(offset, len(data), values))
704718

705719
return values
706720

@@ -766,22 +780,19 @@ def main_unpack(self, data):
766780
# calculate crc from data
767781
crc_check = self.calc_crc(data)
768782
if crc_values["crc"] != crc_check:
769-
print("invalid message crc ({}/{}) {}".format(crc_values["crc"], crc_check, data))
770-
return "INVALID", None, None
783+
raise ValueError("invalid message crc ({}/{})".format(crc_values["crc"], crc_check))
771784

772785
# unpack main frame
773786
main_values, offset = self.unpack(FORMAT["MAIN_FRAME"], data)
774787

775788
# verify there's no data left
776789
if offset != len(data):
777-
print("invalid message length ({}/{}) {}".format(offset, len(data), data))
778-
return "INVALID", None, None
790+
raise ValueError("invalid message length ({}/{}) {}".format(offset, len(data)))
779791

780792
# verify ID is valid
781793
id = main_values["id"]
782794
if id not in MSG_KEYS.keys() or id == 0:
783-
print("invalid message ID ({}) {}".format(id, data))
784-
return "INVALID", None, None
795+
raise ValueError("invalid message ID ({})".format(id))
785796

786797
key = MSG_KEYS[id]
787798
prot = PROT[key]
@@ -837,7 +848,7 @@ def log_message(self, addr, key, count="-", length="", values=None, recv=True):
837848
True for received / False for sent
838849
"""
839850
if key != "AUDIO" or self.log_audio:
840-
output = "{} {} #{}: {} ({})".format(
851+
output = "{} {} #{} {} ({})".format(
841852
addr,
842853
" >" if recv else "< ",
843854
count,
@@ -912,24 +923,26 @@ def recvfrom(self, timeout=None, ackn=True, bufsize=MAX_SIZE_BYTES_NETW_BUF):
912923
except socket.timeout:
913924
raise TimeoutError
914925

915-
# detect protocol messages
916-
if len(data) >= 9 and data[:2] == b"\x00\x00":
917-
key, count, values = self.main_unpack(data)
918-
self.log_message(addr, key, count=count, length=len(data), values=values, recv=True)
919-
if ackn:
920-
self.send_ack(addr, key, count)
921-
922-
# assume audio messages
923-
elif len(data) >= 1:
924-
key = "AUDIO"
925-
count = None
926-
values = self.unpack(FORMAT["AUDIO_FRAME"], data)
927-
self.log_message(addr, key, length=len(data), values=values, recv=True)
926+
key = "INVALID"
927+
count = None
928+
values = None
928929

929-
else:
930-
key = "INVALID"
931-
count = None
932-
values = None
930+
try:
931+
# detect protocol messages
932+
if len(data) >= 9 and data[:2] == b"\x00\x00":
933+
key, count, values = self.main_unpack(data)
934+
self.log_message(addr, key, count=count, length=len(data), values=values, recv=True)
935+
if ackn:
936+
self.send_ack(addr, key, count)
937+
938+
# assume audio messages
939+
elif len(data) >= 1:
940+
key = "AUDIO"
941+
values = self.unpack(FORMAT["AUDIO_FRAME"], data)[0]
942+
self.log_message(addr, key, length=len(data), values=values, recv=True)
943+
944+
except ValueError as error:
945+
print("error decoding message from {}: {} - {}".format(addr, error, data))
933946

934947
return (addr, key, count, values)
935948

0 commit comments

Comments
 (0)