async def send_msg_body(self, msg_body, user: Optional[User] = None, status=200, allow_udp=False, f_pro_force=False): # StatusCode: https://ja.wikipedia.org/wiki/HTTPステータスコード assert isinstance(msg_body, bytes), 'msg_body is bytes' assert 200 <= status < 600, 'Not found status code {}'.format(status) # check user existence if len(self.user) == 0: raise PeerToPeerError('there is no user connection') # select random user if user is None: user = random.choice(self.user) # send message if allow_udp and f_pro_force: self._send_udp_body(msg_body, user) elif allow_udp and user.header.p2p_udp_accept and len(msg_body) < 1400: self._send_udp_body(msg_body, user) else: msg_body = zlib.compress(msg_body) msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) msg_len = len(msg_body).to_bytes(4, 'big') send_data = msg_len + msg_body await user.send(send_data) self.traffic.put_traffic_up(send_data) return user
async def send_msg_body(self, msg_body, user: Optional[User] = None, allow_udp=False, f_pro_force=False): assert isinstance(msg_body, bytes), 'msg_body is bytes' # check user existence if len(self.user) == 0: raise PeerToPeerError('there is no user connection') # select random user if user is None: user = random.choice(self.user) # send message if allow_udp and f_pro_force: loop.run_in_executor(None, self.send_udp_body, msg_body, user) elif allow_udp and user.header.p2p_udp_accept and len(msg_body) < 1400: loop.run_in_executor(None, self.send_udp_body, msg_body, user) else: msg_body = zlib.compress(msg_body) msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) msg_len = len(msg_body).to_bytes(4, 'big') send_data = msg_len + msg_body await user.send(send_data) self.traffic.put_traffic_up(send_data) return user
def _send_udp_body(self, msg_body, user): name_len = len(V.SERVER_NAME.encode()).to_bytes(1, 'big') msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) send_data = name_len + V.SERVER_NAME.encode() + msg_body host_port = user.get_host_port() sock_family = socket.AF_INET if len( host_port) == 2 else socket.AF_INET6 with socket.socket(sock_family, socket.SOCK_DGRAM) as sock: sock.sendto(send_data, host_port) self.traffic.put_traffic_up(send_data)
def _udp_body(self, msg_body, user): name_len = len(V.SERVER_NAME.encode()).to_bytes(1, 'big') msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) send_data = name_len + V.SERVER_NAME.encode() + msg_body host_port = user.get_host_port() if len(host_port) == 2: self.udp_ipv4_sock.sendto(send_data, host_port) else: self.udp_ipv6_sock.sendto(send_data, host_port) self.traffic.put_traffic_up(send_data)
def send_udp_body(self, msg_body, user): """send UDP message""" name_len = len(V.SERVER_NAME.encode()).to_bytes(1, 'big') msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) send_data = name_len + V.SERVER_NAME.encode() + msg_body host_port = user.get_host_port() sock_family = socket.AF_INET if len( host_port) == 2 else socket.AF_INET6 # warning: may block this closure, use run_in_executor with socket.socket(sock_family, socket.SOCK_DGRAM) as sock: sock.sendto(send_data, host_port) self.traffic.put_traffic_up(send_data)
def udp_server_listen(server_sock, mask): try: msg, address = server_sock.recvfrom(8192) msg_len = msg[0] msg_name, msg_body = msg[1:msg_len + 1], msg[msg_len + 1:] user = self.name2user(msg_name.decode()) if user is None or not user.p2p_udp_accept: return self.traffic.put_traffic_down(msg_body) msg_body = AESCipher.decrypt(key=user.aeskey, enc=msg_body) if msg_body == b'Ping': logging.debug("Get udp accept from {}".format(user)) self.send_msg_body(msg_body=b'Pong', user=user) else: logging.debug("Get udp packet from {}".format(user)) self.core_que.broadcast((user, msg_body)) except OSError as e: logging.debug("OSError {}".format(e)) except Exception as e: logging.debug(e, exc_info=Debug.P_EXCEPTION)
async def udp_server_handle(msg, addr, core: Core): msg_body = None try: msg_len = msg[0] msg_name, msg_body = msg[1:msg_len + 1], msg[msg_len + 1:] user = core.name2user(msg_name.decode()) if user is None: return core.traffic.put_traffic_down(msg_body) msg_body = AESCipher.decrypt(key=user.aeskey, enc=msg_body) if msg_body == b'Ping': log.info(f"get udp ping from {user.header.name} addr:{addr}") await core.send_msg_body(msg_body=b'Pong', user=user) else: log.debug(f"get udp packet from {user.header.name} addr:{addr}") await core.core_que.put((user, msg_body)) except ValueError as e: log.debug(f"maybe decrypt failed by {e} {msg_body}") except OSError as e: log.debug(f"OSError on udp listen by {str(e)}") except Exception as e: log.debug("UDP handle exception", exc_info=Debug.P_PRINT_EXCEPTION)
def send_msg_body(self, msg_body, user=None, status=200, f_udp=False, f_pro_force=False): # StatusCode: https://ja.wikipedia.org/wiki/HTTPステータスコード assert type(msg_body) == bytes, 'msg_body is bytes' assert 200 <= status < 600, 'Not found status code {}'.format(status) # get client if len(self.user) == 0: raise ConnectionError('client connection is zero.') elif len(msg_body) > C.MAX_RECEIVE_SIZE + 5000: error = 'Max message size is {}kb (You try {}Kb)'.format( round(C.MAX_RECEIVE_SIZE / 1000000, 3), round(len(msg_body) / 1000000, 3)) self.send_msg_body(msg_body=bjson.dumps(error), user=user, status=500) raise ConnectionRefusedError(error) elif user is None: user = random.choice(self.user) # send message if f_udp and f_pro_force: self._udp_body(msg_body, user) elif f_udp and user.p2p_udp_accept and len(msg_body) < 1400: self._udp_body(msg_body, user) else: msg_body = zlib.compress(msg_body) msg_body = AESCipher.encrypt(key=user.aeskey, raw=msg_body) msg_len = len(msg_body).to_bytes(4, 'big') send_data = msg_len + msg_body user.send(send_data) self.traffic.put_traffic_up(send_data) # logging.debug("Send {}Kb to '{}'".format(len(msg_len+msg_body) / 1000, user.name)) return user
async def receive_loop(self, user: User): # Accept connection for check_user in self.user.copy(): if check_user.header.name != user.header.name: continue elif await self.ping(check_user): error = f"same origin found and ping success, remove new connection" self.remove_connection(user, error) return else: error = f"same origin found but ping failed, remove old connection" self.remove_connection(check_user, error) self.user.append(user) log.info(f"check success and go into loop {user}") bio = BytesIO( ) # Warning: don't use initial_bytes, same duplicate ID used? bio_length = 0 msg_length = 0 f_raise_timeout = False error = None while not self.f_stop: try: get_msg = await user.recv() if len(get_msg) == 0: error = "Fall in loop, socket closed." break # check message params init bio_length += bio.write(get_msg) if msg_length == 0: # init message params msg_bytes = bio.getvalue() msg_length, initial_bytes = int.from_bytes( msg_bytes[:4], 'big'), msg_bytes[4:] bio.truncate(0) bio.seek(0) bio_length = bio.write(initial_bytes) elif bio_length == 0: error = "Why bio_length is zero?, msg_length={}".format( msg_length, bio_length) break else: pass # check complete message receive if bio_length >= msg_length: # success, get all message msg_bytes = bio.getvalue() msg_body, initial_bytes = msg_bytes[: msg_length], msg_bytes[ msg_length:] if len(initial_bytes) == 0: # no another message msg_length = 0 f_raise_timeout = False elif len(initial_bytes) < 4: error = "Failed to get message length? {}".format( initial_bytes) break else: # another message pushing msg_length, initial_bytes = int.from_bytes( initial_bytes[:4], 'big'), initial_bytes[4:] f_raise_timeout = True bio.truncate(0) bio.seek(0) bio_length = bio.write(initial_bytes) else: # continue getting message f_raise_timeout = True continue # continue to process msg_body self.traffic.put_traffic_down(msg_body) msg_body = AESCipher.decrypt(key=user.aeskey, enc=msg_body) msg_body = zlib.decompress(msg_body) if msg_body == b'Ping': log.debug(f"receive Ping from {user.header.name}") await self.send_msg_body(b'Pong', user) elif msg_body == b'Pong': log.debug(f"receive Pong from {user.header.name}") self._ping.set() else: await self.core_que.put((user, msg_body)) f_raise_timeout = False except asyncio.TimeoutError: if f_raise_timeout: error = "Timeout: Not allowed timeout when getting message!" break except ConnectionError as e: error = "ConnectionError: " + str(e) break except OSError as e: error = "OSError: " + str(e) break except Exception: import traceback error = "Exception: " + str(traceback.format_exc()) break # After exit from loop, close socket if not bio.closed: bio.close() self.remove_connection(user, error)
async def initial_connection_check(self, reader: StreamReader, writer: StreamWriter): host_port = writer.get_extra_info('peername') new_user: Optional[User] = None try: # 1. send plain message writer.write(b'hello') await writer.drain() # 2. receive other's header try: received = await asyncio.wait_for(reader.read(BUFFER_SIZE), 5.0) if len(received) == 0: raise PeerToPeerError('empty msg receive') header = json.loads(received.decode()) except asyncio.TimeoutError: raise PeerToPeerError('timeout on other\'s header receive') except json.JSONDecodeError: raise PeerToPeerError( 'json decode error on other\'s header receive') # 3. generate new user user_header = UserHeader(**header) new_user = User(user_header, self.number, reader, writer, host_port, AESCipher.create_key(), SERVER_SIDE) self.number += 1 if new_user.header.name == V.SERVER_NAME: raise ConnectionAbortedError('Same origin connection') # 4. send my public key my_sec, my_pub = generate_keypair() send = json.dumps({'public-key': my_pub}).encode() await new_user.send(send) self.traffic.put_traffic_up(send) # 5. receive public key try: receive = await new_user.recv() self.traffic.put_traffic_down(receive) if len(receive) == 0: raise ConnectionAbortedError('received msg is zero.') data = json.loads(receive.decode()) except asyncio.TimeoutError: raise PeerToPeerError('timeout on public key receive') except json.JSONDecodeError: raise PeerToPeerError( 'json decode error on public key receive') # 6. encrypt and send AES key and header send = json.dumps({ 'aes-key': new_user.aeskey, 'header': self.get_server_header(), }) key = generate_shared_key(my_sec, data['public-key']) encrypted = AESCipher.encrypt(key, send.encode()) await new_user.send(encrypted) self.traffic.put_traffic_up(encrypted) # 7. receive accept signal try: encrypted = await new_user.recv() self.traffic.put_traffic_down(encrypted) except asyncio.TimeoutError: raise PeerToPeerError('timeout on accept signal receive') receive = AESCipher.decrypt(new_user.aeskey, encrypted) if receive != b'accept': raise PeerToPeerError(f"Not accept signal! {receive}") # 8. accept connection log.info( f"established connection as server from {new_user.header.name} {new_user.get_host_port()}" ) asyncio.ensure_future(self.receive_loop(new_user)) # server port's reachable check await asyncio.sleep(1.0) await self.check_reachable(new_user) return except (ConnectionAbortedError, ConnectionResetError) as e: msg = f"disconnect error {host_port} {e}" except PeerToPeerError as e: msg = f"peer2peer error {host_port} {e}" except Exception as e: msg = "InitialConnCheck: {}".format(e) log.error(msg, exc_info=True) # EXCEPTION! if new_user: # remove user self.remove_connection(new_user, msg) else: # close socket log.debug(msg) try: writer.write(msg.encode()) await writer.drain() except Exception: pass try: writer.close() except Exception: pass
async def create_connection(self, host, port): for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, canonname, host_port = res if host_port[0] in ban_address: return False # baned address try: if V.TOR_CONNECTION: if af != socket.AF_INET: continue sock = socks.socksocket() sock.setproxy(socks.PROXY_TYPE_SOCKS5, V.TOR_CONNECTION[0], V.TOR_CONNECTION[1]) else: sock = socket.socket(af, socktype, proto) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.connect(host_port) sock.setblocking(False) reader, writer = await asyncio.open_connection(sock=sock, loop=loop) break except ConnectionRefusedError: continue # try to connect closed socket except OSError as e: log.debug(f"socket creation error by {str(e)}") continue else: # create no connection return False log.debug(f"success create connection to {host_port}") try: # 1. receive plain message try: msg = await asyncio.wait_for(reader.read(BUFFER_SIZE), 5.0) if msg != b'hello': raise PeerToPeerError( 'first plain msg not correct? {}'.format(msg)) except asyncio.TimeoutError: raise PeerToPeerError('timeout on first plain msg receive') # 2. send my header send = json.dumps(self.get_server_header()).encode() writer.write(send) await writer.drain() self.traffic.put_traffic_up(send) # 3. receive public key try: my_sec, my_pub = generate_keypair() receive = await asyncio.wait_for(reader.read(BUFFER_SIZE), 5.0) self.traffic.put_traffic_down(receive) msg = json.loads(receive.decode()) except asyncio.TimeoutError: raise PeerToPeerError('timeout on public key receive') except json.JSONDecodeError: raise PeerToPeerError( 'json decode error on public key receive') # 4. send public key send = json.dumps({'public-key': my_pub}).encode() writer.write(send) await writer.drain() self.traffic.put_traffic_up(send) # 5. Get AES key and header and decrypt try: receive = await asyncio.wait_for(reader.read(BUFFER_SIZE), 5.0) self.traffic.put_traffic_down(receive) key = generate_shared_key(my_sec, msg['public-key']) dec = AESCipher.decrypt(key, receive) data = json.loads(dec.decode()) except asyncio.TimeoutError: raise PeerToPeerError('timeout on AES key and header receive') except json.JSONDecodeError: raise PeerToPeerError( 'json decode error on AES key and header receive') aeskey, header = data['aes-key'], data['header'] # 6. generate new user user_header = UserHeader(**header) new_user = User(user_header, self.number, reader, writer, host_port, aeskey, CLIENT_SIDE) # 7. check header if new_user.header.network_ver != V.NETWORK_VER: raise PeerToPeerError( 'Don\'t same network version [{}!={}]'.format( new_user.header.network_ver, V.NETWORK_VER)) self.number += 1 # 8. send accept signal encrypted = AESCipher.encrypt(new_user.aeskey, b'accept') await new_user.send(encrypted) self.traffic.put_traffic_up(encrypted) # 9. accept connection log.info( f"established connection as client to {new_user.header.name} {new_user.get_host_port()}" ) asyncio.ensure_future(self.receive_loop(new_user)) # server port's reachable check await asyncio.sleep(1.0) await self.check_reachable(new_user) return True except PeerToPeerError as e: msg = "peer2peer error, {} ({})".format(e, host) except ConnectionRefusedError as e: msg = "connection refused error, {} ({})".format(e, host) except ValueError as e: msg = "ValueError: {} {}".format(host, e) except Exception as e: log.error("NewConnectionError", exc_info=True) msg = "NewConnectionError {} {}".format(host, e) # close socket log.debug(msg) if not writer.transport.is_closing(): writer.close() return False
def _receive_msg(self, user): # Accept connection with self.lock: for check_user in self.user: if check_user.name != user.name: continue if self.ping(check_user): error = "Remove new connection {}, continue connect {}".format( user, check_user) self.remove_connection(user, error) logging.info(error) else: error = "Same origin, Replace new connection {} => {}".format( check_user, user) self.remove_connection(check_user, error) logging.info(error) self.user.append(user) logging.info("Accept connection \"{}\"".format(user.name)) # pooling msg_prefix = b'' msg_len = 0 msg_body = b'' error = None try: while not self.f_stop: if len(msg_prefix) == 0: user.sock.settimeout(3600) first_msg = user.sock.recv(self.buffsize) user.sock.settimeout(10) else: first_msg, msg_prefix = msg_prefix, b'' # Start receive message msg_len = int.from_bytes(first_msg[:4], 'big') msg_body = first_msg[4:] # Notice long message if Debug.F_LONG_MSG_INFO and msg_len != len(msg_body): logging.debug("Receive long msg, len=%d, body=%d" % (msg_len, len(msg_body))) if msg_len == 0: raise ConnectionAbortedError( "1:Socket error, fall in loop.") elif len(msg_body) == 0: raise ConnectionAbortedError( "2:Socket error, fall in loop.") elif len(msg_body) >= msg_len: msg_body, msg_prefix = msg_body[:msg_len], msg_body[ msg_len:] self.traffic.put_traffic_down(msg_body) msg_body = AESCipher.decrypt(key=user.aeskey, enc=msg_body) msg_body = zlib.decompress(msg_body) if msg_body == b'Ping': logging.debug("receive ping from {}".format(user.name)) self.send_msg_body(b'Pong', user) continue elif msg_body == b'Pong': logging.debug("receive Pong from {}".format(user.name)) self._ping.set() continue else: self.core_que.broadcast((user, msg_body)) continue # continue receiving message while True: new_body = user.sock.recv(self.buffsize) msg_body += new_body if len(new_body) == 0: raise ConnectionAbortedError( "3:Socket error, fall in loop.") elif len(msg_body) >= msg_len: msg_body, msg_prefix = msg_body[:msg_len], msg_body[ msg_len:] self.traffic.put_traffic_down(msg_body) msg_body = AESCipher.decrypt(key=user.aeskey, enc=msg_body) msg_body = zlib.decompress(msg_body) self.core_que.broadcast((user, msg_body)) break elif len(msg_body) > C.MAX_RECEIVE_SIZE + 5000: raise ConnectionAbortedError( "Too many data! (MAX {}Kb)".format( C.MAX_RECEIVE_SIZE // 1000)) else: continue except socket.timeout: error = "socket timeout {}".format(user.name) logging.debug(error) except ConnectionAbortedError as e: error = "ConnectionAbortedError :len={}, msg={} e={}".format( msg_len, msg_body, e) except ConnectionResetError: error = "ConnectionResetError by {}".format(user.name) except OSError as e: error = "OSError by {}, {}".format(user.name, e) except Exception as e: error = "BaseException by {}, {}".format(user.name, e) # raised exception on loop logging.debug(error) if not self.remove_connection(user, error): logging.debug("Failed remove user {}".format(user.name))
def _initial_connection_check(self, sock, host_port): sock.settimeout(10) try: # ヘッダーを受取る received = sock.recv(self.buffsize) if len(received) == 0: raise ConnectionAbortedError('zero msg, connection closed.') self.traffic.put_traffic_down(received) header = json.loads(received.decode()) with self.lock: new_user = User(self.number, sock, host_port, aeskey=AESCipher.create_key(), sock_type=C.T_SERVER) self.number += 1 new_user.deserialize(header) if new_user.name == V.SERVER_NAME: raise ConnectionAbortedError('Same origin connection.') # こちらの公開鍵を送る send = json.dumps({'public-key': self.ecc.pk}).encode() sock.sendall(send) self.traffic.put_traffic_up(send) # 公開鍵を取得する receive = new_user.sock.recv(self.buffsize) self.traffic.put_traffic_down(receive) if len(receive) == 0: raise ConnectionAbortedError('received msg is zero.') public_key = json.loads(receive.decode())['public-key'] # AESKEYとHeaderを暗号化して送る encrypted = self.ecc.encrypt(recipient_pk=public_key, msg=json.dumps({ 'aes-key': new_user.aeskey, 'header': self.get_server_header() }).encode(), encode='raw') new_user.send(encrypted) self.traffic.put_traffic_up(encrypted) # Accept信号を受け取る encrypted = new_user.sock.recv(self.buffsize) self.traffic.put_traffic_down(encrypted) receive = AESCipher.decrypt(new_user.aeskey, encrypted) if receive != b'accept': raise ConnectionAbortedError('Not accept signal.') # Accept connection logging.info("New connection from \"{}\" {}".format( new_user.name, new_user.get_host_port())) Thread(target=self._receive_msg, name='S:' + new_user.name, args=(new_user, ), daemon=True).start() # Port accept check sleep(10) if new_user in self.user: self.is_reachable(new_user) return except ConnectionAbortedError as e: error = "ConnectionAbortedError, {}".format(e) except json.decoder.JSONDecodeError as e: error = "JSONDecodeError, {}".format(e) except socket.timeout: error = "socket.timeout" except Exception as e: error = "Exception as {}".format(e) # close socket error = "Close on initial check " + error logging.debug(error) try: sock.sendall(error.encode()) except: pass try: sock.close() except: pass
def create_connection(self, host, port): sock = host_port = None try: for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, canonname, host_port = res if host_port[0] in ban_address: return False # baned address try: sock = socket.socket(af, socktype, proto) except OSError: continue sock.settimeout(10) # Connection try: sock.connect(host_port) break except OSError: sock.close() continue else: # create no connection return False logging.debug("Success connection create to {}".format(host_port)) # ヘッダーを送る send = json.dumps(self.get_server_header()).encode() sock.sendall(send) self.traffic.put_traffic_up(send) # 公開鍵を受取る receive = sock.recv(self.buffsize) self.traffic.put_traffic_down(receive) public_key = json.loads(receive.decode())['public-key'] # 公開鍵を送る send = json.dumps({'public-key': self.ecc.pk}).encode() sock.sendall(send) self.traffic.put_traffic_up(send) # AESKEYとヘッダーを取得し復号化する receive = sock.recv(self.buffsize) self.traffic.put_traffic_down(receive) data = json.loads( self.ecc.decrypt(sender_pk=public_key, enc=receive).decode()) aeskey, header = data['aes-key'], data['header'] logging.debug("Success ase-key receive {}".format(host_port)) # ユーザーを作成する with self.lock: new_user = User(self.number, sock, host_port, aeskey, C.T_CLIENT) new_user.deserialize(header) # headerのチェック if new_user.network_ver != V.NETWORK_VER: raise PeerToPeerError( 'Don\'t same network version [{}!={}]'.format( new_user.network_ver, V.NETWORK_VER)) self.number += 1 # Acceptシグナルを送る encrypted = AESCipher.encrypt(new_user.aeskey, b'accept') sock.sendall(encrypted) self.traffic.put_traffic_up(encrypted) logging.info("New connection to \"{}\" {}".format( new_user.name, new_user.get_host_port())) Thread(target=self._receive_msg, name='C:' + new_user.name, args=(new_user, ), daemon=True).start() c = 20 while new_user not in self.user and c > 0: sleep(1) c -= 1 self.is_reachable(new_user) if c == 0: return False else: return True except json.JSONDecodeError: error = "Json decode error." except PeerToPeerError as e: error = "NewConnectionError {} {}".format(host_port, e) except ConnectionRefusedError as e: error = "ConnectionRefusedError {} {}".format(host_port, e) except Exception as e: error = "NewConnectionError {} {}".format(host_port, e) # close socket logging.debug(error) try: sock.sendall(error.encode()) except: pass try: sock.shutdown(socket.SHUT_RDWR) except: pass try: sock.close() except: pass return False