static inline void hsep_fix_endian(hsep_triple *messaget, size_t n) { #if IS_LITTLE_ENDIAN (void) messaget; (void) n; #else size_t i, j; /* * Convert message from little endian to native byte order. * Only the part of the message we are using is converted. * If native byte order is little endian, do nothing. */ for (i = 0; i < n; i++) { for (j = 0; j < G_N_ELEMENTS(messaget[0]); j++) { poke_le64(&messaget[i][j], messaget[i][j]); } } #endif /* IS_LITTLE_ENDIAN */ }
/** * 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; }
void tiger(gconstpointer data, guint64 length, char hash[24]) { guint64 i, j, res[3]; const guint8 *data_u8 = data; union { guint64 u64[8]; guint8 u8[64]; } temp; res[0] = U64_FROM_2xU32(0x01234567UL, 0x89ABCDEFUL); res[1] = U64_FROM_2xU32(0xFEDCBA98UL, 0x76543210UL); res[2] = U64_FROM_2xU32(0xF096A5B4UL, 0xC3B2E187UL); #if G_BYTE_ORDER == G_BIG_ENDIAN for (i = length; i >= 64; i -= 64) { for (j = 0; j < 64; j++) { temp.u8[j ^ 7] = data_u8[j]; } tiger_compress(temp.u64, res); data_u8 += 64; } #else /* !BIG ENDIAN */ if ((gulong) data & 7) { for (i = length; i >= 64; i -= 64) { memcpy(temp.u64, data_u8, 64); tiger_compress(temp.u64, res); data_u8 += 64; } } else { for (i = length; i >= 64; i -= 64) { tiger_compress((gconstpointer) data_u8, res); data_u8 += 64; } } #endif /* BIG ENDIAN */ #if G_BYTE_ORDER == G_BIG_ENDIAN for (j = 0; j < i; j++) { temp.u8[j ^ 7] = data_u8[j]; } temp.u8[j ^ 7] = 0x01; j++; for (; j & 7; j++) { temp.u8[j ^ 7] = 0; } #else for(j = 0; j < i; j++) { temp.u8[j] = data_u8[j]; } temp.u8[j++] = 0x01; for (; j & 7; j++) { temp.u8[j] = 0; } #endif if (j > 56) { for (; j < 64; j++) { temp.u8[j] = 0; } tiger_compress(temp.u64, res); j = 0; } for (; j < 56; j++) { temp.u8[j] = 0; } temp.u64[7] = length << 3; tiger_compress(temp.u64, res); for (i = 0; i < 3; i++) { poke_le64(&hash[i * 8], res[i]); } }
void hsep_send_msg(struct gnutella_node *n, time_t now) { hsep_triple tmp[G_N_ELEMENTS(n->hsep->sent_table)], other; unsigned int i, j, msglen, msgsize, triples, opttriples; gnutella_msg_hsep_t *msg; hsep_ctx_t *hsep; g_assert(n); g_assert(n->hsep); hsep = n->hsep; ZERO(&other); /* * If we are a leaf, we just need to send one triple, * which contains our own data (this triple is expanded * to the needed number of triples on the peer's side). * As the 0'th global and 0'th connection triple are zero, * it contains only our own triple, which is correct. */ triples = settings_is_leaf() ? 1 : G_N_ELEMENTS(tmp); /* * Allocate and initialize message to send. */ msgsize = GTA_HEADER_SIZE + triples * (sizeof *msg - GTA_HEADER_SIZE); msg = walloc(msgsize); { gnutella_header_t *header; header = gnutella_msg_hsep_header(msg); message_set_muid(header, GTA_MSG_HSEP_DATA); gnutella_header_set_function(header, GTA_MSG_HSEP_DATA); gnutella_header_set_ttl(header, 1); gnutella_header_set_hops(header, 0); } /* * Collect HSEP data to send and convert the data to * little endian byte order. */ if (triples > 1) { /* determine what we know about non-HSEP nodes in 1 hop distance */ hsep_get_non_hsep_triple(&other); } for (i = 0; i < triples; i++) { for (j = 0; j < G_N_ELEMENTS(other); j++) { uint64 val; val = hsep_own[j] + (0 == i ? 0 : other[j]) + hsep_global_table[i][j] - hsep->table[i][j]; poke_le64(&tmp[i][j], val); } } STATIC_ASSERT(sizeof hsep->sent_table == sizeof tmp); /* check if the table differs from the previously sent table */ if ( 0 == memcmp(tmp, hsep->sent_table, sizeof tmp) ) { WFREE_NULL(msg, msgsize); goto charge_timer; } memcpy(cast_to_char_ptr(msg) + GTA_HEADER_SIZE, tmp, triples * sizeof tmp[0]); /* store the table for later comparison */ memcpy(hsep->sent_table, tmp, triples * sizeof tmp[0]); /* * Note that on big endian architectures the message data is now in * the wrong byte order. Nevertheless, we can use hsep_triples_to_send() * with that data. */ /* optimize number of triples to send */ opttriples = hsep_triples_to_send(cast_to_pointer(tmp), triples); if (GNET_PROPERTY(hsep_debug) > 1) { printf("HSEP: Sending %d %s to node %s (msg #%u): ", opttriples, opttriples == 1 ? "triple" : "triples", host_addr_port_to_string(n->addr, n->port), hsep->msgs_sent + 1); } for (i = 0; i < opttriples; i++) { if (GNET_PROPERTY(hsep_debug) > 1) { char buf[G_N_ELEMENTS(hsep_own)][32]; for (j = 0; j < G_N_ELEMENTS(buf); j++) { uint64 v; v = hsep_own[j] + hsep_global_table[i][j] - hsep->table[i][j]; uint64_to_string_buf(v, buf[j], sizeof buf[0]); } STATIC_ASSERT(3 == G_N_ELEMENTS(buf)); printf("(%s, %s, %s) ", buf[0], buf[1], buf[2]); } } if (GNET_PROPERTY(hsep_debug) > 1) puts("\n"); /* write message size */ msglen = opttriples * 24; gnutella_header_set_size(gnutella_msg_hsep_header(msg), msglen); /* correct message length */ msglen += GTA_HEADER_SIZE; /* send message to peer node */ gmsg_sendto_one(n, msg, msglen); WFREE_NULL(msg, msgsize); /* * Update counters. */ hsep->msgs_sent++; hsep->triples_sent += opttriples; charge_timer: hsep->last_sent = now; hsep->random_skew = random_value(2 * HSEP_MSG_SKEW) - HSEP_MSG_SKEW; }