Exemple #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 */
}
Exemple #2
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 #3
0
static void _set_node_from_user_input(QSP_ARG_DECL  Spink_Node *skn_p)
{
	if( ! NODE_IS_WRITABLE(skn_p) ){
		sprintf(ERROR_STRING,"Node %s is not writable!?",skn_p->skn_name);
		warn(ERROR_STRING);
		get_dummy_input();
		return;
	}
	(*(skn_p->skn_type_p->snt_set_func))(QSP_ARG  skn_p);
}
Exemple #4
0
/**
 * If we had to route hits to the specified node destination, would we?
 */
bool
dh_would_route(const struct guid *muid, gnutella_node_t *dest)
{
	dqhit_t *dh;

	if (!NODE_IS_WRITABLE(dest))
		return FALSE;

	dh = dh_locate(muid);

	if (dh == NULL)
		return TRUE;		/* Unknown, no hits yet => would route */

	return DH_FORWARD == dh_can_forward(dh, dest->outq, TRUE);
}
Exemple #5
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;
}
Exemple #6
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;
}