86 def receive(self, sock: socket.socket) -> str | bool:
87 buffer = bytearray()
88 received_chunks = None
89 total_chunks = None
90
91 try:
92 while True:
93 sock.settimeout(10)
94 head = self._recv_all(sock, self._header_size)
95 if head is None:
96 print("[RECEIVE ERROR] Timeout or socket closed before header")
97 return False
98
99 total_chunks_read = (
100 (head[0] << 24) |
101 (head[1] << 16) |
102 (head[2] << 8) |
103 (head[3] << 0)
104 )
105 chunk_index = (
106 (head[4] << 24) |
107 (head[5] << 16) |
108 (head[6] << 8) |
109 (head[7] << 0)
110 )
111 chunk_len = (head[8] << 8) | head[9]
112
113 needed = self._header_size + chunk_len + 1
114 if needed > self._buffer_size:
115 print(f"[RECEIVE ERROR] chunk_len {chunk_len} inconsistent (need {needed} > {self._buffer_size})")
116 return False
117
118
119 payload_plus_eot = self._recv_all(sock, chunk_len + 1)
120 if payload_plus_eot is None:
121 print(f"[RECEIVE ERROR] Timeout or socket closed while reading payload for chunk {chunk_index}")
122 return False
123
124
125 if payload_plus_eot[-1] != 0x04:
126 print(f"[RECEIVE ERROR] Missing or misplaced EOT for chunk {chunk_index}")
127 return False
128
129 remainder_padding = self._buffer_size - needed
130 if remainder_padding > 0:
131 pad = self._recv_all(sock, remainder_padding)
132 if pad is None:
133 print(f"[RECEIVE ERROR] Timeout or socket closed while reading padding for chunk {chunk_index}")
134 return False
135
136
137 if total_chunks is None:
138 total_chunks = total_chunks_read
139 if total_chunks <= 0:
140 print(f"[RECEIVE ERROR] invalid total_chunks = {total_chunks}")
141 return False
142 received_chunks = [False] * total_chunks
143
144
145 if not (0 <= chunk_index < total_chunks):
146 print(f"[RECEIVE ERROR] chunk_index {chunk_index} out of bounds (total {total_chunks})")
147 return False
148
149 if received_chunks[chunk_index]:
150 print(f"[RECEIVE ERROR] duplicate chunk {chunk_index}")
151 return False
152
153
154 ciphertext = payload_plus_eot[:chunk_len]
155 buffer.extend(ciphertext)
156 received_chunks[chunk_index] = True
157
158
159 if all(received_chunks):
160 break
161
162 return self._crypto.decrypt(bytes(buffer))
163
164 except Exception as e:
165 print(f"[RECEIVE EXCEPTION] {e}")
166 return False