/** * 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); }
/** * Build a Local Node Info message. * * @return a /LNI message. */ pmsg_t * g2_build_lni(void) { g2_tree_t *t; pmsg_t *mb; t = g2_tree_alloc_empty(G2_NAME(LNI)); /* LS -- library statistics */ { uint32 files, kbytes; char payload[8]; void *p = payload; g2_tree_t *c; files = MIN(shared_files_scanned(), ~((uint32) 0U)); kbytes = MIN(shared_kbytes_scanned(), ~((uint32) 0U)); p = poke_le32(p, files); p = poke_le32(p, kbytes); c = g2_tree_alloc_copy("LS", payload, sizeof payload); g2_tree_add_child(t, c); } g2_build_add_firewalled(t); /* FW -- whether servent is firewalled */ g2_build_add_uptime(t); /* UP -- servent uptime */ g2_build_add_vendor(t); /* V -- vendor code */ g2_build_add_guid(t); /* GU -- the GUID of this node */ g2_build_add_node_address(t); /* NA -- the IP:port of this node */ g2_build_add_tls(t); /* TLS -- whether TLS is supported */ mb = g2_build_pmsg(t); g2_tree_free_null(&t); return mb; }
/** * Build a QHT RESET message. * * @param slots amount of slots in the table (power of 2) * @param inf_val infinity value (1) * * @return a /QHT message with a RESET payload. */ pmsg_t * g2_build_qht_reset(int slots, int inf_val) { g2_tree_t *t; char body[6]; void *p = &body[0]; pmsg_t *mb; g_assert(is_pow2(slots)); g_assert(1 == inf_val); /* Only 1-bit patches in G2 */ p = poke_u8(p, G2_QHT_RESET); p = poke_le32(p, slots); p = poke_u8(p, inf_val); t = g2_tree_alloc(G2_NAME(QHT), body, sizeof body); mb = g2_build_pmsg(t); g2_tree_free_null(&t); return mb; }
/** * Add file to the current query hit. * * @return TRUE if we kept the file, FALSE if we did not include it in the hit. */ static bool g2_build_qh2_add(struct g2_qh2_builder *ctx, const shared_file_t *sf) { const sha1_t *sha1; g2_tree_t *h, *c; shared_file_check(sf); /* * Make sure the file is still in the library. */ if (0 == shared_file_index(sf)) return FALSE; /* * On G2, the H/URN child is required, meaning we need the SHA1 at least. */ if (!sha1_hash_available(sf)) return FALSE; /* * Do not send duplicates, as determined by the SHA1 of the resource. * * A user may share several files with different names but the same SHA1, * and if all of them are hits, we only want to send one instance. * * When generating hits for host-browsing, we do not care about duplicates * and ctx->hs is NULL then. */ sha1 = shared_file_sha1(sf); /* This is an atom */ if (ctx->hs != NULL) { if (hset_contains(ctx->hs, sha1)) return FALSE; hset_insert(ctx->hs, sha1); } /* * Create the "H" child and attach it to the current tree. */ if (NULL == ctx->t) g2_build_qh2_start(ctx); h = g2_tree_alloc_empty("H"); g2_tree_add_child(ctx->t, h); /* * URN -- Universal Resource Name * * If there is a known TTH, then we can generate a bitprint, otherwise * we just convey the SHA1. */ { const tth_t * const tth = shared_file_tth(sf); char payload[SHA1_RAW_SIZE + TTH_RAW_SIZE + sizeof G2_URN_BITPRINT]; char *p = payload; if (NULL == tth) { p = mempcpy(p, G2_URN_SHA1, sizeof G2_URN_SHA1); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), sha1, SHA1_RAW_SIZE); } else { p = mempcpy(p, G2_URN_BITPRINT, sizeof G2_URN_BITPRINT); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), sha1, SHA1_RAW_SIZE); p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload), tth, TTH_RAW_SIZE); } g_assert(ptr_diff(p, payload) <= sizeof payload); c = g2_tree_alloc_copy("URN", payload, ptr_diff(p, payload)); g2_tree_add_child(h, c); } /* * URL -- empty to indicate that we share the file via uri-res. */ if (ctx->flags & QHIT_F_G2_URL) { uint known; uint16 csc; c = g2_tree_alloc_empty("URL"); g2_tree_add_child(h, c); /* * CSC -- if we know alternate sources, indicate how many in "CSC". * * This child is only emitted when they requested "URL". */ known = dmesh_count(sha1); csc = MIN(known, MAX_INT_VAL(uint16)); if (csc != 0) { char payload[2]; poke_le16(payload, csc); c = g2_tree_alloc_copy("CSC", payload, sizeof payload); g2_tree_add_child(h, c); } /* * PART -- if we only have a partial file, indicate how much we have. * * This child is only emitted when they requested "URL". */ if (shared_file_is_partial(sf) && !shared_file_is_finished(sf)) { filesize_t available = shared_file_available(sf); char payload[8]; /* If we have to encode file size as 64-bit */ uint32 av32; time_t mtime = shared_file_modification_time(sf); c = g2_tree_alloc_empty("PART"); g2_tree_add_child(h, c); av32 = available; if (av32 == available) { /* Fits within a 32-bit quantity */ poke_le32(payload, av32); g2_tree_set_payload(c, payload, sizeof av32, TRUE); } else { /* Encode as a 64-bit quantity then */ poke_le64(payload, available); g2_tree_set_payload(c, payload, sizeof payload, TRUE); } /* * GTKG extension: encode the last modification time of the * partial file in an "MT" child. This lets the other party * determine whether the host is still able to actively complete * the file. */ poke_le32(payload, (uint32) mtime); g2_tree_add_child(c, g2_tree_alloc_copy("MT", payload, sizeof(uint32))); } /* * CT -- creation time of the resource (GTKG extension). */ { time_t create_time = shared_file_creation_time(sf); if ((time_t) -1 != create_time) { char payload[8]; int n; create_time = MAX(0, create_time); n = vlint_encode(create_time, payload); g2_tree_add_child(h, g2_tree_alloc_copy("CT", payload, n)); /* No trailing 0s */ } } } /* * DN -- distinguished name. * * Note that the presence of DN also governs the presence of SZ if the * file length does not fit a 32-bit unsigned quantity. */ if (ctx->flags & QHIT_F_G2_DN) { char payload[8]; /* If we have to encode file size as 64-bit */ uint32 fs32; filesize_t fs = shared_file_size(sf); const char *name; const char *rp; c = g2_tree_alloc_empty("DN"); fs32 = fs; if (fs32 == fs) { /* Fits within a 32-bit quantity */ poke_le32(payload, fs32); g2_tree_set_payload(c, payload, sizeof fs32, TRUE); } else { /* Does not fit a 32-bit quantity, emit a SZ child */ poke_le64(payload, fs); g2_tree_add_child(h, g2_tree_alloc_copy("SZ", payload, sizeof payload)); } name = shared_file_name_nfc(sf); g2_tree_append_payload(c, name, shared_file_name_nfc_len(sf)); g2_tree_add_child(h, c); /* * GTKG extension: if there is a file path, expose it as a "P" child * under the DN node. */ rp = shared_file_relative_path(sf); if (rp != NULL) { g2_tree_add_child(c, g2_tree_alloc_copy("P", rp, strlen(rp))); } } /* * GTKG extension: if they requested alt-locs in the /Q2/I with "A", then * send them some known alt-locs in an "ALT" child. * * Note that these alt-locs can be for Gnutella hosts: since both Gnutella * and G2 share a common HTTP-based file transfer mechanism with compatible * extra headers, there is no need to handle them separately. */ if (ctx->flags & QHIT_F_G2_ALT) { gnet_host_t hvec[G2_BUILD_QH2_MAX_ALT]; int hcnt = 0; hcnt = dmesh_fill_alternate(sha1, hvec, N_ITEMS(hvec)); if (hcnt > 0) { int i; c = g2_tree_alloc_empty("ALT"); for (i = 0; i < hcnt; i++) { host_addr_t addr; uint16 port; addr = gnet_host_get_addr(&hvec[i]); port = gnet_host_get_port(&hvec[i]); if (host_addr_is_ipv4(addr)) { char payload[6]; host_ip_port_poke(payload, addr, port, NULL); g2_tree_append_payload(c, payload, sizeof payload); } } /* * If the payload is still empty, then drop the "ALT" child. * Otherwise, attach it to the "H" node. */ if (NULL == g2_tree_node_payload(c, NULL)) { g2_tree_free_null(&c); } else { g2_tree_add_child(h, c); } } } /* * Update the size of the query hit we're generating. */ ctx->current_size += g2_frame_serialize(h, NULL, 0); return TRUE; }
/** * Close the layer, flushing all the data there is. * Once this is done, invoke the supplied callback. */ static void tx_deflate_close(txdrv_t *tx, tx_closed_t cb, void *arg) { struct attr *attr = tx->opaque; g_assert(tx->flags & TX_CLOSING); if (tx_deflate_debugging(9)) { g_debug("TX %s: (%s) send=%d buffer #%d, nagle %s, " "unflushed %zu) [%c%c]", G_STRFUNC, gnet_host_to_string(&tx->host), attr->send_idx, attr->fill_idx, (attr->flags & DF_NAGLE) ? "on" : "off", attr->unflushed, (attr->flags & DF_FLOWC) ? 'C' : '-', (attr->flags & DF_FLUSH) ? 'f' : '-'); } /* * Flush whatever we can. */ tx_deflate_flush(tx); if (attr->gzip.enabled && 0 == tx_deflate_pending(tx)) { /* See RFC 1952 - GZIP file format specification version 4.3 */ struct buffer *b; uint32 trailer[2]; /* 0: CRC32, 1: SIZE % (1 << 32) */ /* We don't want to send the trailer more than once */ attr->gzip.enabled = FALSE; attr->send_idx = 0; b = &attr->buf[attr->send_idx]; poke_le32(&trailer[0], (uint32) attr->gzip.crc); poke_le32(&trailer[1], attr->gzip.size); g_assert(sizeof trailer <= (size_t) (b->end - b->wptr)); b->wptr = mempcpy(b->wptr, trailer, sizeof trailer); deflate_send(tx); } if (0 == tx_deflate_pending(tx)) { if (tx_deflate_debugging(9)) { g_debug("TX %s: flushed everything immediately", G_STRFUNC); } (*cb)(tx, arg); return; } /* * We were unable to flush everything. */ attr->closed = cb; attr->closed_arg = arg; if (tx_deflate_debugging(9)) { g_debug("TX %s: (%s) delayed! send=%d buffer #%d, nagle %s, " "flushed %zu, unflushed %zu) [%c%c]", G_STRFUNC, gnet_host_to_string(&tx->host), attr->send_idx, attr->fill_idx, (attr->flags & DF_NAGLE) ? "on" : "off", attr->flushed, attr->unflushed, (attr->flags & DF_FLOWC) ? 'C' : '-', (attr->flags & DF_FLUSH) ? 'f' : '-'); } }