Skip to content

Commit 009ce05

Browse files
authored
Merge pull request #326 from Phlogi/fix-packet-error-partial-read
Fix partial socket reads causing ‘Failed reading packet’ errors
2 parents dafd539 + b88bafd commit 009ce05

2 files changed

Lines changed: 23 additions & 5 deletions

File tree

librespot/core.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1932,6 +1932,20 @@ def read(self, length: int) -> bytes:
19321932
"""
19331933
return self.__socket.recv(length)
19341934

1935+
def read_exact(self, length: int) -> bytes:
1936+
"""Read exactly length bytes from socket or raise on EOF."""
1937+
if length == 0:
1938+
return b""
1939+
buffer = bytearray()
1940+
remaining = length
1941+
while remaining > 0:
1942+
chunk = self.__socket.recv(remaining)
1943+
if chunk == b"":
1944+
raise ConnectionError("EOF")
1945+
buffer.extend(chunk)
1946+
remaining -= len(chunk)
1947+
return bytes(buffer)
1948+
19351949
def read_int(self) -> int:
19361950
"""Read integer from socket
19371951
@@ -2042,7 +2056,8 @@ def run(self) -> None:
20422056
format(util.bytes_to_hex(packet.cmd),
20432057
packet.payload))
20442058
continue
2045-
except (RuntimeError, ConnectionResetError) as ex:
2059+
except (RuntimeError, ConnectionResetError,
2060+
ConnectionError) as ex:
20462061
if self.__running:
20472062
self.__session.logger.fatal(
20482063
"Failed reading packet! {}".format(ex))

librespot/crypto.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,19 @@ def receive_encoded(self, connection: Session.ConnectionHolder) -> Packet:
5555
try:
5656
self.__receive_cipher.nonce(self.__receive_nonce)
5757
self.__receive_nonce += 1
58-
header_bytes = self.__receive_cipher.decrypt(connection.read(3))
58+
header_bytes = self.__receive_cipher.decrypt(
59+
connection.read_exact(3))
5960
cmd = struct.pack(">s", bytes([header_bytes[0]]))
6061
payload_length = (header_bytes[1] << 8) | (header_bytes[2] & 0xff)
6162
payload_bytes = self.__receive_cipher.decrypt(
62-
connection.read(payload_length))
63-
mac = connection.read(4)
63+
connection.read_exact(payload_length))
64+
mac = connection.read_exact(4)
6465
expected_mac = self.__receive_cipher.finish(4)
6566
if mac != expected_mac:
66-
raise RuntimeError()
67+
raise RuntimeError("Bad MAC")
6768
return Packet(cmd, payload_bytes)
69+
except ConnectionError:
70+
raise
6871
except (IndexError, OSError):
6972
raise RuntimeError("Failed to receive packet")
7073

0 commit comments

Comments
 (0)