Exemple #1
0
/**
 * 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);
}
Exemple #2
0
/**
 * 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;
}