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