示例#1
0
文件: udp.c 项目: MrJoe/gtk-gnutella
/**
 * Send a new Gnutella ping message to the specified host.
 *
 * @param muid		the MUID to use (allocated randomly if NULL)
 * @param addr		address to which ping should be sent
 * @param port		port number
 * @param uhc_ping	if TRUE, include the "SCP" GGEP extension
 *
 * @return TRUE if we sent the ping, FALSE it we throttled it.
 */
bool
udp_send_ping(const struct guid *muid, const host_addr_t addr, uint16 port,
	bool uhc_ping)
{
	gnutella_msg_init_t *m;
	uint32 size;

	/*
	 * Don't send too frequent pings: they may throttle us anyway.
	 */

	if (aging_lookup(udp_aging_pings, &addr)) {
		if (GNET_PROPERTY(udp_debug) > 1) {
			g_warning("UDP throttling %sping to %s",
				uhc_ping ? "UHC " : "", host_addr_to_string(addr));
		}
		return FALSE;
	}

	if (uhc_ping && GNET_PROPERTY(log_uhc_pings_tx)) {
		g_debug("UDP UHC sending ping to %s", host_addr_to_string(addr));
	}

	m = build_ping_msg(muid, 1, uhc_ping, &size);
	return udp_send_ping_with_callback(m, size, addr, port, NULL, NULL, FALSE);
}
示例#2
0
/**
 * Log whitelist item.
 */
static void
log_whitelist_item(const struct whitelist *item, const char *what)
{
	const char *host;
	uint8 bits;

	if (item->host != NULL) {
		host = 0 == item->port ? item->host->name :
			host_port_to_string(item->host->name, ipv4_unspecified, item->port);
	} else {
		host = 0 == item->port ? host_addr_to_string(item->addr) :
			host_addr_port_to_string(item->addr, item->port);
	}

	if (is_host_addr(item->addr)) {
		bits = item->bits == addr_default_mask(item->addr) ? 0 : item->bits;
	} else {
		bits = 0;
	}

	g_debug("WLIST %s %s%s%s%s", what,
		item->use_tls ? "tls:" : "", host,
		bits == 0 ? "" : "/",
		bits == 0 ? "" : uint32_to_string(bits));
}
示例#3
0
文件: udp.c 项目: MrJoe/gtk-gnutella
/**
 * Select proper RX layer for semi-reliable UDP traffic.
 *
 * Source address is checked for hostile hosts in order to enforce a
 * total blackout.
 *
 * @param utp		UDP traffic type
 * @param from		source address
 * @param len		length of message, for logging only
 *
 * @return the RX layer if found, NULL if none or the address is hostile.
 */
static rxdrv_t *
udp_get_rx_semi_reliable(enum udp_traffic utp, host_addr_t from, size_t len)
{
	unsigned i = 0;

	if (hostiles_is_bad(from)) {
		if (GNET_PROPERTY(udp_debug)) {
			hostiles_flags_t flags = hostiles_check(from);
			g_warning("UDP got %s (%zu bytes) from hostile %s (%s) -- dropped",
				udp_traffic_to_string(utp), len, host_addr_to_string(from),
				hostiles_flags_to_string(flags));
		}
		gnet_stats_inc_general(GNR_UDP_SR_RX_FROM_HOSTILE_IP);
		return NULL;		/* Ignore message */
	}

	switch (host_addr_net(from)) {
	case NET_TYPE_IPV4:		i = 0; break;
	case NET_TYPE_IPV6:		i = 1; break;
	case NET_TYPE_LOCAL:
	case NET_TYPE_NONE:
		g_assert_not_reached();
	}

	return
		SEMI_RELIABLE_GTA == utp ? rx_sr_gta[i] :
		SEMI_RELIABLE_GND == utp ? rx_sr_gnd[i] :
		NULL;
}
示例#4
0
static void
host_lookup_callback(const gchar *hostname, gpointer key)
{
	const struct nid *node_id = key;
	gnet_node_info_t info;
	struct node_data *data;
	host_addr_t addr;
	guint16 port;

	if (!ht_pending_lookups)
		goto finish;

	if (!remove_item(ht_pending_lookups, node_id))
		goto finish;

	data = find_node(node_id);
	if (!data)
		goto finish;

	guc_node_fill_info(node_id, &info);
	g_assert(node_id == info.node_id);
	
	addr = info.addr;
	port = info.port;
	guc_node_clear_info(&info);

	WFREE_NULL(data->host, data->host_size);
	
	if (hostname) {
		const gchar *host;
		gchar *to_free;

		if (utf8_is_valid_string(hostname)) {
			to_free = NULL;
			host = hostname;
		} else {
			to_free = locale_to_utf8_normalized(hostname, UNI_NORM_GUI);
			host = to_free;
		}
		
		data->host_size = w_concat_strings(&data->host,
							host, " (",
							host_addr_port_to_string(addr, port), ")",
							(void *) 0);

		G_FREE_NULL(to_free);
	} else {
		statusbar_gui_warning(10,
			_("Reverse lookup for %s failed"), host_addr_to_string(addr));
		data->host_size = w_concat_strings(&data->host,
							host_addr_port_to_string(addr, port),
							(void *) 0);
	}

finish:
	nid_unref(node_id);
}
示例#5
0
/**
 * Start a "discovery rpc" sequence.
 *
 * @param np		existing NAT-PMP gateway (NULL if unknown yet)
 * @param retries	amount of retries before timeouting
 * @param cb		callback to invoke on completion / timeout
 * @param arg		user-defined callback argument
 */
static void
natpmp_rpc_discover(natpmp_t *np, unsigned retries,
	natpmp_discover_cb_t cb, void *arg)
{
	struct natpmp_rpc *rd;
	host_addr_t addr;
	pmsg_t *mb;

	if (np != NULL) {
		natpmp_check(np);
		addr = np->gateway;
	} else {
		/*
		 * If we can't determine the default gateway, we can't go much further.
		 * We notify of the discovery failure synchronously.
		 */

		if (0 != getgateway(&addr)) {
			if (GNET_PROPERTY(natpmp_debug))
				g_warning("NATPMP cannot find default gateway");
			(*cb)(FALSE, NULL, arg);
			return;
		} else {
			if (GNET_PROPERTY(natpmp_debug)) {
				g_info("NATPMP gateway is %s", host_addr_to_string(addr));
			}
		}
	}

	/*
	 * Build the discovery request:
	 *
     *    0                   1
     *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *   | Vers = 0      | OP = 0        |
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */

	mb = pmsg_new(PMSG_P_DATA, NULL, 2);
	pmsg_write_u8(mb, NATPMP_VERSION);
	pmsg_write_u8(mb, NATPMP_OP_DISCOVERY);

	/*
	 * Initiate asynchronous iteration discovery.
	 */

	rd = natpmp_rpc_alloc(np, addr, NATPMP_OP_DISCOVERY, mb);
	rd->cb.discovery = cb;
	rd->arg = arg;
	if (retries != 0)
		rd->retries = MIN(retries, rd->retries);

	cq_main_insert(1, natpmp_rpc_iterate, rd);
}
示例#6
0
static int
iprange_net4_collision(const void *p, const void *q)
{
	const struct iprange_net4 *a = p, *b = q;

	g_warning("iprange_sync(): %s/%u overlaps with %s/%u",
		ip_to_string(a->ip), a->bits,
		host_addr_to_string(host_addr_get_ipv4(b->ip)), b->bits);

	return a->bits < b->bits ? 1 : -1;
}
示例#7
0
static inline int
iprange_net_collision(const void *p, const void *q)
{
	const struct iprange_net *a = p, *b = q;

	g_warning("iprange_sync(): %s/0x%x overlaps with %s/0x%x",
		ip_to_string(a->ip), a->mask,
		host_addr_to_string(host_addr_get_ipv4(b->ip)), b->mask);

	return a->mask < b->mask ? 1 : -1;
}
示例#8
0
文件: adns.c 项目: MrJoe/gtk-gnutella
static void
adns_reply_ready(const struct adns_response *ans)
{
	time_t now = tm_time();

	g_assert(ans != NULL);

	if (ans->common.reverse) {
		if (common_dbg > 1) {
			const struct adns_reverse_reply *reply = &ans->reply.reverse;
			
			g_debug("%s: resolved \"%s\" to \"%s\".",
				G_STRFUNC, host_addr_to_string(reply->addr), reply->hostname);
		}
	} else {
		const struct adns_reply *reply = &ans->reply.by_addr;
		size_t num;

		num = count_addrs(reply->addrs, G_N_ELEMENTS(reply->addrs));
		num = MAX(1, num); /* For negative caching */
		
		if (common_dbg > 1) {
			size_t i;
			
			for (i = 0; i < num; i++) {
				g_debug("%s: resolved \"%s\" to \"%s\".", G_STRFUNC,
					reply->hostname, host_addr_to_string(reply->addrs[i]));
			}
		}

		
		if (!adns_cache_lookup(adns_cache, now, reply->hostname, NULL, 0)) {
			adns_cache_add(adns_cache, now, reply->hostname, reply->addrs, num);
		}
	}

	g_assert(ans->common.user_callback);
	adns_invoke_user_callback(ans);
}
示例#9
0
文件: adns.c 项目: MrJoe/gtk-gnutella
/**
 * Copies user_callback and user_data from the query buffer to the
 * reply buffer. This function won't fail. However, if gethostbyname()
 * fails ``reply->addr'' will be set to zero.
 */
static void
adns_gethostbyname(const struct adns_request *req, struct adns_response *ans)
{
	g_assert(NULL != req);
	g_assert(NULL != ans);

	ans->common = req->common;

	if (req->common.reverse) {
		const struct adns_reverse_query *query = &req->query.reverse;
		struct adns_reverse_reply *reply = &ans->reply.reverse;
		const char *host;

		if (common_dbg > 1) {
			g_debug("%s: resolving \"%s\" ...",
					G_STRFUNC, host_addr_to_string(query->addr));
		}

		reply->addr = query->addr;
		host = host_addr_to_name(query->addr);
		clamp_strcpy(reply->hostname, sizeof reply->hostname, host ? host : "");
	} else {
		const struct adns_query *query = &req->query.by_addr;
		struct adns_reply *reply = &ans->reply.by_addr;
		GSList *sl_addr, *sl;
		size_t i = 0;

		if (common_dbg > 1) {
			g_debug("%s: resolving \"%s\" ...", G_STRFUNC, query->hostname);
		}
		clamp_strcpy(reply->hostname, sizeof reply->hostname, query->hostname);

		sl_addr = name_to_host_addr(query->hostname, query->net);
		for (sl = sl_addr; NULL != sl; sl = g_slist_next(sl)) {
			host_addr_t *addr = sl->data;
			g_assert(addr);
			if (i >= G_N_ELEMENTS(reply->addrs)) {
				break;
			}
			reply->addrs[i++] = *addr;
		}
		host_addr_free_list(&sl_addr);

		if (i < G_N_ELEMENTS(reply->addrs)) {
			reply->addrs[i] = zero_host_addr;
		}
	}
}
示例#10
0
/**
 * Redefine callback invoked when we got the whole HTTP reply.
 *
 * @param ha		the HTTP async request descriptor
 * @param s			the socket on which we got the reply
 * @param status	the first HTTP status line
 * @param header	the parsed header structure
 */
static void
gwc_got_reply(const http_async_t *ha,
              const gnutella_socket_t *s, const char *status, const header_t *header)
{
    if (GNET_PROPERTY(bootstrap_debug) > 3)
        g_debug("GWC got reply from %s", http_async_url(ha));

    if (GNET_PROPERTY(bootstrap_debug) > 5) {
        g_debug("----Got GWC reply from %s:",
                host_addr_to_string(s->addr));
        if (log_printable(LOG_STDERR)) {
            fprintf(stderr, "%s\n", status);
            header_dump(stderr, header, "----");
        }
    }
}
示例#11
0
文件: soap.c 项目: Haxe/gtk-gnutella
/**
 * Redefine callback invoked when we got the whole HTTP reply.
 */
static void
soap_got_reply(const http_async_t *ha,
	const struct gnutella_socket *s, const char *status, const header_t *header)
{
	soap_rpc_t *sr = http_async_get_opaque(ha);

	soap_rpc_check(sr);
	
	if (GNET_PROPERTY(soap_trace) & SOCK_TRACE_IN) {
		g_debug("----Got SOAP HTTP reply from %s:",
			host_addr_to_string(s->addr));
		if (log_printable(LOG_STDERR)) {
			fprintf(stderr, "%s\n", status);
			header_dump(stderr, header, "----");
		}
	}
}
示例#12
0
/**
 * RPC timeout callback.
 */
static void
g2_rpc_timeout(cqueue_t *cq, void *obj)
{
	struct g2_rpc *gr = obj;

	g2_rpc_check(gr);

	if (GNET_PROPERTY(g2_rpc_debug) > 1) {
		g_debug("%s(): /%s RPC to %s timed out, calling %s()",
			G_STRFUNC, g2_msg_type_name(gr->key.type),
			host_addr_to_string(gr->key.addr),
			stacktrace_function_name(gr->cb));
	}

	cq_zero(cq, &gr->timeout_ev);
	(*gr->cb)(NULL, NULL, gr->arg);
	g2_rpc_free(gr, FALSE);
}
示例#13
0
/**
 * Called when we get a reply from the ADNS process.
 */
static void
whitelist_dns_cb(const host_addr_t *addrs, size_t n, void *udata)
{
	struct whitelist_dns *ctx = udata;
	struct whitelist *item = ctx->item;

	if (ctx->generation != whitelist_generation) {
		if (GNET_PROPERTY(whitelist_debug))
			log_whitelist_item(item, "late DNS resolution");
		if (!ctx->revalidate) {
			whitelist_free(item);
		}
	} else {
		item->host->last_resolved = tm_time();

		if (n < 1) {
			if (GNET_PROPERTY(whitelist_debug))
				log_whitelist_item(item, "could not DNS-resolve");
			if (ctx->revalidate) {
				item->addr = ipv4_unspecified;
				item->bits = 0;
			} else {
				whitelist_free(item);
			}
		} else {
			item->addr = addrs[random_value(n - 1)];	/* Pick one randomly */
			item->bits = addr_default_mask(item->addr);

			if (GNET_PROPERTY(whitelist_debug) > 1) {
				g_debug("WLIST DNS-resolved %s as %s (out of %zu result%s)",
					item->host->name, host_addr_to_string(item->addr),
					n, plural(n));
			}
			if (!ctx->revalidate) {
				whitelist_add(item);
			}
		}
	}

	WFREE(ctx);
}
示例#14
0
/**
 * Write an IPv4 or IPv6 address.
 */
void
pmsg_write_ipv4_or_ipv6_addr(pmsg_t *mb, host_addr_t addr)
{
	g_assert(pmsg_is_writable(mb));	/* Not shared, or would corrupt data */
	g_assert(pmsg_available(mb) >= 17);

	switch (host_addr_net(addr)) {
	case NET_TYPE_IPV4:
		pmsg_write_u8(mb, 4);
		pmsg_write_be32(mb, host_addr_ipv4(addr));
		break;
	case NET_TYPE_IPV6:
		pmsg_write_u8(mb, 16);
		pmsg_write(mb, host_addr_ipv6(&addr), 16);
		break;
	case NET_TYPE_LOCAL:
	case NET_TYPE_NONE:
		g_error("unexpected address in pmsg_write_ipv4_or_ipv6_addr(): %s",
			host_addr_to_string(addr));
	}
}
示例#15
0
/**
 * @return A pointer to a static buffer holding the host address as string.
 */
const gchar * 
uploads_gui_host_string(const gnet_upload_info_t *u)
{
	static gchar buf[1024];
	const gchar *peer;

	if (u->gnet_port && is_host_addr(u->gnet_addr)) {
		peer = host_addr_port_to_string(u->gnet_addr, u->gnet_port);
	} else {
		peer = NULL;
	}

	concat_strings(buf, sizeof buf,
		host_addr_to_string(u->addr),
		u->encrypted ? " (E) " : " ",
		peer ? " <" : "",
		peer ? peer : "",
		peer ? ">"  : "",
		(void *) 0);

	return buf;
}
示例#16
0
/**
 * Update internal information about the NAT-PMP gateway upon reception
 * of an RPC reply.
 */
static void
natpmp_update(natpmp_t *np, unsigned sssoe)
{
	time_delta_t d;
	unsigned conservative_sssoe;

	natpmp_check(np);

	d = delta_time(tm_time(), np->last_update);
	conservative_sssoe = uint_saturate_add(np->sssoe, 7 * d / 8);

	if (sssoe < conservative_sssoe && conservative_sssoe - sssoe > 1) {
		np->rebooted = TRUE;
		if (GNET_PROPERTY(natpmp_debug) > 1) {
			g_debug("NATPMP new SSSOE=%u < conservative SSSOE=%u, %s rebooted",
				sssoe, conservative_sssoe, host_addr_to_string(np->gateway));
		}
	}

	np->last_update = tm_time();
	np->sssoe = sssoe;
}
示例#17
0
文件: adns.c 项目: lucab/gtk-gnutella
/**
 * Looks for ``hostname'' in ``cache'' wrt to cache->timeout. If
 * ``hostname'' is not found or the entry is expired, FALSE will be
 * returned. Expired entries will be removed! ``addr'' is allowed to
 * be NULL, otherwise the cached IP will be stored into the variable
 * ``addr'' points to.
 *
 * @param addrs An array of host_addr_t items. If not NULL, up to
 *              ``n'' items will be copied from the cache.
 * @param n The number of items "addrs" can hold.
 * @return The number of cached addresses for the given hostname.
 */
static size_t
adns_cache_lookup(adns_cache_t *cache, time_t now,
	const char *hostname, host_addr_t *addrs, size_t n)
{
	adns_cache_entry_t *entry;

	g_assert(NULL != cache);
	g_assert(NULL != hostname);
	g_assert(0 == n || NULL != addrs);

	entry = hikset_lookup(cache->ht, hostname);
	if (entry) {
		if (delta_time(now, entry->timestamp) < cache->timeout) {
			size_t i;

			for (i = 0; i < n; i++) {
				if (i < entry->n) {
					addrs[i] = entry->addrs[i];
					if (common_dbg > 0)
						g_debug("%s: \"%s\" cached (addr=%s)", G_STRFUNC,
							entry->hostname, host_addr_to_string(addrs[i]));
				} else {
					addrs[i] = zero_host_addr;
				}
			}
		} else {
			if (common_dbg > 0) {
				g_debug("%s: removing \"%s\" from cache",
					G_STRFUNC, entry->hostname);
			}

			hikset_remove(cache->ht, hostname);
			adns_cache_free_entry(cache, entry->id);
			entry = NULL;
		}
	}

	return entry ? entry->n : 0;
}
示例#18
0
文件: ghc.c 项目: graaff/gtk-gnutella
/**
 * Called from parse_dispatch_lines() for each complete line of output.
 *
 * @return FALSE to stop processing of any remaining data.
 */
static bool
ghc_host_line(struct parse_context *ctx, const char *buf, size_t len)
{
	if (GNET_PROPERTY(bootstrap_debug) > 2)
		g_debug("BOOT GHC host line #%u (%zu bytes): %s",
			ctx->processed + 1, len, buf);

	if (len) {
		host_addr_t addr;
		uint16 port;

		if (string_to_host_addr_port(buf, NULL, &addr, &port)) {
			ctx->processed++;
			hcache_add_caught(HOST_ULTRA, addr, port, "GHC");

			if (GNET_PROPERTY(bootstrap_debug) > 1)
				g_debug("BOOT collected %s from GHC %s",
					host_addr_to_string(addr), http_async_url(ctx->handle));
		}
	}

	return TRUE;
}
示例#19
0
static void
print_upload_info(struct gnutella_shell *sh,
	const struct gnet_upload_info *info)
{
	char buf[1024];

	g_return_if_fail(sh);
	g_return_if_fail(info);

	str_bprintf(buf, sizeof buf, "%-3.3s %-16.40s %s %s@%s %s%s%s",
		info->encrypted ? "(E)" : "",
		host_addr_to_string(info->addr),
		iso3166_country_cc(info->country),
		compact_size(info->range_end - info->range_start,
			GNET_PROPERTY(display_metric_units)),
		short_size(info->range_start,
			GNET_PROPERTY(display_metric_units)),
		info->name ? "\"" : "<",
		info->name ? info->name : "none",
		info->name ? "\"" : ">");

	shell_write(sh, buf);
	shell_write(sh, "\n");	/* Terminate line */
}
示例#20
0
/**
 * Create a security token from host address and port using specified key.
 *
 * Optionally, extra contextual data may be given (i.e. the token is not
 * only based on the address and port) to make the token more unique to
 * a specific context.
 *
 * @param stg		the security token generator
 * @param n			key index to use
 * @param tok		where security token is written
 * @param addr		address of the host for which we're generating a token
 * @param port		port of the host for which we're generating a token
 * @param data		optional contextual data
 * @param len		length of contextual data
 */
static void
sectoken_generate_n(sectoken_gen_t *stg, size_t n,
	sectoken_t *tok, host_addr_t addr, uint16 port,
	const void *data, size_t len)
{
	char block[8];
	char enc[8];
	char *p = block;

	sectoken_gen_check(stg);
	g_assert(tok != NULL);
	g_assert(size_is_non_negative(n));
	g_assert(n < stg->keycnt);
	g_assert((NULL != data) == (len != 0));

	switch (host_addr_net(addr)) {
	case NET_TYPE_IPV4:
		p = poke_be32(p, host_addr_ipv4(addr));
		break;
	case NET_TYPE_IPV6:
		{
			uint val;

			val = binary_hash(host_addr_ipv6(&addr), 16);
			p = poke_be32(p, val);
		}
		break;
	case NET_TYPE_LOCAL:
	case NET_TYPE_NONE:
		g_error("unexpected address for security token generation: %s",
			host_addr_to_string(addr));
	}

	p = poke_be16(p, port);
	p = poke_be16(p, 0);		/* Filler */

	g_assert(p == &block[8]);

	STATIC_ASSERT(sizeof(tok->v) == sizeof(uint32));
	STATIC_ASSERT(sizeof(block) == sizeof(enc));

	tea_encrypt(&stg->keys[n], enc, block, sizeof block);

	/*
	 * If they gave contextual data, encrypt them by block of 8 bytes,
	 * filling the last partial block with zeroes if needed.
	 */

	if (data != NULL) {
		const void *q = data;
		size_t remain = len;
		char denc[8];

		STATIC_ASSERT(sizeof(denc) == sizeof(enc));

		while (remain != 0) {
			size_t fill = MIN(remain, 8U);
			unsigned i;

			if (fill != 8U)
				ZERO(&block);

			memcpy(block, q, fill);
			remain -= fill;
			q = const_ptr_add_offset(q, fill);

			/*
			 * Encrypt block of contextual data (possibly filled with trailing
			 * zeroes) and merge back the result into the main encryption
			 * output with XOR.
			 */

			tea_encrypt(&stg->keys[n], denc, block, sizeof block);

			for (i = 0; i < sizeof denc; i++)
				enc[i] ^= denc[i];
		}
	}

	poke_be32(tok->v, tea_squeeze(enc, sizeof enc));
}
示例#21
0
/**
 * Callback for adns_resolve(), invoked when the resolution is complete.
 */
static void
uhc_host_resolved(const host_addr_t *addrs, size_t n, void *uu_udata)
{
	(void) uu_udata;
	g_assert(addrs);

	/*
	 * If resolution failed, try again if possible.
	 */

	if (0 == n) {
		if (GNET_PROPERTY(bootstrap_debug))
			g_warning("could not resolve UDP host cache \"%s\"",
				uhc_ctx.host);

		uhc_try_next();
		return;
	}

	if (n > 1) {
		size_t i;
		host_addr_t *hav;
		/* Current UHC was moved to tail by uhc_get_next() */
		struct uhc *uhc = hash_list_tail(uhc_list);

		/*
		 * UHC resolved to multiple endpoints. Could be roundrobbin or
		 * IPv4 and IPv6 addresss. Adding them as seperate entries: if the
		 * IPv6 is unreachable we have an opportunity to skip it.
		 * 		-- JA 24/7/2011
		 *
		 * Shuffle the address array before appending them to the UHC list.
		 *		--RAM, 2015-10-01
		 */

		hav = HCOPY_ARRAY(addrs, n);
		SHUFFLE_ARRAY_N(hav, n);

		for (i = 0; i < n; i++) {
			const char *host = host_addr_port_to_string(hav[i], uhc_ctx.port);
			g_debug("BOOT UDP host cache \"%s\" resolved to %s (#%zu)",
				uhc_ctx.host, host, i + 1);

			uhc_list_append(host);
		}

		hash_list_remove(uhc_list, uhc);	/* Replaced by IP address list */
		uhc_free(&uhc);

		/*
		 * We're going to continue and process the first address (in our
		 * shuffled array).  Make sure it is put at the end of the list
		 * and marked as being used, mimicing what uhc_get_next() would do.
		 *		--RAM, 2015-10-01
		 */

		{
			struct uhc key;

			key.host = host_addr_port_to_string(hav[0], uhc_ctx.port);
			uhc = hash_list_lookup(uhc_list, &key);
			g_assert(uhc != NULL);	/* We added the entry above! */
			uhc->stamp = tm_time();
			uhc->used++;
			hash_list_moveto_tail(uhc_list, uhc);
		}

		uhc_ctx.addr = hav[0];		/* Struct copy */
		HFREE_NULL(hav);
	} else {
		uhc_ctx.addr = addrs[0];
	}

	if (GNET_PROPERTY(bootstrap_debug))
		g_debug("BOOT UDP host cache \"%s\" resolved to %s",
			uhc_ctx.host, host_addr_to_string(uhc_ctx.addr));


	/*
	 * Now send the ping.
	 */

	uhc_send_ping();
}
示例#22
0
文件: udp.c 项目: MrJoe/gtk-gnutella
/**
 * Notification from the socket layer that we got a new datagram.
 *
 * @param s				the receiving socket (with s->addr and s->port set)
 * @param data			start of received data (not necessarily s->buf)
 * @param len			length of received data (not necessarily s->pos)
 * @param truncated		whether received datagram was truncated
 *
 * If `truncated' is true, then the message was too large for the
 * socket buffer.
 */
void
udp_received(const gnutella_socket_t *s,
	const void *data, size_t len, bool truncated)
{
	gnutella_node_t *n;
	bool bogus = FALSE;
	bool dht = FALSE;
	bool rudp = FALSE;

	/*
	 * This must be regular Gnutella / DHT traffic.
	 */

	inet_udp_got_incoming(s->addr);

	/*
	 * We need to identify semi-reliable UDP traffic early, because that
	 * traffic needs to go through the RX stack to reassemble the final
	 * payload out of the many fragments, or to process the acknowledgments.
	 *
	 * We have to apply heuristics however because the leading 8 bytes could
	 * be just a part of gnutella message (the first 8 bytes of a GUID).
	 * One thing is certain though: if the size is less than that of a a
	 * Gnutella header, it has to be semi-reliable UDP traffic...
	 *
	 * Because semi-reliable UDP uses small payloads, much smaller than our
	 * socket buffer, the datagram cannot be truncated.
	 */

	if (!truncated) {
		enum udp_traffic utp;
		rxdrv_t *rx;

		utp = udp_intuit_traffic_type(s, data, len);

		switch (utp) {
		case GNUTELLA:
			goto unreliable;
		case RUDP:
			rudp = TRUE;
			gnet_stats_count_general(GNR_RUDP_RX_BYTES, len);
			goto rudp;		/* Don't account this message in UDP statistics */
		case DHT:
			dht = TRUE;
			goto unreliable;
		case UNKNOWN:
			goto unknown;
		case SEMI_RELIABLE_GTA:
		case SEMI_RELIABLE_GND:
			break;
		}

		/*
		 * We are going to treat this message a a semi-reliable UDP fragment.
		 *
		 * Account the size of the payload for traffic purposes, then redirect
		 * the message to the RX layer that reassembles and dispatches these
		 * messages.
		 */

		bws_udp_count_read(len, FALSE);	/* We know it's not DHT traffic */

		rx = udp_get_rx_semi_reliable(utp, s->addr, len);

		if (rx != NULL) {
			gnet_host_t from;

			gnet_host_set(&from, s->addr, s->port);
			ut_got_message(rx, data, len, &from);
		}

		return;
	}

unknown:
	/*
	 * Discriminate between Gnutella UDP and DHT messages, so that we
	 * can account received data with the proper bandwidth scheduler.
	 */

	if (len >= GTA_HEADER_SIZE)
		dht = GTA_MSG_DHT == gnutella_header_get_function(data);

	/* FALL THROUGH */

unreliable:
	/*
	 * Account for Gnutella / DHT incoming UDP traffic.
	 */

	bws_udp_count_read(len, dht);

	/* FALL THROUGH */

rudp:
	/*
	 * The RUDP layer is used to implement firewalled-to-firewalled transfers
	 * via a mini TCP-like layer built on top of UDP.  Therefore, it is used
	 * as the basis for higher-level connections (HTTP) and will have to be
	 * accounted for once the type of traffic is known, by upper layers, as
	 * part of the upload/download traffic.
	 *
	 * Of course, the higher levels will never see all the bytes that pass
	 * through, such as acknowledgments or retransmissions, but that is also
	 * the case for TCP-based sockets.
	 *		--RAM, 2012-11-02.
	 */

	/*
	 * If we get traffic from a bogus IP (unroutable), warn, for now.
	 */

	if (bogons_check(s->addr)) {
		bogus = TRUE;

		if (GNET_PROPERTY(udp_debug)) {
			g_warning("UDP %sdatagram (%zu byte%s) received from bogus IP %s",
				truncated ? "truncated " : "",
				len, 1 == len ? "" : "s",
				host_addr_to_string(s->addr));
		}
		gnet_stats_inc_general(GNR_UDP_BOGUS_SOURCE_IP);
	}

	/*
	 * Get proper pseudo-node.
	 *
	 * These routines can return NULL if the address/port combination is
	 * not correct, but this will be handled by udp_is_valid_gnet().
	 */

	n = dht ? node_dht_get_addr_port(s->addr, s->port) :
		node_udp_get_addr_port(s->addr, s->port);

	if (!udp_is_valid_gnet(n, s, truncated, data, len))
		return;

	/*
	 * RUDP traffic does not go to the upper Gnutella processing layers.
	 */

	if (rudp) {
		/* Not ready for prime time */
#if 0
		rudp_handle_packet(s->addr, s->port. data, len);
#endif
		return;
	}

	/*
	 * Process message as if it had been received from regular Gnet by
	 * another node, only we'll use a special "pseudo UDP node" as origin.
	 */

	if (GNET_PROPERTY(udp_debug) > 19 || (bogus && GNET_PROPERTY(udp_debug)))
		g_debug("UDP got %s from %s%s", gmsg_infostr_full(data, len),
			bogus ? "BOGUS " : "", host_addr_port_to_string(s->addr, s->port));

	node_udp_process(n, s, data, len);
}
示例#23
0
/**
 * Handle reply to a discovery request.
 *
 * @param payload		the received reply
 * @param len			length of reply
 * @param rd			the RPC request descriptor
 *
 * @return TRUE if we successfully processed the reply and notified the
 * user code about the outcome of the request, FALSE if we need to resend
 * the request.
 */
static bool
natpmp_handle_discovery_reply(
	const void *payload, size_t len, struct natpmp_rpc *rd)
{
	bstr_t *bs;
	uint8 version;
	uint8 code;
	uint16 result;
	uint32 ip;
	host_addr_t wan_ip;
	natpmp_t *np;

	natpmp_rpc_check(rd);

	/**
	 * A NAT gateway will reply with the following message:
	 *
     *    0                   1                   2                   3
     *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *   | Vers = 0      | OP = 128 + 0  | Result Code                   |
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *   | Seconds Since Start of Epoch                                  |
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *   | External IP Address (a.b.c.d)                                 |
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *
	 * The first 32-bits are always present, the remaining of the packet
	 * may or may not be there depending on the result code.
	 */

	bs = bstr_open(payload, len,
			GNET_PROPERTY(natpmp_debug) ? BSTR_F_ERROR : 0);

	/*
	 * Make sure we got a valid reply.
	 */

	bstr_read_u8(bs, &version);
	bstr_read_u8(bs, &code);
	bstr_read_be16(bs, &result);

	if (bstr_has_error(bs))
		goto error;

	if (GNET_PROPERTY(natpmp_debug) > 5) {
		g_debug("NATPMP version=%u, code=%u, result_code=%u (%s)",
			version, code, result, natpmp_strerror(result));
	}

	if (version != NATPMP_VERSION || code != NATPMP_REPLY_OFF + rd->op)
		goto error;

	if (NATPMP_E_OK != result)
		goto failed;

	bstr_read_be32(bs, &rd->sssoe);
	bstr_read_be32(bs, &ip);

	if (bstr_has_error(bs))
		goto error;

	wan_ip = host_addr_get_ipv4(ip);

	if (GNET_PROPERTY(natpmp_debug) > 5) {
		g_debug("NATPMP SSSOE=%u, WAN IP is %s",
			rd->sssoe, host_addr_to_string(wan_ip));
	}

	if (!host_addr_is_routable(wan_ip))
		goto failed;

	/*
	 * Good, we got a valid reply from the gateway, with a routable WAN IP.
	 */

	if (rd->np != NULL) {
		natpmp_check(rd->np);
		np = rd->np;
		natpmp_update(np, rd->sssoe);
		np->wan_ip = wan_ip;
	} else {
		np = natpmp_alloc(rd->gateway, rd->sssoe, wan_ip);
	}

	(*rd->cb.discovery)(TRUE, np, rd->arg);

	bstr_free(&bs);
	return TRUE;		/* OK */

failed:
	if (GNET_PROPERTY(natpmp_debug))
		g_warning("NATPMP did not find any suitable NAT-PMP gateway");

	(*rd->cb.discovery)(FALSE, rd->np, rd->arg);
	return TRUE;		/* We're done for now */

error:
	if (GNET_PROPERTY(natpmp_debug)) {
		if (bstr_has_error(bs)) {
			g_warning("NATPMP parsing error while processing discovery reply "
				"(%zu byte%s): %s",
				len, 1 == len ? "" : "s", bstr_error(bs));
		} else {
			g_warning("NATPMP inconsistent discovery reply (%zu byte%s)",
				len, 1 == len ? "" : "s");
		}
	}
	bstr_free(&bs);
	return FALSE;
}
示例#24
0
/**
 * Displays assorted status information
 */
enum shell_reply
shell_exec_status(struct gnutella_shell *sh, int argc, const char *argv[])
{
	const char *cur;
	const option_t options[] = {
		{ "i", &cur },
	};
	int parsed;
	char buf[2048];
	time_t now;

	shell_check(sh);
	g_assert(argv);
	g_assert(argc > 0);

	parsed = shell_options_parse(sh, argv, options, G_N_ELEMENTS(options));
	if (parsed < 0)
		return REPLY_ERROR;

	argv += parsed;	/* args[0] is first command argument */
	argc -= parsed;	/* counts only command arguments now */

	now = tm_time();

	/* Leading flags */
	{
		char flags[47];
		const char *fw;
		const char *fd;
		const char *pmp;
		const char *dht;

		/*
		 * The flags are displayed as followed:
		 *
		 * UMP          port mapping configured via UPnP
		 * NMP          port mapping configured via NAT-PMP
		 * pmp          port mapping available (UPnP or NAT-PMP), un-configured
		 * CLK			clock, GTKG expired
		 * !FD or FD	red or yellow bombs for fd shortage
		 * STL			upload stalls
		 * gUL/yUL/rUL  green, yellow or red upload early stalling levels
		 * CPU			cpu overloaded
		 * MOV			file moving
		 * SHA			SHA-1 rebuilding or verifying
		 * TTH			TTH rebuilding or verifying
		 * LIB			library rescan
		 * :FW or FW	indicates whether hole punching is possible
		 * udp or UDP	indicates UDP firewalling (lowercased for hole punching)
		 * TCP			indicates TCP-firewalled
		 * -			the happy face: no firewall
		 * sDH/lDH/bDH  seeded, own KUID looking or bootstrapping DHT
		 * A or P       active or passive DHT mode
		 * UP or LF		ultrapeer or leaf mode
		 */

		pmp = (GNET_PROPERTY(upnp_possible) || GNET_PROPERTY(natpmp_possible))
			? "pmp " : empty;
		if (
			(GNET_PROPERTY(enable_upnp) || GNET_PROPERTY(enable_natpmp)) &&
			GNET_PROPERTY(port_mapping_successful)
		) {
			pmp = GNET_PROPERTY(enable_natpmp) ? "NMP " : "UMP ";
		}

		if (dht_enabled()) {
			dht = empty;
			switch ((enum dht_bootsteps) GNET_PROPERTY(dht_boot_status)) {
			case DHT_BOOT_NONE:
			case DHT_BOOT_SHUTDOWN:
				break;
			case DHT_BOOT_SEEDED:
				dht = "sDH ";
				break;
			case DHT_BOOT_OWN:
				dht = "lDH ";
				break;
			case DHT_BOOT_COMPLETING:
				dht = "bDH ";
				break;
			case DHT_BOOT_COMPLETED:
				dht = dht_is_active() ? "A " : "P ";
				break;
			case DHT_BOOT_MAX_VALUE:
				g_assert_not_reached();
			}
		} else {
			dht = empty;
		}

		if (GNET_PROPERTY(is_firewalled) && GNET_PROPERTY(is_udp_firewalled)) {
			fw = GNET_PROPERTY(recv_solicited_udp) ? ":FW " : "FW ";
		} else if (GNET_PROPERTY(is_firewalled)) {
			fw = "TCP ";
		} else if (GNET_PROPERTY(is_udp_firewalled)) {
			fw = GNET_PROPERTY(recv_solicited_udp) ? "udp " : "UDP ";
		} else {
			fw = "- ";
		}

		if (GNET_PROPERTY(file_descriptor_runout)) {
			fd = "!FD ";
		} else if (GNET_PROPERTY(file_descriptor_shortage)) {
			fd = "FD ";
		} else {
			fd = empty;
		}

		gm_snprintf(flags, sizeof flags,
			"<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>",
			pmp,
			GNET_PROPERTY(download_queue_frozen) ? "DFZ " : empty,
			GNET_PROPERTY(ancient_version) ? "CLK " : empty,
			fd,
			GNET_PROPERTY(uploads_stalling) ? "STL " : empty,
			GNET_PROPERTY(uploads_bw_ignore_stolen) ? "gUL " : empty,
			GNET_PROPERTY(uploads_bw_uniform) ? "yUL " : empty,
			GNET_PROPERTY(uploads_bw_no_stealing) ? "rUL " : empty,
			GNET_PROPERTY(overloaded_cpu) ? "CPU " : empty,
			GNET_PROPERTY(file_moving) ? "MOV " : empty,
			(GNET_PROPERTY(sha1_rebuilding) || GNET_PROPERTY(sha1_verifying)) ?
				"SHA " : empty,
			(GNET_PROPERTY(tth_rebuilding) || GNET_PROPERTY(tth_verifying)) ?
				"TTH " : empty,
			GNET_PROPERTY(library_rebuilding) ? "LIB " : empty,
			fw, dht,
			settings_is_ultra() ? "UP" : "LF");

		gm_snprintf(buf, sizeof buf,
			"+%s+\n"
			"| %-18s%51s |\n"
			"|%s|\n",
			dashes, "Status", flags, equals);
		shell_write(sh, buf);
	}

	/* General status */ 
	{
		const char *blackout;
		short_string_t leaf_switch;
		short_string_t ultra_check;
	
		leaf_switch = timestamp_get_string(
						GNET_PROPERTY(node_last_ultra_leaf_switch));
		ultra_check = timestamp_get_string(
						GNET_PROPERTY(node_last_ultra_check));

		if (GNET_PROPERTY(is_firewalled) && GNET_PROPERTY(is_udp_firewalled)) {
			blackout =
				GNET_PROPERTY(recv_solicited_udp) ?  "TCP,udp" : "TCP,UDP";
		} else if (GNET_PROPERTY(is_firewalled)) {
			blackout = "TCP";
		} else if (GNET_PROPERTY(is_udp_firewalled)) {
			blackout = GNET_PROPERTY(recv_solicited_udp) ? "udp" : "UDP";
		} else {
			blackout = "None";
		}

		gm_snprintf(buf, sizeof buf,
			"|   Mode: %-9s                   Last Switch: %-19s%2s|\n"
			"| Uptime: %-9s                    Last Check: %-19s%2s|\n"
			"|   Port: %-9u                      Blackout: %-7s%14s|\n"
			"|%s|\n",
			GNET_PROPERTY(online_mode)
				? node_peermode_to_string(GNET_PROPERTY(current_peermode))
				: "offline",
			GNET_PROPERTY(node_last_ultra_leaf_switch)
				? leaf_switch.str : "never", space,
			short_time(delta_time(now, GNET_PROPERTY(start_stamp))),
			GNET_PROPERTY(node_last_ultra_check)
				? ultra_check.str : "never", space,
			socket_listen_port(), blackout, space,
			equals);
		shell_write(sh, buf);
	}

	/* IPv4 info */ 
	switch (GNET_PROPERTY(network_protocol)) {
	case NET_USE_BOTH:
	case NET_USE_IPV4:
		gm_snprintf(buf, sizeof buf,
			"| IPv4: %-44s Since: %-12s|\n",
			host_addr_to_string(listen_addr()),
			short_time(delta_time(now, GNET_PROPERTY(current_ip_stamp))));
		shell_write(sh, buf);
	}

	/* IPv6 info */ 
	switch (GNET_PROPERTY(network_protocol)) {
	case NET_USE_BOTH:
		gm_snprintf(buf, sizeof buf, "|%s|\n", dashes);
		shell_write(sh, buf);
		/* FALL THROUGH */
	case NET_USE_IPV6:
		gm_snprintf(buf, sizeof buf,
			"| IPv6: %-44s Since: %-12s|\n",
			host_addr_to_string(listen_addr6()),
			short_time(delta_time(now, GNET_PROPERTY(current_ip6_stamp))));
		shell_write(sh, buf);
	}

	/* Node counts */
	gm_snprintf(buf, sizeof buf,
		"|%s|\n"
		"| Peers: %-7u Ultra %4u/%-7u  Leaf %4u/%-6u  Legacy %4u/%-4u |\n"
		"|            Downloads %4u/%-4u  Uploads %4u/%-7u Browse %4u/%-4u |\n"
		"|%s|\n",
		equals,
		GNET_PROPERTY(node_ultra_count)
			+ GNET_PROPERTY(node_leaf_count)
			+ GNET_PROPERTY(node_normal_count),
		GNET_PROPERTY(node_ultra_count),
		settings_is_ultra() ?
			GNET_PROPERTY(max_connections) : GNET_PROPERTY(max_ultrapeers),
		GNET_PROPERTY(node_leaf_count),
		GNET_PROPERTY(max_leaves),
		GNET_PROPERTY(node_normal_count),
		GNET_PROPERTY(normal_connections),
		GNET_PROPERTY(dl_active_count), GNET_PROPERTY(dl_running_count),
		GNET_PROPERTY(ul_running), GNET_PROPERTY(ul_registered),
		GNET_PROPERTY(html_browse_served) + GNET_PROPERTY(qhits_browse_served),
		GNET_PROPERTY(html_browse_count) + GNET_PROPERTY(qhits_browse_count),
		equals);
	shell_write(sh, buf);

	/* Bandwidths */
	{	
		const bool metric = GNET_PROPERTY(display_metric_units);
		short_string_t gnet_in, http_in, leaf_in, gnet_out, http_out, leaf_out;
		short_string_t dht_in, dht_out;
		gnet_bw_stats_t bw_stats, bw2_stats;
		const char *bwtype = cur ? "(cur)" : "(avg)";

		gnet_get_bw_stats(BW_GNET_IN, &bw_stats);
		gnet_get_bw_stats(BW_GNET_UDP_IN, &bw2_stats);
		gnet_in = short_rate_get_string(
			cur ? bw_stats.current + bw2_stats.current
				: bw_stats.average + bw2_stats.average, metric);

		gnet_get_bw_stats(BW_GNET_OUT, &bw_stats);
		gnet_get_bw_stats(BW_GNET_UDP_OUT, &bw2_stats);
		gnet_out = short_rate_get_string(
			cur ? bw_stats.current + bw2_stats.current
				: bw_stats.average + bw2_stats.average, metric);
		
		gnet_get_bw_stats(BW_HTTP_IN, &bw_stats);
		http_in = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);
		
		gnet_get_bw_stats(BW_HTTP_OUT, &bw_stats);
		http_out = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);
		
		gnet_get_bw_stats(BW_LEAF_IN, &bw_stats);
		leaf_in = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);

		gnet_get_bw_stats(BW_LEAF_OUT, &bw_stats);
		leaf_out = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);

		gnet_get_bw_stats(BW_DHT_IN, &bw_stats);
		dht_in = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);

		gnet_get_bw_stats(BW_DHT_OUT, &bw_stats);
		dht_out = short_rate_get_string(
			cur ? bw_stats.current : bw_stats.average, metric);

		gm_snprintf(buf, sizeof buf,
			"| %-70s|\n"
			"|%71s|\n"
			"| %5s  In:  %13s %13s %13s %13s   |\n"
			"| %5s Out:  %13s %13s %13s %13s   |\n",
			"Bandwidth:"
				"       Gnutella          Leaf          HTTP           DHT",
			dashes,
			bwtype, gnet_in.str, leaf_in.str, http_in.str, dht_in.str,
			bwtype, gnet_out.str, leaf_out.str, http_out.str, dht_out.str);
		shell_write(sh, buf);
	}
	
	{
		char line[128];
		bool metric = GNET_PROPERTY(display_metric_units);

		gm_snprintf(buf, sizeof buf, "|%s|\n", equals);
		shell_write(sh, buf);
		concat_strings(line, sizeof line,
			"Shares ",
			uint64_to_string(shared_files_scanned()),
			" file",
			shared_files_scanned() == 1 ? "" : "s",
			" ",
			short_kb_size(shared_kbytes_scanned(), metric),
			" total",
			(void *) 0);
		gm_snprintf(buf, sizeof buf,
			"| %-35s Up: %-11s Down: %-11s |\n",
			line,
			short_byte_size(GNET_PROPERTY(ul_byte_count), metric),
			short_byte_size2(GNET_PROPERTY(dl_byte_count), metric));
		shell_write(sh, buf);
		gm_snprintf(buf, sizeof buf, "+%s+\n", dashes);
		shell_write(sh, buf);
	}

	return REPLY_READY;
}
示例#25
0
/**
 * UDP RPC reply (or timeout) callback.
 */
static void
natpmp_rpc_reply(enum urpc_ret type, host_addr_t addr, uint16 port,
	const void *payload, size_t len, void *arg)
{
	struct natpmp_rpc *rd = arg;

	natpmp_rpc_check(rd);

	if (GNET_PROPERTY(natpmp_debug) > 4) {
		g_debug("NATPMP %s for \"%s\" #%u (%lu byte%s) from %s",
			URPC_TIMEOUT == type ? "timeout" : "got reply",
			natpmp_op_to_string(rd->op), rd->count,
			(unsigned long) len, 1 == len ? "" : "s",
			host_addr_port_to_string(addr, port));
	}

	if (URPC_TIMEOUT == type)
		goto iterate;

	/*
	 * Silently discard a reply not coming from the host to whom we
	 * sent the RPC.
	 */

	if (!host_addr_equal(addr, rd->gateway)) {
		if (GNET_PROPERTY(natpmp_debug)) {
			g_warning("NATPMP discarding reply from %s (sent %s to %s)",
				host_addr_port_to_string(addr, port),
				natpmp_op_to_string(rd->op),
				host_addr_to_string(rd->gateway));
		}
		goto iterate;
	}

	/*
	 * Dispatch reply processing.
	 */

	switch (rd->op) {
	case NATPMP_OP_DISCOVERY:
		if (!natpmp_handle_discovery_reply(payload, len, rd))
			goto iterate;
		break;
	case NATPMP_OP_MAP_TCP:
	case NATPMP_OP_MAP_UDP:
		if (!natpmp_handle_mapping_reply(payload, len, rd))
			goto iterate;
		break;
	case NATPMP_OP_INVALID:
		g_assert_not_reached();
	}

	/*
	 * All done, request was successful.
	 */

	natpmp_rpc_free(rd);
	return;

iterate:
	natpmp_rpc_iterate(NULL, rd);
}
示例#26
0
/**
 * Notification that a message was received that could be the answer to
 * a pending RPC.
 *
 * @param n		the node from which we got the message
 * @param t		the received message tree
 *
 * @return TRUE if the message was indeed an RPC reply, FALSE otherwise.
 */
bool
g2_rpc_answer(const gnutella_node_t *n, const g2_tree_t *t)
{
	struct g2_rpc *gr;
	struct g2_rpc_key key;
	enum g2_msg type;

	type = g2_msg_name_type(g2_tree_name(t));

	key.type = g2_rpc_send_type(type);
	key.addr = n->addr;

	gr = hevset_lookup(g2_rpc_pending, &key);

	if (NULL == gr) {
		/*
		 * No known RPC, but wait... we can receive a /QKA when we issue a /Q2
		 * and the query key we knew for the remote host has expired, hence
		 * we must look whether there is not a /Q2 pending as well in that
		 * case.  Once again, the lack of MUID in these messages is a handicap.
		 */

		if (G2_MSG_QKA == type) {
			key.type = G2_MSG_Q2;
			gr = hevset_lookup(g2_rpc_pending, &key);
			if (gr != NULL)
				goto found;		/* Sent a /Q2, got a /QKA back */
		}

		if (GNET_PROPERTY(g2_rpc_debug) > 1) {
			g_debug("%s(): unexpected /%s RPC reply from %s",
				G_STRFUNC, g2_msg_type_name(key.type), node_infostr(n));
		}

		return FALSE;
	}

found:

	/*
	 * Got a reply for an RPC we sent, based solely on message type and
	 * source address of the message.  This is weak, but G2 works like that.
	 *
	 * Weakness comes from the fact that we cannot have multiple RPCs with
	 * a same IP address but towards different ports, nor have concurrent
	 * RPCs with the same host for several different by similar requests
	 * (although this can be viewed as anti-hammering, servers should protect
	 * against that in different ways, a crippled protocol not being an answer).
	 *
	 * The only transaction where we could use the MUID is /Q2 -> /QA but we
	 * leave that check to the GUESS layer and make sure here that we have only
	 * one single RPC transaction at a time with a given IP address.
	 */

	if (GNET_PROPERTY(g2_rpc_debug) > 2) {
		g_debug("%s(): /%s RPC to %s got a /%s reply, calling %s()",
			G_STRFUNC, g2_msg_type_name(gr->key.type),
			host_addr_to_string(gr->key.addr), g2_tree_name(t),
			stacktrace_function_name(gr->cb));
	}

	(*gr->cb)(n, t, gr->arg);
	g2_rpc_free(gr, FALSE);

	return TRUE;
}
示例#27
0
/**
 * Loads the whitelist into memory.
 */
static void G_COLD
whitelist_retrieve(void)
{
	char line[1024];
	FILE *f;
	filestat_t st;
	unsigned linenum = 0;
	file_path_t fp[1];

	whitelist_generation++;

	file_path_set(fp, settings_config_dir(), whitelist_file);
	f = file_config_open_read_norename("Host Whitelist", fp, N_ITEMS(fp));
	if (!f)
		return;

	if (fstat(fileno(f), &st)) {
		g_warning("%s(): fstat() failed: %m", G_STRFUNC);
		fclose(f);
		return;
	}

    while (fgets(line, sizeof line, f)) {
		pslist_t *sl_addr, *sl;
		const char *endptr, *start;
		host_addr_t addr;
    	uint16 port;
		uint8 bits;
		bool item_ok;
		bool use_tls;
		char *hname;

        linenum++;

		if (!file_line_chomp_tail(line, sizeof line, NULL)) {
			g_warning("%s(): line %u too long, aborting", G_STRFUNC, linenum);
			break;
		}

        if (file_line_is_skipable(line))
			continue;

		sl_addr = NULL;
		addr = zero_host_addr;
		endptr = NULL;
		hname = NULL;

		endptr = is_strprefix(line, "tls:");
		if (endptr) {
			use_tls = TRUE;
			start = endptr;
		} else {
			use_tls = FALSE;
			start = line;
		}

		port = 0;
		if (string_to_host_addr_port(start, &endptr, &addr, &port)) {
       		sl_addr = name_to_host_addr(host_addr_to_string(addr),
							settings_dns_net());
		} else if (string_to_host_or_addr(start, &endptr, &addr)) {
			uchar c = *endptr;

			switch (c) {
			case '\0':
			case ':':
			case '/':
				break;
			default:
				if (!is_ascii_space(c))
					endptr = NULL;
			}

			if (!endptr) {
				g_warning("%s(): line %d: "
					"expected a hostname or IP address \"%s\"",
					G_STRFUNC, linenum, line);
				continue;
			}

			/* Terminate the string for name_to_host_addr() */
			hname = h_strndup(start, endptr - start);
		} else {
            g_warning("%s(): line %d: expected hostname or IP address \"%s\"",
				G_STRFUNC, linenum, line);
			continue;
		}

       	g_assert(sl_addr != NULL || hname != NULL);
		g_assert(NULL != endptr);
		bits = 0;
		item_ok = TRUE;

		/*
		 * When an explicit address is given (no hostname) and with no
		 * port, one can suffix the address with bits to indicate a CIDR
		 * range of whitelisted addresses.
		 */

		if (0 == port) {
			/* Ignore trailing items separated by a space */
			while ('\0' != *endptr && !is_ascii_space(*endptr)) {
				uchar c = *endptr++;

				if (':' == c) {
					int error;
					uint32 v;

					if (0 != port) {
						g_warning("%s(): line %d: multiple colons after host",
							G_STRFUNC, linenum);
						item_ok = FALSE;
						break;
					}

					v = parse_uint32(endptr, &endptr, 10, &error);
					port = (error || v > 0xffff) ? 0 : v;
					if (0 == port) {
						g_warning("%s(): line %d: "
							"invalid port value after host",
							G_STRFUNC, linenum);
						item_ok = FALSE;
						break;
					}
				} else if ('/' == c) {
					const char *ep;
					uint32 mask;

					if (0 != bits) {
						g_warning("%s(): line %d: "
							"multiple slashes after host", G_STRFUNC, linenum);
						item_ok = FALSE;
						break;
					}

					if (string_to_ip_strict(endptr, &mask, &ep)) {
						if (!host_addr_is_ipv4(addr)) {
							g_warning("%s(): line %d: "
								"IPv4 netmask after non-IPv4 address",
								G_STRFUNC, linenum);
							item_ok = FALSE;
							break;
						}
						endptr = ep;

						if (0 == (bits = netmask_to_cidr(mask))) {
							g_warning("%s(): line %d: "
								"IPv4 netmask after non-IPv4 address",
								G_STRFUNC, linenum);
							item_ok = FALSE;
							break;
						}

					} else {
						int error;
						uint32 v;

						v = parse_uint32(endptr, &endptr, 10, &error);
						if (
							error ||
							0 == v ||
							(v > 32 && host_addr_is_ipv4(addr)) ||
							(v > 128 && host_addr_is_ipv6(addr))
						) {
							g_warning("%s(): line %d: "
								"invalid numeric netmask after host",
								G_STRFUNC, linenum);
							item_ok = FALSE;
							break;
						}
						bits = v;
					}
				} else {
					g_warning("%s(): line %d: "
						"unexpected character after host", G_STRFUNC, linenum);
					item_ok = FALSE;
					break;
				}
			}
		}

		if (item_ok) {
			struct whitelist *item;
			if (hname) {
				item = whitelist_hostname_create(use_tls, hname, port);
				whitelist_dns_resolve(item, FALSE);
			} else {
				PSLIST_FOREACH(sl_addr, sl) {
					host_addr_t *aptr = sl->data;
					g_assert(aptr != NULL);
					item = whitelist_addr_create(use_tls, *aptr, port, bits);
					whitelist_add(item);
				}
			}
		} else {
示例#28
0
/**
 * Check version of servent, and if it's a gtk-gnutella more recent than we
 * are, record that fact and change the status bar.
 *
 * The `addr' is being passed solely for the tok_version_valid() call.
 *
 * @returns TRUE if we properly checked the version, FALSE if we got something
 * looking as gtk-gnutella but which failed the token-based sanity checks.
 */
bool
version_check(const char *str, const char *token, const host_addr_t addr)
{
	version_t their_version;
	version_ext_t their_version_ext;
	version_t *target_version;
	int cmp;
	const char *version;
	const char *end;
	bool extended;

	if (!version_parse(str, &their_version, &end))
		return TRUE;			/* Not gtk-gnutella, or unparseable */

	/*
	 * Check for extended version information (git commit ID, dirty status).
	 */

	ZERO(&their_version_ext);
	extended = version_ext_parse(end, &their_version_ext);
	if (!extended) {
		/* Structure could have been partially filled */
		ZERO(&their_version_ext);
	}

	/*
	 * Is their version a development one, or a release?
	 */

	if (their_version.tag == 'u')
		target_version = &last_dev_version;
	else
		target_version = &last_rel_version;

	cmp = version_cmp(target_version, &their_version);

	if (GNET_PROPERTY(version_debug) > 1)
		version_dump(str, &their_version,
			cmp == 0 ? "=" :
			cmp > 0 ? "-" : "+");

	/*
	 * Check timestamp.
	 */

	version_stamp(str, &their_version);
	their_version_ext.version = their_version;		/* Struct copy */

	if (GNET_PROPERTY(version_debug) > 3)
		g_debug("VERSION time=%d", (int) their_version.timestamp);

	/*
	 * If version claims something older than TOKEN_START_DATE, then
	 * there must be a token present.
	 */

	if (delta_time(their_version.timestamp, 0) >= TOKEN_START_DATE) {
		tok_error_t error;

		if (token == NULL) {
            if (GNET_PROPERTY(version_debug)) {
                g_debug("got GTKG vendor string \"%s\" without token!", str);
            }
			return FALSE;	/* Can't be correct */
		}

		error = tok_version_valid(str, token, strlen(token), addr);

		/*
		 * Unfortunately, if our token has expired, we can no longer
		 * validate the tokens of the remote peers, since they are using
		 * a different set of keys.
		 *
		 * This means an expired GTKG will blindly trust well-formed remote
		 * tokens at face value.  But it's their fault, they should not run
		 * an expired version!
		 *		--RAM, 2005-12-21
		 */

		if (error == TOK_BAD_KEYS && tok_is_ancient(tm_time()))
			error = TOK_OK;		/* Our keys have expired, cannot validate */

		if (error != TOK_OK) {
            if (GNET_PROPERTY(version_debug)) {
                g_debug("vendor string \"%s\" [%s] has wrong token "
                    "\"%s\": %s ", str, host_addr_to_string(addr), token,
                    tok_strerror(error));
            }
			return FALSE;
		}

		/*
		 * OK, so now we know we can "trust" this version string as being
		 * probably genuine.  It makes sense to extract version information
		 * out of it.
		 */
	}

	if (cmp > 0)			/* We're more recent */
		return TRUE;

	/*
	 * If timestamp is greater and we were comparing against a stable
	 * release, and cmp == 0, then this means an update in SVN about
	 * a "released" version, probably alpha/beta.
	 */

	if (
		cmp == 0 &&
		(delta_time(their_version.timestamp, target_version->timestamp) > 0
			|| their_version.build > target_version->build) &&
		target_version == &last_rel_version
	) {
		if (GNET_PROPERTY(version_debug) > 3)
			g_debug("VERSION is a SVN update of a release");

		if (version_build_cmp(&last_dev_version, &their_version) > 0) {
			if (GNET_PROPERTY(version_debug) > 3)
				g_debug("VERSION is less recent than latest dev we know");
			return TRUE;
		}
		target_version = &last_dev_version;
	}

	/*
	 * Their version is more recent, but is unstable -- only continue if
	 * our version is also unstable.
	 */

	if (cmp < 0 && their_version.tag == 'u' && our_version.tag != 'u')
		return TRUE;

	if (
		delta_time(their_version.timestamp, target_version->timestamp) < 0 ||
		their_version.build <= target_version->build
	)
		return TRUE;

	if (
		delta_time(their_version.timestamp, our_version.timestamp) == 0 &&
		their_version.build <= our_version.build
	)
		return TRUE;

	/*
	 * We found a more recent version than the last version seen.
	 */

	if (GNET_PROPERTY(version_debug) > 1)
		g_debug("more recent %s VERSION \"%s\"",
			target_version == &last_dev_version ? "dev" : "rel", str);

	*target_version = their_version;		/* struct copy */

	/*
	 * Signal new version to user.
	 *
	 * Unless they run a development version, don't signal development
	 * updates to them: they're probably not interested.
	 */

	version =  version_ext_str(&their_version_ext, FALSE);	/* No OS name */

	g_message("more recent %s version of gtk-gnutella: %s",
		target_version == &last_dev_version ? "development" : "released",
		version);

	if (target_version == &last_rel_version)
		version_new_found(version, TRUE);
	else if (our_version.tag == 'u')
		version_new_found(version, FALSE);

	return TRUE;
}