/** * Handle message coming from G2 node. */ void g2_node_handle(gnutella_node_t *n) { g2_tree_t *t; size_t plen; enum g2_msg type; node_check(n); g_assert(NODE_TALKS_G2(n)); t = g2_frame_deserialize(n->data, n->size, &plen, FALSE); if (NULL == t) { if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) { g_warning("%s(): cannot deserialize /%s from %s", G_STRFUNC, g2_msg_raw_name(n->data, n->size), node_infostr(n)); } if (GNET_PROPERTY(log_bad_g2)) dump_hex(stderr, "G2 Packet", n->data, n->size); return; } else if (plen != n->size) { if (GNET_PROPERTY(g2_debug) > 0 || GNET_PROPERTY(log_bad_g2)) { g_warning("%s(): consumed %zu bytes but /%s from %s had %u", G_STRFUNC, plen, g2_msg_raw_name(n->data, n->size), node_infostr(n), n->size); } if (GNET_PROPERTY(log_bad_g2)) dump_hex(stderr, "G2 Packet", n->data, n->size); hostiles_dynamic_add(n->addr, "cannot parse incoming messages", HSTL_GIBBERISH); goto done; } else if (GNET_PROPERTY(g2_debug) > 19) { g_debug("%s(): received packet from %s", G_STRFUNC, node_infostr(n)); g2_tfmt_tree_dump(t, stderr, G2FMT_O_PAYLEN); } type = g2_msg_name_type(g2_tree_name(t)); switch (type) { case G2_MSG_PI: g2_node_handle_ping(n, t); break; case G2_MSG_PO: g2_node_handle_pong(n, t); break; case G2_MSG_LNI: g2_node_handle_lni(n, t); break; case G2_MSG_KHL: g2_node_handle_khl(t); break; case G2_MSG_PUSH: handle_push_request(n, t); break; case G2_MSG_Q2: g2_node_handle_q2(n, t); break; case G2_MSG_QA: case G2_MSG_QKA: g2_node_handle_rpc_answer(n, t, type); break; case G2_MSG_QH2: search_g2_results(n, t); break; default: g2_node_drop(G_STRFUNC, n, t, "default"); break; } done: g2_tree_free_null(&t); }
/** * 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: hostiles_dynamic_add(n->addr, "improper Gnutella header", HSTL_GIBBERISH); msg = "Header flags undefined for now"; goto drop; case GMSG_INVALID: hostiles_dynamic_add(n->addr, "invalid Gnutella header size", HSTL_GIBBERISH); 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)) { hostiles_flags_t flags; /* * Do not pollute logs with errors from messages coming from known * hostile addresses: no dumping of datagram, and flag the host as * hostile anyway so that we know. */ flags = hostiles_check(s->addr); g_warning("UDP got invalid %sGnutella packet (%zu byte%s) " "\"%s\" %sfrom %s%s: %s", socket_udp_is_old(s) ? "OLD " : "", len, plural(len), len >= GTA_HEADER_SIZE ? gmsg_infostr_full_split(header, payload, len - GTA_HEADER_SIZE) : "<incomplete Gnutella header>", truncated ? "(truncated) " : "", (flags & HSTL_STATIC) ? "static hostile " : "", NULL == n ? host_addr_port_to_string(s->addr, s->port) : node_infostr(n), msg); if (len != 0 && !(flags & HSTL_STATIC)) { if (len <= GTA_HEADER_SIZE) { dump_hex(stderr, "UDP datagram", header, len); } else { 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 */ }