Транспорт сделан
parent
4eebb0cb84
commit
90d83aede9
103
mods/stream.py
103
mods/stream.py
|
@ -2,16 +2,15 @@ import socketserver
|
||||||
import pickle
|
import pickle
|
||||||
import simplecrypto
|
import simplecrypto
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from datetime import datetime, timedelta
|
import datetime, io
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from rpyc.lib import safe_import, Timeout
|
|
||||||
|
|
||||||
peers = {}
|
peers = {}
|
||||||
|
|
||||||
RETRANSMIT_RETRIES = 3
|
RETRANSMIT_RETRIES = 3
|
||||||
DATAGRAM_MAX_SIZE = 9000
|
DATAGRAM_MAX_SIZE = 9000
|
||||||
|
RAW_DATA_MAX_SIZE = 8000
|
||||||
PACKET_NUM_SEQ_TTL = 300
|
PACKET_NUM_SEQ_TTL = 300
|
||||||
|
|
||||||
SOCK_SEND_TIMEOUT = 60
|
SOCK_SEND_TIMEOUT = 60
|
||||||
|
@ -28,6 +27,20 @@ class InvalidPacket(Exception): pass
|
||||||
|
|
||||||
def pickle_data(data):
|
def pickle_data(data):
|
||||||
return pickle.dumps(data, protocol=4)
|
return pickle.dumps(data, protocol=4)
|
||||||
|
################
|
||||||
|
class RestrictedUnpickler(pickle.Unpickler):
|
||||||
|
def find_class(self, module, name):
|
||||||
|
# Only allow datetime
|
||||||
|
if module == "datetime" and name == 'datetime':
|
||||||
|
return getattr(datetime, name)
|
||||||
|
# Forbid everything else.
|
||||||
|
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
|
||||||
|
(module, name))
|
||||||
|
def restricted_pickle_loads(s):
|
||||||
|
"""Helper function analogous to pickle.loads()."""
|
||||||
|
return RestrictedUnpickler(io.BytesIO(s)).load()
|
||||||
|
################
|
||||||
|
|
||||||
|
|
||||||
# From rpyc.lib
|
# From rpyc.lib
|
||||||
class Timeout:
|
class Timeout:
|
||||||
|
@ -48,7 +61,7 @@ class Timeout:
|
||||||
class Packet:
|
class Packet:
|
||||||
def __init__(self, packet_payload):
|
def __init__(self, packet_payload):
|
||||||
try:
|
try:
|
||||||
d = pickle.loads(packet_payload)
|
d = restricted_pickle_loads(packet_payload)
|
||||||
self.sid = d['sid']
|
self.sid = d['sid']
|
||||||
self.type = d['type']
|
self.type = d['type']
|
||||||
self.reset_timestamp = d['reset_timestamp']
|
self.reset_timestamp = d['reset_timestamp']
|
||||||
|
@ -68,14 +81,14 @@ class Peer:
|
||||||
self.confirm_wait_packet = None
|
self.confirm_wait_packet = None
|
||||||
self.last_packet = None
|
self.last_packet = None
|
||||||
self.request_lock = threading.Lock()
|
self.request_lock = threading.Lock()
|
||||||
self.num_seq_ttl = timedelta(seconds=PACKET_NUM_SEQ_TTL)
|
self.num_seq_ttl = datetime.timedelta(seconds=PACKET_NUM_SEQ_TTL)
|
||||||
self.last_sent_packet_num = -1
|
self.last_sent_packet_num = -1
|
||||||
self.last_sent_packet_num_reset_time = datetime.utcnow()
|
self.last_sent_packet_num_reset_time = datetime.datetime.utcnow()
|
||||||
self.last_received_packet_num = -1
|
self.last_received_packet_num = -1
|
||||||
self.last_received_packet_num_reset_time = None
|
self.last_received_packet_num_reset_time = None
|
||||||
self.retransmit_count = 0
|
self.retransmit_count = 0
|
||||||
def next_packet_num(self):
|
def next_packet_num(self):
|
||||||
new_time = datetime.utcnow()
|
new_time = datetime.datetime.utcnow()
|
||||||
if (new_time - self.last_sent_packet_num_reset_time) >= self.num_seq_ttl:
|
if (new_time - self.last_sent_packet_num_reset_time) >= self.num_seq_ttl:
|
||||||
self.last_sent_packet_num = -1
|
self.last_sent_packet_num = -1
|
||||||
self.last_sent_packet_num += 1
|
self.last_sent_packet_num += 1
|
||||||
|
@ -98,18 +111,21 @@ class Peer:
|
||||||
if confirm:
|
if confirm:
|
||||||
self.last_packet = data
|
self.last_packet = data
|
||||||
self.confirm_wait_packet = (d['reset_timestamp'], d['num'])
|
self.confirm_wait_packet = (d['reset_timestamp'], d['num'])
|
||||||
self.sock.sendto(pickle_data(d), self.endpoint)
|
self.sock.sendto(data, self.endpoint)
|
||||||
def mark_packet(self, d):
|
def mark_packet(self, d):
|
||||||
d['num'] = self.next_packet_num()
|
d['num'] = self.next_packet_num()
|
||||||
d['reset_timestamp'] = self.last_sent_packet_num_reset_time
|
d['reset_timestamp'] = self.last_sent_packet_num_reset_time
|
||||||
return d
|
return d
|
||||||
def retransmit(self):
|
def retransmit(self):
|
||||||
pass
|
self.retransmit_count += 1
|
||||||
|
if self.retransmit_count > RETRANSMIT_RETRIES:
|
||||||
|
raise EOFError('retransmit limit reached')
|
||||||
|
self.sock.sendto(self.last_packet, self.endpoint)
|
||||||
def reply_my_pub_key(self, packet):
|
def reply_my_pub_key(self, packet):
|
||||||
try:
|
try:
|
||||||
self.peer_pub_key = simplecrypto.RsaPublicKey(packet.data)
|
self.peer_pub_key = simplecrypto.RsaPublicKey(packet.data)
|
||||||
except:
|
except:
|
||||||
raise EOFError
|
raise EOFError('invalid pubkey data')
|
||||||
self.my_key = simplecrypto.RsaKeypair()
|
self.my_key = simplecrypto.RsaKeypair()
|
||||||
d = {
|
d = {
|
||||||
'type': PACKET_TYPE_PEER_PUB_KEY_REPLY,
|
'type': PACKET_TYPE_PEER_PUB_KEY_REPLY,
|
||||||
|
@ -133,12 +149,23 @@ class Peer:
|
||||||
'reset_timestamp': packet.reset_timestamp
|
'reset_timestamp': packet.reset_timestamp
|
||||||
}
|
}
|
||||||
self.send(d)
|
self.send(d)
|
||||||
|
def check_received_packet(self, packet):
|
||||||
|
if self.last_received_packet_num_reset_time:
|
||||||
|
if self.last_received_packet_num_reset_time > packet.reset_timestamp:
|
||||||
|
raise EOFError('packet from past')
|
||||||
|
elif self.last_received_packet_num_reset_time < packet.reset_timestamp:
|
||||||
|
self.last_received_packet_num_reset_time = packet.reset_timestamp
|
||||||
|
if (self.last_received_packet_num + 1) != packet.num:
|
||||||
|
raise EOFError('packet sequence corrupt')
|
||||||
|
else:
|
||||||
|
self.last_received_packet_num_reset_time = packet.reset_timestamp
|
||||||
|
self.last_received_packet_num = packet.num
|
||||||
|
def send_recv_confirmation(self, packet):
|
||||||
|
pass
|
||||||
def hello(self):
|
def hello(self):
|
||||||
self.sid = uuid4().hex
|
self.sid = uuid4().hex
|
||||||
d = {
|
d = {
|
||||||
'type': PACKET_TYPE_HELLO,
|
'type': PACKET_TYPE_HELLO,
|
||||||
'reset_timestamp': None,
|
|
||||||
'num': None
|
|
||||||
}
|
}
|
||||||
self.sock.sendto(pickle_data(d))
|
self.sock.sendto(pickle_data(d))
|
||||||
def recv_packet(self, packet_payload):
|
def recv_packet(self, packet_payload):
|
||||||
|
@ -146,9 +173,9 @@ class Peer:
|
||||||
try:
|
try:
|
||||||
packet = Packet(packet_payload)
|
packet = Packet(packet_payload)
|
||||||
if packet.type == PACKET_TYPE_GOODBUY:
|
if packet.type == PACKET_TYPE_GOODBUY:
|
||||||
raise EOFError
|
raise EOFError('connection closed')
|
||||||
except:
|
except:
|
||||||
raise EOFError
|
raise EOFError('invalid packet')
|
||||||
if packet.type != PACKET_TYPE_HELLO and (not self.sid or self.sid != packet.sid):
|
if packet.type != PACKET_TYPE_HELLO and (not self.sid or self.sid != packet.sid):
|
||||||
self.hello()
|
self.hello()
|
||||||
return
|
return
|
||||||
|
@ -159,7 +186,7 @@ class Peer:
|
||||||
self.peer_pub_key = simplecrypto.RsaPublicKey(self.my_key.decrypt_raw(packet.data))
|
self.peer_pub_key = simplecrypto.RsaPublicKey(self.my_key.decrypt_raw(packet.data))
|
||||||
return
|
return
|
||||||
except:
|
except:
|
||||||
raise EOFError
|
raise EOFError('create pubkey failed')
|
||||||
elif packet.type == PACKET_TYPE_PEER_PUB_KEY_REQUEST:
|
elif packet.type == PACKET_TYPE_PEER_PUB_KEY_REQUEST:
|
||||||
self.reply_my_pub_key(packet)
|
self.reply_my_pub_key(packet)
|
||||||
return
|
return
|
||||||
|
@ -168,14 +195,48 @@ class Peer:
|
||||||
return
|
return
|
||||||
############################################
|
############################################
|
||||||
if self.confirm_wait_packet:
|
if self.confirm_wait_packet:
|
||||||
if (packet.reset_timestamp, packet.num) == self.confirm_wait_packet:
|
if (packet.reset_timestamp, packet.num) == self.confirm_wait_packet and packet.type == PACKET_TYPE_CONFIRM_RECV:
|
||||||
self.confirm_packet_recv(packet)
|
self.confirm_packet_recv(packet)
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
pass
|
self.retransmit()
|
||||||
|
return
|
||||||
|
############################################
|
||||||
|
else:
|
||||||
|
if packet.type == PACKET_TYPE_PACKET:
|
||||||
|
try:
|
||||||
|
raw = self.my_key.decrypt_raw(packet.data)
|
||||||
|
except:
|
||||||
|
raise EOFError('decrypt packet error')
|
||||||
|
self.check_received_packet(packet)
|
||||||
|
self.put_block(raw)
|
||||||
|
else:
|
||||||
|
raise EOFError('connection lost')
|
||||||
|
def send_packet(self, raw):
|
||||||
|
if self.confirm_wait_packet:
|
||||||
|
timeout = Timeout(SOCK_SEND_TIMEOUT)
|
||||||
|
while timeout.timeleft():
|
||||||
|
if not self.confirm_wait_packet: break
|
||||||
|
if self.confirm_wait_packet:
|
||||||
|
raise EOFError('connection lost')
|
||||||
|
d = {
|
||||||
|
'type': PACKET_TYPE_PACKET,
|
||||||
|
'data': raw
|
||||||
|
}
|
||||||
|
self.send(self.mark_packet(d), encrypted=True, confirm=True)
|
||||||
|
|
||||||
class UDPRequestHandler(socketserver.DatagramRequestHandler):
|
class UDPRequestHandler(socketserver.DatagramRequestHandler):
|
||||||
|
def finish(self):
|
||||||
|
'''Don't send anything'''
|
||||||
|
pass
|
||||||
def handle(self):
|
def handle(self):
|
||||||
datagram = self.rfile.read(BUFSIZE)
|
datagram = self.rfile.read(DATAGRAM_MAX_SIZE)
|
||||||
if self.client_address not in peers:
|
peer_addr = self.client_address
|
||||||
|
if peer_addr not in peers: peers[peer_addr] = Peer(self.socket, peer_addr)
|
||||||
|
try:
|
||||||
|
peers[peer_addr].recv_packet(datagram)
|
||||||
|
except EOFError:
|
||||||
|
del peers[peer_addr]
|
||||||
|
|
||||||
|
class EncryptedUDPStream:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue