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