예제 #1
0
파일: keys.c 프로젝트: lucab/gtk-gnutella
/**
 * DBMW foreach iterator to reload keyinfo.
 *
 * @return TRUE if persisted entry can be deleted.
 */
static G_GNUC_COLD bool
reload_ki(void *key, void *value, size_t u_len, void *data)
{
	struct keys_create_context *ctx = data;
	const struct keydata *kd = value;
	const kuid_t *id = key;
	struct keyinfo *ki;
	size_t common;
	time_t next_expire = TIME_T_MAX, last_expire = 0, now = tm_time();
	unsigned i;

	(void) u_len;

	if (0 == kd->values)
		return TRUE;

	/*
	 * Check whether key has expired: if all the values associated with it
	 * have expired, there will be no need to recreate the keyinfo and
	 * retrieve the associated value data.
	 */

	for (i = 0; i < kd->values; i++) {
		last_expire = MAX(last_expire, kd->expire[i]);
	}

	if (delta_time(now, last_expire) >= 0)
		return TRUE;

	/*
	 * We're going to keep at least one of the values, prepare keyinfo.
	 */

	common = kuid_common_prefix(ctx->our_kuid, id);
	ki = allocate_keyinfo(id, common);
	hikset_insert_key(keys, &ki->kuid);

	/*
	 * Values will be inserted later, just prepare the DB keys we need to
	 * load to restore them.
	 */

	for (i = 0; i < kd->values; i++) {
		uint64 dbkey = kd->dbkeys[i];

		if (delta_time(now, kd->expire[i]) >= 0)
			continue;		/* This value has already expired */

		next_expire = MIN(next_expire, kd->expire[i]);

		if (!hset_contains(ctx->dbkeys, &dbkey)) {
			const uint64 *dbatom = atom_uint64_get(&dbkey);
			hset_insert(ctx->dbkeys, dbatom);
		}
	}

	ki->next_expire = next_expire;

	return FALSE;		/* Keep keydata */
}
예제 #2
0
static int subscribe_channel(sub_client *c, sds channel)
{
    TrieData data;
    size_t len;
    AlphaChar *chan_alpha;

    len = sdslen(channel);
    chan_alpha  = (AlphaChar *) malloc(sizeof(AlphaChar) * len + 1);

    conv_to_alpha(server.dflt_to_alpha_conv, channel, chan_alpha, len+1);
    /* channel not exists in trie, create */
    if (!trie_retrieve(server.sub_trie, chan_alpha, &data)) {
        if (!trie_store(server.sub_trie, chan_alpha, TRIE_DATA_DFLT)) {
            srv_log(LOG_ERROR, "Failed to insert key %s into sub trie", channel);
            return SUBCLI_ERR;
        }
        /* create hashtable mapping from subscribe-channel to client id set */
        hset *hs = hset_create(SUB_SET_LEN);
        hset_insert(hs, CLIENT_ID_LEN, c->id, c);
        if (ght_insert(server.subscibe_table, hs, len, channel) == -1) {
            hset_release(hs);
            srv_log(LOG_ERROR,
                    "Failed to insert key[%s]->set into subscribe hashtable",
                    channel);
            return SUBCLI_ERR;
        }
    }

    return SUBCLI_OK;
}
예제 #3
0
/*
 * If we detect that stdio cannot handle file descriptors above 255, this
 * functions tries to reassign 'fd' to a file descriptor above 255 in order to
 * reserve lower file descriptors for stdio. File descriptors below 3 or above
 * 255 are returned as-is. The original file descriptor is closed if it was
 * reassigned. On systems which do not need this workaround, the original
 * file descriptor is returned.
 *
 * @note The FD_CLOEXEC flag set will be cleared on the new file descriptor if
 *		 the file descriptor is successfully reassigned.
 *
 * @return	On success a new file descriptor above 255 is returned.
 *         	On failure or if reassigning was not necessary the original file
 *			descriptor is returned.
 */
int
fd_get_non_stdio(int fd)
{
	if (fd_need_non_stdio() && fd > 2 && fd < 256) {
		int nfd, saved_errno;

		saved_errno = errno;
		nfd = fcntl(fd, F_DUPFD, 256);
		if (nfd > 0) {
			close(fd);
			compat_socket_duped(fd, nfd);
			fd = nfd;
		}
		errno = saved_errno;
	}

	/*
	 * On Windows, files and sockets do not share the same ID space.
	 * Therefore, on that platform we use this routine as a hook to
	 * record active socket descriptors in a table.
	 *		--RAM, 2015-04-05
	 */

	if (is_running_on_mingw()) {
		/* We don't expect thread race conditions here */
		if G_UNLIKELY(NULL == fd_sockets)
			fd_sockets = hset_create(HASH_KEY_SELF, 0);

		if (is_a_socket(fd))
			hset_insert(fd_sockets, int_to_pointer(fd));
	}

	return fd;
}
예제 #4
0
파일: nodes.c 프로젝트: lucab/gtk-gnutella
static inline void
nodes_gui_reverse_lookup_selected_helper(GtkTreeModel *model,
		GtkTreePath *unused_path, GtkTreeIter *iter, gpointer unused_data)
{
	struct node_data *data;
	gnet_node_info_t info;

	(void) unused_path;
	(void) unused_data;

	gtk_tree_model_get(model, iter, 0, &data, (-1));
	g_assert(NULL != find_node(data->node_id));

	if (hset_contains(ht_pending_lookups, data->node_id))
		return;

	guc_node_fill_info(data->node_id, &info);
	g_assert(data->node_id == info.node_id);

	if (!info.is_pseudo) {
		const struct nid *key = nid_ref(data->node_id);

		WFREE_NULL(data->host, data->host_size);
		data->host_size = w_concat_strings(&data->host,
				_("Reverse lookup in progress..."),
				" (", host_addr_port_to_string(info.addr, info.port), ")",
				(void *) 0);

		hset_insert(ht_pending_lookups, key);
		adns_reverse_lookup(info.addr, host_lookup_callback,
			deconstify_gpointer(nid_ref(key)));
	}
	guc_node_clear_info(&info);
}
예제 #5
0
/**
 * Callback invoked when the node's user-visible flags are changed.
 *
 * This schedules an update of the node information in the gui at the
 * next tick.
 */
static void
nodes_gui_node_flags_changed(const struct nid *node_id)
{
    if (!hset_contains(hs_node_flags_changed, node_id)) {
		const struct nid *key = nid_ref(node_id);
    	hset_insert(hs_node_flags_changed, key);
	}
}
예제 #6
0
/**
 * Mark a file descriptor as being preserved from closing by
 * fd_close_unpreserved_from().
 */
void
fd_preserve(int fd)
{
	g_assert(!is_a_socket(fd));

	ONCE_FLAG_RUN(fd_preserved_allocated, fd_preserved_allocate);
	hset_insert(fd_preserved, int_to_pointer(fd));
}
예제 #7
0
파일: sq.c 프로젝트: MrJoe/gtk-gnutella
/**
 * Record search handle in the hash table.
 */
static void
sqh_put(squeue_t *sq, gnet_search_t sh)
{
	g_assert(sq != NULL);
	g_assert(!sqh_exists(sq, sh));

	hset_insert(sq->handles, uint_to_pointer(sh));
}
예제 #8
0
파일: cq.c 프로젝트: MrJoe/gtk-gnutella
/**
 * Register object in the supplied hash table (passed by reference, created
 * if not existing already).
 */
static void
cq_register_object(hset_t **hptr, void *o)
{
	hset_t *h = *hptr;

	g_assert(o != NULL);

	if (NULL == h)
		*hptr = h = hset_create(HASH_KEY_SELF, 0);

	g_assert(!hset_contains(h, o));

	hset_insert(h, o);
}
예제 #9
0
static GSList *
resolve_hostname(const char *host, enum net_type net)
#ifdef HAS_GETADDRINFO
{
	static const struct addrinfo zero_hints;
	struct addrinfo hints, *ai, *ai0 = NULL;
	hset_t *hs;
	GSList *sl_addr;
	int error;

	g_assert(host);
	
	hints = zero_hints;
	hints.ai_family = net_type_to_pf(net);

	error = getaddrinfo(host, NULL, &hints, &ai0);
	if (error) {
		g_message("getaddrinfo() failed for \"%s\": %s",
				host, gai_strerror(error));
		return NULL;
	}

	sl_addr = NULL;
	hs = hset_create_any(host_addr_hash_func, NULL, host_addr_eq_func);
	for (ai = ai0; ai; ai = ai->ai_next) {
		host_addr_t addr;

		if (!ai->ai_addr)
			continue;

		addr = addrinfo_to_addr(ai);

		if (is_host_addr(addr) && !hset_contains(hs, &addr)) {
			host_addr_t *addr_copy;

			addr_copy = wcopy(&addr, sizeof addr);
			sl_addr = g_slist_prepend(sl_addr, addr_copy);
			hset_insert(hs, addr_copy);
		}
	}
	hset_free_null(&hs);

	if (ai0)
		freeaddrinfo(ai0);

	return g_slist_reverse(sl_addr);
}
예제 #10
0
/**
 * Check attributes for URI usage.
 */
static void
xfmt_handle_pass1_attr(const char *uri,
                       const char *local, const char *value, void *data)
{
    struct xfmt_pass1 *xp1 = data;

    (void) local;
    (void) value;

    if (uri != NULL) {
        xfmt_uri_declare(uri, xp1);

        if (xp1->attr_uris != NULL) {
            hset_insert(xp1->attr_uris, uri);
        }
    }
}
예제 #11
0
/**
 * Send message (eslist iterator callback).
 *
 * @return TRUE if message was sent and freed up.
 */
static bool
udp_tx_desc_send(void *data, void *udata)
{
	struct udp_tx_desc *txd = data;
	udp_sched_t *us = udata;
	unsigned prio;

	udp_sched_check(us);
	udp_tx_desc_check(txd);

	if (us->used_all)
		return FALSE;

	/*
	 * Avoid flushing consecutive queued messages to the same destination,
	 * for regular (non-prioritary) messages.
	 *
	 * This serves two purposes:
	 *
	 * 1- It makes sure one single host does not capture all the available
	 *    outgoing bandwidth.
	 *
	 * 2- It somehow delays consecutive packets to a given host thereby reducing
	 *    flooding and hopefully avoiding saturation of its RX flow.
	 */

	prio = pmsg_prio(txd->mb);

	if (PMSG_P_DATA == prio && hset_contains(us->seen, txd->to)) {
		udp_sched_log(2, "%p: skipping mb=%p (%d bytes) to %s",
			us, txd->mb, pmsg_size(txd->mb), gnet_host_to_string(txd->to));
		return FALSE;
	}

	if (udp_sched_mb_sendto(us, txd->mb, txd->to, txd->tx, txd->cb)) {
		if (PMSG_P_DATA == prio && pmsg_was_sent(txd->mb))
			hset_insert(us->seen, atom_host_get(txd->to));
	} else {
		return FALSE;		/* Unsent, leave it in the queue */
	}

	us->buffered = size_saturate_sub(us->buffered, pmsg_size(txd->mb));
	udp_tx_desc_flag_release(txd, us);
	return TRUE;
}
예제 #12
0
/**
 * Add file to the current query hit.
 *
 * @return TRUE if we kept the file, FALSE if we did not include it in the hit.
 */
static bool
g2_build_qh2_add(struct g2_qh2_builder *ctx, const shared_file_t *sf)
{
	const sha1_t *sha1;
	g2_tree_t *h, *c;

	shared_file_check(sf);

	/*
	 * Make sure the file is still in the library.
	 */

	if (0 == shared_file_index(sf))
		return FALSE;

	/*
	 * On G2, the H/URN child is required, meaning we need the SHA1 at least.
	 */

	if (!sha1_hash_available(sf))
		return FALSE;

	/*
	 * Do not send duplicates, as determined by the SHA1 of the resource.
	 *
	 * A user may share several files with different names but the same SHA1,
	 * and if all of them are hits, we only want to send one instance.
	 *
	 * When generating hits for host-browsing, we do not care about duplicates
	 * and ctx->hs is NULL then.
	 */

	sha1 = shared_file_sha1(sf);		/* This is an atom */

	if (ctx->hs != NULL) {
		if (hset_contains(ctx->hs, sha1))
			return FALSE;

		hset_insert(ctx->hs, sha1);
	}

	/*
	 * Create the "H" child and attach it to the current tree.
	 */

	if (NULL == ctx->t)
		g2_build_qh2_start(ctx);

	h = g2_tree_alloc_empty("H");
	g2_tree_add_child(ctx->t, h);

	/*
	 * URN -- Universal Resource Name
	 *
	 * If there is a known TTH, then we can generate a bitprint, otherwise
	 * we just convey the SHA1.
	 */

	{
		const tth_t * const tth = shared_file_tth(sf);
		char payload[SHA1_RAW_SIZE + TTH_RAW_SIZE + sizeof G2_URN_BITPRINT];
		char *p = payload;

		if (NULL == tth) {
			p = mempcpy(p, G2_URN_SHA1, sizeof G2_URN_SHA1);
			p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload),
				sha1, SHA1_RAW_SIZE);
		} else {
			p = mempcpy(p, G2_URN_BITPRINT, sizeof G2_URN_BITPRINT);
			p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload),
				sha1, SHA1_RAW_SIZE);
			p += clamp_memcpy(p, sizeof payload - ptr_diff(p, payload),
				tth, TTH_RAW_SIZE);
		}

		g_assert(ptr_diff(p, payload) <= sizeof payload);

		c = g2_tree_alloc_copy("URN", payload, ptr_diff(p, payload));
		g2_tree_add_child(h, c);
	}

	/*
	 * URL -- empty to indicate that we share the file via uri-res.
	 */

	if (ctx->flags & QHIT_F_G2_URL) {
		uint known;
		uint16 csc;

		c = g2_tree_alloc_empty("URL");
		g2_tree_add_child(h, c);

		/*
		 * CSC -- if we know alternate sources, indicate how many in "CSC".
		 *
		 * This child is only emitted when they requested "URL".
		 */

		known = dmesh_count(sha1);
		csc = MIN(known, MAX_INT_VAL(uint16));

		if (csc != 0) {
			char payload[2];

			poke_le16(payload, csc);
			c = g2_tree_alloc_copy("CSC", payload, sizeof payload);
			g2_tree_add_child(h, c);
		}

		/*
		 * PART -- if we only have a partial file, indicate how much we have.
		 *
		 * This child is only emitted when they requested "URL".
		 */

		if (shared_file_is_partial(sf) && !shared_file_is_finished(sf)) {
			filesize_t available = shared_file_available(sf);
			char payload[8];	/* If we have to encode file size as 64-bit */
			uint32 av32;
			time_t mtime = shared_file_modification_time(sf);

			c = g2_tree_alloc_empty("PART");
			g2_tree_add_child(h, c);

			av32 = available;
			if (av32 == available) {
				/* Fits within a 32-bit quantity */
				poke_le32(payload, av32);
				g2_tree_set_payload(c, payload, sizeof av32, TRUE);
			} else {
				/* Encode as a 64-bit quantity then */
				poke_le64(payload, available);
				g2_tree_set_payload(c, payload, sizeof payload, TRUE);
			}

			/*
			 * GTKG extension: encode the last modification time of the
			 * partial file in an "MT" child.  This lets the other party
			 * determine whether the host is still able to actively complete
			 * the file.
			 */

			poke_le32(payload, (uint32) mtime);
			g2_tree_add_child(c,
				g2_tree_alloc_copy("MT", payload, sizeof(uint32)));
		}

		/*
		 * CT -- creation time of the resource (GTKG extension).
		 */

		{
			time_t create_time = shared_file_creation_time(sf);

			if ((time_t) -1 != create_time) {
				char payload[8];
				int n;

				create_time = MAX(0, create_time);
				n = vlint_encode(create_time, payload);
				g2_tree_add_child(h,
					g2_tree_alloc_copy("CT", payload, n));	/* No trailing 0s */
			}
		}
	}

	/*
	 * DN -- distinguished name.
	 *
	 * Note that the presence of DN also governs the presence of SZ if the
	 * file length does not fit a 32-bit unsigned quantity.
	 */

	if (ctx->flags & QHIT_F_G2_DN) {
		char payload[8];		/* If we have to encode file size as 64-bit */
		uint32 fs32;
		filesize_t fs = shared_file_size(sf);
		const char *name;
		const char *rp;

		c = g2_tree_alloc_empty("DN");

		fs32 = fs;
		if (fs32 == fs) {
			/* Fits within a 32-bit quantity */
			poke_le32(payload, fs32);
			g2_tree_set_payload(c, payload, sizeof fs32, TRUE);
		} else {
			/* Does not fit a 32-bit quantity, emit a SZ child */
			poke_le64(payload, fs);
			g2_tree_add_child(h,
				g2_tree_alloc_copy("SZ", payload, sizeof payload));
		}

		name = shared_file_name_nfc(sf);
		g2_tree_append_payload(c, name, shared_file_name_nfc_len(sf));
		g2_tree_add_child(h, c);

		/*
		 * GTKG extension: if there is a file path, expose it as a "P" child
		 * under the DN node.
		 */

		rp = shared_file_relative_path(sf);
		if (rp != NULL) {
			g2_tree_add_child(c, g2_tree_alloc_copy("P", rp, strlen(rp)));
		}
	}

	/*
	 * GTKG extension: if they requested alt-locs in the /Q2/I with "A", then
	 * send them some known alt-locs in an "ALT" child.
	 *
	 * Note that these alt-locs can be for Gnutella hosts: since both Gnutella
	 * and G2 share a common HTTP-based file transfer mechanism with compatible
	 * extra headers, there is no need to handle them separately.
	 */

	if (ctx->flags & QHIT_F_G2_ALT) {
		gnet_host_t hvec[G2_BUILD_QH2_MAX_ALT];
		int hcnt = 0;

		hcnt = dmesh_fill_alternate(sha1, hvec, N_ITEMS(hvec));

		if (hcnt > 0) {
			int i;

			c = g2_tree_alloc_empty("ALT");

			for (i = 0; i < hcnt; i++) {
				host_addr_t addr;
				uint16 port;

				addr = gnet_host_get_addr(&hvec[i]);
				port = gnet_host_get_port(&hvec[i]);

				if (host_addr_is_ipv4(addr)) {
					char payload[6];

					host_ip_port_poke(payload, addr, port, NULL);
					g2_tree_append_payload(c, payload, sizeof payload);
				}
			}

			/*
			 * If the payload is still empty, then drop the "ALT" child.
			 * Otherwise, attach it to the "H" node.
			 */

			if (NULL == g2_tree_node_payload(c, NULL)) {
				g2_tree_free_null(&c);
			} else {
				g2_tree_add_child(h, c);
			}
		}
	}

	/*
	 * Update the size of the query hit we're generating.
	 */

	ctx->current_size += g2_frame_serialize(h, NULL, 0);

	return TRUE;
}
예제 #13
0
/**
 * Removes the URL from the set of known URL, but do not free its memory
 * and keeps it in the set of failed URLs for the session.
 */
static void
gwc_forget_url(const char *url)
{
    struct gwc url_tmp[MAX_GWC_URLS];			/* Temporary copy */
    int count = hset_count(gwc_known_url);
    int i;
    int j = 0;

    g_assert(count > 0);
    g_assert(count <= MAX_GWC_URLS);
    g_assert(count == MAX_GWC_URLS || gwc_url_slot < count);
    g_assert(gwc_url_slot >= 0);
    STATIC_ASSERT(sizeof(url_tmp) == sizeof(gwc_url));

    if (GNET_PROPERTY(bootstrap_debug))
        g_warning("forgetting GWC URL \"%s\"", url);

    /*
     * It is possible that the URL we're trying to forget was
     * already removed from the cache if it was at a slot overridden
     * in the round-robin buffer, should we have got new GWC URL since
     * it was selected.
     */

    if (hset_contains(gwc_known_url, url))
        hset_remove(gwc_known_url, url);
    else {
        if (GNET_PROPERTY(bootstrap_debug))
            g_warning("URL was already gone from GWC");
        return;
    }
    hset_insert(gwc_failed_url, url);

    /*
     * Because we have a round-robin buffer, removing something in the
     * middle of the buffer is not straightforward.  The `gwc_url_slot'
     * variable points to the last filled value in the buffer.
     *
     * We're going to build a copy in url_tmp[], filled from 0 to "count - 1",
     * and we'll move back that copy into the regular gwc_url[] cache.
     * The reason is that since there will be less entries in the cache
     * than the maximum amount, the round-robin buffer must be linearily
     * filled from 0 and upwards.
     */

    memset(url_tmp, 0, sizeof(url_tmp));

    if (count == MAX_GWC_URLS) {		/* Buffer was full */
        for (i = gwc_url_slot;;) {
            if (gwc_url[i].url != url)	/* Atoms: we can compare addresses */
                url_tmp[j++] = gwc_url[i];
            i++;
            if (i == MAX_GWC_URLS)
                i = 0;
            if (i == gwc_url_slot)		/* Back to where we started */
                break;
        }
    } else {							/* Buffer was partially filled */
        for (i = 0; i <= gwc_url_slot; i++) {
            if (gwc_url[i].url != url)	/* Atoms: we can compare addresses */
                url_tmp[j++] = gwc_url[i];
        }
    }

    count--;							/* New amount of data in cache */
    gwc_url_slot = j - 1;				/* Last position we filled */
    gwc_url_slot = MAX(0, gwc_url_slot);	/* If we removed ALL entries */
    g_assert(gwc_url_slot == MAX(0, count - 1));
    memcpy(gwc_url, url_tmp, sizeof(gwc_url));
    g_assert(gwc_url_slot >= 0 && gwc_url_slot < MAX_GWC_URLS);

    gwc_file_dirty = TRUE;
}
예제 #14
0
/**
 * Add new URL to cache, possibly pushing off an older one if cache is full.
 *
 * @return TRUE if the URL was added, FALSE otherwise.
 */
static bool
gwc_add(const char *new_url)
{
    const char *url_atom;
    const char *old_url;
    char *url, *ret;

    url = h_strdup(new_url); /* url_normalize() can modify the URL */

    ret = url_normalize(url, URL_POLICY_GWC_RULES);
    if (!ret) {
        g_warning("%s(): ignoring bad web cache URL \"%s\"",
                  G_STRFUNC, new_url);
        HFREE_NULL(url);
        return FALSE;
    }
    if (ret != url) {
        HFREE_NULL(url);
        url = ret;
    }

    /*
     * Don't add duplicates to the cache.
     */

    if (
        hset_contains(gwc_known_url, url) ||
        hset_contains(gwc_failed_url, url)
    ) {
        HFREE_NULL(url);
        return FALSE;
    }

    /*
     * OK, record new entry at the `gwc_url_slot'.
     */

    if (++gwc_url_slot >= MAX_GWC_URLS)
        gwc_url_slot = 0;

    g_assert(url != NULL);
    url_atom = atom_str_get(url);
    HFREE_NULL(url);

    /*
     * Expire any entry present at the slot we're about to write into.
     */

    old_url = gwc_url[gwc_url_slot].url;

    if (old_url != NULL) {
        g_assert(hset_contains(gwc_known_url, old_url));
        hset_remove(gwc_known_url, old_url);
        atom_str_free_null(&old_url);
        gwc_url[gwc_url_slot].url = NULL;
    }

    hset_insert(gwc_known_url, url_atom);

    gwc_url[gwc_url_slot].url = url_atom;
    gwc_url[gwc_url_slot].stamp = 0;
    gwc_file_dirty = TRUE;

    if (GNET_PROPERTY(bootstrap_debug)) {
        g_debug("%s(): loaded GWC URL %s", G_STRFUNC, url_atom);
    }

    return TRUE;
}