/** * Fetch the MUID in the message, if any is architected. * * @param t the message tree * @param buf the buffer to fill with a copy of the MUID * * @return a pointer to `buf' if OK and we filled the MUID, NULL if there is * no valid MUID in the message or the message is not carrying any MUID. */ guid_t * g2_msg_get_muid(const g2_tree_t *t, guid_t *buf) { enum g2_msg m; const void *payload; size_t paylen; size_t offset; g_assert(t != NULL); g_assert(buf != NULL); m = g2_msg_name_type(g2_tree_name(t)); switch (m) { case G2_MSG_Q2: case G2_MSG_QA: offset = 0; break; case G2_MSG_QH2: offset = 1; /* First payload byte is the hop count */ break; default: return NULL; /* No MUID in message */ } payload = g2_tree_node_payload(t, &paylen); if (NULL == payload || paylen < GUID_RAW_SIZE + offset) return NULL; /* * Copy the MUID in the supplied buffer for alignment purposes, since * the MUID is offset by 1 byte in /QH2 messages, and return that aligned * pointer. */ memcpy(buf, const_ptr_add_offset(payload, offset), GUID_RAW_SIZE); return buf; }
/** * Handle message coming from G2 node. */ void g2_node_handle(gnutella_node_t *n) { g2_tree_t *t; size_t plen; enum g2_msg type; node_check(n); g_assert(NODE_TALKS_G2(n)); t = g2_frame_deserialize(n->data, n->size, &plen, FALSE); if (NULL == t) { if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) { g_warning("%s(): cannot deserialize /%s from %s", G_STRFUNC, g2_msg_raw_name(n->data, n->size), node_infostr(n)); } if (GNET_PROPERTY(log_bad_g2)) dump_hex(stderr, "G2 Packet", n->data, n->size); return; } else if (plen != n->size) { if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) { g_warning("%s(): consumed %zu bytes but /%s from %s had %u", G_STRFUNC, plen, g2_msg_raw_name(n->data, n->size), node_infostr(n), n->size); } if (GNET_PROPERTY(log_bad_g2)) dump_hex(stderr, "G2 Packet", n->data, n->size); hostiles_dynamic_add(n->addr, "cannot parse incoming messages", HSTL_GIBBERISH); goto done; } else if (GNET_PROPERTY(g2_debug) > 19) { g_debug("%s(): received packet from %s", G_STRFUNC, node_infostr(n)); g2_tfmt_tree_dump(t, stderr, G2FMT_O_PAYLEN); } type = g2_msg_name_type(g2_tree_name(t)); switch (type) { case G2_MSG_PI: g2_node_handle_ping(n, t); break; case G2_MSG_PO: g2_node_handle_pong(n, t); break; case G2_MSG_LNI: g2_node_handle_lni(n, t); break; case G2_MSG_KHL: g2_node_handle_khl(t); break; case G2_MSG_PUSH: handle_push_request(n, t); break; case G2_MSG_Q2: g2_node_handle_q2(n, t); break; case G2_MSG_QA: case G2_MSG_QKA: g2_node_handle_rpc_answer(n, t, type); break; case G2_MSG_QH2: search_g2_results(n, t); break; default: g2_node_drop(G_STRFUNC, n, t, "default"); break; } done: g2_tree_free_null(&t); }
/** * 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; }