diff options
| author | 2025-09-14 15:20:59 +0330 | |
|---|---|---|
| committer | 2025-09-14 15:20:59 +0330 | |
| commit | d54aabd5d0d55331a6d9b74e99ef311c347abb82 (patch) | |
| tree | c26db0c47bfd9fe547e6bcaba776cd1c79ece6bd /client | |
| parent | add custom port support (diff) | |
query packets by author feature
Diffstat (limited to 'client')
| -rw-r--r-- | client/3rd-party/client.py | 69 | ||||
| -rw-r--r-- | client/3rd-party/client.ts | 72 | ||||
| -rw-r--r-- | client/client.c | 69 |
3 files changed, 183 insertions, 27 deletions
diff --git a/client/3rd-party/client.py b/client/3rd-party/client.py index 56a8faa..52675ad 100644 --- a/client/3rd-party/client.py +++ b/client/3rd-party/client.py @@ -411,6 +411,17 @@ class MoonpasteFFI: self.lib.mp_generate_keypair.argtypes = [] self.lib.mp_generate_keypair.restype = KeyPair + # mp_query_author_packets + self.lib.mp_query_author_packets.argtypes = [ + ctypes.c_char_p, # ip + c_int, # port + KeyPair, # author_keys + c_uint16, # count + c_uint64, # offset + POINTER(DataPacketList) # out_list + ] + self.lib.mp_query_author_packets.restype = c_int + class MoonpasteClient: '''High-level Python client for moonpaste''' @@ -680,4 +691,60 @@ class MoonpasteClient: # Push to server success = self.push_packet(ip, port, packet) - return packet, success
\ No newline at end of file + return packet, success + + def query_author_packets(self, ip: str, port: int, author_keys: PyKeyPair, count: int = 10, offset: int = 0) -> PyDataPacketList: + '''Query packets for a specific author''' + c_keys = author_keys.to_c() + packet_list = DataPacketList() + + result = self.ffi.lib.mp_query_author_packets( + ip.encode('utf-8'), + port, + c_keys, + count, + offset, + byref(packet_list) + ) + + if result != ClientError.SUCCESS: + raise MoonpasteClientError(ClientError(result)) + + packets = [] + if packet_list.count > 0 and packet_list.packets: + for i in range(packet_list.count): + packets.append(PyDataPacket.from_c(packet_list.packets[i])) + + # Free the allocated memory safely + try: + import ctypes.util + libc_name = ctypes.util.find_library('c') + if libc_name: + libc = ctypes.CDLL(libc_name) + libc.free.argtypes = [c_void_p] + libc.free(cast(packet_list.packets, c_void_p)) + else: + # Platform-specific fallback + try: + if sys.platform.startswith('linux'): + libc = ctypes.CDLL('libc.so.6') + elif sys.platform == 'darwin': + libc = ctypes.CDLL('libc.dylib') + elif sys.platform == 'win32': + libc = ctypes.CDLL('msvcrt.dll') + else: + libc = None + + if libc: + libc.free.argtypes = [c_void_p] + libc.free(cast(packet_list.packets, c_void_p)) + except OSError: + pass # Can't free, but better than crash + except: + pass # Memory will be freed when process exits + + return PyDataPacketList( + packets=packets, + count=packet_list.count, + new_offset=packet_list.new_offset + )
\ No newline at end of file diff --git a/client/3rd-party/client.ts b/client/3rd-party/client.ts index 44f4634..711dc3b 100644 --- a/client/3rd-party/client.ts +++ b/client/3rd-party/client.ts @@ -155,7 +155,11 @@ class MoonpasteFFI { mp_generate_keypair: { args: [], returns: FFIType.ptr - } + }, + mp_query_author_packets: { + args: [FFIType.cstring, FFIType.i32, FFIType.ptr, FFIType.u16, FFIType.u64, FFIType.ptr], + returns: FFIType.i32 + }, }) break @@ -204,7 +208,7 @@ class MoonpasteFFI { try { const ffi = require('ffi-napi') // dependency const ref = require('ref-napi') // dependency - + this.lib = ffi.Library(libPath, { mp_get_server_info: ['int', ['string', 'int', 'pointer']], mp_get_server_neighbors: ['int', ['string', 'int', 'uint8', 'pointer', 'pointer']], @@ -248,9 +252,9 @@ class MoonpasteFFI { async getServerInfo(ip: string, port: number): Promise<RelayInfo> { const infoBuffer = this.allocateBuffer(1024) const ipBuffer = this.stringToBuffer(ip) - + const result = this.lib.symbols.mp_get_server_info(ipBuffer, port, infoBuffer) - + if (result !== ClientError.Success) { throw new Error(`Failed to get server info: ${result}`) } @@ -262,9 +266,9 @@ class MoonpasteFFI { const neighborsPtr = this.allocateBuffer(8) // pointer const countPtr = this.allocateBuffer(1) // uint8 const ipBuffer = this.stringToBuffer(ip) - + const result = this.lib.symbols.mp_get_server_neighbors(ipBuffer, port, count, neighborsPtr, countPtr) - + if (result !== ClientError.Success) { throw new Error(`Failed to get neighbors: ${result}`) } @@ -276,9 +280,9 @@ class MoonpasteFFI { const successPtr = this.allocateBuffer(1) const ipBuffer = this.stringToBuffer(ip) const packetBuffer = this.serializePacket(packet) - + const result = this.lib.symbols.mp_push_packet(ipBuffer, port, packetBuffer, successPtr) - + if (result !== ClientError.Success) { throw new Error(`Failed to push packet: ${result}`) } @@ -289,9 +293,9 @@ class MoonpasteFFI { async getPacket(ip: string, port: number, hash: Uint8Array): Promise<DataPacket> { const packetBuffer = this.allocateBuffer(70000) // approximation const ipBuffer = this.stringToBuffer(ip) - + const result = this.lib.symbols.mp_get_packet(ipBuffer, port, hash, packetBuffer) - + if (result !== ClientError.Success) { throw new Error(`Failed to get packet: ${result}`) } @@ -302,9 +306,9 @@ class MoonpasteFFI { async getFirehosePackets(ip: string, port: number, offset: bigint, count: number): Promise<DataPacketList> { const listBuffer = this.allocateBuffer(1024) const ipBuffer = this.stringToBuffer(ip) - + const result = this.lib.symbols.mp_get_firehose_packets(ipBuffer, port, offset, count, listBuffer) - + if (result !== ClientError.Success) { throw new Error(`Failed to get firehose packets: ${result}`) } @@ -312,10 +316,24 @@ class MoonpasteFFI { return this.parseDataPacketList(listBuffer) } + async queryAuthorPackets(ip: string, port: number, authorKeys: KeyPair, count: number, offset: bigint): Promise<DataPacketList> { + const keysBuffer = this.serializeKeyPair(authorKeys) + const listBuffer = this.allocateBuffer(1024) + const ipBuffer = this.stringToBuffer(ip) + + const result = this.lib.symbols.mp_query_author_packets(ipBuffer, port, keysBuffer, count, offset, listBuffer) + + if (result !== ClientError.Success) { + throw new Error(`Failed to query author packets: ${result}`) + } + + return this.parseDataPacketList(listBuffer) + } + createPacketFromBytes(input: Uint8Array, recipientCount: number, recipients: Uint8Array[] | null, authorKeys: KeyPair): DataPacket { const recipientsBuffer = recipients ? this.serializeRecipients(recipients) : null const keysBuffer = this.serializeKeyPair(authorKeys) - + const result = this.lib.symbols.mp_create_packet_from_bytes( input, input.length, @@ -323,7 +341,7 @@ class MoonpasteFFI { recipientsBuffer, keysBuffer ) - + return this.parseDataPacket(result) } @@ -332,7 +350,7 @@ class MoonpasteFFI { const filenameBuffer = this.stringToBuffer(filename) const recipientsBuffer = recipients ? this.serializeRecipients(recipients) : null const keysBuffer = this.serializeKeyPair(authorKeys) - + const result = this.lib.symbols.mp_create_packet_from_file( filenameBuffer, recipientCount, @@ -340,7 +358,7 @@ class MoonpasteFFI { keysBuffer, packetBuffer ) - + if (result !== ClientError.Success) { throw new Error(`Failed to create packet from file: ${result}`) } @@ -365,7 +383,7 @@ class MoonpasteFFI { const majorVersion = view.getUint8(offset++) const minorVersion = view.getUint8(offset++) - + const motdBytes = new Uint8Array(buffer, offset, 128) const motd = new TextDecoder().decode(motdBytes).replace(/\0.*$/, '') offset += 128 @@ -407,11 +425,11 @@ class MoonpasteFFI { private parseRelayNeighborAt(buffer: any, offset: number): RelayNeighbor { const view = new DataView(buffer instanceof ArrayBuffer ? buffer : buffer.buffer) - + const active = view.getUint8(offset) !== 0 const hasIpv4 = view.getUint8(offset + 1) !== 0 const hasIpv6 = view.getUint8(offset + 2) !== 0 - + const v4Addr = new Uint8Array(buffer, offset + 3, 4) const v6Addr = new Uint8Array(buffer, offset + 7, 16) @@ -421,14 +439,14 @@ class MoonpasteFFI { private parseNeighbors(neighborsPtr: any, countPtr: any): RelayNeighbor[] { const count = this.readUint8(countPtr) const neighbors: RelayNeighbor[] = [] - + if (count > 0 && neighborsPtr) { for (let i = 0; i < count; i++) { const neighborOffset = i * 23 // RELAY_NEIGHBOR_WIRE_LEN neighbors.push(this.parseRelayNeighborAt(neighborsPtr, neighborOffset)) } } - + return neighbors } @@ -481,12 +499,12 @@ class MoonpasteFFI { private parseDataPacketList(buffer: any): DataPacketList { const view = new DataView(buffer instanceof ArrayBuffer ? buffer : buffer.buffer) - + const newOffset = view.getBigUint64(0, true) const availableBytes = buffer.byteLength - 8 const packetSize = 4 + 32 + (1 + 8 + 32 + 32 + 1 + (32 * 32) + 65536 + 64) // PACKET_SIZE const packetCount = Math.floor(availableBytes / packetSize) - + const packets: DataPacket[] = [] for (let i = 0; i < packetCount; i++) { const packetOffset = 8 + (i * packetSize) @@ -624,14 +642,14 @@ export class MoonpasteClient { const data = typeof message === 'string' ? new TextEncoder().encode(message) : message const keys = authorKeys || this.generateKeyPair() const recipientCount = recipients ? recipients.length : 0 - + return this.ffi.createPacketFromBytes(data, recipientCount, recipients || null, keys) } async createPacketFromFile(filename: string, recipients?: Uint8Array[], authorKeys?: KeyPair): Promise<DataPacket> { const keys = authorKeys || this.generateKeyPair() const recipientCount = recipients ? recipients.length : 0 - + return this.ffi.createPacketFromFile(filename, recipientCount, recipients || null, keys) } @@ -643,6 +661,10 @@ export class MoonpasteClient { return this.ffi.generateKeyPair() } + async queryAuthorPackets(ip: string, port: number, authorKeys: KeyPair, count: number = 10, offset: bigint = 0n): Promise<DataPacketList> { + return this.ffi.queryAuthorPackets(ip, port, authorKeys, count, offset) + } + // Utility methods static hashToHex(hash: Uint8Array): string { return Array.from(hash).map(b => b.toString(16).padStart(2, '0')).join('') diff --git a/client/client.c b/client/client.c index bac1b6e..155d895 100644 --- a/client/client.c +++ b/client/client.c @@ -525,4 +525,71 @@ struct data_packet mp_run_pow(struct data_packet packet, byte leading_zeros) { return packet; } -keypair mp_generate_keypair(void) { return generate_keypair(); }
\ No newline at end of file +keypair mp_generate_keypair(void) { return generate_keypair(); } + +mp_client_error_t mp_query_author_packets( + const char *ip, + int port, + keypair author_keys, + uint16_t count, + uint64_t offset, + struct data_packet_list *out_list) { + byte msg[ 32 + 32 + 2 + 8 ]; + memcpy(msg, author_keys.encryption_pub, 32); + memcpy(msg + 32, author_keys.signing_pub, 32); + write_u16_le(msg + 64, count); + write_u64_le(msg + 66, offset); + byte sig[ 64 ]; + if (crypto_sign_detached(sig, NULL, msg, sizeof(msg), author_keys.signing_priv) != 0) { + return MP_CLIENT_ERROR_BAD_ARGUMENT; + } + const size_t send_len = 1 + 32 + 32 + 64 + 2 + 8; + byte *send_data = malloc(send_len); + if (!send_data) return MP_CLIENT_ERROR_ALLOC_FAILED; + size_t off = 0; + send_data[ off++ ] = query_packets; + memcpy(send_data + off, author_keys.encryption_pub, 32); + off += 32; + memcpy(send_data + off, author_keys.signing_pub, 32); + off += 32; + memcpy(send_data + off, sig, 64); + off += 64; + write_u16_le(send_data + off, count); + off += 2; + write_u64_le(send_data + off, offset); + off += 8; + byte *response = NULL; + size_t resp_len = 0; + mp_client_error_t ret = mp_client_request(ip, port, send_data, send_len, &response, &resp_len); + free(send_data); + if (ret != MP_CLIENT_SUCCESS) { + free(response); + return ret; + } + if (resp_len < 11 || response[ 0 ] != queried_author_packets) { + free(response); + return MP_CLIENT_ERROR_INVALID_RESPONSE; + } + uint16_t resp_count = read_u16_le(response + 1); + uint64_t new_offset = read_u64_le(response + 3); + size_t expected_len = 11 + (size_t) resp_count * PACKET_SIZE; + if (resp_len != expected_len) { + free(response); + return MP_CLIENT_ERROR_INVALID_RESPONSE; + } + out_list->packets = malloc((size_t) resp_count * sizeof(struct data_packet)); + if (!out_list->packets) { + free(response); + return MP_CLIENT_ERROR_ALLOC_FAILED; + } + out_list->count = 0; + out_list->new_offset = new_offset; + for (uint16_t i = 0; i < resp_count; ++i) { + bool success; + struct data_packet packet + = s2data_packet(PACKET_SIZE, response + 11 + (size_t) i * PACKET_SIZE, &success); + if (success) { out_list->packets[ out_list->count++ ] = packet; } + } + free(response); + return MP_CLIENT_SUCCESS; +}
\ No newline at end of file |
