/** * Mark blocks in the supplied vector as allocated in the checking bitmap. * * @param db the sdbm database * @param bvec vector where allocated block numbers are stored * @param bcnt amount of blocks in vector */ static void big_file_mark_used(DBM *db, const void *bvec, int bcnt) { DBMBIG *dbg = db->big; const void *q; int n; if (!big_check_start(db)) return; for (q = bvec, n = bcnt; n > 0; n--) { size_t bno = peek_be32(q); bit_field_t *map; long bmap; size_t bit; bmap = bno / BIG_BITCOUNT; /* Bitmap handling this block */ bit = bno & (BIG_BITCOUNT - 1); /* Index within bitmap */ q = const_ptr_add_offset(q, sizeof(guint32)); /* * It's because of this sanity check that we don't want to consider * the bitcheck field as a huge continuous map. Also doing that would * violate the encapsulation: we're not supposed to know how bits are * allocated in the field. */ if (bmap >= dbg->bitmaps) continue; map = ptr_add_offset(dbg->bitcheck, bmap * BIG_BLKSIZE); bit_field_set(map, bit); } }
/** * 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; } } }
/** * 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 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); }
/** * Make sure vector of block numbers is ordered and points to allocated data, * but was not already flagged as being used by another key / value. * * @param what string describing what is being tested (key or value) * @param db the sdbm database * @param bvec vector where allocated block numbers are stored * @param bcnt amount of blocks in vector * * @return TRUE on success. */ static gboolean big_file_check(const char *what, DBM *db, const void *bvec, int bcnt) { size_t prev_bno = 0; /* 0 is invalid: it's the first bitmap */ const void *q; int n; if (!big_check_start(db)) return TRUE; /* Cannot validate, assume it's OK */ for (q = bvec, n = bcnt; n > 0; n--) { size_t bno = peek_be32(q); bit_field_t *map; long bmap; size_t bit; if (!big_block_is_allocated(db, bno)) { g_warning("sdbm: \"%s\": " "%s from .pag refers to unallocated block %lu in .dat", sdbm_name(db), what, (unsigned long) bno); return FALSE; } if (prev_bno != 0 && bno <= prev_bno) { g_warning("sdbm: \"%s\": " "%s from .pag lists unordered block list (corrupted file?)", sdbm_name(db), what); return FALSE; } q = const_ptr_add_offset(q, sizeof(guint32)); prev_bno = bno; /* * Make sure block is not used by someone else. * * Because we mark blocks as used in big keys and values only after * we validated both the key and the value for a given pair, we cannot * detect shared blocks between the key and value of a pair. */ bmap = bno / BIG_BITCOUNT; /* Bitmap handling this block */ bit = bno & (BIG_BITCOUNT - 1); /* Index within bitmap */ g_assert(bmap < db->big->bitmaps); map = ptr_add_offset(db->big->bitcheck, bmap * BIG_BLKSIZE); if (bit_field_get(map, bit)) { g_warning("sdbm: \"%s\": " "%s from .pag refers to already seen block %lu in .dat", sdbm_name(db), what, (unsigned long) bno); return FALSE; } } return TRUE; }
static void handle_vmsg_time_sync_reply(const struct gnutella_header *header, const char *data, size_t size) { if (size < 17) { printf("Too short: %s\n", escape_buffer(data, size)); } else { bool used_ntp = data[8] & 0x1; char buf[32], *p; printf("NTP: %s\n", used_ntp ? "yes" : "no"); p = print_iso8601_date(buf, sizeof buf, peek_be32(&header->guid.data[0])); *p = '\0'; printf("T1: %s (%lu us)\n", buf, (unsigned long) peek_be32(&header->guid.data[4])); p = print_iso8601_date(buf, sizeof buf, peek_be32(&data[9])); *p = '\0'; printf("T2: %s (%lu us)\n", buf, (unsigned long) peek_be32(&data[13])); p = print_iso8601_date(buf, sizeof buf, peek_be32(&header->guid.data[8])); *p = '\0'; printf("T3: %s (%lu us)\n", buf, (unsigned long) peek_be32(&header->guid.data[12])); if (size > 17) { handle_extension(&data[17], size - 17); } } }
/** * Minimal pseudo-random number generation, combining a simple PRNG with * past-collected entropy. * * @return a 31-bit random number. */ static int entropy_rand31(void) { int result; static size_t offset; result = rand31(); /* * Combine with previously generated entropy to create even better * randomness. That previous entropy is refreshed each time a new * entropy collection cycle is initiated. We simply loop over the * five 32-bit words, interpreted in a big-endian way. */ result += peek_be32(ptr_add_offset(&entropy_previous, offset)); offset = (offset + 4) % sizeof entropy_previous; return result & RAND31_MASK; }
/** * Free allocated blocks from the .dat file. * * @param db the sdbm database * @param bvec vector where allocated block numbers are stored * @param bcnt amount of blocks in vector to free */ static void big_file_free(DBM *db, const void *bvec, int bcnt) { size_t bno; const void *q; int n; for (q = bvec, n = bcnt; n > 0; n--) { bno = peek_be32(q); big_ffree(db, bno); q = const_ptr_add_offset(q, sizeof(guint32)); } /* * If database is not volatile, sync the bitmap to make sure the freed * blocks are reusable even if we crash later. */ if (!db->is_volatile) big_sync(db); }
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 inline uint32 host_addr_6to4_client_ipv4(const host_addr_t ha) { return peek_be32(&ha.addr.ipv6[2]); /* 2002:AABBCCDD::/48 */ }
/** * Parse ``size'' bytes starting at ``data'' and fill-in the information * about the next record in ``header''. * * @return the length of the record successfully parsed, 0 on error (since * the minimal amount of bytes for an empty record would be DIME_HEADER_SIZE). */ static size_t dime_parse_record_header(const char *data, size_t size, struct dime_record *header) { const char * const data0 = data; size_t n; g_assert(data); g_assert(header); n = DIME_HEADER_SIZE; if (size < n) { goto failure; } header->version = peek_u8(&data[0]) >> 3; if (DIME_VERSION != header->version) { g_warning("dime_parse_record_header(): Cannot parse dime version %u, " "only version %u is supported", header->version, DIME_VERSION); goto failure; } header->flags = peek_u8(&data[0]) & (DIME_F_MB | DIME_F_ME | DIME_F_CF); header->type_t = peek_u8(&data[1]) >> 4; header->resrvd = peek_u8(&data[1]) & 0x0F; header->options_length = peek_be16(&data[2]); header->id_length = peek_be16(&data[4]); header->type_length = peek_be16(&data[6]); header->data_length = peek_be32(&data[8]); size -= n; data += n; header->options = data; n = dime_ceil(header->options_length); if (size < n) { dime_log_truncated_record("options", header, header->options_length, size); goto failure; } size -= n; data += n; header->id = data; n = dime_ceil(header->id_length); if (size < n) { dime_log_truncated_record("ID", header, header->id_length, size); goto failure; } size -= n; data += n; header->type = data; n = dime_ceil(header->type_length); if (size < n) { dime_log_truncated_record("type", header, header->type_length, size); goto failure; } size -= n; data += n; header->data = data; n = dime_ceil(header->data_length); if (size < n) { dime_log_truncated_record("data", header, header->data_length, size); goto failure; } size -= n; data += n; return data - data0; failure: return 0; }
/** * Validate a base64-encoded version token `tokenb64' of `len' bytes. * The `ip' is given only for clock update operations. * * @returns error code, or TOK_OK if token is valid. */ tok_error_t tok_version_valid( const char *version, const char *tokenb64, int len, host_addr_t addr) { time_t now = tm_time(); time_t stamp; uint32 crc; const struct tokkey *tk; const struct tokkey *rtk; const struct tokkey *latest; uint idx; const char *key; SHA1Context ctx; char lvldigest[1024]; char token[TOKEN_VERSION_SIZE]; struct sha1 digest; version_t rver; char *end; int toklen; int lvllen; int lvlsize; uint i; end = strchr(tokenb64, ';'); /* After 25/02/2003 */ toklen = end ? (end - tokenb64) : len; /* * Verify token. */ if (toklen != TOKEN_BASE64_SIZE) return TOK_BAD_LENGTH; if (!base64_decode_into(tokenb64, toklen, token, TOKEN_VERSION_SIZE)) return TOK_BAD_ENCODING; stamp = (time_t) peek_be32(&token); /* * Use that stamp, whose precision is TOKEN_LIFE, to update our * clock skew if necessary. */ clock_update(stamp, TOKEN_LIFE, addr); if (ABS(stamp - clock_loc2gmt(now)) > TOKEN_CLOCK_SKEW) return TOK_BAD_STAMP; if (!version_fill(version, &rver)) /* Remote version */ return TOK_BAD_VERSION; tk = find_tokkey_version(&rver, stamp); /* The keys they used */ if (tk == NULL) return TOK_BAD_KEYS; idx = (uchar) token[6] & 0x1f; /* 5 bits for the index */ if (idx >= tk->count) return TOK_BAD_INDEX; key = tk->keys[idx]; SHA1Reset(&ctx); SHA1Input(&ctx, key, strlen(key)); SHA1Input(&ctx, token, 7); SHA1Input(&ctx, version, strlen(version)); SHA1Result(&ctx, &digest); if (0 != memcmp(&token[7], digest.data, SHA1_RAW_SIZE)) return TOK_INVALID; if (version_build_cmp(&rver, &tk->ver) < 0) return TOK_OLD_VERSION; if (end == NULL) return TOK_MISSING_LEVEL; latest = find_latest(&rver); if (latest == NULL) /* Unknown in our key set */ return TOK_OLD_VERSION; /* * Verify build. * * Build numbers were emitted when we switched to SVN on 2006-08-26 * and stopped being a monotonous increasing function when we switched * to git on 2011-09-11. */ if ( rver.timestamp >= 1156543200 && /* 2006-08-26 */ rver.timestamp < GIT_SWITCH /* 2011-09-11 */ ) { if (0 == rver.build) return TOK_MISSING_BUILD; if (rver.build < latest->ver.build) return TOK_WRONG_BUILD; } /* * Verify level. */ lvllen = len - toklen - 2; /* Forget about "; " */ end += 2; /* Skip "; " */ if (UNSIGNED(lvllen) >= sizeof(lvldigest) || lvllen <= 0) return TOK_BAD_LEVEL_LENGTH; if (lvllen & 0x3) return TOK_BAD_LEVEL_LENGTH; lvllen = base64_decode_into(end, lvllen, lvldigest, sizeof(lvldigest)); if (lvllen == 0 || (lvllen & 0x1)) return TOK_BAD_LEVEL_ENCODING; g_assert(lvllen >= 2); g_assert((lvllen & 0x1) == 0); /* * Only check the highest keys we can check. */ lvllen /= 2; /* # of keys held remotely */ lvlsize = G_N_ELEMENTS(token_keys) - (tk - token_keys); lvlsize = MIN(lvllen, lvlsize); g_assert(lvlsize >= 1); rtk = tk + (lvlsize - 1); /* Keys at that level */ crc = crc32_update(0, token, TOKEN_VERSION_SIZE); crc = tok_crc(crc, rtk); lvlsize--; /* Move to 0-based offset */ if (peek_be16(&lvldigest[2*lvlsize]) != crc) return TOK_INVALID_LEVEL; for (i = 0; i < G_N_ELEMENTS(token_keys); i++) { rtk = &token_keys[i]; if (rtk->ver.timestamp > rver.timestamp) { rtk--; /* `rtk' could not exist remotely */ break; } } if (lvlsize < rtk - tk) return TOK_SHORT_LEVEL; return TOK_OK; }
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; }
static inline bool host_addr_is_teredo(const host_addr_t ha) { return host_addr_is_ipv6(ha) && 0x20010000UL == peek_be32(&ha.addr.ipv6[0]); }
/** * Store big value in the .dat file, writing to the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param data start of data to write * @param len length of data to write * * @return -1 on error with errno set, 0 if OK. */ static int big_store(DBM *db, const void *bvec, const void *data, size_t len) { DBMBIG *dbg = db->big; int bcnt = bigbcnt(len); int n; const void *p; const char *q; size_t remain; g_return_val_if_fail(NULL == dbg->bitcheck, -1); if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; /* * Look at the amount of consecutive block numbers we have to be able * to write into them via a single system call. */ n = bcnt; p = bvec; q = data; remain = len; while (n > 0) { size_t towrite = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); guint32 prev_bno = bno; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, towrite); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); towrite += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigwrite++; if (-1 == compat_pwrite(dbg->fd, q, towrite, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not write %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) towrite, bno, g_strerror(errno)); ioerr(db, TRUE); return -1; } q += towrite; dbg->bigwrite_blk += bigblocks(towrite); g_assert(ptr_diff(q, data) <= len); } g_assert(ptr_diff(q, data) == len); return 0; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag file) */ return -1; }
/** * Fetch data block from the .dat file, reading from the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param len length of the data to be read * * @return -1 on error with errno set, 0 if OK. Read data is left in the * scratch buffer. */ static int big_fetch(DBM *db, const void *bvec, size_t len) { int bcnt = bigbcnt(len); DBMBIG *dbg = db->big; int n; const void *p; char *q; size_t remain; guint32 prev_bno; if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; if (dbg->scratch_len < len) big_scratch_grow(dbg, len); /* * Read consecutive blocks in one single system call. */ n = bcnt; p = bvec; q = dbg->scratch; remain = len; while (n > 0) { size_t toread = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); prev_bno = bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, toread); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); toread += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigread++; if (-1 == compat_pread(dbg->fd, q, toread, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not read %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) toread, bno, g_strerror(errno)); ioerr(db, FALSE); return -1; } q += toread; dbg->bigread_blk += bigblocks(toread); g_assert(UNSIGNED(q - dbg->scratch) <= dbg->scratch_len); } g_assert(UNSIGNED(q - dbg->scratch) == len); return 0; corrupted_database: g_warning("sdbm: \"%s\": cannot read unallocated data block #%u", sdbm_name(db), prev_bno); goto fault; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); /* FALL THROUGH */ fault: ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag or .dat file) */ return -1; }
/** * Allocate blocks (consecutive if possible) from the .dat file. * Block numbers are written back in the specified vector, in sequence. * * Blocks are always allocated with increasing block numbers, i.e. the list * of block numbers returned is guaranteed to be sorted. This will help * upper layers to quickly determine whether all the blocks are contiguous * for instance. * * The file is extended as necessary to be able to allocate the blocks but * this is only done when there are no more free blocks available. * * @param db the sdbm database * @param bvec vector where allocated block numbers will be stored * @param bcnt amount of blocks in vector (amount to allocate) * * @return TRUE if we were able to allocate all the requested blocks. */ static gboolean big_file_alloc(DBM *db, void *bvec, int bcnt) { DBMBIG *dbg = db->big; size_t first; int n; void *q; int bmap = 0; /* Initial bitmap from which we allocate */ g_assert(bcnt > 0); g_return_val_if_fail(NULL == dbg->bitcheck, FALSE); if (-1 == dbg->fd && -1 == big_open(dbg)) return FALSE; /* * First try to allocate all the blocks sequentially. */ retry: first = big_falloc_seq(db, bmap, bcnt); if (first != 0) { while (bcnt-- > 0) { bvec = poke_be32(bvec, first++); } goto success; } /* * There are no "bcnt" consecutive free blocks in the file. * * Before extending the file, we're going to fill the holes as much * as possible. */ for (first = 0, q = bvec, n = bcnt; n > 0; n--) { first = big_falloc(db, first + 1); if (0 == first) break; q = poke_be32(q, first); } if (0 == n) goto success; /* Found the requested "bcnt" free blocks */ /* * Free the incompletely allocated blocks: since we're about to extend * the file, we'll use consecutive blocks from the new chunk governed * by the added empty bitmap. */ for (q = bvec, n = bcnt - n; n > 0; n--) { first = peek_be32(q); big_ffree(db, first); q = ptr_add_offset(q, sizeof(guint32)); } /* * Extend the file by allocating another bitmap. */ g_assert(0 == bmap); /* Never retried yet */ if (dbg->bitbuf_dirty && !flush_bitbuf(db)) return FALSE; memset(dbg->bitbuf, 0, BIG_BLKSIZE); bit_field_set(dbg->bitbuf, 0); /* First page is the bitmap itself */ dbg->bitbno = dbg->bitmaps * BIG_BITCOUNT; dbg->bitmaps++; /* * Now retry starting to allocate blocks from the newly added bitmap. * * This will likely succeed if we're trying to allocate less than 8 MiB * worth of data (with 1 KiB blocks). */ bmap = dbg->bitmaps - 1; goto retry; success: /* * We successfully allocated blocks from the bitmap. * * If the database is not volatile, we need to flush the bitmap to disk * immediately in case of a crash, to avoid reusing these parts of the file. */ if (!db->is_volatile && dbg->bitbuf_dirty && !flush_bitbuf(db)) { /* Cannot flush -> cannot allocate the blocks: free them */ for (q = bvec, n = bcnt; n > 0; n--) { first = peek_be32(q); big_ffree(db, first); q = ptr_add_offset(q, sizeof(guint32)); } return FALSE; } return TRUE; /* Succeeded */ }