/** * Notification from the socket layer that we got a new datagram. * If `truncated' is true, then the message was too large for the * socket buffer. */ static void urpc_received(struct gnutella_socket *s, bool truncated) { struct urpc_cb *ucb; inet_udp_got_incoming(s->addr); ucb = htable_lookup(pending, s); if (NULL == ucb) { g_warning("UDP got unexpected %s%zu-byte RPC reply from %s", truncated ? "truncated " : "", s->pos, host_addr_port_to_string(s->addr, s->port)); return; } if (GNET_PROPERTY(udp_debug) > 1) { g_debug("UDP [%s] got %s%zu-byte RPC reply from %s", ucb->what, truncated ? "truncated " : "", s->pos, host_addr_port_to_string(s->addr, s->port)); } /* * Invoke user callback so that reply can be processed. * Then discard the socket. */ (*ucb->cb)(URPC_REPLY, s->addr, s->port, s->buf, s->pos, ucb->arg); urpc_cb_free(ucb, FALSE); }
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); }
/** * 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)); }
static ssize_t tls_write_intern(struct wrap_io *wio, const void *buf, size_t size) { struct gnutella_socket *s = wio->ctx; ssize_t ret; g_assert((0 == s->tls.snarf) ^ (NULL == buf)); g_assert((0 == s->tls.snarf) ^ (0 == size)); size = tls_adjust_send_size(s, size); ret = gnutls_record_send(tls_socket_get_session(s), buf, size); if (ret < 0) { switch (ret) { case GNUTLS_E_INTERRUPTED: case GNUTLS_E_AGAIN: if (0 == s->tls.snarf) { s->tls.snarf = size; ret = size; } else { errno = VAL_EAGAIN; ret = -1; } break; case GNUTLS_E_PULL_ERROR: case GNUTLS_E_PUSH_ERROR: /* Logging already done by tls_transport_debug() */ errno = (SOCK_F_CONNRESET & s->flags) ? ECONNRESET : EIO; ret = -1; goto finish; default: if (GNET_PROPERTY(tls_debug)) { g_carp("tls_write(): gnutls_record_send(fd=%d) failed: " "host=%s snarf=%zu error=\"%s\"", s->file_desc, host_addr_port_to_string(s->addr, s->port), s->tls.snarf, gnutls_strerror(ret)); } errno = EIO; ret = -1; goto finish; } } else { if (s->tls.snarf) { g_assert(s->tls.snarf >= (size_t) ret); s->tls.snarf -= ret; errno = VAL_EAGAIN; ret = -1; goto finish; } } if (s->tls.snarf) { tls_socket_evt_change(s, INPUT_EVENT_WX); } finish: g_assert(ret == (ssize_t) -1 || (size_t) ret <= size); return ret; }
static void tls_print_session_info(const host_addr_t addr, uint16 port, gnutls_session session, bool incoming) { const char *proto, *cert, *kx, *ciph, *mac, *comp; g_return_if_fail(session); proto = gnutls_protocol_get_name(gnutls_protocol_get_version(session)); cert = gnutls_certificate_type_get_name( gnutls_certificate_type_get(session)); kx = gnutls_kx_get_name(gnutls_kx_get(session)); comp = gnutls_compression_get_name(gnutls_compression_get(session)); ciph = gnutls_cipher_get_name(gnutls_cipher_get(session)); mac = gnutls_mac_get_name(gnutls_mac_get (session)); g_debug( "TLS session info (%s):\n" " Host: %s\n" " Protocol: %s\n" " Certificate: %s\n" " Key Exchange: %s\n" " Cipher: %s\n" " MAC: %s\n" " Compression: %s", incoming ? "incoming" : "outgoing", host_addr_port_to_string(addr, port), NULL_STRING(proto), NULL_STRING(cert), NULL_STRING(kx), NULL_STRING(ciph), NULL_STRING(mac), NULL_STRING(comp) ); }
void hsep_connection_init(struct gnutella_node *n, uint8 major, uint8 minor) { static const hsep_ctx_t zero_hsep; time_t now = tm_time(); uint i; g_assert(n); if (GNET_PROPERTY(hsep_debug) > 1) printf("HSEP: Initializing node %s\n", host_addr_port_to_string(n->addr, n->port)); WALLOC(n->hsep); *n->hsep = zero_hsep; /* Initializes everything to 0 */ n->hsep->last_sent = now; n->hsep->major = major; n->hsep->minor = minor; /* this is what we know before receiving the first message */ for (i = 1; i < G_N_ELEMENTS(hsep_global_table); i++) { n->hsep->table[i][HSEP_IDX_NODES] = 1; hsep_global_table[i][HSEP_IDX_NODES]++; } hsep_sanity_check(); hsep_fire_global_table_changed(now); }
/** * Adds the given node to the gui. */ void nodes_gui_add_node(gnet_node_info_t *n) { GtkCList *clist_nodes; const gchar *titles[c_gnet_num]; gchar proto_tmp[32]; gint row; g_assert(n != NULL); str_bprintf(proto_tmp, sizeof proto_tmp, "%d.%d", n->proto_major, n->proto_minor); titles[c_gnet_host] = host_addr_port_to_string(n->addr, n->port); titles[c_gnet_flags] = "..."; titles[c_gnet_user_agent] = n->vendor ? lazy_utf8_to_locale(n->vendor) : "..."; titles[c_gnet_loc] = iso3166_country_cc(n->country); titles[c_gnet_version] = proto_tmp; titles[c_gnet_connected] = "..."; titles[c_gnet_uptime] = "..."; titles[c_gnet_info] = "..."; clist_nodes = GTK_CLIST(gui_main_window_lookup("clist_nodes")); row = gtk_clist_append(clist_nodes, (gchar **) titles); /* override const */ gtk_clist_set_row_data(clist_nodes, row, deconstify_gpointer(nid_ref(n->node_id))); }
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); }
/** * Adds the given node to the gui. */ void nodes_gui_add_node(gnet_node_info_t *info) { static const struct node_data zero_data; struct node_data *data; gnet_node_flags_t flags; g_return_if_fail(info); g_return_if_fail(!htable_contains(nodes_handles, info->node_id)); WALLOC(data); *data = zero_data; data->node_id = nid_ref(info->node_id); data->user_agent = info->vendor ? atom_str_get(info->vendor) : NULL; data->country = info->country; data->host_size = w_concat_strings(&data->host, host_addr_port_to_string(info->addr, info->port), (void *) 0); str_bprintf(data->version, sizeof data->version, "%u.%u", info->proto_major, info->proto_minor); guc_node_fill_flags(data->node_id, &flags); nodes_gui_update_node_flags(data, &flags); htable_insert(nodes_handles, data->node_id, data); gtk_list_store_append(nodes_model, &data->iter); gtk_list_store_set(nodes_model, &data->iter, 0, data, (-1)); }
/** * Send a Gnutella ping to the specified host via UDP, using the * specified MUID. */ void udp_connect_back(const host_addr_t addr, uint16 port, const struct guid *muid) { if (udp_send_ping(muid, addr, port, FALSE)) { if (GNET_PROPERTY(udp_debug) > 19) g_debug("UDP queued connect-back PING #%s to %s\n", guid_hex_str(muid), host_addr_port_to_string(addr, port)); } }
/** * Convert magnet source to a string representation. * * @return A newly allocated string. */ char * magnet_source_to_string(const struct magnet_source *s) { char *url; g_return_val_if_fail(s, NULL); if (s->url) { url = g_strdup(s->url); } else { char *proxies = NULL; const char *host, *prefix; char prefix_buf[256]; char port_buf[16]; if (s->guid) { char guid_buf[GUID_HEX_SIZE + 1]; guid_to_string_buf(s->guid, guid_buf, sizeof guid_buf); concat_strings(prefix_buf, sizeof prefix_buf, "push://", guid_buf, (void *) 0); prefix = prefix_buf; } else { prefix = "http://"; } port_buf[0] = '\0'; if (s->hostname) { host = s->hostname; if (80 != s->port) { str_bprintf(port_buf, sizeof port_buf, ":%u", (unsigned) s->port); } } else if (s->guid) { proxies = proxies_to_string(s->proxies); host = proxies; } else { host = host_addr_port_to_string(s->addr, s->port); } if (s->path) { url = g_strconcat(prefix, host, port_buf, s->path, (void *) 0); } else if (s->sha1) { url = g_strconcat(prefix, host, port_buf, "/uri-res/N2R?", bitprint_to_urn_string(s->sha1, s->tth), (void *) 0); } else { url = g_strconcat(prefix, host, port_buf, "/", (void *) 0); } HFREE_NULL(proxies); } return url; }
static inline void tls_transport_debug(const char *op, const struct gnutella_socket *s, size_t size, ssize_t ret) { if ((ssize_t) -1 == ret) { unsigned level = is_temporary_error(errno) ? 2 : 0; if (GNET_PROPERTY(tls_debug) > level) { g_debug("%s(): fd=%d size=%zu host=%s ret=-1 errno=%m", op, s->file_desc, size, host_addr_port_to_string(s->addr, s->port)); } } else { if (GNET_PROPERTY(tls_debug) > 2) { g_debug("%s(): fd=%d size=%zu host=%s ret=%zu", op, s->file_desc, size, host_addr_port_to_string(s->addr, s->port), ret); } } }
/** * Main RPC iteration loop. */ static void natpmp_rpc_iterate(cqueue_t *unused_cq, void *obj) { struct natpmp_rpc *rd = obj; int ret; natpmp_rpc_check(rd); (void) unused_cq; if (rd->count++ > rd->retries) goto finished; ret = urpc_send("NAT-PMP", rd->gateway, NATPMP_SRV_PORT, pmsg_start(rd->mb), pmsg_size(rd->mb), rd->timeout, natpmp_rpc_reply, rd); if (0 != ret) { if (GNET_PROPERTY(natpmp_debug)) { g_warning("NATPMP could not send \"%s\" #%u to %s: %m", natpmp_op_to_string(rd->op), rd->count, host_addr_port_to_string(rd->gateway, NATPMP_SRV_PORT)); } goto finished; } else { if (GNET_PROPERTY(natpmp_debug) > 4) { g_debug("NATPMP sent \"%s\" #%u to %s, with %u ms timeout", natpmp_op_to_string(rd->op), rd->count, host_addr_port_to_string(rd->gateway, NATPMP_SRV_PORT), rd->timeout); } } rd->timeout = uint_saturate_mult(rd->timeout, 2); /* For next time */ return; finished: natpmp_rpc_error(rd); }
/** * Change node's version */ void knode_change_version(knode_t *kn, uint8 major, uint8 minor) { knode_check(kn); if (GNET_PROPERTY(dht_debug)) g_warning("DHT node %s at %s changed from v%u.%u to v%u.%u", kuid_to_hex_string(kn->id), host_addr_port_to_string(kn->addr, kn->port), kn->major, kn->minor, major, minor); kn->major = major; kn->minor = minor; }
static void search_stats_notify_routed(query_type_t unused_type, const char *unused_search, const host_addr_t addr, guint16 port) { word_vec_t wovec; (void) unused_type; (void) unused_search; wovec.word = deconstify_char(host_addr_port_to_string(addr, port)); wovec.len = strlen(wovec.word); wovec.amount = 1; search_stats_tally(&wovec); }
/** * Callback invoked when the HTTP data of the request have been sent. */ static void soap_sent_data(const struct http_async *ha, const struct gnutella_socket *s, const char *data, size_t len, gboolean deferred) { soap_rpc_t *sr = http_async_get_opaque(ha); soap_rpc_check(sr); if (GNET_PROPERTY(soap_trace) & SOCK_TRACE_OUT) { g_debug("----Sent SOAP HTTP data%s to %s (%u bytes):", deferred ? " completely" : "", host_addr_port_to_string(s->addr, s->port), (unsigned) len); dump_string(stderr, data, len, "----"); } }
/** * RPC timed out. */ static void urpc_timed_out(cqueue_t *unused_cq, void *obj) { struct urpc_cb *ucb = obj; urpc_cb_check(ucb); (void) unused_cq; ucb->timeout_ev = NULL; if (GNET_PROPERTY(udp_debug)) { g_message("UDP [%s] RPC to %s timed out", ucb->what, host_addr_port_to_string(ucb->addr, ucb->port)); } (*ucb->cb)(URPC_TIMEOUT, ucb->addr, ucb->port, NULL, 0, ucb->arg); urpc_cb_free(ucb, FALSE); }
static int tls_flush(struct wrap_io *wio) { struct gnutella_socket *s = wio->ctx; socket_check(s); if (s->tls.snarf) { if (GNET_PROPERTY(tls_debug > 1)) { g_debug("tls_flush: snarf=%zu host=%s fd=%d", s->tls.snarf, host_addr_port_to_string(s->addr, s->port), s->file_desc); } (void ) tls_write_intern(wio, NULL, 0); if (s->tls.snarf) return -1; } return 0; }
/** * Change node's vendor code. */ void knode_change_vendor(knode_t *kn, vendor_code_t vcode) { knode_check(kn); if (GNET_PROPERTY(dht_debug)) { char vc_old[VENDOR_CODE_BUFLEN]; char vc_new[VENDOR_CODE_BUFLEN]; vendor_code_to_string_buf(kn->vcode.u32, vc_old, sizeof vc_old); vendor_code_to_string_buf(vcode.u32, vc_new, sizeof vc_new); g_warning("DHT node %s at %s changed vendor from %s to %s", kuid_to_hex_string(kn->id), host_addr_port_to_string(kn->addr, kn->port), vc_old, vc_new); } kn->vcode = vcode; }
/** * Send an UDP ping to the host cache. */ static void uhc_send_ping(void) { g_assert(uhc_connecting); guid_random_muid(&uhc_ctx.muid); if (udp_send_ping(&uhc_ctx.muid, uhc_ctx.addr, uhc_ctx.port, TRUE)) { if (GNET_PROPERTY(bootstrap_debug) || GNET_PROPERTY(log_uhc_pings_tx)) { g_debug("BOOT sent UDP SCP ping #%s to %s:%u", guid_hex_str(&uhc_ctx.muid), uhc_ctx.host, uhc_ctx.port); } /* * Give GUI feedback. */ { char msg[256]; str_bprintf(msg, sizeof msg, _("Sent ping to UDP host cache %s:%u"), uhc_ctx.host, uhc_ctx.port); gcu_statusbar_message(msg); } /* * Arm a timer to see whether we should not try to ping another * host cache if we don't get a timely reply. */ g_assert(uhc_ctx.timeout_ev == NULL); uhc_ctx.timeout_ev = cq_main_insert(UHC_TIMEOUT, uhc_ping_timeout, NULL); } else { g_warning("BOOT failed to send UDP SCP to %s", host_addr_port_to_string(uhc_ctx.addr, uhc_ctx.port)); uhc_try_next(); } }
/** * @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; }
/** * Updates the global HSEP table when a connection is about * to be closed. The connection's HSEP data is restored to * zero and the CAN_HSEP attribute is cleared. */ void hsep_connection_close(struct gnutella_node *n, bool in_shutdown) { unsigned int i, j; g_assert(n); g_assert(n->hsep); if (GNET_PROPERTY(hsep_debug) > 1) printf("HSEP: Deinitializing node %s\n", host_addr_port_to_string(n->addr, n->port)); if (in_shutdown) goto cleanup; for (i = 1; i < G_N_ELEMENTS(hsep_global_table); i++) { for (j = 0; j < G_N_ELEMENTS(hsep_global_table[0]); j++) { hsep_global_table[i][j] -= n->hsep->table[i][j]; n->hsep->table[i][j] = 0; } } if (GNET_PROPERTY(hsep_debug) > 1) hsep_dump_table(); hsep_fire_global_table_changed(tm_time()); /* * Clear CAN_HSEP attribute so that the HSEP code * will not use the node any longer. */ cleanup: n->attrs &= ~NODE_A_CAN_HSEP; WFREE(n->hsep); n->hsep = NULL; }
void tls_bye(struct gnutella_socket *s) { int ret; socket_check(s); g_return_if_fail(s->tls.ctx); g_return_if_fail(s->tls.ctx->session); if ((SOCK_F_EOF | SOCK_F_SHUTDOWN) & s->flags) return; if (tls_flush(&s->wio) && GNET_PROPERTY(tls_debug)) { g_warning("%s(): tls_flush(fd=%d) failed", G_STRFUNC, s->file_desc); } ret = gnutls_bye(s->tls.ctx->session, SOCK_CONN_INCOMING != s->direction ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR); if (ret < 0) { switch (ret) { case GNUTLS_E_INTERRUPTED: case GNUTLS_E_AGAIN: break; case GNUTLS_E_PULL_ERROR: case GNUTLS_E_PUSH_ERROR: /* Logging already done by tls_transport_debug() */ break; default: if (GNET_PROPERTY(tls_debug)) { g_carp("gnutls_bye() failed: host=%s error=%m", host_addr_port_to_string(s->addr, s->port)); } } } }
/** * Called when a pong with an "IPP" extension was received. */ void uhc_ipp_extract(gnutella_node_t *n, const char *payload, int paylen, enum net_type type) { int i, cnt; int len = NET_TYPE_IPV6 == type ? 18 : 6; const void *p; g_assert(0 == paylen % len); cnt = paylen / len; if (GNET_PROPERTY(bootstrap_debug)) g_debug("extracting %d host%s in UDP IPP pong #%s from %s", cnt, plural(cnt), guid_hex_str(gnutella_header_get_muid(&n->header)), node_addr(n)); for (i = 0, p = payload; i < cnt; i++, p = const_ptr_add_offset(p, len)) { host_addr_t ha; uint16 port; host_ip_port_peek(p, type, &ha, &port); hcache_add_caught(HOST_ULTRA, ha, port, "UDP-HC"); if (GNET_PROPERTY(bootstrap_debug) > 2) g_debug("BOOT collected %s from UDP IPP pong from %s", host_addr_port_to_string(ha, port), node_addr(n)); } if (!uhc_connecting) return; /* * Check whether this was a reply from our request. * * The reply could come well after we decided it timed out and picked * another UDP host cache, which ended-up replying, so we must really * check whether we're still in a probing cycle. */ if (!guid_eq(&uhc_ctx.muid, gnutella_header_get_muid(&n->header))) return; if (GNET_PROPERTY(bootstrap_debug)) { g_debug("BOOT UDP cache \"%s\" replied: got %d host%s from %s", uhc_ctx.host, cnt, plural(cnt), node_addr(n)); } /* * Terminate the probing cycle if we got hosts. */ if (cnt > 0) { char msg[256]; cq_cancel(&uhc_ctx.timeout_ev); uhc_connecting = FALSE; str_bprintf(msg, sizeof(msg), NG_("Got %d host from UDP host cache %s", "Got %d hosts from UDP host cache %s", cnt), cnt, uhc_ctx.host); gcu_statusbar_message(msg); } else { uhc_try_next(); } }
/** * 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(); }
/** * 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); }
/** * Identify the traffic type received on the UDP socket. * * This routine uses simple heuristics that ensure we're properly discriminating * incoming traffic on the UDP socket between regular Gnutella traffic and * semi-reliable UDP traffic (which adds a small header before its actual * payload). * * Most messages will be un-ambiguous, and the probabilty of misclassifying * an ambiguous message (one that look like valid for both types, based on * header inspections) is brought down to less than 1 in a billion, making * it perfectly safe in practice. * * @return intuited type */ static enum udp_traffic udp_intuit_traffic_type(const gnutella_socket_t *s, const void *data, size_t len) { enum udp_traffic utp; utp = udp_check_semi_reliable(data, len); if (len >= GTA_HEADER_SIZE) { uint16 size; /* Payload size, from the Gnutella message */ gmsg_valid_t valid; valid = gmsg_size_valid(data, &size); switch (valid) { case GMSG_VALID: case GMSG_VALID_MARKED: if ((size_t) size + GTA_HEADER_SIZE == len) { uint8 function, hops, ttl; function = gnutella_header_get_function(data); /* * If the header cannot be that of a known semi-reliable * UDP protocol, there is no ambiguity. */ if (UNKNOWN == utp) { return GTA_MSG_DHT == function ? DHT : GTA_MSG_RUDP == function ? RUDP : GNUTELLA; } /* * Message is ambiguous: its leading header appears to be * both a legitimate Gnutella message and a semi-reliable UDP * header. * * We have to apply some heuristics to decide whether to handle * the message as a Gnutella one or as a semi-reliable UDP one, * knowing that if we improperly classify it, the message will * not be handled correctly. * * Note that this is highly unlikely. There is about 1 chance * in 10 millions (1 / 2^23 exactly) to mis-interpret a random * Gnutella MUID as the start of one of the semi-reliable * protocols we support. Our discriminating logic probes a * few more bytes (say 2 at least) which are going to let us * decide with about 99% certainety. So mis-classification * will occur only once per billion -- a ratio which is OK. * * We could also mistakenely handle a semi-reliable UDP message * as a Gnutella one. For that to happen, the payload must * contain a field that will be exactly the message size, * a 1 / 2^32 event (since the size is 4 bytes in Gnutella). * However, if message flags are put to use for Gnutella UDP, * this ratio could lower to 1 / 2^16 and that is too large * a chance (about 1.5 in 100,000). * * So when we think an ambiguous message could be a valid * Gnutella message, we also check whether the message could * not be interpreted as a valid semi-reliable UDP one, and * we give priority to that classification if we have a match: * correct sequence number, consistent count and emitting host. * This checks roughly 3 more bytes in the message, yielding * a misclassification for about 1 / 2^(16+24) random cases. */ hops = gnutella_header_get_hops(data); ttl = gnutella_header_get_ttl(data); gnet_stats_inc_general(GNR_UDP_AMBIGUOUS); if (GNET_PROPERTY(udp_debug)) { g_debug("UDP ambiguous datagram from %s: " "%zu bytes (%u-byte payload), " "function=%u, hops=%u, TTL=%u, size=%u", host_addr_port_to_string(s->addr, s->port), len, size, function, hops, ttl, gnutella_header_get_size(data)); dump_hex(stderr, "UDP ambiguous datagram", data, len); } switch (function) { case GTA_MSG_DHT: /* * A DHT message must be larger than KDA_HEADER_SIZE bytes. */ if (len < KDA_HEADER_SIZE) break; /* Not a DHT message */ /* * DHT messages have no bits defined in the size field * to mark them. */ if (valid != GMSG_VALID) break; /* Higest bit set, not a DHT message */ /* * If it is a DHT message, it must have a valid opcode. */ function = kademlia_header_get_function(data); if (function > KDA_MSG_MAX_ID) break; /* Not a valid DHT opcode */ /* * Check the contact address length: it must be 4 in the * header, because there is only room for an IPv4 address. */ if (!kademlia_header_constants_ok(data)) break; /* Not a valid Kademlia header */ /* * Make sure we're not mistaking a valid semi-reliable UDP * message as a DHT message. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " DHT function is %s", host_addr_port_to_string(s->addr, s->port), len, kmsg_name(function)); return DHT; case GTA_MSG_INIT: case GTA_MSG_PUSH_REQUEST: case GTA_MSG_SEARCH: /* * No incoming messages of this type can have a TTL * indicating a deflated payload, since there is no * guarantee the host would be able to read it (deflated * UDP is negotiated and can therefore only come from a * response). */ if (ttl & GTA_UDP_DEFLATED) break; /* Not Gnutella, we're positive */ /* FALL THROUGH */ case GTA_MSG_INIT_RESPONSE: case GTA_MSG_VENDOR: case GTA_MSG_SEARCH_RESULTS: /* * To further discriminate, look at the hop count. * Over UDP, the hop count will be low (0 or 1 mostly) * and definitely less than 3 since the only UDP-relayed * messages are from GUESS, and they can travel at most * through a leaf and an ultra node before reaching us. */ if (hops >= 3U) break; /* Gnutella is very unlikely */ /* * Check the TTL, cleared from bits that indicate * support for deflated UDP or a deflated payload. * No servent should send a TTL greater than 7, which * was the de-facto limit in the early Gnutella days. */ if ((ttl & ~(GTA_UDP_CAN_INFLATE | GTA_UDP_DEFLATED)) > 7U) break; /* Gnutella is very unlikely */ /* * Make sure we're not mistaking a valid semi-reliable UDP * message as a Gnutella message. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " Gnutella function is %s, hops=%u, TTL=%u", host_addr_port_to_string(s->addr, s->port), len, gmsg_name(function), hops, ttl); return GNUTELLA; case GTA_MSG_RUDP: /* * RUDP traffic is special: the only meaningful fields * of the Gnutella header are the opcode field (which we * have read here since we fall into this case) and the * Gnutella header size. * * The TTL and hops fields cannot be interpreted to * disambiguate, so our only option is deeper inspection. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " interpreted as RUDP packet", host_addr_port_to_string(s->addr, s->port), len); return RUDP; case GTA_MSG_STANDARD: /* Nobody is using this function code */ default: break; /* Not a function we expect over UDP */ } /* * Will be handled as semi-reliable UDP. */ gnet_stats_inc_general(GNR_UDP_AMBIGUOUS_AS_SEMI_RELIABLE); { udp_tag_t tag; memcpy(tag.value, data, sizeof tag.value); g_warning("UDP ambiguous message (%zu bytes total), " "not Gnutella (function is %d, hops=%u, TTL=%u) " "handling as semi-reliable UDP (tag=\"%s\")", len, function, hops, ttl, udp_tag_to_string(tag)); } return utp; } /* FALL THROUGH */ case GMSG_VALID_NO_PROCESS: case GMSG_INVALID: break; } } return utp; }
/** * Look whether the datagram we received is a valid Gnutella packet. * * The routine also handles traffic statistics (reception and dropping). * * If ``n'' is not NULL, then ``s'' may be NULL. If ``n'' is NULL, then * ``s'' must not be NULL. * * @param n the pseudo UDP reception node (NULL if invalid IP:port) * @param s the socket on which we got the UDP datagram * @param truncated whether datagram was truncated during reception * @param header header of message * @param payload payload of message (maybe not contiguous with header) * @param len total length of message (header + payload) * * @return TRUE if valid, FALSE otherwise. */ bool udp_is_valid_gnet_split(gnutella_node_t *n, const gnutella_socket_t *s, bool truncated, const void *header, const void *payload, size_t len) { const char *msg; uint16 size; /**< Payload size, from the Gnutella message */ g_assert(s != NULL || n != NULL); /* * If we can't get a proper UDP node for this address/port combination, * ignore the message. */ if (NULL == n) { msg = "Invalid address/port combination"; goto not; } if (len < GTA_HEADER_SIZE) { msg = "Too short"; goto not; } /* * We have enough to account for packet reception. * Note that packet could be garbage at this point. */ memcpy(n->header, header, sizeof n->header); n->size = len - GTA_HEADER_SIZE; /* Payload size if Gnutella msg */ gnet_stats_count_received_header(n); gnet_stats_count_received_payload(n, payload); /* * If the message was truncated, then there is also going to be a * size mismatch, but we want to flag truncated messages as being * "too large" because this is mainly why we reject them. They may * be legitimate Gnutella packets, too bad. */ if (truncated) { msg = "Truncated (too large?)"; goto too_large; } /* * Message sizes are architecturally limited to 64K bytes. * * We don't ensure the leading bits are zero in the size field because * this constraint we put allows us to use those bits for flags in * future extensions. * * The downside is that we have only 3 bytes (2 bytes for the size and * 1 byte for the function type) to identify a valid Gnutella packet. */ switch (gmsg_size_valid(header, &size)) { case GMSG_VALID: case GMSG_VALID_MARKED: break; case GMSG_VALID_NO_PROCESS: msg = "Header flags undefined for now"; goto drop; case GMSG_INVALID: msg = "Invalid size (greater than 64 KiB without flags)"; goto not; /* Probably just garbage */ } if ((size_t) size + GTA_HEADER_SIZE != len) { msg = "Size mismatch"; goto not; } /* * We only support a subset of Gnutella message from UDP. In particular, * messages like HSEP data, BYE or QRP are not expected! */ switch (gnutella_header_get_function(header)) { case GTA_MSG_INIT: case GTA_MSG_INIT_RESPONSE: case GTA_MSG_VENDOR: case GTA_MSG_STANDARD: case GTA_MSG_PUSH_REQUEST: case GTA_MSG_SEARCH_RESULTS: case GTA_MSG_RUDP: case GTA_MSG_DHT: return TRUE; case GTA_MSG_SEARCH: if (settings_is_ultra() && GNET_PROPERTY(enable_guess)) { return TRUE; /* GUESS query accepted */ } msg = "Query from UDP refused"; goto drop; } msg = "Gnutella message not processed from UDP"; drop: gnet_stats_count_dropped(n, MSG_DROP_UNEXPECTED); gnet_stats_inc_general(GNR_UDP_UNPROCESSED_MESSAGE); goto log; too_large: gnet_stats_count_dropped(n, MSG_DROP_TOO_LARGE); gnet_stats_inc_general(GNR_UDP_UNPROCESSED_MESSAGE); goto log; not: gnet_stats_inc_general(GNR_UDP_ALIEN_MESSAGE); /* FALL THROUGH */ log: if (GNET_PROPERTY(udp_debug)) { g_warning("UDP got invalid %sGnutella packet (%zu byte%s) " "\"%s\" %sfrom %s: %s", socket_udp_is_old(s) ? "OLD " : "", len, 1 == len ? "" : "s", len >= GTA_HEADER_SIZE ? gmsg_infostr_full_split(header, payload, len - GTA_HEADER_SIZE) : "<incomplete Gnutella header>", truncated ? "(truncated) " : "", NULL == n ? host_addr_port_to_string(s->addr, s->port) : node_infostr(n), msg); if (len != 0) { iovec_t iov[2]; iovec_set(&iov[0], header, GTA_HEADER_SIZE); iovec_set(&iov[1], payload, len - GTA_HEADER_SIZE); dump_hex_vec(stderr, "UDP datagram", iov, G_N_ELEMENTS(iov)); } } return FALSE; /* Dropped */ }
/** * 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; }