/** * Free routine for a query message. */ static void sq_pmsg_free(pmsg_t *mb, void *arg) { struct smsg_info *smi = arg; g_assert(pmsg_is_extended(mb)); /* * If we're still in leaf mode, let the search know that we sent a * query for it to the specified node ID. */ if (settings_is_leaf()) search_notify_sent(smi->shandle, smi->node_id); nid_unref(smi->node_id); WFREE(smi); }
/** * Periodic host heartbeat timer. */ void host_timer(void) { guint count; int missing; host_addr_t addr; guint16 port; host_type_t htype; guint max_nodes; gboolean empty_cache = FALSE; if (in_shutdown || !GNET_PROPERTY(online_mode)) return; max_nodes = settings_is_leaf() ? GNET_PROPERTY(max_ultrapeers) : GNET_PROPERTY(max_connections); count = node_count(); /* Established + connecting */ missing = node_keep_missing(); if (GNET_PROPERTY(host_debug) > 1) g_debug("host_timer - count %u, missing %u", count, missing); /* * If we are not connected to the Internet, apparently, make sure to * connect to at most one host, to avoid using all our hostcache. * Also, we don't connect each time we are called. */ if (!GNET_PROPERTY(is_inet_connected)) { static time_t last_try; if (last_try && delta_time(tm_time(), last_try) < 20) return; last_try = tm_time(); if (GNET_PROPERTY(host_debug)) g_debug("host_timer - not connected, trying to connect"); } /* * Allow more outgoing connections than the maximum amount of * established Gnet connection we can maintain, but not more * than quick_connect_pool_size This is the "greedy mode". */ if (count >= GNET_PROPERTY(quick_connect_pool_size)) { if (GNET_PROPERTY(host_debug) > 1) g_debug("host_timer - count %u >= pool size %u", count, GNET_PROPERTY(quick_connect_pool_size)); return; } if (count < max_nodes) missing -= whitelist_connect(); /* * If we are under the number of connections wanted, we add hosts * to the connection list */ htype = HOST_ULTRA; if ( settings_is_ultra() && GNET_PROPERTY(node_normal_count) < GNET_PROPERTY(normal_connections) && GNET_PROPERTY(node_ultra_count) >= (GNET_PROPERTY(up_connections) - GNET_PROPERTY(normal_connections)) ) { htype = HOST_ANY; } if (hcache_size(htype) == 0) htype = HOST_ANY; if (hcache_size(htype) == 0) empty_cache = TRUE; if (GNET_PROPERTY(host_debug) && missing > 0) g_debug("host_timer - missing %d host%s%s", missing, missing == 1 ? "" : "s", empty_cache ? " [empty caches]" : ""); if (!GNET_PROPERTY(stop_host_get)) { if (missing > 0) { static time_t last_try; unsigned fan, max_pool, to_add; max_pool = MAX(GNET_PROPERTY(quick_connect_pool_size), max_nodes); fan = (missing * GNET_PROPERTY(quick_connect_pool_size))/ max_pool; fan = MAX(1, fan); to_add = GNET_PROPERTY(is_inet_connected) ? fan : (guint) missing; /* * Every so many calls, attempt to ping all our neighbours to * get fresh pongs, in case our host cache is not containing * sufficiently fresh hosts and we keep getting connection failures. */ if ( 0 == last_try || delta_time(tm_time(), last_try) >= HOST_PINGING_PERIOD ) { ping_all_neighbours(); last_try = tm_time(); } /* * Make sure that we never use more connections then the * quick pool or the maximum number of hosts allow. */ if (to_add + count > max_pool) to_add = max_pool - count; if (GNET_PROPERTY(host_debug) > 2) { g_debug("host_timer - connecting - " "add: %d fan:%d miss:%d max_hosts:%d count:%d extra:%d", to_add, fan, missing, max_nodes, count, GNET_PROPERTY(quick_connect_pool_size)); } missing = to_add; if (missing > 0 && (0 == connected_nodes() || host_low_on_pongs)) { gnet_host_t host[HOST_DHT_MAX]; int hcount; int i; hcount = dht_fill_random(host, MIN(UNSIGNED(missing), G_N_ELEMENTS(host))); missing -= hcount; for (i = 0; i < hcount; i++) { addr = gnet_host_get_addr(&host[i]); port = gnet_host_get_port(&host[i]); if (!hcache_node_is_bad(addr)) { if (GNET_PROPERTY(host_debug) > 3) { g_debug("host_timer - UHC pinging and connecting " "to DHT node at %s", host_addr_port_to_string(addr, port)); } /* Try to use the host as an UHC before connecting */ udp_send_ping(NULL, addr, port, TRUE); if (!host_gnutella_connect(addr, port)) { missing++; /* Did not use entry */ } } else { missing++; /* Did not use entry */ } } } while (hcache_size(htype) && missing-- > 0) { if (hcache_get_caught(htype, &addr, &port)) { if (!(hostiles_check(addr) || hcache_node_is_bad(addr))) { if (!host_gnutella_connect(addr, port)) { missing++; /* Did not use entry */ } } else { missing++; /* Did not use entry */ } } } if (missing > 0 && (empty_cache || host_cache_allow_bypass())) { if (!uhc_is_waiting()) { if (GNET_PROPERTY(host_debug)) g_debug("host_timer - querying UDP host cache"); uhc_get_hosts(); /* Get new hosts from UHCs */ } } } } else if (GNET_PROPERTY(use_netmasks)) { /* Try to find better hosts */ if (hcache_find_nearby(htype, &addr, &port)) { if (node_remove_worst(TRUE)) node_add(addr, port, 0); else hcache_add_caught(htype, addr, port, "nearby host"); } } }
void hsep_send_msg(struct gnutella_node *n, time_t now) { hsep_triple tmp[G_N_ELEMENTS(n->hsep->sent_table)], other; unsigned int i, j, msglen, msgsize, triples, opttriples; gnutella_msg_hsep_t *msg; hsep_ctx_t *hsep; g_assert(n); g_assert(n->hsep); hsep = n->hsep; ZERO(&other); /* * If we are a leaf, we just need to send one triple, * which contains our own data (this triple is expanded * to the needed number of triples on the peer's side). * As the 0'th global and 0'th connection triple are zero, * it contains only our own triple, which is correct. */ triples = settings_is_leaf() ? 1 : G_N_ELEMENTS(tmp); /* * Allocate and initialize message to send. */ msgsize = GTA_HEADER_SIZE + triples * (sizeof *msg - GTA_HEADER_SIZE); msg = walloc(msgsize); { gnutella_header_t *header; header = gnutella_msg_hsep_header(msg); message_set_muid(header, GTA_MSG_HSEP_DATA); gnutella_header_set_function(header, GTA_MSG_HSEP_DATA); gnutella_header_set_ttl(header, 1); gnutella_header_set_hops(header, 0); } /* * Collect HSEP data to send and convert the data to * little endian byte order. */ if (triples > 1) { /* determine what we know about non-HSEP nodes in 1 hop distance */ hsep_get_non_hsep_triple(&other); } for (i = 0; i < triples; i++) { for (j = 0; j < G_N_ELEMENTS(other); j++) { uint64 val; val = hsep_own[j] + (0 == i ? 0 : other[j]) + hsep_global_table[i][j] - hsep->table[i][j]; poke_le64(&tmp[i][j], val); } } STATIC_ASSERT(sizeof hsep->sent_table == sizeof tmp); /* check if the table differs from the previously sent table */ if ( 0 == memcmp(tmp, hsep->sent_table, sizeof tmp) ) { WFREE_NULL(msg, msgsize); goto charge_timer; } memcpy(cast_to_char_ptr(msg) + GTA_HEADER_SIZE, tmp, triples * sizeof tmp[0]); /* store the table for later comparison */ memcpy(hsep->sent_table, tmp, triples * sizeof tmp[0]); /* * Note that on big endian architectures the message data is now in * the wrong byte order. Nevertheless, we can use hsep_triples_to_send() * with that data. */ /* optimize number of triples to send */ opttriples = hsep_triples_to_send(cast_to_pointer(tmp), triples); if (GNET_PROPERTY(hsep_debug) > 1) { printf("HSEP: Sending %d %s to node %s (msg #%u): ", opttriples, opttriples == 1 ? "triple" : "triples", host_addr_port_to_string(n->addr, n->port), hsep->msgs_sent + 1); } for (i = 0; i < opttriples; i++) { if (GNET_PROPERTY(hsep_debug) > 1) { char buf[G_N_ELEMENTS(hsep_own)][32]; for (j = 0; j < G_N_ELEMENTS(buf); j++) { uint64 v; v = hsep_own[j] + hsep_global_table[i][j] - hsep->table[i][j]; uint64_to_string_buf(v, buf[j], sizeof buf[0]); } STATIC_ASSERT(3 == G_N_ELEMENTS(buf)); printf("(%s, %s, %s) ", buf[0], buf[1], buf[2]); } } if (GNET_PROPERTY(hsep_debug) > 1) puts("\n"); /* write message size */ msglen = opttriples * 24; gnutella_header_set_size(gnutella_msg_hsep_header(msg), msglen); /* correct message length */ msglen += GTA_HEADER_SIZE; /* send message to peer node */ gmsg_sendto_one(n, msg, msglen); WFREE_NULL(msg, msgsize); /* * Update counters. */ hsep->msgs_sent++; hsep->triples_sent += opttriples; charge_timer: hsep->last_sent = now; hsep->random_skew = random_value(2 * HSEP_MSG_SKEW) - HSEP_MSG_SKEW; }
/** * 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; }