/** * Send a message to target node. * * @param n the G2 node to which message should be sent * @param mb the message to sent (ownership taken, will be freed later) */ void g2_node_send(const gnutella_node_t *n, pmsg_t *mb) { node_check(n); g_assert(NODE_TALKS_G2(n)); if (NODE_IS_UDP(n)) mq_udp_node_putq(n->outq, mb, n); else if (NODE_IS_WRITABLE(n)) mq_tcp_putq(n->outq, mb, NULL); else goto drop; if (GNET_PROPERTY(log_sending_g2)) { g_debug("%s(): sending %s to %s", G_STRFUNC, g2_msg_infostr_mb(mb), node_infostr(n)); } return; drop: if (GNET_PROPERTY(log_sending_g2)) { g_debug("%s(): aborting sending %s to %s", G_STRFUNC, g2_msg_infostr_mb(mb), node_infostr(n)); } pmsg_free(mb); /* Cannot send it, free it now */ }
/** * Send time synchronization request to specified node. * * When node_id is non-zero, it refers to the connected node to which * we're sending the time synchronization request. */ void tsync_send(struct gnutella_node *n, const struct nid *node_id) { struct tsync *ts; g_return_if_fail(n->port != 0); if (!NODE_IS_WRITABLE(n)) return; WALLOC(ts); ts->magic = TSYNC_MAGIC; tm_now_exact(&ts->sent); ts->sent.tv_sec = clock_loc2gmt(ts->sent.tv_sec); ts->node_id = nid_ref(node_id); ts->udp = booleanize(NODE_IS_UDP(n)); /* * As far as time synchronization goes, we must get the reply within * the next TSYNC_EXPIRE_MS millisecs. */ ts->expire_ev = cq_main_insert(TSYNC_EXPIRE_MS, tsync_expire, ts); hevset_insert(tsync_by_time, ts); vmsg_send_time_sync_req(n, GNET_PROPERTY(ntp_detected), &ts->sent); }
/** * 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); }
/** * Send a /QHT RESET to node. * * @param n the TCP node to which we need to send the /QHT * @param slots amount of slots in the table (power of 2) * @param inf_val infinity value (1) */ void g2_node_send_qht_reset(gnutella_node_t *n, int slots, int inf_val) { pmsg_t *mb = g2_build_qht_reset(slots, inf_val); node_check(n); g_assert(!NODE_IS_UDP(n)); g2_node_send(n, mb); }
/** * Send a /LNI to node. */ void g2_node_send_lni(gnutella_node_t *n) { pmsg_t *mb = g2_build_lni(); node_check(n); g_assert(!NODE_IS_UDP(n)); g2_node_send(n, mb); }
/** * Send a /QHT RESET to node. * * @param n the TCP node to which we need to send the /QHT * @param seqno the patch sequence number * @param seqsize the total length of the sequence * @param compressed whether patch is compressed * @param bits amount of bits for each entry (1) * @param buf start of patch data * @param len length in byte of patch data */ void g2_node_send_qht_patch(gnutella_node_t *n, int seqno, int seqsize, bool compressed, int bits, char *buf, int len) { pmsg_t *mb = g2_build_qht_patch(seqno, seqsize, compressed, bits, buf, len); node_check(n); g_assert(!NODE_IS_UDP(n)); g2_node_send(n, mb); }
/** * Send a datagram to the specified node, made of `len' bytes from `buf', * forming a valid Gnutella message, with a "control" priority. */ void udp_ctrl_send_msg(const gnutella_node_t *n, const void *buf, int len) { pmsg_t *mb; g_assert(NODE_IS_UDP(n)); g_return_if_fail(n->outq); mb = gmsg_to_ctrl_pmsg(buf, len); if (NODE_CAN_SR_UDP(n)) pmsg_mark_reliable(mb); /* Send reliably if node supports it */ mq_udp_node_putq(n->outq, mb, n); }
/** * Dump relayed or locally-emitted packet. * If ``from'' is NULL, packet was emitted locally. */ static void dump_packet_from_to(struct dump *dump, const struct gnutella_node *from, const struct gnutella_node *to, const pmsg_t *mb) { struct dump_header dh_to; struct dump_header dh_from; g_assert(to != NULL); g_assert(mb != NULL); g_assert(pmsg_read_base(mb) == pmsg_start(mb)); if (!dump_initialize(dump)) return; /* * This is only for Gnutella packets, leave DHT messages out. */ if (GTA_MSG_DHT == gnutella_header_get_function(pmsg_start(mb))) return; if (!ipset_contains_addr(&dump_tx_to_addrs, to->addr, TRUE)) return; if (NULL == from) { struct gnutella_node local; local.peermode = NODE_IS_UDP(to) ? NODE_P_UDP : NODE_P_NORMAL; local.addr = listen_addr(); local.port = GNET_PROPERTY(listen_port); if (!ipset_contains_addr(&dump_tx_from_addrs, local.addr, TRUE)) return; dump_header_set(&dh_from, &local); } else { if (!ipset_contains_addr(&dump_tx_from_addrs, from->addr, TRUE)) return; dump_header_set(&dh_from, from); } dump_header_set(&dh_to, to); dh_to.data[0] |= DH_F_TO; if (pmsg_prio(mb) != PMSG_P_DATA) dh_to.data[0] |= DH_F_CTRL; dump_append(dump, dh_to.data, sizeof dh_to.data); dump_append(dump, dh_from.data, sizeof dh_from.data); dump_append(dump, pmsg_read_base(mb), pmsg_size(mb)); dump_flush(dump); }
/** * Dump transmitted message block via TCP. * If ``from'' is NULL, packet was emitted locally. */ void dump_tx_tcp_packet( const struct gnutella_node *from, const struct gnutella_node *to, const pmsg_t *mb) { if (GNET_PROPERTY(dump_transmitted_gnutella_packets)) { g_assert(to != NULL); g_assert(mb != NULL); g_assert(!NODE_IS_UDP(to)); dump_packet_from_to(&dump_tx, from, to, mb); } else if (dump_tx.initialized) { dump_disable(&dump_tx); } }
/** * Send a message to specified UDP node. * * It is up to the caller to clone the message if needed, otherwise the * node's queue becomes the sole owner of the message and will pmsg_free() it. */ void udp_send_mb(const gnutella_node_t *n, pmsg_t *mb) { if (NULL == n || NULL == n->outq) { pmsg_free(mb); /* emit warnings */ g_return_if_fail(n); g_return_if_fail(n->outq); g_assert_not_reached(); } g_assert(NODE_IS_UDP(n)); if (NODE_CAN_SR_UDP(n)) pmsg_mark_reliable(mb); /* Send reliably if node supports it */ mq_udp_node_putq(n->outq, mb, n); }
/** * Handle reception of an RPC answer (/QKA, /QA) * * @param n the node from which the answer came * @param t the message tree * @param type the type of message */ static void g2_node_handle_rpc_answer(gnutella_node_t *n, const g2_tree_t *t, enum g2_msg type) { /* * /QKA received from UDP must be RPC replies to /QKR, otherwise * it can be sent when a /Q2 bearing the wrong query key is received * by a host. * * A /QA is sent back by a hub upon reception of the /Q2 message if * the query key was correct. */ if (NODE_IS_UDP(n)) { if (!g2_rpc_answer(n, t)) { /* * Special-case /QA which can come VERY late in the process, * well after the associated RPC has expired. Still a /QA * contains information that can be perused, and the associated * GUESS query may still be alive. Give them to the GUESS layer * as late-comers, to see how much information we can extract. */ if (G2_MSG_QA == type && guess_late_qa(n, t, NULL)) return; g2_node_drop(G_STRFUNC, n, t, "coming from UDP"); } return; } else { /* * We can get a /QA from TCP when we send a /Q2 to a neighbouring hub. * Since they are not linked to a GUESS query, but we may want to * collect new addresses from the packet and possibly update the * re-query time limit, handle these as "late" QA messages. */ if (G2_MSG_QA == type && guess_late_qa(n, t, NULL)) return; } /* * We do not expect these from TCP, since they are UDP RPC replies. */ g2_node_drop(G_STRFUNC, n, t, "coming from TCP"); }
/** * Send a datagram to the specified node, made of `len' bytes from `buf', * forming a valid Gnutella message. */ void udp_send_msg(const gnutella_node_t *n, const void *buf, int len) { pmsg_t *mb; g_assert(NODE_IS_UDP(n)); g_return_if_fail(n->outq); /* * If message is directed to a UDP node that can do semi-reliable UDP, * then turn on reliability on the message. */ mb = gmsg_to_pmsg(buf, len); if (NODE_CAN_SR_UDP(n)) pmsg_mark_reliable(mb); mq_udp_node_putq(n->outq, mb, n); }
/** * Handle reception of a /PI */ static void g2_node_handle_ping(gnutella_node_t *n, const g2_tree_t *t) { g2_tree_t *c; /* * Throttle pings received from UDP. */ if (NODE_IS_UDP(n)) { if (aging_lookup(g2_udp_pings, &n->addr)) { gnet_stats_count_dropped(n, MSG_DROP_THROTTLE); return; } aging_record(g2_udp_pings, WCOPY(&n->addr)); /* FALL THROUGH */ } c = g2_tree_first_child(t); /* * If there is no payload, it's a keep-alive ping, send back a pong. */ if (NULL == c) { g2_node_send_pong(n); return; } /* * There are children. * * If there is a /PI/UDP present, drop the message: we're not a hub, * we don't have to relay this message to its UDP target (we're only * connected to hubs, and the hub which got it should only forward that * message it its neighbouring hubs, not to leaves). * * If there is a /PI/RELAY, the ping was relayed by a hub, but it made * a mistake because we are a leaf node. */ g2_node_drop(G_STRFUNC, n, t, "has children and we are a leaf"); }
/** * Handle reception of a /PO */ static void g2_node_handle_pong(gnutella_node_t *n, const g2_tree_t *t) { /* * Pongs received from UDP must be RPC replies to pings. */ if (NODE_IS_UDP(n)) { if (!g2_rpc_answer(n, t)) g2_node_drop(G_STRFUNC, n, t, "coming from UDP"); return; } /* * Must be a pong received because we sent an alive ping earlier. */ alive_ack_ping(n->alive_pings, NULL); /* No MUID on G2 */ }
/** * Extract the UDP IP:port from a /Q2/UDP and populate the search request info * if we have a valid address. */ static void g2_node_extract_udp(const g2_tree_t *t, search_request_info_t *sri, const gnutella_node_t *n) { const char *p; size_t paylen; p = g2_tree_node_payload(t, &paylen); /* * Only handle if we have an IP:port entry. * We only handle IPv4 because G2 does not support IPv6. * * We don't care about the presence of the query key because as G2 leaf, * we only process /Q2 coming from our TCP-connected hubs, and they * are in charge of validating it. Now hubs may forward us /Q2 coming * from neighbouring hubs and those won't have a query key, hence we * need to handle payloads with no trailing 32-bit QK. */ if (6 == paylen || 10 == paylen) { /* IPv4 + port (+ QK usually) */ host_addr_t addr = host_addr_peek_ipv4(p); uint16 port = peek_le16(&p[4]); if (host_is_valid(addr, port)) { sri->addr = addr; sri->port = port; /* * If the address is that of the node sending us the query, * and it is not a UDP node, then we can deliver the hit * back via the TCP connection we have, so no need to use OOB. */ if (n->port == port && host_addr_equiv(addr, n->gnet_addr)) sri->oob = NODE_IS_UDP(n); else sri->oob = TRUE; } } }
/** * Route query hits from one node to the other. */ void dh_route(gnutella_node_t *src, gnutella_node_t *dest, int count) { pmsg_t *mb; struct dh_pmsg_info *pmi; const struct guid *muid; dqhit_t *dh; mqueue_t *mq; g_assert( gnutella_header_get_function(&src->header) == GTA_MSG_SEARCH_RESULTS); g_assert(count >= 0); if (!NODE_IS_WRITABLE(dest)) goto drop_shutdown; muid = gnutella_header_get_muid(&src->header); dh = dh_locate(muid); g_assert(dh != NULL); /* Must have called dh_got_results() first! */ if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH #%s got %d hit%s: " "msg=%u, hits_recv=%u, hits_sent=%u, hits_queued=%u", guid_hex_str(muid), count, plural(count), dh->msg_recv, dh->hits_recv, dh->hits_sent, dh->hits_queued); } mq = dest->outq; /* * Can we forward the message? */ switch (dh_can_forward(dh, mq, FALSE)) { case DH_DROP_FC: goto drop_flow_control; case DH_DROP_THROTTLE: goto drop_throttle; case DH_DROP_TRANSIENT: goto drop_transient; case DH_FORWARD: default: break; } /* * Allow message through. */ WALLOC(pmi); pmi->hits = count; dh->hits_queued += count; dh->msg_queued++; g_assert(dh->hits_queued >= UNSIGNED(count)); /* * Magic: we create an extended version of a pmsg_t that contains a * free routine, which will be invoked when the message queue frees * the message. * * This enables us to track how much results we already queued/sent. */ if (NODE_IS_UDP(dest)) { gnet_host_t to; pmsg_t *mbe; gnet_host_set(&to, dest->addr, dest->port); /* * With GUESS we may route back a query hit to an UDP node. */ if (GNET_PROPERTY(guess_server_debug) > 19) { g_debug("GUESS sending %d hit%s (%s) for #%s to %s", count, plural(count), NODE_CAN_SR_UDP(dest) ? "reliably" : NODE_CAN_INFLATE(dest) ? "possibly deflated" : "uncompressed", guid_hex_str(muid), node_infostr(dest)); } /* * Attempt to compress query hit if the destination supports it. * * If we're going to send the hit using semi-reliable UDP, there's * no need to compress beforehand, since the transport layer will * attempt its own compression anyway. */ if (!NODE_CAN_SR_UDP(dest) && NODE_CAN_INFLATE(dest)) { mb = gmsg_split_to_deflated_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); if (gnutella_header_get_ttl(pmsg_start(mb)) & GTA_UDP_DEFLATED) gnet_stats_inc_general(GNR_UDP_TX_COMPRESSED); } else { mb = gmsg_split_to_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); } mbe = pmsg_clone_extend(mb, dh_pmsg_free, pmi); pmsg_free(mb); if (NODE_CAN_SR_UDP(dest)) pmsg_mark_reliable(mbe); mq_udp_putq(mq, mbe, &to); } else { mb = gmsg_split_to_pmsg_extend(&src->header, src->data, src->size + GTA_HEADER_SIZE, dh_pmsg_free, pmi); mq_tcp_putq(mq, mb, src); if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH enqueued %d hit%s for #%s to %s", count, plural(count), guid_hex_str(muid), node_infostr(dest)); } } return; drop_shutdown: gnet_stats_count_dropped(src, MSG_DROP_SHUTDOWN); return; drop_flow_control: gnet_stats_count_dropped(src, MSG_DROP_FLOW_CONTROL); gnet_stats_count_flowc(&src->header, TRUE); return; drop_throttle: gnet_stats_count_dropped(src, MSG_DROP_THROTTLE); return; drop_transient: gnet_stats_count_dropped(src, MSG_DROP_TRANSIENT); return; }
/** * 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); }