/** * Handle reception of a /LNI */ static void g2_node_handle_lni(gnutella_node_t *n, const g2_tree_t *t) { g2_tree_t *c; /* * Handle the children of /LNI. */ G2_TREE_CHILD_FOREACH(t, c) { enum g2_lni_child ct = TOKENIZE(g2_tree_name(c), g2_lni_children); const char *payload; size_t paylen; switch (ct) { case G2_LNI_GU: /* the node's GUID */ payload = g2_tree_node_payload(c, &paylen); if (GUID_RAW_SIZE == paylen) node_set_guid(n, (guid_t *) payload, TRUE); break; case G2_LNI_NA: /* the node's address, with listening port */ { host_addr_t addr; uint16 port; if (g2_node_parse_address(c, &addr, &port)) { if (host_address_is_usable(addr)) n->gnet_addr = addr; n->gnet_port = port; } } break; case G2_LNI_LS: /* library statistics */ payload = g2_tree_node_payload(c, &paylen); if (paylen >= 8) { uint32 files = peek_le32(payload); uint32 kbytes = peek_le32(&payload[4]); n->gnet_files_count = files; n->gnet_kbytes_count = kbytes; n->flags |= NODE_F_SHARED_INFO; } break; case G2_LNI_V: /* vendor code */ payload = g2_tree_node_payload(c, &paylen); if (paylen >= 4) n->vcode.u32 = peek_be32(payload); break; case G2_LNI_UP: /* uptime */ payload = g2_tree_node_payload(c, &paylen); if (paylen <= 4) n->up_date = tm_time() - vlint_decode(payload, paylen); break; } } }
static char * thex_upload_uuid(const struct tth *tth) { static char buf[64]; const char *data; data = tth->data; str_bprintf(buf, sizeof buf, "uuid:%08x-%04x-%04x-%04x-%08x%04x", peek_le32(&data[0]), peek_le16(&data[4]), peek_le16(&data[6]), peek_le16(&data[8]), peek_le32(&data[10]), peek_le16(&data[14])); return buf; }
static void handle_pong(const char * const data, const size_t size) { if (size < 14) { printf("Too short: %s\n", escape_buffer(data, size)); } else { printf("Source: %s\n", net_addr_port_to_string(net_addr_peek_ipv4(&data[2]), peek_le16(data))); printf("Files: %lu\n", (unsigned long) peek_le32(&data[6])); printf("Volume: %lu KiB\n", (unsigned long) peek_le32(&data[10])); if (size > 14) { handle_extension(&data[14], size - 14); } } }
/** * Encrypt a block with the supplied key. * * @param res where result is stored * @param key the encryption key * @param value the value to encrypt */ static void G_HOT t_encrypt(tea_block_t *res, const tea_key_t *key, const tea_block_t *value) { uint32 v0, v1, sum = 0; int i; uint32 delta = TEA_CONSTANT; uint32 k0, k1, k2, k3; /* cache key */ v0 = peek_le32(&value->v[0]); v1 = peek_le32(&value->v[4]); k0 = peek_le32(&key->v[0]); k1 = peek_le32(&key->v[4]); k2 = peek_le32(&key->v[8]); k3 = peek_le32(&key->v[12]); #define TEA_ROUND_ESTEP \ sum += delta; \ v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1); \ v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3); for (i = 0; i < TEA_ROUNDS / 4; i++) { TEA_ROUND_ESTEP; TEA_ROUND_ESTEP; TEA_ROUND_ESTEP; TEA_ROUND_ESTEP; } poke_le32(&res->v[0], v0); poke_le32(&res->v[4], v1); }
g2_node_extract_size_request(const g2_tree_t *t, uint64 *min, uint64 *max) { const char *p; size_t paylen; /* * The payload can be 2 32-bit or 2 64-bit values. */ p = g2_tree_node_payload(t, &paylen); if (8 == paylen) { *min = (uint64) peek_le32(p); *max = (uint64) peek_le32(&p[4]); return TRUE; } else if (16 == paylen) { *min = peek_le64(p); *max = peek_le64(&p[8]); return TRUE; } return FALSE; }
static void handle_push(const char *data, size_t size) { if (size < 26) { fprintf(stderr, "handle_push(): Too small\n"); return; } printf("ServentID: %08lx-%08lx-%08lx-%08lx\n", (unsigned long) peek_be32(&data[0]), (unsigned long) peek_be32(&data[4]), (unsigned long) peek_be32(&data[8]), (unsigned long) peek_be32(&data[12])); printf("Index: %lu\n", (unsigned long) peek_le32(&data[16])); printf("Target: %s\n", net_addr_port_to_string(net_addr_peek_ipv4(&data[20]), peek_le16(&data[24]))); handle_extension(&data[26], size - 26); }
/** * Squeeze buffer to a 32-bit value. * Buffer length must be a multiple of 4. */ uint32 tea_squeeze(void *buf, size_t len) { char *p; size_t remain; uint32 result = 0; g_assert(0 == (len & 0x03)); /* multiple of 4 bytes */ for (remain = len, p = buf; remain >= 4; remain -= 4, p += 4) { uint32 val; val = peek_le32(p); result ^= val; } g_assert(0 == remain); return result; }
static void handle_qhit(const char *data, size_t size) { const struct gnutella_qhit_header *header; const struct gnutella_guid *guid; const struct gnutella_qhit_item *item; size_t guid_offset = size - sizeof *guid; unsigned hits; size_t pos; RUNTIME_ASSERT(size <= GNUTELLA_MAX_PAYLOAD); if (size < sizeof *header) { fprintf(stderr, "handle_qhit(): Too little payload for header.\n"); return; } header = cast_to_const_void_ptr(data); hits = (unsigned char) header->hits; printf("Hits: %u\n", hits); printf("Address: %s\n", net_addr_port_to_string(net_addr_peek_ipv4(cast_to_const_char_ptr(header->addr)), peek_le16(header->port))); printf("Speed: %lu\n", (unsigned long) peek_le32(header->speed)); if (size < sizeof *header + sizeof *guid) { fprintf(stderr, "handle_qhit(): Insufficient payload for query hit.\n"); return; } if (size >= sizeof *header + sizeof *guid) { guid = cast_to_const_void_ptr(&data[guid_offset]); printf("Servent ID: %08lx-%08lx-%08lx-%08lx\n", (unsigned long) peek_be32(&guid->data[0]), (unsigned long) peek_be32(&guid->data[4]), (unsigned long) peek_be32(&guid->data[8]), (unsigned long) peek_be32(&guid->data[12])); } printf("----\n"); pos = sizeof *header; for (/* NOTHING */; hits > 0; hits--) { const char *nul_ptr; if (pos >= guid_offset || guid_offset - pos < sizeof *item + 2) break; item = cast_to_const_void_ptr(&data[pos]); printf("Index: %lu\n", (unsigned long) peek_le32(item->index)); printf("Size: %lu\n", (unsigned long) peek_le32(item->size)); pos += sizeof *item; nul_ptr = memchr(&data[pos], 0, guid_offset - pos); if (!nul_ptr) { fprintf(stderr, "handle_qhit(): Non-terminated filename.\n"); return; } else { size_t len; len = (nul_ptr - &data[pos]); if (len > (((size_t) -1) / 4 - 1)) { fprintf(stderr, "handle_qhit(): Filename is too long.\n"); /* Ignore */ } else { const char *p; size_t avail; printf("Filename: "); avail = len; p = &data[pos]; while (avail > 0) { uint32_t cp; cp = utf8_decode(p, avail); if ((uint32_t) -1 != cp) { uint8_t u_len, i; u_len = utf8_first_byte_length_hint((unsigned char) *p); RUNTIME_ASSERT(u_len > 0); RUNTIME_ASSERT(avail >= u_len); avail -= u_len; if (cp >= 0x20 && cp != 0x7f) { for (i = 0; i < u_len; i++) { putchar((unsigned char) p[i]); } } else { char ch = cp & 0xff; printf("%s", escape_buffer(&ch, 1)); } p += u_len; } else { if (verbosity > 0) { fprintf(stderr, "handle_qhit(): Invalid UTF-8.\n"); } break; } } } printf("\n"); pos += len; } RUNTIME_ASSERT(nul_ptr); RUNTIME_ASSERT(&data[pos] == nul_ptr); RUNTIME_ASSERT('\0' == *nul_ptr); pos++; RUNTIME_ASSERT(pos <= guid_offset); nul_ptr = memchr(&data[pos], 0, guid_offset - pos); if (!nul_ptr) { fprintf(stderr, "handle_qhit(): Non-terminated extension block.\n"); return; } else if (nul_ptr != &data[pos]) { size_t len = nul_ptr - &data[pos]; printf("Extension size: %lu\n", (unsigned long) len); handle_extension(&data[pos], len); pos += len; } RUNTIME_ASSERT(nul_ptr); RUNTIME_ASSERT(&data[pos] == nul_ptr); RUNTIME_ASSERT('\0' == *nul_ptr); pos++; RUNTIME_ASSERT(pos <= guid_offset); printf("------\n"); } if (hits > 0) { fprintf(stderr, "handle_qhit(): Expected %u more hits.\n", hits); } if (pos < guid_offset) { static const unsigned vendor_id_len = 4; printf("Extended QHD size: %lu\n", (unsigned long) guid_offset - pos); if (guid_offset - pos >= vendor_id_len) { printf("Vendor ID: %s\n", escape_buffer(&data[pos], vendor_id_len)); pos += vendor_id_len; if (pos < guid_offset) { uint8_t open_data_size = data[pos]; bool has_ggep = false; printf("Open data size: %u\n", open_data_size); pos++; if (open_data_size > guid_offset - pos) { printf("Open data size is too large.\n"); return; } if (open_data_size >= 2) { uint8_t mask = data[pos]; uint8_t value = data[pos + 1]; printf("mask: 0x%02x\n", mask); printf("value: 0x%02x\n", value); if (0x20 & mask) { has_ggep = 0x20 & value; printf("Has GGEP: %s\n", has_ggep ? "yes" : "no"); } if (0x10 & mask) { printf("Has speed: %s\n", (0x10 & value) ? "yes" : "no"); } if (0x08 & mask) { printf("Has uploaded: %s\n", (0x08 & value) ? "yes" : "no"); } if (0x04 & mask) { printf("Busy: %s\n", (0x04 & value) ? "yes" : "no"); } /* mask and value are swapped */ if (0x01 & value) { printf("Must push: %s\n", (0x01 & mask) ? "yes" : "no"); } } pos += open_data_size; if (pos < guid_offset) { size_t priv_data_size = guid_offset - pos; static const char id_deflate[] = "{deflate}"; const char *priv_data, *x; priv_data = &data[pos]; priv_data_size = guid_offset - pos; printf("Private data area size: %lu\n", (unsigned long) priv_data_size); handle_extension(priv_data, priv_data_size); x = compat_memmem(priv_data, priv_data_size, id_deflate, STATIC_STRLEN(id_deflate)); if (x) { char buf[64 * 1024]; const char *src; size_t n, src_len; src = &x[STATIC_STRLEN(id_deflate)]; src_len = priv_data_size - STATIC_STRLEN(id_deflate); n = buffer_inflate(buf, sizeof buf, src, src_len); if ((size_t) -1 != n) { printf("Inflated: %s\n", escape_buffer(buf, n)); } } pos += priv_data_size; } RUNTIME_ASSERT(pos == guid_offset); } } } printf("----\n"); }
int main(int argc, char *argv[]) { const char *filename; bool skip_hs = false; bool with_dump_headers = false; int fd, c; setvbuf(stdout, NULL, _IOLBF, 0); while (-1 != (c = getopt(argc, argv, "DHh"))) { switch (c) { case 'h': usage(EXIT_SUCCESS); break; case 'H': skip_hs = true; break; case 'D': with_dump_headers = true; break; default: fprintf(stderr, "Unsupported option: -- %c\n", c); usage(EXIT_FAILURE); } } argc -= optind; argv += optind; if (argc > 1) { fprintf(stderr, "Error: " "Specify exactly one filename or none to read from the standard input." "\n"); usage(EXIT_FAILURE); } filename = argv[0]; if (filename) { fd = open(filename, O_RDONLY, 0); if (fd < 0) { fprintf(stderr, "open(\"%s\", O_RDONLY, 0) failed: %s\n", filename, strerror(errno)); exit(EXIT_FAILURE); } } else { fd = STDIN_FILENO; } /* Discard the handshake */ if (skip_hs) { if (0 != skip_handshake(fd)) { fprintf(stderr, "Failed to skip handshake\n"); exit(EXIT_FAILURE); } printf("Skipped handshake\n"); } for (;;) { struct gnutella_header header; static char *payload; size_t ret; uint32_t payload_size; STATIC_ASSERT(23 == sizeof header); if (!payload) { payload = malloc(GNUTELLA_MAX_PAYLOAD); if (!payload) { fprintf(stderr, "malloc(%lu) failed: %s", (unsigned long) GNUTELLA_MAX_PAYLOAD, strerror(errno)); return -1; } } if (with_dump_headers) { struct dump_header dh; safe_read(fd, &dh, sizeof dh); if (dh.flags & DH_F_TO) { struct dump_header dh_from; safe_read(fd, &dh_from, sizeof dh_from); print_dump_from_header(&dh_from); print_dump_to_header(&dh); } else { print_dump_from_header(&dh); } } ret = fill_buffer_from_fd(fd, &header, sizeof header); switch (ret) { case 0: fprintf(stderr, "Error: Unexpected end of file.\n"); exit(EXIT_FAILURE); case (size_t) -1: fprintf(stderr, "Error: Could not fill packet buffer: %s\n", strerror(errno)); exit(EXIT_FAILURE); case 1: break; default: RUNTIME_ASSERT(0); } payload_size = peek_le32(header.size); if (payload_size > GNUTELLA_MAX_PAYLOAD) { fprintf(stderr, "Error: Message is too large.\n"); return -1; } if (payload_size > 0) { ret = fill_buffer_from_fd(fd, payload, payload_size); switch (ret) { case 0: case (size_t) -1: exit(EXIT_FAILURE); case 1: break; default: RUNTIME_ASSERT(0); } } printf("GUID: %08lx-%08lx-%08lx-%08lx\n", (unsigned long) peek_be32(&header.guid.data[0]), (unsigned long) peek_be32(&header.guid.data[4]), (unsigned long) peek_be32(&header.guid.data[8]), (unsigned long) peek_be32(&header.guid.data[12])); if (header.type != GPT_DHT) { printf("Type: %s\n", packet_type_to_string(header.type)); printf("TTL : %u\n", (unsigned char) header.ttl); printf("Hops: %u\n", (unsigned char) header.hops); printf("Size: %lu\n", (unsigned long) payload_size); } else { printf("Type: %s\n", kademlia_type_to_string(header.type)); printf("V : %u.%u\n", (unsigned char) header.ttl, (unsigned char) header.hops); printf("Size: %lu\n", (unsigned long) payload_size + 23 - 61); } printf("--\n"); switch ((enum gnutella_packet_type) header.type) { case GPT_PING: handle_ping(payload, payload_size); break; case GPT_PONG: handle_pong(payload, payload_size); break; case GPT_QRP: handle_qrp(payload, payload_size); break; case GPT_VMSG_PRIV: handle_vmsg_priv(&header, payload, payload_size); break; case GPT_VMSG_STD: handle_vmsg_std(payload, payload_size); break; case GPT_PUSH: handle_push(payload, payload_size); break; case GPT_RUDP: handle_rudp(payload, payload_size); break; case GPT_DHT: handle_dht(payload, payload_size); break; case GPT_QUERY: handle_query(&header, payload, payload_size); break; case GPT_QHIT: handle_qhit(payload, payload_size); break; case GPT_HSEP: handle_hsep(payload, payload_size); break; } printf("==========\n"); } return 0; }