void gnet_stats_g2_count_flowc(const gnutella_node_t *n, const void *base, size_t len) { uint t; uint8 f, ttl, hops; g_assert(thread_is_main()); f = g2_msg_type(base, len); if (GNET_PROPERTY(node_debug) > 3) g_debug("FLOWC G2 %s", g2_msg_type_name(f)); if (f != G2_MSG_MAX) { f += MSG_G2_BASE; } else { f = G_N_ELEMENTS(stats_lut) - 1; /* Last, holds MSG_UNKNOWN */ } ttl = NODE_USES_UDP(n) ? 1 : 2; /* Purely made up, but cannot be 0 */ hops = 0; /* Locally generated, this is TX flowc */ t = stats_lut[f]; gnet_stats_flowc_internal(t, f, ttl, hops, len); }
/** * Fill supplied buffer with the formatted string describing the message. * * @param data start of the G2 message * @param len length of the message * @param buf buffer where formatted string is written * @param buflen length of the destination buffer * * @return the amount of bytes written. */ size_t g2_msg_infostr_to_buf(const void *data, size_t len, char *buf, size_t buflen) { enum g2_msg m; const guid_t *muid = NULL; g_assert(size_is_non_negative(len)); g_assert(size_is_non_negative(buflen)); /* * Check whether we need to decompile the packet to access the GUID, which * is the payload of the root element in the tree. Given the way things * are serialized, that would be the last 16 bytes of the message, so * we don't have to deserialize everything just to access it. */ m = g2_msg_type(data, len); switch (m) { case G2_MSG_Q2: case G2_MSG_QA: case G2_MSG_QH2: if (len > GUID_RAW_SIZE) muid = const_ptr_add_offset(data, len - GUID_RAW_SIZE); /* FALL THROUGH */ default: break; } return str_bprintf(buf, buflen, "/%s (%zu byte%s)%s%s", g2_msg_type_name(m), len, plural(len), NULL == muid ? "" : " #", NULL == muid ? "" : guid_hex_str(muid)); }
/** * RPC timeout callback. */ static void g2_rpc_timeout(cqueue_t *cq, void *obj) { struct g2_rpc *gr = obj; g2_rpc_check(gr); if (GNET_PROPERTY(g2_rpc_debug) > 1) { g_debug("%s(): /%s RPC to %s timed out, calling %s()", G_STRFUNC, g2_msg_type_name(gr->key.type), host_addr_to_string(gr->key.addr), stacktrace_function_name(gr->cb)); } cq_zero(cq, &gr->timeout_ev); (*gr->cb)(NULL, NULL, gr->arg); g2_rpc_free(gr, FALSE); }
/** * Notification that a message was received that could be the answer to * a pending RPC. * * @param n the node from which we got the message * @param t the received message tree * * @return TRUE if the message was indeed an RPC reply, FALSE otherwise. */ bool g2_rpc_answer(const gnutella_node_t *n, const g2_tree_t *t) { struct g2_rpc *gr; struct g2_rpc_key key; enum g2_msg type; type = g2_msg_name_type(g2_tree_name(t)); key.type = g2_rpc_send_type(type); key.addr = n->addr; gr = hevset_lookup(g2_rpc_pending, &key); if (NULL == gr) { /* * No known RPC, but wait... we can receive a /QKA when we issue a /Q2 * and the query key we knew for the remote host has expired, hence * we must look whether there is not a /Q2 pending as well in that * case. Once again, the lack of MUID in these messages is a handicap. */ if (G2_MSG_QKA == type) { key.type = G2_MSG_Q2; gr = hevset_lookup(g2_rpc_pending, &key); if (gr != NULL) goto found; /* Sent a /Q2, got a /QKA back */ } if (GNET_PROPERTY(g2_rpc_debug) > 1) { g_debug("%s(): unexpected /%s RPC reply from %s", G_STRFUNC, g2_msg_type_name(key.type), node_infostr(n)); } return FALSE; } found: /* * Got a reply for an RPC we sent, based solely on message type and * source address of the message. This is weak, but G2 works like that. * * Weakness comes from the fact that we cannot have multiple RPCs with * a same IP address but towards different ports, nor have concurrent * RPCs with the same host for several different by similar requests * (although this can be viewed as anti-hammering, servers should protect * against that in different ways, a crippled protocol not being an answer). * * The only transaction where we could use the MUID is /Q2 -> /QA but we * leave that check to the GUESS layer and make sure here that we have only * one single RPC transaction at a time with a given IP address. */ if (GNET_PROPERTY(g2_rpc_debug) > 2) { g_debug("%s(): /%s RPC to %s got a /%s reply, calling %s()", G_STRFUNC, g2_msg_type_name(gr->key.type), host_addr_to_string(gr->key.addr), g2_tree_name(t), stacktrace_function_name(gr->cb)); } (*gr->cb)(n, t, gr->arg); g2_rpc_free(gr, FALSE); return TRUE; }
/** * Start a G2 RPC with the specified host. * * @param host the host to which message is sent * @param mb the message to send * @param cb if non-NULL, callback to invoke on reply or timeout * @param arg additional callback argument * @param timeout amount of seconds before timeout * * @return TRUE if we initiated the RPC, FALSE if another of the same * kind was already in progress with the host. */ bool g2_rpc_launch(const gnet_host_t *host, pmsg_t *mb, g2_rpc_cb_t cb, void *arg, unsigned timeout) { struct g2_rpc *gr; struct g2_rpc_key key; gnutella_node_t *n; key.type = g2_msg_type_mb(mb); key.addr = gnet_host_get_addr(host); /* * Because there is no MUID in /PI and /QKR messages, we cannot use that * as a key to detect the RPC reply. Therefore, we use the message type * and the IP address of the host. When a /PO or /QKA comes back, we'll * be able to see whether we had a pending RPC from that host for that * type of transaction. * * The downside is that we can only have one pending RPC at a time of * a given kind towards a given IP address. We don't use the port in * the key because we cannot assume the reply will come from the same port * we sent the message to, if the remote host is behind NAT or does not * use its listening UDP socket to reply. */ if (hevset_contains(g2_rpc_pending, &key)) { if (GNET_PROPERTY(g2_rpc_debug)) { g_debug("%s(): cannot issue /%s RPC to %s: concurrent request", G_STRFUNC, g2_msg_type_name(key.type), gnet_host_to_string(host)); } return FALSE; } /* * Make sure the node is valid. */ n = node_udp_g2_get_addr_port(key.addr, gnet_host_get_port(host)); if (NULL == n) { if (GNET_PROPERTY(g2_rpc_debug)) { g_debug("%s(): cannot issue /%s RPC to %s: cannot get G2 node", G_STRFUNC, g2_msg_type_name(key.type), gnet_host_to_string(host)); } return FALSE; /* Invalid node, or G2 disabled */ } /* * Good, we can issue the RPC. */ WALLOC(gr); gr->magic = G2_RPC_MAGIC; gr->key = key; /* struct copy */ gr->cb = cb; gr->arg = arg; gr->timeout_ev = cq_main_insert(timeout * 1000, g2_rpc_timeout, gr); hevset_insert(g2_rpc_pending, gr); if (GNET_PROPERTY(g2_rpc_debug) > 1) { g_debug("%s(): issuing /%s RPC to %s, timeout %u sec%s", G_STRFUNC, g2_msg_type_name(key.type), gnet_host_to_string(host), timeout, plural(timeout)); } /* * Do not send RPCs reliably: this can cause problems if we don't receive * the ACK backm yet the message was received and processed remotely: the * remote host will send a reply back and the message will still appear to * be "unsent" locally. * * Furthermore, this alleviates the need for the remote side to actually * acknowledge the request: targeted hosts can be busy so it's best to * make the RPC "unreliable" to limit processing and bandwidth requirements. */ g2_node_send(n, mb); return TRUE; }