/** * 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); }
/** * 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; }