A Python port of the Invisible Internet Project (I2P)
at main 134 lines 5.1 kB view raw
1"""Diagnostic: connect to a single peer, dump all details.""" 2import asyncio 3import base64 4import hashlib 5import logging 6import random 7import sys 8import time 9 10sys.path.insert(0, "src") 11 12from i2p_data.router import RouterInfo 13from i2p_data.keys_and_cert import KeysAndCert 14from i2p_netdb.reseed import ReseedClient 15from i2p_router.identity import ( 16 RouterKeyBundle, 17 create_full_router_identity, 18) 19from i2p_router.peer_connector import extract_ntcp2_address 20from i2p_transport.ntcp2_real_server import NTCP2RealConnector 21from i2p_transport.ntcp2_blocks import BLOCK_TERMINATION, decode_blocks 22 23logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(name)s %(message)s") 24logger = logging.getLogger("diag") 25 26 27async def main(): 28 # 1. Generate keys 29 bundle = RouterKeyBundle.generate() 30 identity, ri = create_full_router_identity(bundle, "0.0.0.0", 9000) 31 our_ri_bytes = ri.to_bytes() 32 33 logger.info("=== OUR ROUTERINFO ===") 34 logger.info("RI total bytes: %d", len(our_ri_bytes)) 35 logger.info("Identity bytes: %d", len(identity.to_bytes())) 36 logger.info("Signable bytes: %d", len(ri._signable_bytes())) 37 logger.info("Signature bytes: %d", len(ri.signature)) 38 logger.info("Self-verify: %s", ri.verify()) 39 logger.info("Options: %s", ri.options) 40 logger.info("Addresses: %s", [(a.transport, a.options) for a in ri.addresses]) 41 42 # Roundtrip check 43 ri2 = RouterInfo.from_bytes(our_ri_bytes) 44 signable1 = ri._signable_bytes() 45 signable2 = ri2._signable_bytes() 46 logger.info("Roundtrip signable match: %s", signable1 == signable2) 47 logger.info("Roundtrip verify: %s", ri2.verify()) 48 49 # Show identity hex 50 id_bytes = identity.to_bytes() 51 logger.info("Identity cert: %s", id_bytes[384:].hex()) 52 logger.info("Signing pub: %s", bundle.signing_public.hex()) 53 logger.info("Enc pub (X25519): %s", bundle.ntcp2_public.hex()) 54 55 # Show full RI hex 56 logger.info("Full RI hex:\n%s", our_ri_bytes.hex()) 57 58 # 2. Reseed to get peers 59 logger.info("=== RESEEDING ===") 60 client = ReseedClient(target_count=10, min_servers=1, timeout=15) 61 ri_list = await client.reseed() 62 logger.info("Got %d RIs from reseed", len(ri_list)) 63 64 # 3. Try to connect to peers 65 connector_keypair = (bundle.ntcp2_private, bundle.ntcp2_public) 66 peer_ri_hash_for_connect = hashlib.sha256(identity.to_bytes()).digest() 67 68 random.shuffle(ri_list) 69 attempts = 0 70 for peer_bytes in ri_list: 71 if attempts >= 5: 72 break 73 try: 74 peer_ri = RouterInfo.from_bytes(peer_bytes) 75 params = extract_ntcp2_address(peer_ri) 76 if params is None: 77 continue 78 79 host, port, peer_static_pub, peer_iv = params 80 peer_identity_bytes = peer_ri.identity.to_bytes() 81 peer_hash = hashlib.sha256(peer_identity_bytes).digest() 82 83 attempts += 1 84 logger.info("=== CONNECTING TO %s:%d (hash=%s...) ===", host, port, peer_hash[:4].hex()) 85 logger.info("Peer static pub: %s", peer_static_pub.hex()) 86 logger.info("Peer IV: %s", peer_iv.hex()) 87 logger.info("Peer RI hash: %s", peer_hash.hex()) 88 logger.info("Peer RI valid: %s", peer_ri.verify()) 89 logger.info("Peer options: %s", peer_ri.options) 90 91 connector = NTCP2RealConnector() 92 try: 93 conn = await asyncio.wait_for( 94 connector.connect( 95 host=host, 96 port=port, 97 our_static_key=connector_keypair, 98 our_ri_bytes=our_ri_bytes, 99 peer_static_pub=peer_static_pub, 100 peer_ri_hash=peer_hash, 101 peer_iv=peer_iv, 102 ), 103 timeout=15, 104 ) 105 logger.info("CONNECTED! Reading first frame...") 106 try: 107 blocks = await asyncio.wait_for(conn.recv_frame(), timeout=5) 108 for block in blocks: 109 logger.info("Block type=%d len=%d data=%s", 110 block.block_type, len(block.data), block.data.hex()) 111 if block.block_type == BLOCK_TERMINATION: 112 reason = block.data[-1] if block.data else -1 113 logger.info("TERMINATION reason=%d", reason) 114 except asyncio.TimeoutError: 115 logger.info("No frame received within 5s (peer might be OK!)") 116 except Exception as e: 117 logger.info("Error reading frame: %s", e) 118 119 await conn.close() 120 121 except asyncio.TimeoutError: 122 logger.info("Connection timed out") 123 except Exception as e: 124 logger.info("Connection failed: %s", e, exc_info=True) 125 126 except Exception as e: 127 logger.debug("Skip peer: %s", e) 128 continue 129 130 logger.info("=== DONE ===") 131 132 133if __name__ == "__main__": 134 asyncio.run(main())