/** * Check whether the Gnutella host vector already contains the address:port. * * @return TRUE if the host vector already contains it. */ bool gnet_host_vec_contains(gnet_host_vec_t *vec, host_addr_t addr, uint16 port) { size_t i; g_return_val_if_fail(vec, FALSE); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: for (i = 0; i < vec->n_ipv4; i++) { char *dest = cast_to_pointer(&vec->hvec_v4[i]); uint32 ip = peek_be32(&dest[0]); uint16 pt = peek_le16(&dest[4]); if (pt == port && host_addr_ipv4(addr) == ip) return TRUE; } break; case NET_TYPE_IPV6: for (i = 0; i < vec->n_ipv6; i++) { char *dest = cast_to_pointer(&vec->hvec_v6[i]); uint16 pt = peek_le16(&dest[16]); if (pt == port && 0 == memcmp(dest, host_addr_ipv6(&addr), 16)) return TRUE; } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } return FALSE; }
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_vmsg_port(const struct gnutella_header *header, const char *data, size_t size) { const char *base = &data[8]; const size_t len = size - 8; (void) header; if (len == 2) { printf("Port: %u\n", peek_le16(base)); } else { handle_extension(base, len); } }
static void handle_vmsg_priv(const struct gnutella_header *header, const char *data, size_t size) { if (size < 8) { printf("Too short: %s\n", escape_buffer(data, size)); } else { char vendor[32], *p; size_t vendor_size = sizeof vendor; struct vmsg_head vh; unsigned i; p = append_escaped_chars(vendor, &vendor_size, data, 4); *p = '\0'; vh.vendor = peek_be32(&data[0]); vh.selector = peek_le16(&data[4]); vh.version = peek_le16(&data[6]); printf("%s/%uv%u", vendor, vh.selector, vh.version); for (i = 0; i < ARRAY_LEN(vmsg_table); i++) { if ( vmsg_table[i].vendor == vh.vendor && vmsg_table[i].selector == vh.selector && vmsg_table[i].version == vh.version ) { printf(" %s\n", vmsg_table[i].name); vmsg_table[i].handler(header, data, size); break; } } if (ARRAY_LEN(vmsg_table) == i) { printf("\n"); handle_extension(&data[8], size - 8); } } }
static void handle_vmsg_addr_port(const struct gnutella_header *header, const char *data, size_t size) { const char *base = &data[8]; const size_t len = size - 8; (void) header; if (len == 6) { printf("Address: %s\n", net_addr_port_to_string( net_addr_peek_ipv4(&base[0]), peek_le16(&base[4]))); } else { handle_extension(base, len); } }
/** * Extract the IP and port number from the GUID of queries marked for OOB * query hit delivery. * * Bytes 0 to 3 of the guid are the 4 octet bytes of the IP address. * Bytes 13 and 14 are the little endian representation of the port. */ void guid_oob_get_addr_port(const guid_t *guid, host_addr_t *addr, uint16 *port) { if (addr) { /* * IPv6-Ready: this is always 4 bytes, even if the final address is * an IPv6 one because the GGEP "6" key will supply us the IPv6 * address should the IPv4 one be 127.0.0.0. */ *addr = host_addr_peek_ipv4(&guid->v[0]); } if (port) { *port = peek_le16(&guid->v[13]); } }
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); } } }
/** * Deserialization convenience for IP:port. * * The supplied buffer must hold either 6 or 18 more bytes of data, depending * on the address type we want to deserialize. */ void host_ip_port_peek(const void *p, enum net_type nt, host_addr_t *addr, uint16 *port) { const void *q = p; if (NET_TYPE_IPV4 == nt) { *addr = host_addr_peek_ipv4(q); q = const_ptr_add_offset(q, 4); } else if (NET_TYPE_IPV6 == nt) { *addr = host_addr_peek_ipv6(q); q = const_ptr_add_offset(q, 16); } else { /* Can only deserialize IPv4:port or IPv6:port */ g_assert_not_reached(); } *port = peek_le16(q); }
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); }
/** * Extract the UDP IP:port from a /Q2/UDP and populate the search request info * if we have a valid address. */ static void g2_node_extract_udp(const g2_tree_t *t, search_request_info_t *sri, const gnutella_node_t *n) { const char *p; size_t paylen; p = g2_tree_node_payload(t, &paylen); /* * Only handle if we have an IP:port entry. * We only handle IPv4 because G2 does not support IPv6. * * We don't care about the presence of the query key because as G2 leaf, * we only process /Q2 coming from our TCP-connected hubs, and they * are in charge of validating it. Now hubs may forward us /Q2 coming * from neighbouring hubs and those won't have a query key, hence we * need to handle payloads with no trailing 32-bit QK. */ if (6 == paylen || 10 == paylen) { /* IPv4 + port (+ QK usually) */ host_addr_t addr = host_addr_peek_ipv4(p); uint16 port = peek_le16(&p[4]); if (host_is_valid(addr, port)) { sri->addr = addr; sri->port = port; /* * If the address is that of the node sending us the query, * and it is not a UDP node, then we can deliver the hit * back via the TCP connection we have, so no need to use OOB. */ if (n->port == port && host_addr_equiv(addr, n->gnet_addr)) sri->oob = NODE_IS_UDP(n); else sri->oob = TRUE; } } }
/** * Parse the payload of given node to extract a node address + port. * * @param t the tree node whose payload we wish to parse * @param addr where to write the address part * @param port where to write the port part * * @return TRUE if OK, FALSE if we could not extract anything. */ bool g2_node_parse_address(const g2_tree_t *t, host_addr_t *addr, uint16 *port) { const char *payload; size_t paylen; payload = g2_tree_node_payload(t, &paylen); /* * Only handle if we have an IP:port entry. * We only handle IPv4 because G2 does not support IPv6. */ if (6 == paylen) { /* IPv4 + port */ *addr = host_addr_peek_ipv4(payload); *port = peek_le16(&payload[4]); return TRUE; } return FALSE; /* Unrecognized payload length */ }
/** * Tree message iterator to handle "CH" nodes and extract their IP:port. */ static void g2_node_extract_ch(void *data, void *udata) { const g2_tree_t *t = data; (void) udata; if (0 == strcmp("CH", g2_tree_name(t))) { const char *payload; size_t paylen; payload = g2_tree_node_payload(t, &paylen); if (10 == paylen) { /* IPv4:port + 32-bit timestamp */ host_addr_t addr = host_addr_peek_ipv4(payload); uint16 port = peek_le16(&payload[4]); if (host_is_valid(addr, port) && !hostiles_is_bad(addr)) guess_add_hub(addr, port); } } }
static void handle_peer_array(const char *data, size_t size) { size_t pos; if (0 == size) { printf(" <No payload>"); return; } if (0 != (size % 6)) { printf(" <Invalid length (%lu); not a multiple of 6>", (unsigned long) size); return; } for (pos = 0; pos < size; pos += 6) { if (pos > 0) { printf(","); } printf(" %s", net_addr_port_to_string(net_addr_peek_ipv4(&data[pos]), peek_le16(&data[pos + 4]))); } }
static void handle_query(const struct gnutella_header *header, const char * const data, const size_t size) { const char *end; if (size < 2) { printf("Too short: %s\n", escape_buffer(data, size)); } else { uint16_t flags; int swapped = 0; flags = peek_be16(data); if (!(flags & QUERY_F_MARK)) { uint16_t mask = QUERY_F_MARK | QUERY_F_GGEP_H | QUERY_F_LEAF_GUIDED; uint16_t reversed; /* Try to decode endian swapped flags as emitted by RAZA */ reversed = peek_le16(data); if ((reversed & mask) == mask) { swapped = 1; flags = reversed; } } if (flags & QUERY_F_MARK) { int is_firewalled = flags & QUERY_F_FIREWALLED; int want_xml = flags & QUERY_F_WANT_XML; int leaf_guided = flags & QUERY_F_LEAF_GUIDED; int ggep_h = flags & QUERY_F_GGEP_H; int is_oob = flags & QUERY_F_OOB; int rudp_supp = flags & QUERY_F_RUDP; int bit8 = flags & QUERY_F_BIT8; printf("Flags: %s%s%s%s%s%s%s%s\n" , is_firewalled ? "firewalled " : "" , want_xml ? "XML " : "" , leaf_guided ? "leaf-guided " : "" , ggep_h ? "GGEP/H " : "" , is_oob ? "OOB " : "" , rudp_supp ? "RUDP " : "" , bit8 ? "bit8 " : "" , swapped ? "[SWAPPED]" : "" ); if (is_oob) { printf("OOB address: %s\n", net_addr_port_to_string(net_addr_peek_ipv4(&header->guid.data[0]), peek_le16(&header->guid.data[13])) ); } } else { printf("Flags: 0x%04X\n", flags); } end = memchr(&data[2], '\0', size - 2); if (end) { end++; } else { end = &data[size]; } printf("Query: \"%s\"\n", escape_buffer(&data[2], (end - &data[2]) - 1)); if (&data[size] != end) { size_t ext_len; ext_len = &data[size] - end; if ('\0' == data[size - 1]) { ext_len--; } handle_extension(end, ext_len); } } }
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"); }