Пример #1
0
 def create_cert(master_sk, signer_pk, cert_start, cert_stop):
     assert int(time.time()) < cert_start < cert_stop, 'wrong time setting of cert.'
     master_ecc = Encryption()
     master_ecc.sk = master_sk
     cert_raw = bjson.dumps((master_ecc.pk, signer_pk, cert_start, cert_stop), compress=False)
     cert = {
         'master': master_ecc.pk,
         'start': cert_start,
         'stop': cert_stop,
         'sign': master_ecc.sign(cert_raw, encode='hex')}
     return cert
Пример #2
0
 def __init__(self, host='', listen=15, buffsize=2048):
     assert V.DATA_PATH is not None, 'Setup p2p params before CoreClass init.'
     super().__init__(name='InnerCore', daemon=True)
     self.start_time = int(time.time())
     self.number = 0
     self.user = list()
     self.lock = Lock()
     self.host = host
     self.ecc = Encryption()
     self.ecc.secret_key()
     self.ecc.public_key()
     self.core_que = QueueSystem(maxsize=listen * 100)
     self.listen = listen
     self.buffsize = buffsize
     self.traffic = Traffic()
Пример #3
0
 def remove_file_by_master(self, signer_sk, cert, file_hash):
     file_hash = file_hash.lower()
     file_path = os.path.join(V.DATA_PATH, 'file.' + file_hash + '.dat')
     uuid = random.randint(10, 99999999)
     signer_ecc = Encryption()
     signer_ecc.sk = signer_sk
     try:
         os.remove(file_path)
         sign_raw = bjson.dumps((file_hash, uuid), compress=False)
         send_data = {
             'signer': signer_ecc.pk,
             'sign': signer_ecc.sign(msg=sign_raw, encode='hex'),
             'cert': cert}
         dummy, result = self.send_command(ClientCmd.FILE_DELETE, send_data, uuid=uuid)
         logging.debug("File delete success.")
     except:
         logging.debug("Failed delete file.")
         pass
Пример #4
0
 def __init__(self, host=None, listen=15, buffsize=4096):
     assert V.DATA_PATH is not None, 'Setup p2p params before CoreClass init.'
     self.start_time = int(time())
     self.number = 0
     self.user = list()
     self.lock = Lock()
     self.host = host  # local=>'localhost', 'global'=>None
     self.ecc = Encryption()
     self.ecc.secret_key()
     self.ecc.public_key()
     self.core_que = QueueSystem(maxsize=listen * 100)
     self.listen = listen
     self.buffsize = buffsize
     self.traffic = Traffic()
     self._ping = Event()
     self._ping.set()
     self.udp_ipv4_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     self.udp_ipv6_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
Пример #5
0
def change_encrypt_key(new_key, cur):
    # SecretKeyの鍵を変更する、Noneの場合は非暗号化
    d = cur.execute("SELECT `id`, `sk`, `pk` FROM `pool`").fetchall()
    updates = list()
    ecc = Encryption(prefix=V.BLOCK_PREFIX)
    for uuid, sk, pk in d:
        sk = AESCipher.decrypt(V.ENCRYPT_KEY, sk) if V.ENCRYPT_KEY else sk
        sk = hexlify(sk).decode()
        ecc.public_key(sk=sk)
        if ecc.pk != hexlify(pk).decode():
            raise BlockChainError(
                'Decryption error! wrong key. [{}=>{}]'.format(
                    hexlify(ecc.pk).decode(),
                    hexlify(pk).decode()))
        new_sk = sk if new_key is None else AESCipher.encrypt(key=new_key,
                                                              raw=sk)
        updates.append((uuid, new_sk))
    cur.executemany(
        """
        UPDATE `pool` SET `sk`=? WHERE `id`=?
        """, updates)
Пример #6
0
    def type_request(self, user, item):
        temperate = {
            'type': T_RESPONSE,
            'cmd': item['cmd'],
            'data': None,
            'time': time.time(),
            'uuid': item['uuid']}
        allow_list = list()
        deny_list = list()
        ack_list = list()

        if item['cmd'] == ClientCmd.PING_PONG:
            temperate['data'] = {
                'ping': item['data'],
                'pong': time.time()}
            allow_list.append(user)

        elif item['cmd'] == ClientCmd.BROADCAST:
            if item['uuid'] in self.__broadcast_uuid:
                return  # already get broadcast data
            elif self.__waiting_result.include(item['uuid']):
                return  # I'm broadcaster, get from ack
            elif not self.broadcast_check(item['data']):
                user.warn += 1
                self.__broadcast_uuid.append(item['uuid'])
                return  # not allowed broadcast data
            else:
                self.__broadcast_uuid.append(item['uuid'])
                self.broadcast_que.broadcast(item['data'])
                deny_list.append(user)
                allow_list = None
                # send ACK
                ack_list.append(user)
                # send Response
                temperate['type'] = T_REQUEST
                temperate['data'] = item['data']

        elif item['cmd'] == ClientCmd.GET_PEER_INFO:
            # [[(host,port), header],..]
            temperate['data'] = self.peers.data
            allow_list.append(user)

        elif item['cmd'] == ClientCmd.GET_NEARS:
            temperate['data'] = {user.get_host_port(): user.serialize() for user in self.p2p.user}
            allow_list.append(user)

        elif item['cmd'] == ClientCmd.CHECK_REACHABLE:
            try:
                port = item['data']['port']
            except:
                port = user.p2p_port
            temperate['data'] = is_reachable(host=user.host_port[0], port=port)
            allow_list.append(user)

        elif item['cmd'] == ClientCmd.FILE_CHECK:
            # {'hash': hash, 'uuid': uuid}
            file_hash = item['data']['hash']
            file_path = os.path.join(V.TMP_PATH, 'file.' + file_hash + '.dat')
            f_existence = os.path.exists(file_path)
            if 'uuid' in item['data']:
                f_asked = self.__user2user_route.include(item['data']['uuid'])
            else:
                f_asked = False
            temperate['data'] = {'have': f_existence, 'asked': f_asked}
            allow_list.append(user)

        elif item['cmd'] == ClientCmd.FILE_GET:
            def asking():
                # ファイル要求元のNodeに近いNode群を無視する
                nears_name = set(user_.name for user_ in self.p2p.user)
                best_name = list(nears_name - already_asked_user)
                random.shuffle(best_name)
                nears_name = list(nears_name)
                random.shuffle(nears_name)
                # nearを最後に探索するように並び替え
                try_to_ask_name = best_name + nears_name

                # ファイル所持Nodeを見つけたら即コマンド送る、それ以外は候補をリスト化
                candidates = list()
                for ask_name in try_to_ask_name:
                    try:
                        ask_user = self.p2p.name2user(ask_name)
                        if ask_user is None:
                            continue
                        send_data = {'hash': file_hash, 'uuid': item['uuid']}
                        dummy, data = self.send_command(cmd=ClientCmd.FILE_CHECK, user=ask_user,
                                                        data=send_data, timeout=2)
                    except Exception as e:
                        logging.debug("Check file existence one by one, %s", e)
                        continue
                    if data['have']:
                        # ファイル所持Nodeを発見したのでGETを即送信
                        hopeful = ask_user
                        break
                    elif not data['asked']:
                        candidates.append(ask_user)
                    else:
                        pass
                else:
                    # 候補がいなければここで探索終了
                    if len(candidates) == 0:
                        temperate['type'] = T_RESPONSE
                        self._send_msg(item=temperate, allows=[user], denys=list())
                        logging.debug("Asking, stop asking file.")
                        return
                    else:
                        hopeful = random.choice(candidates)  # 一番新しいのを候補

                logging.debug("Asking, Candidate={}, ask=>{}".format(len(candidates), hopeful.name))
                try:
                    data = {'hash': file_hash, 'asked': nears_name}
                    self.__user2user_route.put(uuid=item['uuid'], item=(user, hopeful))
                    from_client, data = self.send_command(ClientCmd.FILE_GET, data,
                                                          item['uuid'], user=hopeful, timeout=5)
                    temperate['data'] = data
                    if data is None:
                        logging.debug("Asking failed from {} {}".format(hopeful.name, file_hash))
                    else:
                        logging.debug("Asking success {} {}".format(hopeful.name, file_hash))
                except Exception as e:
                    logging.debug("Asking raised {} {} {}".format(hopeful.name, file_hash, e))
                    temperate['data'] = None
                temperate['type'] = T_RESPONSE
                count = self._send_msg(item=temperate, allows=[user], denys=list())
                logging.debug("Response file to {} {}({})".format(user.name, count, file_hash))
                return

            def sending():
                with open(file_path, mode='br') as f:
                    raw = f.read()
                temperate['type'] = T_RESPONSE
                temperate['data'] = raw
                self.__user2user_route.put(uuid=item['uuid'], item=(user, user))
                if 0 < self._send_msg(item=temperate, allows=[user], denys=list()):
                    logging.debug("Send file to {} {}".format(user.name, file_hash))
                else:
                    logging.debug("Failed send file to {} {}".format(user.name, file_hash))

            if self.__user2user_route.include(item['uuid']):
                return
            logging.debug("Asked file get by {}".format(user.name))
            file_hash = item['data']['hash']
            already_asked_user = set(item['data']['asked'])
            file_path = os.path.join(V.TMP_PATH, 'file.' + file_hash + '.dat')
            # When you have file, sending. When you don't have file, asking
            if os.path.exists(file_path):
                Thread(target=sending, name='Sending', daemon=True).start()
            elif V.F_FILE_CONTINUE_ASKING:
                # Default disable
                Thread(target=asking, name='Asking', daemon=True).start()

        elif item['cmd'] == ClientCmd.FILE_DELETE:
            item_ = item['data']
            file_hash = item_['hash']
            signer_pk = item_['signer']
            sign = item_['sign']
            cert_sign = item_['cert']['sign']
            master_pk = item_['cert']['master']
            cert_start = item_['cert']['start']
            cert_stop = item_['cert']['stop']

            if not(cert_start < int(time.time()) < cert_stop):
                return  # old signature
            elif master_pk not in C.MASTER_KEYS:
                return
            elif item['uuid'] in self.__broadcast_uuid:
                return  # already get broadcast data
            elif self.__waiting_result.include(item['uuid']):
                return  # I'm broadcaster, get from ack

            self.__broadcast_uuid.append(item['uuid'])
            cert_raw = bjson.dumps((master_pk, signer_pk, cert_start, cert_stop), compress=False)
            sign_raw = bjson.dumps((file_hash, item['uuid']), compress=False)
            deny_list.append(user)
            allow_list = None
            # send ACK
            ack_list.append(user)
            # send Response
            temperate['type'] = T_REQUEST
            temperate['data'] = item['data']
            # delete file check
            try:
                logging.debug("1:Delete request {}".format(file_hash))
                ecc = Encryption()
                ecc.pk = master_pk  # 署名者の署名者チェック
                ecc.verify(msg=cert_raw, signature=cert_sign)
                ecc.pk = signer_pk  # 署名者チェック
                ecc.verify(msg=sign_raw, signature=sign)
                if self.remove_file(file_hash):
                    logging.info("2:Delete request accepted!")
            except ValueError:
                allow_list = list()  # No sending

        elif item['cmd'] == ClientCmd.DIRECT_CMD:
            def direct_cmd():
                data = item['data']
                temperate['data'] = self.event.work(cmd=data['cmd'], data=data['data'])
                self._send_msg(item=temperate, allows=[user])
            if 'cmd' in item['data'] and item['data']['cmd'] in self.event:
                Thread(target=direct_cmd, name='DirectCmd', daemon=True).start()
        else:
            pass

        # send message
        send_count = self._send_msg(item=temperate, allows=allow_list, denys=deny_list)
        # send ack
        ack_count = 0
        if len(ack_list) > 0:
            temperate['type'] = T_ACK
            temperate['data'] = send_count
            ack_count = self._send_msg(item=temperate, allows=ack_list)
        # debug
        if Debug.P_RECEIVE_MSG_INFO:
            logging.debug("Reply to request {} All={}, Send={}, Ack={}"
                          .format(temperate['cmd'], len(self.p2p.user), send_count, ack_count))
Пример #7
0
class Core(Thread):
    f_stop = False
    f_finish = False
    f_running = False
    server_sock = None

    def __init__(self, host='', listen=15, buffsize=2048):
        assert V.DATA_PATH is not None, 'Setup p2p params before CoreClass init.'
        super().__init__(name='InnerCore', daemon=True)
        self.start_time = int(time.time())
        self.number = 0
        self.user = list()
        self.lock = Lock()
        self.host = host
        self.ecc = Encryption()
        self.ecc.secret_key()
        self.ecc.public_key()
        self.core_que = QueueSystem(maxsize=listen * 100)
        self.listen = listen
        self.buffsize = buffsize
        self.traffic = Traffic()

    def close(self):
        if not self.f_running:
            raise PeerToPeerError('Core is not running.')
        self.f_stop = True
        self.traffic.close()
        # Server/client のソケットを全て閉じる
        try:
            self.server_sock.close()
        except:
            pass
        for user in self.user:
            try:
                user.sock.close()
            except:
                pass

    def run(self):
        self.traffic.start()
        # Pooling connection
        if not V.P2P_ACCEPT:
            logging.info('You set p2p accept flag False.')
            return

        sock = host_port = None
        server_sock = self.__create_server_sock()
        logging.info("Start server %d" % V.P2P_PORT)
        self.f_running = True
        while not self.f_stop:
            try:
                sock, host_port = server_sock.accept()
                Thread(target=self.__initial_connection_check,
                       args=(sock, host_port),
                       daemon=True).start()

            except json.decoder.JSONDecodeError:
                try:
                    sock.close()
                except:
                    pass
                logging.debug("JSONDecodeError by {}".format(host_port[0]))
            except OSError as e:
                try:
                    sock.close()
                except:
                    pass
                logging.debug("OSError {}".format(e))
            except Exception as e:
                try:
                    sock.close()
                except:
                    pass
                logging.debug(e, exc_info=Debug.P_EXCEPTION)
        try:
            server_sock.close()
        except:
            pass
        self.f_finish = True
        self.f_running = False
        logging.info("Server closed.")

    def __create_server_sock(self):
        server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_sock.bind((self.host, V.P2P_PORT))
        server_sock.listen(self.listen)
        self.server_sock = server_sock
        return server_sock

    def get_server_header(self):
        return {
            'name': V.SERVER_NAME,
            'client_ver': V.CLIENT_VER,
            'network_ver': V.NETWORK_VER,
            'p2p_accept': V.P2P_ACCEPT,
            'p2p_port': V.P2P_PORT,
            'start_time': self.start_time
        }

    def create_connection(self, host, port):
        host_port = (socket.gethostbyname(host), int(port))
        if self.host_port2user(host_port) is not None:
            return False  # Already connected.
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(10)
        try:
            sock.connect(host_port)
            # ヘッダーを送る
            send = json.dumps(self.get_server_header()).encode()
            with self.lock:
                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()
            with self.lock:
                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']
            # ユーザーを作成する
            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')
            with self.lock:
                sock.sendall(encrypted)
            self.traffic.put_traffic_up(encrypted)

            Thread(target=self.__receive_msg,
                   name='C:' + new_user.name,
                   daemon=True,
                   args=(new_user, )).start()

            c = 20
            while len(self.user) == 0 and c > 0:
                time.sleep(1)
                c -= 1
            if c == 0:
                return False
            else:
                return True
        except PeerToPeerError as e:
            try:
                sock.close()
            except:
                pass
            logging.debug("NewConnectionError {} {}".format(host_port, e),
                          exc_info=Debug.P_EXCEPTION)
        except ConnectionRefusedError as e:
            try:
                sock.close()
            except:
                pass
            logging.debug("ConnectionRefusedError {} {}".format(host_port, e),
                          exc_info=Debug.P_EXCEPTION)
        except Exception as e:
            try:
                sock.close()
            except:
                pass
            logging.error("NewConnectionError {} {}".format(host_port, e),
                          exc_info=Debug.P_EXCEPTION)
        return False

    def remove_connection(self, user):
        with self.lock:
            if user in self.user:
                self.user.remove(user)
                try:
                    user.close()
                except:
                    pass
                logging.debug("remove connection to %s" % user.name)
                return True
            return False

    def send_msg_body(self, msg_body, user=None):
        assert type(msg_body) == bytes, 'msg_body is bytes'

        # get client
        if len(self.user) == 0:
            raise ConnectionError('client connection is zero.')
        elif len(msg_body) > C.MAX_RECEIVE_SIZE + 5000:
            raise ConnectionRefusedError(
                'Max message size is {}Kb (You try {}Kb)'.format(
                    C.MAX_RECEIVE_SIZE / 1000,
                    len(msg_body) / 1000))
        elif user is None:
            user = random.choice(self.user)

        # send message
        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')
        with self.lock:
            user.sock.sendall(msg_len + msg_body)
        self.traffic.put_traffic_up(msg_len + msg_body)
        # logging.debug("Send {}Kb to '{}'".format(len(msg_len+msg_body) / 1000, user.name))
        return user

    def __initial_connection_check(self, sock, host_port):
        sock.settimeout(10)
        try:
            # ヘッダーを受取る
            received = sock.recv(self.buffsize)
            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)
                if self.host_port2user(new_user.get_host_port()) is not None:
                    return  # Already connected.
                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()
            with self.lock:
                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')
            with self.lock:
                new_user.sock.sendall(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.')
        except ConnectionAbortedError as e:
            logging.debug(e)
        except json.decoder.JSONDecodeError:
            pass
        except socket.timeout:
            pass
        except Exception as e:
            logging.debug(e, exc_info=Debug.P_EXCEPTION)
        else:
            Thread(target=self.__receive_msg,
                   name='S:' + new_user.name,
                   daemon=True,
                   args=(new_user, )).start()
            return
        # close socket
        try:
            sock.close()
        except:
            pass

    def __receive_msg(self, user):
        # Accept connection
        with self.lock:
            self.user.append(user)
        logging.info("New connection from {}".format(user.name))

        # pooling
        msg_prefix = b''
        msg_len = 0
        msg_body = b''
        user.sock.settimeout(3600)
        while not self.f_stop:
            try:
                if len(msg_prefix) == 0:

                    first_msg = user.sock.recv(self.buffsize)
                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 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)
                    self.core_que.broadcast((user, msg_body))
                    continue
                else:
                    pass

                # 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:
                logging.debug("socket timeout {}".format(user.name))
                break
            except ConnectionAbortedError:
                logging.debug("1ConnectionAbortedError",
                              exc_info=Debug.P_EXCEPTION)
                logging.debug("2ConnectionAbortedError :len={}, msg={}".format(
                    msg_len, msg_body))
                break
            except ConnectionResetError:
                logging.debug("ConnectionResetError by {}".format(user.name),
                              exc_info=Debug.P_EXCEPTION)
                break
            except OSError as e:
                logging.debug("OSError by {}, {}".format(user.name, e),
                              exc_info=Debug.P_EXCEPTION)
                break
            except Exception as e:
                logging.debug("BaseException by {}, {}".format(user.name, e),
                              exc_info=Debug.P_EXCEPTION)
                break

        # raised exception on loop
        if not self.remove_connection(user):
            logging.debug("Failed remove user {}".format(user.name))

    def name2user(self, name):
        for user in self.user:
            if user.name == name:
                return user
        return None

    def host_port2user(self, host_port):
        for user in self.user:
            if host_port == user.get_host_port():
                return user
        return None
Пример #8
0
class Core:
    f_stop = False
    f_finish = False
    f_running = False

    def __init__(self, host=None, listen=15, buffsize=4096):
        assert V.DATA_PATH is not None, 'Setup p2p params before CoreClass init.'
        self.start_time = int(time())
        self.number = 0
        self.user = list()
        self.lock = Lock()
        self.host = host  # local=>'localhost', 'global'=>None
        self.ecc = Encryption()
        self.ecc.secret_key()
        self.ecc.public_key()
        self.core_que = QueueSystem(maxsize=listen * 100)
        self.listen = listen
        self.buffsize = buffsize
        self.traffic = Traffic()
        self._ping = Event()
        self._ping.set()
        self.udp_ipv4_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.udp_ipv6_sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)

    def close(self):
        if not self.f_running:
            raise PeerToPeerError('Core is not running.')
        self.traffic.close()
        for user in self.user.copy():
            self.remove_connection(user, 'Manually closing.')
        listen_sel.close()
        self.f_stop = True

    def ping(self, user: User, f_udp=False):
        try:
            self._ping.wait(10)
            self._ping.clear()
            self.send_msg_body(msg_body=b'Ping',
                               user=user,
                               f_udp=f_udp,
                               f_pro_force=True)
            r = self._ping.wait(5)
            self._ping.set()
            return r
        except Exception as e:
            logging.debug("Failed ping by {} udp={}".format(e, f_udp))
            self._ping.set()
            return False

    def start(self, s_family=socket.AF_UNSPEC):
        def tcp_server_listen(server_sock, mask):
            try:
                sock, host_port = server_sock.accept()
                Thread(target=self._initial_connection_check,
                       args=(sock, host_port),
                       daemon=True).start()
                logging.info("Server accept from {}".format(host_port))
            except OSError as e:
                logging.debug("OSError {}".format(e))
            except Exception as e:
                logging.debug(e, exc_info=Debug.P_EXCEPTION)

        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)

        def create_tcp_server_socks():
            for res in socket.getaddrinfo(self.host, V.P2P_PORT, s_family,
                                          socket.SOCK_STREAM, 0,
                                          socket.AI_PASSIVE):
                af, sock_type, proto, canon_name, sa = res
                try:
                    sock = socket.socket(af, sock_type, proto)
                except OSError as e:
                    logging.debug("Failed tcp socket.socket {}".format(sa))
                    continue
                try:
                    sock.bind(sa)
                    sock.listen(self.listen)
                    sock.setblocking(False)
                except OSError as e:
                    sock.close()
                    logging.debug("Failed tcp bind or listen {}".format(sa))
                    continue
                if af == socket.AF_INET or af == socket.AF_INET6:
                    listen_sel.register(sock, selectors.EVENT_READ,
                                        tcp_server_listen)
                    logging.info("New tcp server {} {}".format(
                        "IPV4" if sock.family == 2 else "IPV6", sa))
                else:
                    logging.warning("Not found socket type {}".format(af))
            if len(listen_sel.get_map()) == 0:
                logging.error('could not open tcp sockets')
                V.P2P_ACCEPT = False

        def create_udp_server_socks():
            before_num = len(listen_sel.get_map())
            for res in socket.getaddrinfo(self.host, V.P2P_PORT, s_family,
                                          socket.SOCK_DGRAM, 0,
                                          socket.AI_PASSIVE):
                af, sock_type, proto, canon_name, sa = res
                try:
                    sock = socket.socket(af, sock_type, proto)
                except OSError as e:
                    logging.debug("Failed udp socket.socket {}".format(sa))
                    continue
                try:
                    sock.bind(sa)
                    sock.setblocking(False)
                except OSError as e:
                    sock.close()
                    logging.debug("Failed udp bind {}".format(sa))
                    continue
                if af == socket.AF_INET or af == socket.AF_INET6:
                    listen_sel.register(sock, selectors.EVENT_READ,
                                        udp_server_listen)
                    logging.info("New udp server {} {}".format(
                        "IPV4" if sock.family == 2 else "IPV6", sa))
                else:
                    logging.warning("Not found socket type {}".format(af))
            if len(listen_sel.get_map()) == before_num:
                logging.error('could not open udp sockets')
                V.P2P_UDP_ACCEPT = False

        def sock_listen_loop():
            while not self.f_stop:
                try:
                    listen_map = listen_sel.get_map()
                    if listen_map is None:
                        logging.debug("Closed.")
                        return
                    while len(listen_map) == 0:
                        sleep(0.5)
                    events = listen_sel.select()
                    for key, mask in events:
                        callback = key.data
                        callback(key.fileobj, mask)
                except Exception as e:
                    logging.error(e)
                    sleep(3)

        assert s_family in (socket.AF_INET, socket.AF_INET6, socket.AF_UNSPEC)
        self.traffic.start()
        # Pooling connection
        if not V.P2P_ACCEPT:
            logging.info('You set p2p accept flag False.')
        else:
            # create server ipv4/ipv6
            create_tcp_server_socks()
            create_udp_server_socks()
            Thread(target=sock_listen_loop, name='Listen', daemon=True).start()
        self.f_running = True

    def get_server_header(self):
        return {
            'name': V.SERVER_NAME,
            'client_ver': V.CLIENT_VER,
            'network_ver': V.NETWORK_VER,
            'p2p_accept': V.P2P_ACCEPT,
            'p2p_udp_accept': V.P2P_UDP_ACCEPT,
            'p2p_port': V.P2P_PORT,
            'start_time': self.start_time
        }

    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

    def remove_connection(self, user, reason=None):
        if user is None:
            return False
        try:
            if reason:
                user.send(b'1111' + str(reason).encode())
        except:
            pass
        user.close()
        if user in self.user.copy():
            with self.lock:
                self.user.remove(user)
            logging.debug("remove connection to {} by \"{}\"".format(
                user.name, reason))
            return True
        else:
            logging.debug(
                "failed remove connection by \"{}\", not found {}".format(
                    reason, user.name))
            return False

    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

    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 _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 _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 is_reachable(self, new_user):
        # Check connect to the user TCP/UDP port
        if new_user not in self.user:
            return
        f_tcp = True
        host_port = new_user.get_host_port()
        af = socket.AF_INET if len(host_port) == 2 else socket.AF_INET6
        try:
            sock = socket.socket(af, socket.SOCK_STREAM)
            r = sock.connect_ex(host_port)
            if r != 0:
                f_tcp = False
            sock.close()
        except OSError:
            f_tcp = False
        f_udp = self.ping(user=new_user, f_udp=True)
        # reflect user status
        if f_tcp is not new_user.p2p_accept:
            logging.debug("{} Update TCP accept status [{}>{}]".format(
                new_user, new_user.p2p_accept, f_tcp))
            new_user.p2p_accept = f_tcp
        if f_udp is not new_user.p2p_udp_accept:
            logging.debug("{} Update UDP accept status [{}>{}]".format(
                new_user, new_user.p2p_udp_accept, f_udp))
            new_user.p2p_udp_accept = f_udp

    def name2user(self, name):
        for user in self.user:
            if user.name == name:
                return user
        return None

    def host_port2user(self, host_port):
        for user in self.user:
            if host_port == user.get_host_port():
                return user
        return None
Пример #9
0
from nem_ed25519.base import Encryption

ecc = Encryption()

print("sk", ecc.secret_key())
print("pk", ecc.public_key())
print("ck", ecc.get_address())

msg = b'hello world nice day.'
signature = ecc.sign(msg, encode='base64')
print("sign", signature)
ecc.verify(msg, signature)

enc = ecc.encrypt(ecc.pk, msg, encode='base64')
print(enc)
print(ecc.decrypt(ecc.pk, enc))