/** * Create a host for given address and port */ gnet_host_t * gnet_host_new(const host_addr_t addr, uint16 port) { gnet_host_t h; gnet_host_set(&h, addr, port); return gnet_host_dup(&h); /* Tightly allocated to fit address */ }
/** * Enqueue message to be sent to the ip:port held in the supplied node. */ void mq_udp_node_putq(mqueue_t *q, pmsg_t *mb, const gnutella_node_t *n) { gnet_host_t to; mq_check_consistency(q); gnet_host_set(&to, n->addr, n->port); mq_udp_putq(q, mb, &to); }
static bool udp_ping_register(const struct guid *muid, host_addr_t addr, uint16 port, udp_ping_cb_t cb, void *data, bool multiple) { struct udp_ping *ping; uint length; g_assert(muid); g_return_val_if_fail(udp_pings, FALSE); if (hash_list_contains(udp_pings, muid)) { /* Probably a duplicate */ return FALSE; } /* random early drop */ length = hash_list_length(udp_pings); if (length >= UDP_PING_MAX) { return FALSE; } else if (length > (UDP_PING_MAX / 4) * 3) { if (random_value(UDP_PING_MAX - 1) < length) return FALSE; } WALLOC(ping); ping->muid = *muid; ping->added = tm_time(); { gnet_host_t host; gnet_host_set(&host, addr, port); ping->host = atom_host_get(&host); } if (cb != NULL) { WALLOC0(ping->callback); ping->callback->cb = cb; ping->callback->data = data; ping->callback->multiple = booleanize(multiple); } else { ping->callback = NULL; } hash_list_append(udp_pings, ping); return TRUE; }
/** * 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); }
/** * Look whether semi-reliable UDP header corresponds to valid traffic. * * This routine is only used for ambiguous traffic that looks like both * Gnutella and semi-reliable UDP: we want to make sure we're not mistaking * a legitimate semi-reliable fragment / ACK for a Gnutella message. * * @param utp already classified semi-reliable protocol * @param s socket which received the message * @param data received data * @param len length of data * * @return TRUE if message corresponds to valid semi-reliable UDP traffic. */ static bool udp_is_valid_semi_reliable(enum udp_traffic utp, const gnutella_socket_t *s, const void *data, size_t len) { struct ut_header uth; void *message = NULL; size_t msglen; bool valid = TRUE; /* * Since we're talking about an ambiguous message, it is highly unlikely * we'll ever be called with an acknowledgement: they should have been * ruled out earlier as improbable since ACKs are short message, much * shorter than a Gnuella header typically. * * So we'll only handle fragments for now, assuming ACKs are legitimate. */ gnet_stats_inc_general(GNR_UDP_AMBIGUOUS_DEEPER_INSPECTION); uth.count = udp_reliable_header_get_count(data); if (0 == uth.count) return TRUE; /* Acknoweldgments */ uth.part = udp_reliable_header_get_part(data) - 1; /* Zero-based */ uth.flags = udp_reliable_header_get_flags(data); uth.seqno = udp_reliable_header_get_seqno(data); /* * We're going to ask the RX layer about the message: is it a known * sequence ID for this host? * * This works only for messages with more than one fragment, of course, * but chances are that, for these, we would have possibly already * received another fragment, not mistaken as a Gnutella message... * * This is OK for acknowledged fragments: we're not going to acknowledge * the unprocessed fragment, but we'll receive other fragments of the * message, and later on we'll get a retransmission of the unprocessed * fragment, which this time will be validated since we have already * partially received the message. */ if (uth.count > 1) { rxdrv_t *rx; gnet_host_t from; gnet_host_set(&from, s->addr, s->port); rx = udp_get_rx_semi_reliable(utp, s->addr, len); return NULL == rx ? FALSE : ut_valid_message(rx, &uth, &from); } /* * We're facing a single-fragment message. * * We can trivially probe it and validate it to see whether it can still * be interpreted as a valid Gnutella message on its own... If the answer * is yes, then we can assert we're facing a valid semi-reliable UDP * message. * * For deflated payloads, we already validated that the start of the * payload is a well-formed zlib header, but we'll attempt deflation anyway * so we will know for sure whether it's a valid message! * * Of course we're doing here work that will have to be redone later when * processing the message, but this is for proper classification and not * happening very often: only on a very very small fraction of messages for * which there is a high level of ambiguity. */ g_assert(0 == uth.part); /* First (and only) fragment */ if (uth.flags & UDP_RF_DEFLATED) { int outlen = settings_max_msg_size(); int ret; message = xmalloc(outlen); ret = zlib_inflate_into( const_ptr_add_offset(data, UDP_RELIABLE_HEADER_SIZE), len - UDP_RELIABLE_HEADER_SIZE, message, &outlen); if (ret != Z_OK) { valid = FALSE; /* Does not inflate properly */ goto done; } msglen = outlen; } else { message = ptr_add_offset( deconstify_pointer(data), UDP_RELIABLE_HEADER_SIZE); msglen = len - UDP_RELIABLE_HEADER_SIZE; } switch (utp) { case SEMI_RELIABLE_GTA: /* * Assume message is valid if the Gnutella size header is consistent * with the length of the whole message. */ { uint16 size; switch (gmsg_size_valid(message, &size)) { case GMSG_VALID: case GMSG_VALID_MARKED: break; case GMSG_VALID_NO_PROCESS: /* Header flags undefined for now */ case GMSG_INVALID: valid = FALSE; goto done; } valid = (size_t) size + GTA_HEADER_SIZE == msglen; } break; case SEMI_RELIABLE_GND: valid = TRUE; /* For now */ break; case GNUTELLA: case DHT: case RUDP: case UNKNOWN: g_assert_not_reached(); } done: if (uth.flags & UDP_RF_DEFLATED) xfree(message); return valid; }
/** * Route query hits from one node to the other. */ void dh_route(gnutella_node_t *src, gnutella_node_t *dest, int count) { pmsg_t *mb; struct dh_pmsg_info *pmi; const struct guid *muid; dqhit_t *dh; mqueue_t *mq; g_assert( gnutella_header_get_function(&src->header) == GTA_MSG_SEARCH_RESULTS); g_assert(count >= 0); if (!NODE_IS_WRITABLE(dest)) goto drop_shutdown; muid = gnutella_header_get_muid(&src->header); dh = dh_locate(muid); g_assert(dh != NULL); /* Must have called dh_got_results() first! */ if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH #%s got %d hit%s: " "msg=%u, hits_recv=%u, hits_sent=%u, hits_queued=%u", guid_hex_str(muid), count, plural(count), dh->msg_recv, dh->hits_recv, dh->hits_sent, dh->hits_queued); } mq = dest->outq; /* * Can we forward the message? */ switch (dh_can_forward(dh, mq, FALSE)) { case DH_DROP_FC: goto drop_flow_control; case DH_DROP_THROTTLE: goto drop_throttle; case DH_DROP_TRANSIENT: goto drop_transient; case DH_FORWARD: default: break; } /* * Allow message through. */ WALLOC(pmi); pmi->hits = count; dh->hits_queued += count; dh->msg_queued++; g_assert(dh->hits_queued >= UNSIGNED(count)); /* * Magic: we create an extended version of a pmsg_t that contains a * free routine, which will be invoked when the message queue frees * the message. * * This enables us to track how much results we already queued/sent. */ if (NODE_IS_UDP(dest)) { gnet_host_t to; pmsg_t *mbe; gnet_host_set(&to, dest->addr, dest->port); /* * With GUESS we may route back a query hit to an UDP node. */ if (GNET_PROPERTY(guess_server_debug) > 19) { g_debug("GUESS sending %d hit%s (%s) for #%s to %s", count, plural(count), NODE_CAN_SR_UDP(dest) ? "reliably" : NODE_CAN_INFLATE(dest) ? "possibly deflated" : "uncompressed", guid_hex_str(muid), node_infostr(dest)); } /* * Attempt to compress query hit if the destination supports it. * * If we're going to send the hit using semi-reliable UDP, there's * no need to compress beforehand, since the transport layer will * attempt its own compression anyway. */ if (!NODE_CAN_SR_UDP(dest) && NODE_CAN_INFLATE(dest)) { mb = gmsg_split_to_deflated_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); if (gnutella_header_get_ttl(pmsg_start(mb)) & GTA_UDP_DEFLATED) gnet_stats_inc_general(GNR_UDP_TX_COMPRESSED); } else { mb = gmsg_split_to_pmsg(&src->header, src->data, src->size + GTA_HEADER_SIZE); } mbe = pmsg_clone_extend(mb, dh_pmsg_free, pmi); pmsg_free(mb); if (NODE_CAN_SR_UDP(dest)) pmsg_mark_reliable(mbe); mq_udp_putq(mq, mbe, &to); } else { mb = gmsg_split_to_pmsg_extend(&src->header, src->data, src->size + GTA_HEADER_SIZE, dh_pmsg_free, pmi); mq_tcp_putq(mq, mb, src); if (GNET_PROPERTY(dh_debug) > 19) { g_debug("DH enqueued %d hit%s for #%s to %s", count, plural(count), guid_hex_str(muid), node_infostr(dest)); } } return; drop_shutdown: gnet_stats_count_dropped(src, MSG_DROP_SHUTDOWN); return; drop_flow_control: gnet_stats_count_dropped(src, MSG_DROP_FLOW_CONTROL); gnet_stats_count_flowc(&src->header, TRUE); return; drop_throttle: gnet_stats_count_dropped(src, MSG_DROP_THROTTLE); return; drop_transient: gnet_stats_count_dropped(src, MSG_DROP_TRANSIENT); return; }
/** * Initiate an UDP RPC transaction. * * The message held in ``data'' is sent to the specified address and port. * Upon reception of a reply from that host, the callback is invoked. * If no reply is received after some time, the callaback is also invoked. * * @param what type of RPC, for logging (static string) * @param addr address where RPC should be sent to * @param port port where RPC should be sent to * @param data message data to send * @param len length of data to send * @param timeout timeout in milliseconds to get a reply * @param cb callback to invoke on reply or timeout * @param arg additionnal callback argument * * @return 0 if OK, -1 if we could not initiate the RPC, with errno set. */ int urpc_send(const char *what, host_addr_t addr, uint16 port, const void *data, size_t len, unsigned long timeout, urpc_cb_t cb, void *arg) { struct urpc_cb *ucb; struct gnutella_socket *s; host_addr_t bind_addr = zero_host_addr; gnet_host_t to; ssize_t r; /* * Create anonymous socket to send/receive the RPC. */ switch (host_addr_net(addr)) { case NET_TYPE_IPV4: bind_addr = ipv4_unspecified; break; case NET_TYPE_IPV6: bind_addr = ipv6_unspecified; break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: g_assert_not_reached(); } s = socket_udp_listen(bind_addr, 0, urpc_received); if (NULL == s) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to create anonymous UDP %s socket for %s RPC: %m", net_type_to_string(host_addr_net(bind_addr)), what); } return -1; } /* * Send the message. */ gnet_host_set(&to, addr, port); r = (s->wio.sendto)(&s->wio, &to, data, len); /* * Reset errno if there was no "real" error to prevent getting a * bogus and possibly misleading error message later. */ if ((ssize_t) -1 == r) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to send UDP %s RPC to %s: %m", what, host_addr_port_to_string(addr, port)); } } else { errno = 0; } if (len != UNSIGNED(r)) { if ((ssize_t) -1 != r) { if (GNET_PROPERTY(udp_debug)) { g_warning("unable to send whole %zu-byte UDP %s RPC to %s: " "only sent %zu byte%s", len, what, host_addr_port_to_string(addr, port), r, 1 == r ? "" : "s"); } } socket_free_null(&s); errno = EIO; return -1; } /* * Make sure socket_udp_event() will only process replies one at a time * since we're going to close the anonymous UDP socket as soon as we * get a reply. */ socket_set_single(s, TRUE); /* * Message was sent, wait for the answer. */ WALLOC(ucb); ucb->magic = URPC_CB_MAGIC; ucb->addr = addr; ucb->port = port; ucb->s = s; ucb->cb = cb; ucb->arg = arg; ucb->timeout_ev = cq_main_insert(timeout, urpc_timed_out, ucb); ucb->what = what; htable_insert(pending, s, ucb); return 0; }