commit 320ad23b296532c6a40785dd68d3e3ec50f0ce72 Author: Mystikfluu Date: Sat May 20 11:37:33 2023 +0200 init diff --git a/client.py b/client.py new file mode 100644 index 0000000..db94711 --- /dev/null +++ b/client.py @@ -0,0 +1,129 @@ +timeout = 1 +serverAddressPort = ("213.47.107.152", 1337) + +possible_value = input("Enter a value: ") +filename = possible_value if (filename := possible_value) else "data.bin" + +import socket, struct, select, time, math + +import hashlib + +#import brotli #pip install brotlipy + +max_frame_payload=508 +header = "!L16s" +header_size = struct.calcsize(header) +max_payload = max_frame_payload - header_size + +UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + + +message = filename + + +UDPClientSocket.sendto(str.encode(message), serverAddressPort) + +msgFromServer = UDPClientSocket.recvfrom(max_frame_payload)[0] + +msg = struct.unpack(f'!LH{len(message)}s', msgFromServer) + +last_packet_size = msg[1] + +if msg[2] != str.encode(message): + print('Server error: wrong file name') + exit() + +total_packets = msg[0] + +packets = msg[0] + +print(f'Packets to receive: {packets}') + +total_data = b'' + +data_packets = [] +for i in range(packets): + data_packets.append(None) + +sha512 = hashlib.sha512() + +serverhash = None + +time_started = time.time() + +while packets > 0 or serverhash == None: + + ready = select.select([UDPClientSocket], [], [], timeout) + + if ready[0]: + + datareceived = UDPClientSocket.recvfrom(max_frame_payload)[0] + if len(datareceived) == 64: + serverhash = struct.unpack("64s", datareceived)[0] + continue + if len(datareceived) != max_frame_payload: + print("\nerror bad packet: wrong size",len(datareceived)) + continue + + + seq,checksum,data = struct.unpack(f"{header}{max_payload}s", datareceived) + if data_packets[seq] != None: + print("\nerror bad packet: received multiple times",seq) + continue + + #check if packet is last packet and cut data to last packet size + if seq == total_packets-1: + #print("len before cut",len(data)) + data = data[:last_packet_size] + # print("len after cut",len(data)) + + #check if packet is corrupted + if checksum.hex() != hashlib.sha3_512(data).digest()[:16].hex(): + print("\nerror bad packet: checksum mismatch",seq) + print(checksum.hex(),hashlib.sha3_512(data).digest()[:16].hex()) + print(data.hex()) + continue + + data_packets[seq] = data + + packets -= 1 + else: + #collect packets that were not received and send them in one message + for i in range(len(data_packets)): + if data_packets[i] == None: + #print("requesting packet", i) + UDPClientSocket.sendto(str.encode(f"{message}/{i}"), serverAddressPort) + + if serverhash == None: + UDPClientSocket.sendto(str.encode(f"{message}:"), serverAddressPort) + print(f'{total_packets - packets}/{total_packets} | {math.floor((total_packets - packets)/(time.time()-time_started if (time.time()-time_started)!=0 else 1)*max_payload)}Bps'+'\t'*10, end='\r') + + + +print("finished transfer", end='\r') + +for packet in data_packets: + sha512.update(packet) + +sha512 = sha512.hexdigest() + +print('sha512: %s' % sha512) + +print('server sha512: %s' % serverhash.hex()) + +if sha512 != serverhash.hex(): + print('hash mismatch') + with open("received/"+filename+".corrupt", 'wb') as f: + f.write(total_data) + exit() + +total_data = b''.join(data_packets) + +with open("received/"+filename, 'wb') as f: + f.write(total_data) + +print(f'{filename} generated with size {len(total_data)} bytes') + + + + diff --git a/generate_data.py b/generate_data.py new file mode 100644 index 0000000..2281e1b --- /dev/null +++ b/generate_data.py @@ -0,0 +1,9 @@ +import secrets + +rndsize = 488*50_000 + +rndbytes = secrets.token_bytes(rndsize) + +open('files/data.bin', 'wb').write(rndbytes) + +print('data.bin generated with size %d bytes' % rndsize) \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..a0794e7 --- /dev/null +++ b/server.py @@ -0,0 +1,92 @@ +import socket, struct, os, select, math, hashlib, re#, brotli + +max_frame_payload=508 +header = "!L16s" +header_size = struct.calcsize(header) +max_payload = max_frame_payload - header_size + +localport = 1337 +localip = '0.0.0.0' + + +UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) + +UDPServerSocket.bind((localip, localport)) +UDPServerSocket.setblocking(0) + +print("UDP server up and listening") + +packet_request_format = re.compile("[a-zA-Z0-9.-_ ]+/[0-9]+") +hash_request_format = re.compile("[a-zA-Z0-9.-_ ]+:") + +while True: + + ready = select.select([UDPServerSocket], [], [], 0.1) + + if ready[0]: + + bytesAddressPair = UDPServerSocket.recvfrom(max_frame_payload) + + message = bytesAddressPair[0] + + if ".." in message.decode(): + continue + + address = bytesAddressPair[1] + + if re.match(packet_request_format, message.decode()): + filename = "./files/"+message.decode().split('/')[0] + if not os.path.isfile(filename): + continue + with open(filename, 'rb') as f: + part = int(message.decode().split('/')[1]) + f.seek(part*max_payload) + data = f.read(max_payload) + checksum = hashlib.sha3_512(data).digest()[:16] + data = struct.pack(f"{header}{max_payload}s", part, checksum, data) + UDPServerSocket.sendto(data, address) + print(f'sent lost packet {message.decode()}') + continue + + if re.match(hash_request_format, message.decode()): + print("hash request") + filename = "./files/"+message.decode().split(':')[0] + if not os.path.isfile(filename): + continue + sha512 = hashlib.sha1() + with open(filename, 'rb') as f: + data = f.read(max_payload) + sha512.update(data) + UDPServerSocket.sendto(struct.pack("64s", sha512.digest()), address) + continue + + filename = "./files/"+message.decode() + if not os.path.isfile(filename): + continue + file_len = math.ceil(os.path.getsize(filename)/max_payload) + + #calculate last packet size + last_packet_size = os.path.getsize(filename) % max_payload + if last_packet_size == 0: + last_packet_size = max_payload # if last packet is full, set size to max + #print(max_payload,last_packet_size) + + tosend = struct.pack(f'!LH{len(message.decode())}s', file_len, last_packet_size, message) + + UDPServerSocket.sendto(tosend, address) + + sha512 = hashlib.sha512() + + with open(filename, 'rb') as f: + for i in range(file_len): + fdata = f.read(max_payload) + sha512.update(fdata) + #if i == 5: #simulate lost packet + # continue + checksum = hashlib.sha3_512(fdata).digest()[:16] + data = struct.pack(f"{header}{max_payload}s", i, checksum, fdata) + #print("sending data",fdata) + UDPServerSocket.sendto(data, address) + print(f'{i+1}/{file_len}', end='\r') + UDPServerSocket.sendto(struct.pack("64s", sha512.digest()), address) + print(' '*100,end='\r') \ No newline at end of file