/** * Fill dump header with node address information. */ static void dump_header_set(struct dump_header *dh, const struct gnutella_node *node) { ZERO(dh); dh->data[0] = NODE_IS_UDP(node) ? DH_F_UDP : DH_F_TCP; switch (host_addr_net(node->addr)) { case NET_TYPE_IPV4: { guint32 ip; dh->data[0] |= DH_F_IPV4; ip = host_addr_ipv4(node->addr); poke_be32(&dh->data[1], ip); } break; case NET_TYPE_IPV6: dh->data[0] |= DH_F_IPV6; memcpy(&dh->data[1], host_addr_ipv6(&node->addr), 16); break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } poke_be16(&dh->data[17], node->port); }
/** * Put large value data into the .dat and fill in the supplied .pag buffer * with the block numbers where the value is stored and other size information. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page * @param val start of value data * @param vlen length of value * * @return TRUE if value was written successfully in the .dat file. */ gboolean bigval_put(DBM *db, char *bval, size_t blen, const char *val, size_t vlen) { g_assert(bigval_length(vlen) == blen); if (!big_file_alloc(db, bigval_blocks(bval), bigblocks(vlen))) return FALSE; /* * Write the value header: * * value size */ poke_be32(bval, (guint32) vlen); /* * And now the indirection block numbers of the value, pointing in .dat. */ if (0 != big_store(db, bigval_blocks(bval), val, vlen)) { big_file_free(db, bigval_blocks(bval), bigblocks(vlen)); return FALSE; } return TRUE; }
/** * Put large key data into the .dat and fill in the supplied .pag buffer * with the block numbers where the key is stored and other size information. * * @param db the sdbm database * @param bkey start of big key in the page * @param blen length of big key in the page * @param key start of key data * @param klen length of key * * @return TRUE if key was written successfully in the .dat file. */ gboolean bigkey_put(DBM *db, char *bkey, size_t blen, const char *key, size_t klen) { g_assert(bigkey_length(klen) == blen); if (!big_file_alloc(db, bigkey_blocks(bkey), bigblocks(klen))) return FALSE; /* * Write the key header: * * key size * first BIG_KEYSAVED bytes of key * last BIG_KEYSAVED bytes of key */ poke_be32(bkey, (guint32) klen); memcpy(bigkey_head(bkey), key, BIG_KEYSAVED); memcpy(bigkey_tail(bkey), key + (klen - BIG_KEYSAVED), BIG_KEYSAVED); /* * And now the indirection block numbers of the key, pointing in .dat. */ if (0 != big_store(db, bigkey_blocks(bkey), key, klen)) { big_file_free(db, bigkey_blocks(bkey), bigblocks(klen)); return FALSE; } return TRUE; }
/** * Generate new token for given version string. */ static char * tok_generate(time_t now, const char *version) { char token[TOKEN_BASE64_SIZE + 1]; char digest[TOKEN_VERSION_SIZE]; char lvldigest[LEVEL_SIZE]; char lvlbase64[LEVEL_BASE64_SIZE + 1]; const struct tokkey *tk; uint32 crc32; uint idx; const char *key; SHA1Context ctx; struct sha1 sha1; int lvlsize; int i; /* * Compute token. */ key = random_key(now, &idx, &tk); now = clock_loc2gmt(now); /* As close to GMT as possible */ poke_be32(&digest[0], now); random_bytes(&digest[4], 3); digest[6] &= 0xe0U; /* Upper 3 bits only */ digest[6] |= idx & 0xffU; /* Has 5 bits for the index */ SHA1Reset(&ctx); SHA1Input(&ctx, key, strlen(key)); SHA1Input(&ctx, digest, 7); SHA1Input(&ctx, version, strlen(version)); SHA1Result(&ctx, &sha1); memcpy(&digest[7], sha1.data, SHA1_RAW_SIZE); /* * Compute level. */ lvlsize = G_N_ELEMENTS(token_keys) - (tk - token_keys); crc32 = crc32_update(0, digest, TOKEN_VERSION_SIZE); for (i = 0; i < lvlsize; i++) { poke_be16(&lvldigest[i*2], tok_crc(crc32, tk)); tk++; } /* * Encode into base64. */ base64_encode_into(digest, TOKEN_VERSION_SIZE, token, TOKEN_BASE64_SIZE); token[TOKEN_BASE64_SIZE] = '\0'; ZERO(&lvlbase64); base64_encode_into(lvldigest, 2 * lvlsize, lvlbase64, LEVEL_BASE64_SIZE); return g_strconcat(token, "; ", lvlbase64, (void *) 0); }
/** * Tries to convert the host address "from" to the network type "to_net" * and stores the converted address in "*to". If conversion is not possible, * FALSE is returned and "*to" is set to zero_host_addr. * * @param from The address to convert. * @param to Will hold the converted address. * @param to_net The network type to convert the address to. * * @return TRUE if the address could be converted, FALSE otherwise. */ bool host_addr_convert(const host_addr_t from, host_addr_t *to, enum net_type to_net) { if (from.net == to_net) { *to = from; return TRUE; } switch (to_net) { case NET_TYPE_IPV4: switch (from.net) { case NET_TYPE_IPV6: if (host_addr_can_convert(from, NET_TYPE_IPV4)) { *to = host_addr_peek_ipv4(&from.addr.ipv6[12]); return TRUE; } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } break; case NET_TYPE_IPV6: switch (from.net) { case NET_TYPE_IPV4: to->net = to_net; memset(to->addr.ipv6, 0, 10); to->addr.ipv6[10] = 0xff; to->addr.ipv6[11] = 0xff; poke_be32(&to->addr.ipv6[12], host_addr_ipv4(from)); return TRUE; case NET_TYPE_NONE: break; } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } *to = zero_host_addr; return FALSE; }
/** * Replace value data in-place. * * @param db the sdbm database * @param bval start of big value in the page * @param data the new value * @param len length of data * * @return 0 if OK, -1 on error with errno set. */ int big_replace(DBM *db, char *bval, const char *data, size_t len) { size_t old_len = big_length(bval); g_assert(size_is_non_negative(len)); g_assert(bigblocks(old_len) == bigblocks(len)); g_assert(len <= MAX_INT_VAL(guint32)); /* * Write data on the same blocks as before, since we know it will fit. */ poke_be32(bval, (guint32) len); /* First 4 bytes: real data length */ return big_store(db, bigval_blocks(bval), data, len); }
/** * Generate GUID for a query with OOB results delivery. * If `initial' is false, this is a requery. * * Bytes 0 to 3 if the GUID are the 4 octet bytes of the IP address. * Bytes 13 and 14 are the little endian representation of the port. * Byte 15 holds an HEC with bit 0 indicating a requery. */ void guid_query_oob_muid(struct guid *muid, const host_addr_t addr, uint16 port, bool initial) { uint32 ip; g_assert(host_addr_is_ipv4(addr)); guid_random_fill(muid); ip = host_addr_ipv4(addr); poke_be32(&muid->v[0], ip); poke_le16(&muid->v[13], port); guid_flag_oob_gtkg(muid); /* Mark as being from GTKG */ if (initial) muid->v[15] &= ~GUID_REQUERY; else muid->v[15] |= GUID_REQUERY; }
/** * Create a dime record header. */ static void dime_fill_record_header(const struct dime_record *record, char *data, size_t size, guint flags) { unsigned char value; g_assert(record); g_assert(data); g_assert(size >= DIME_HEADER_SIZE); value = DIME_VERSION << 3; value |= (DIME_F_MB & flags); value |= (DIME_F_ME & flags); value |= (DIME_F_CF & flags); poke_u8(&data[0], value); poke_u8(&data[1], (record->type_t << 4) | record->resrvd); poke_be16(&data[2], record->options_length); poke_be16(&data[4], record->id_length); poke_be16(&data[6], record->type_length); poke_be32(&data[8], record->data_length); }
/** * Add new host (identified by address and port) to the Gnutella host vector. */ void gnet_host_vec_add(gnet_host_vec_t *vec, host_addr_t addr, uint16 port) { g_return_if_fail(vec); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: if (vec->n_ipv4 < 255) { size_t size, old_size; char *dest; old_size = vec->n_ipv4 * sizeof vec->hvec_v4[0]; size = old_size + sizeof vec->hvec_v4[0]; vec->hvec_v4 = wrealloc(vec->hvec_v4, old_size, size); dest = cast_to_pointer(&vec->hvec_v4[vec->n_ipv4++]); poke_be32(&dest[0], host_addr_ipv4(addr)); poke_le16(&dest[4], port); } break; case NET_TYPE_IPV6: if (vec->n_ipv6 < 255) { size_t size, old_size; char *dest; old_size = vec->n_ipv6 * sizeof vec->hvec_v6[0]; size = old_size + sizeof vec->hvec_v6[0]; vec->hvec_v6 = wrealloc(vec->hvec_v6, old_size, size); dest = cast_to_pointer(&vec->hvec_v6[vec->n_ipv6++]); dest = mempcpy(dest, host_addr_ipv6(&addr), 16); poke_le16(dest, port); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } }
/** * Serialization convenience for IP:port. * * Write the IP:port (IP as big-endian, port as little-endian) into the * supplied buffer, whose length MUST be 18 bytes at least. * * If len is non-NULL, it is written with the length of the serialized data. * * @return pointer following serialization data. */ void * host_ip_port_poke(void *p, const host_addr_t addr, uint16 port, size_t *len) { void *q = p; switch (host_addr_net(addr)) { case NET_TYPE_IPV4: q = poke_be32(q, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: q = mempcpy(q, host_addr_ipv6(&addr), sizeof addr.addr.ipv6); break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_assert_not_reached(); } q = poke_le16(q, port); if (len != NULL) *len = ptr_diff(q, p); return q; }
void utf8_regression_1(void) { uint32_t i; unsigned valid = 0; for (i = 0; i < 0xffffffffUL; i++) { static char data[5]; static char hit[0x10FFFFU + 1]; uint32_t d; if (0 == (i % (((uint32_t) -1) / 100))) { printf("i=%u\n", i / (((uint32_t) -1) / 100)); } poke_be32(data, i); d = utf8_decode(data, 4); if ((uint32_t) -1 == d) continue; if (d > 0x10ffffU || utf32_is_non_character(d) || utf32_is_surrogate(d)) { printf("data: %02x %02x %02x %02x\n", (unsigned char) data[0], (unsigned char) data[1], (unsigned char) data[2], (unsigned char) data[3]); printf("d: U+%lx\n", (unsigned long) d); printf("i: %lu\n", (unsigned long) i); RUNTIME_ASSERT(d <= 0x10ffffU); RUNTIME_ASSERT(!utf32_is_non_character(d)); RUNTIME_ASSERT(!utf32_is_surrogate(d)); } if (!hit[d]) { hit[d] = 1; valid++; } { unsigned n; char buf[4]; n = utf8_encode(d, buf); RUNTIME_ASSERT(n > 0); RUNTIME_ASSERT(n <= 4); RUNTIME_ASSERT(n == utf8_first_byte_length_hint(data[0])); if (0 != memcmp(data, buf, n)) { printf("buf: %02x %02x %02x %02x\n", (unsigned char) buf[0], (unsigned char) buf[1], (unsigned char) buf[2], (unsigned char) buf[3]); printf("data: %02x %02x %02x %02x\n", (unsigned char) data[0], (unsigned char) data[1], (unsigned char) data[2], (unsigned char) data[3]); printf("d: U+%lx\n", (unsigned long) i); printf("i: %lu\n", (unsigned long) i); printf("n: %u\n", n); RUNTIME_ASSERT(0); } } } printf("valid=%u\n", valid); printf("PASSED\n"); }
/** * 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 */ }
/** * Create a security token from host address and port using specified key. * * Optionally, extra contextual data may be given (i.e. the token is not * only based on the address and port) to make the token more unique to * a specific context. * * @param stg the security token generator * @param n key index to use * @param tok where security token is written * @param addr address of the host for which we're generating a token * @param port port of the host for which we're generating a token * @param data optional contextual data * @param len length of contextual data */ static void sectoken_generate_n(sectoken_gen_t *stg, size_t n, sectoken_t *tok, host_addr_t addr, uint16 port, const void *data, size_t len) { char block[8]; char enc[8]; char *p = block; sectoken_gen_check(stg); g_assert(tok != NULL); g_assert(size_is_non_negative(n)); g_assert(n < stg->keycnt); g_assert((NULL != data) == (len != 0)); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: p = poke_be32(p, host_addr_ipv4(addr)); break; case NET_TYPE_IPV6: { uint val; val = binary_hash(host_addr_ipv6(&addr), 16); p = poke_be32(p, val); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_error("unexpected address for security token generation: %s", host_addr_to_string(addr)); } p = poke_be16(p, port); p = poke_be16(p, 0); /* Filler */ g_assert(p == &block[8]); STATIC_ASSERT(sizeof(tok->v) == sizeof(uint32)); STATIC_ASSERT(sizeof(block) == sizeof(enc)); tea_encrypt(&stg->keys[n], enc, block, sizeof block); /* * If they gave contextual data, encrypt them by block of 8 bytes, * filling the last partial block with zeroes if needed. */ if (data != NULL) { const void *q = data; size_t remain = len; char denc[8]; STATIC_ASSERT(sizeof(denc) == sizeof(enc)); while (remain != 0) { size_t fill = MIN(remain, 8U); unsigned i; if (fill != 8U) ZERO(&block); memcpy(block, q, fill); remain -= fill; q = const_ptr_add_offset(q, fill); /* * Encrypt block of contextual data (possibly filled with trailing * zeroes) and merge back the result into the main encryption * output with XOR. */ tea_encrypt(&stg->keys[n], denc, block, sizeof block); for (i = 0; i < sizeof denc; i++) enc[i] ^= denc[i]; } } poke_be32(tok->v, tea_squeeze(enc, sizeof enc)); }
/** * Create a new Gnutella host vector out of a sequence of gnet_host_t items. */ static gnet_host_vec_t * gnet_host_vec_from_sequence(sequence_t *s) { sequence_iter_t *iter; gnet_host_vec_t *vec; uint n_ipv6 = 0, n_ipv4 = 0, hcnt; if (sequence_is_empty(s)) return NULL; hcnt = 0; iter = sequence_forward_iterator(s); while (sequence_iter_has_next(iter)) { const gnet_host_t *host = sequence_iter_next(iter); switch (gnet_host_get_net(host)) { case NET_TYPE_IPV4: n_ipv4++; hcnt++; break; case NET_TYPE_IPV6: n_ipv6++; hcnt++; break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } } sequence_iterator_release(&iter); if (0 == hcnt) return NULL; vec = gnet_host_vec_alloc(); vec->n_ipv4 = MIN(n_ipv4, 255); vec->n_ipv6 = MIN(n_ipv6, 255); if (vec->n_ipv4 > 0) WALLOC_ARRAY(vec->hvec_v4, vec->n_ipv4); if (vec->n_ipv6 > 0) WALLOC_ARRAY(vec->hvec_v6, vec->n_ipv6); n_ipv4 = 0; n_ipv6 = 0; iter = sequence_forward_iterator(s); while (sequence_iter_has_next(iter)) { const gnet_host_t *host = sequence_iter_next(iter); host_addr_t addr = gnet_host_get_addr(host); uint16 port = gnet_host_get_port(host); switch (gnet_host_get_net(host)) { case NET_TYPE_IPV4: if (n_ipv4 < vec->n_ipv4) { char *dest = cast_to_pointer(&vec->hvec_v4[n_ipv4++]); poke_be32(&dest[0], host_addr_ipv4(addr)); poke_le16(&dest[4], port); } break; case NET_TYPE_IPV6: if (n_ipv6 < vec->n_ipv6) { char *dest = cast_to_pointer(&vec->hvec_v6[n_ipv6++]); dest = mempcpy(dest, host_addr_ipv6(&addr), 16); poke_le16(dest, port); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } } sequence_iterator_release(&iter); return vec; }