/** * Handle reception of a /Q2 */ static void g2_node_handle_q2(gnutella_node_t *n, const g2_tree_t *t) { const guid_t *muid; size_t paylen; const g2_tree_t *c; char *dn = NULL; char *md = NULL; uint32 iflags = 0; search_request_info_t sri; bool has_interest = FALSE; node_inc_rx_query(n); /* * As a G2 leaf, we cannot handle queries coming from UDP because we * are not supposed to get any! */ if (NODE_IS_UDP(n)) { g2_node_drop(G_STRFUNC, n, t, "coming from UDP"); return; } /* * The MUID of the query is the payload of the root node. */ muid = g2_tree_node_payload(t, &paylen); if (paylen != GUID_RAW_SIZE) { g2_node_drop(G_STRFUNC, n, t, "missing MUID"); return; } /* * Make sure we have never seen this query already. * * To be able to leverage on Gnutella's routing table to detect duplicates * over a certain lifespan, we are going to fake a minimal Gnutella header * with a message type of GTA_MSG_G2_SEARCH, which is never actually used * on the network. * * The TTL and hops are set to 1 and 0 initially, so that the message seems * to come from a neighbouring host and cannot be forwarded. * * When that is done, we will be able to call route_message() and have * all the necessary bookkeeping done for us. */ { struct route_dest dest; gnutella_header_set_muid(&n->header, muid); gnutella_header_set_function(&n->header, GTA_MSG_G2_SEARCH); gnutella_header_set_ttl(&n->header, 1); gnutella_header_set_hops(&n->header, 0); if (!route_message(&n, &dest)) return; /* Already accounted as duplicated, and logged */ } /* * Setup request information so that we can call search_request() * to process our G2 query. */ ZERO(&sri); sri.magic = SEARCH_REQUEST_INFO_MAGIC; /* * Handle the children of /Q2. */ G2_TREE_CHILD_FOREACH(t, c) { enum g2_q2_child ct = TOKENIZE(g2_tree_name(c), g2_q2_children); const char *payload; switch (ct) { case G2_Q2_DN: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == dn) { uint off = 0; /* Not NUL-terminated, need to h_strndup() it */ dn = h_strndup(payload, paylen); if (!query_utf8_decode(dn, &off)) { gnet_stats_count_dropped(n, MSG_DROP_MALFORMED_UTF_8); goto done; /* Drop the query */ } sri.extended_query = dn + off; sri.search_len = paylen - off; /* In bytes */ } break; case G2_Q2_I: if (!has_interest) iflags = g2_node_extract_interest(c); has_interest = TRUE; break; case G2_Q2_MD: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == md) { /* Not NUL-terminated, need to h_strndup() it */ md = h_strndup(payload, paylen); } break; case G2_Q2_NAT: sri.flags |= QUERY_F_FIREWALLED; break; case G2_Q2_SZR: /* Size limits */ if (g2_node_extract_size_request(c, &sri.minsize, &sri.maxsize)) sri.size_restrictions = TRUE; break; case G2_Q2_UDP: if (!sri.oob) g2_node_extract_udp(c, &sri, n); break; case G2_Q2_URN: g2_node_extract_urn(c, &sri); break; } } /* * When there is no /Q2/I, return a default set of information. */ if (!has_interest) iflags = G2_Q2_F_DFLT; /* * If there are meta-data, try to intuit which media types there are * looking for. * * The payload is XML looking like "<audio/>" or "<video/>" but there * can be attributes and we don't want to do a full XML parsing there. * Hence we'll base our analysis on simple lexical parsing, which is * why we call a routine to "intuit", not to "extract". * * Also, this is poorer than Gnutella's GGEP "M" because apparently there * can be only one single type, since the XML payload must obey some * kind of schema and there is an audio schema, a video schema, etc... * XML was just a wrong design choice there. */ if (md != NULL) sri.media_types = g2_node_intuit_media_type(md); /* * Validate the return address if OOB hit delivery is configured. */ if (sri.oob && !search_oob_is_allowed(n, &sri)) goto done; /* * Update statistics, as done in search_request_preprocess() for Gnutella. */ if (sri.exv_sha1cnt) { gnet_stats_inc_general(GNR_QUERY_G2_SHA1); if (NULL == dn) { int i; for (i = 0; i < sri.exv_sha1cnt; i++) { search_request_listener_emit(QUERY_SHA1, sha1_base32(&sri.exv_sha1[i].sha1), n->addr, n->port); } } } if (dn != NULL && !is_ascii_string(dn)) gnet_stats_inc_general(GNR_QUERY_G2_UTF8); if (dn != NULL) search_request_listener_emit(QUERY_STRING, dn, n->addr, n->port); if (!search_is_valid(n, 0, &sri)) goto done; /* * Perform the query. */ sri.g2_query = TRUE; sri.partials = booleanize(iflags & G2_Q2_F_PFS); sri.g2_wants_url = booleanize(iflags & G2_Q2_F_URL); sri.g2_wants_alt = booleanize(iflags & G2_Q2_F_A); sri.g2_wants_dn = booleanize(iflags & G2_Q2_F_DN); search_request(n, &sri, NULL); done: HFREE_NULL(dn); HFREE_NULL(md); }
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; }