Exemplo n.º 1
0
/**
 * 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 */
}
Exemplo n.º 2
0
/**
 * 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;
}
Exemplo n.º 3
0
/**
 * Decides if the queue can send a message. Currently use simple fixed
 * time base heuristics. May add bursty control later...
 */
void
sq_process(squeue_t *sq, time_t now)
{
    time_delta_t spacing = GNET_PROPERTY(search_queue_spacing);
	GList *item;
	smsg_t *sb;
	struct gnutella_node *n;
	bool sent;

	g_assert(sq->node == NULL || sq->node->outq != NULL);

retry:
	/*
	 * We don't need to do anything if either:
	 *
	 * 1. The queue is empty.
	 * 2. We sent our last search less than "search_queue_spacing" seconds ago.
	 * 3. We never got a packet from that node.
	 * 4. The node activated hops-flow to shut all queries
	 * 5. We activated flow-control on the node locally.
	 *
	 *		--RAM, 01/05/2002
	 */

	if (sq->count == 0)
		return;

    if (delta_time(now, sq->last_sent) < spacing)
		return;

	n = sq->node;					/* Will be NULL for the global SQ */

	if (n != NULL) {
		if (n->received == 0)		/* RX = 0, wait for handshaking ping */
			return;

		if (!node_query_hops_ok(n, 0))		/* Cannot send hops=0 query */
			return;

		if (!NODE_IS_WRITABLE(n))
			return;

		if (NODE_IN_TX_FLOW_CONTROL(n))		/* Don't add to the mqueue yet */
			return;
	} else {
		/*
		 * Processing the global SQ.
		 */

		if (settings_is_leaf())
			return;

		if (3*UNSIGNED(node_keep_missing()) > 2*GNET_PROPERTY(up_connections))
			return;		/* Not enough nodes for querying */
	}

	/*
	 * Queue is managed as a LIFO: we extract the first message, i.e. the last
	 * one enqueued, and pass it along to the node's message queue.
	 */

	g_assert(sq->searches);

	item = g_list_first(sq->searches);
	sb = item->data;

	g_assert(sq->count > 0);
	sq->count--;
	sent = TRUE;			/* Assume we're going to send/initiate it */

	if (n == NULL) {
		g_assert(sb->qhv != NULL);		/* Enqueued via sq_global_putq() */

		if (GNET_PROPERTY(sq_debug) > 2)
			g_debug("sq GLOBAL, queuing \"%s\" (%u left, %d sent)",
				gnutella_msg_search_get_text(pmsg_start(sb->mb)),
				sq->count, sq->n_sent);

		dq_launch_local(sb->shandle, sb->mb, sb->qhv);

	} else if (search_query_allowed(sb->shandle)) {
		/*
		 * Must log before sending, in case the queue discards the message
		 * buffer immediately.
		 */

		g_assert(sb->qhv == NULL);		/* Enqueued via sq_putq() */

		if (GNET_PROPERTY(sq_debug) > 2)
			g_debug("sq for node %s, queuing \"%s\" (%u left, %d sent)",
				node_addr(n), gnutella_msg_search_get_text(pmsg_start(sb->mb)),
				sq->count, sq->n_sent);

		/*
		 * If we're a leaf node, we're doing a leaf-guided dynamic query.
		 * In order to be able to report hits we get to the UPs to whom
		 * we sent our searches, we need to be notified of all the physical
		 * queries that go out.
		 */

		if (settings_is_leaf())
			smsg_mutate(sb, n);

		mq_tcp_putq(n->outq, sb->mb, NULL);

	} else {
		if (GNET_PROPERTY(sq_debug) > 4)
			g_debug("sq for node %s, ignored \"%s\" (%u left, %d sent)",
				node_addr(n), gnutella_msg_search_get_text(pmsg_start(sb->mb)),
				sq->count, sq->n_sent);
		pmsg_free(sb->mb);
		if (sb->qhv)
			qhvec_free(sb->qhv);
		sent = FALSE;
	}

	if (sent) {
		sq->n_sent++;
		sq->last_sent = now;
	}

	sqh_remove(sq, sb->shandle);
	smsg_free(sb);
	sq->searches = g_list_remove_link(sq->searches, item);
	g_list_free_1(item);

	/*
	 * If we ignored the query, retry with the next in the queue.
	 * We don't use a do/while() loop to avoid identing the whole body.
	 */

	if (!sent)
		goto retry;
}