/** * Enqueue message, which becomes owned by the queue. * * The data held in `to' is copied, so the structure can be reclaimed * immediately by the caller. */ void mq_udp_putq(mqueue_t *q, pmsg_t *mb, const gnet_host_t *to) { size_t size; char *mbs; uint8 function; pmsg_t *mbe = NULL; /* Extended message with destination info */ bool error = FALSE; mq_check_consistency(q); dump_tx_udp_packet(to, mb); again: mq_check_consistency(q); g_assert(mb); g_assert(!pmsg_was_sent(mb)); g_assert(pmsg_is_unread(mb)); g_assert(q->ops == &mq_udp_ops); /* Is an UDP queue */ /* * Trap messages enqueued whilst in the middle of an mq_clear() operation * by marking them as sent and dropping them. Idem if queue was * put in "discard" mode. */ if (q->flags & (MQ_CLEAR | MQ_DISCARD)) { pmsg_mark_sent(mb); /* Let them think it was sent */ pmsg_free(mb); /* Drop message */ return; } mq_check(q, 0); size = pmsg_size(mb); if (size == 0) { g_carp("%s: called with empty message", G_STRFUNC); goto cleanup; } /* * Protect against recursion: we must not invoke puthere() whilst in * the middle of another putq() or we would corrupt the qlink array: * Messages received during recursion are inserted into the qwait list * and will be stuffed back into the queue when the initial putq() ends. * --RAM, 2006-12-29 */ if (q->putq_entered > 0) { pmsg_t *extended; if (debugging(20)) g_warning("%s: %s recursion detected (%u already pending)", G_STRFUNC, mq_info(q), slist_length(q->qwait)); /* * We insert extended messages into the waiting queue since we need * the destination information as well. */ extended = mq_udp_attach_metadata(mb, to); slist_append(q->qwait, extended); return; } q->putq_entered++; mbs = pmsg_start(mb); function = gmsg_function(mbs); gnet_stats_count_queued(q->node, function, mbs, size); /* * If queue is empty, attempt a write immediatly. */ if (q->qhead == NULL) { ssize_t written; if (pmsg_check(mb, q)) { written = tx_sendto(q->tx_drv, mb, to); } else { gnet_stats_count_flowc(mbs, FALSE); node_inc_txdrop(q->node); /* Dropped during TX */ written = (ssize_t) -1; } if ((ssize_t) -1 == written) goto cleanup; node_add_tx_given(q->node, written); if ((size_t) written == size) { if (GNET_PROPERTY(mq_udp_debug) > 5) g_debug("MQ UDP sent %s", gmsg_infostr_full(pmsg_start(mb), pmsg_written_size(mb))); goto cleanup; } /* * Since UDP respects write boundaries, the following can never * happen in practice: either we write the whole datagram, or none * of it. */ if (written > 0) { g_warning( "partial UDP write (%zu bytes) to %s for %zu-byte datagram", written, gnet_host_to_string(to), size); goto cleanup; } /* FALL THROUGH */ } if (GNET_PROPERTY(mq_udp_debug) > 5) g_debug("MQ UDP queued %s", gmsg_infostr_full(pmsg_start(mb), pmsg_written_size(mb))); /* * Attach the destination information as metadata to the message, unless * it is already known (possible only during unfolding of the queued data * during re-entrant calls). * * This is later extracted via pmsg_get_metadata() on the extended * message by the message queue to get the destination information. * * Then enqueue the extended message. */ if (NULL == mbe) mbe = mq_udp_attach_metadata(mb, to); q->cops->puthere(q, mbe, size); mb = NULL; /* FALL THROUGH */ cleanup: if (mb) { pmsg_free(mb); mb = NULL; } /* * When reaching that point with a zero putq_entered counter, it means * we triggered an early error condition. Bail out. */ g_assert(q->putq_entered >= 0); if (q->putq_entered == 0) error = TRUE; else q->putq_entered--; mq_check(q, 0); /* * If we're exiting here with no other putq() registered, then we must * pop an item off the head of the list and iterate again. */ if (0 == q->putq_entered && !error) { mbe = slist_shift(q->qwait); if (mbe) { struct mq_udp_info *mi = pmsg_get_metadata(mbe); mb = mbe; /* An extended message "is-a" message */ to = &mi->to; if (debugging(20)) g_warning( "%s: %s flushing waiting to %s (%u still pending)", G_STRFUNC, mq_info(q), gnet_host_to_string(to), slist_length(q->qwait)); goto again; } } return; }
/** * 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; }