/** * Handle reception of a /Q2 */ static void g2_node_handle_q2(gnutella_node_t *n, const g2_tree_t *t) { const guid_t *muid; size_t paylen; const g2_tree_t *c; char *dn = NULL; char *md = NULL; uint32 iflags = 0; search_request_info_t sri; bool has_interest = FALSE; node_inc_rx_query(n); /* * As a G2 leaf, we cannot handle queries coming from UDP because we * are not supposed to get any! */ if (NODE_IS_UDP(n)) { g2_node_drop(G_STRFUNC, n, t, "coming from UDP"); return; } /* * The MUID of the query is the payload of the root node. */ muid = g2_tree_node_payload(t, &paylen); if (paylen != GUID_RAW_SIZE) { g2_node_drop(G_STRFUNC, n, t, "missing MUID"); return; } /* * Make sure we have never seen this query already. * * To be able to leverage on Gnutella's routing table to detect duplicates * over a certain lifespan, we are going to fake a minimal Gnutella header * with a message type of GTA_MSG_G2_SEARCH, which is never actually used * on the network. * * The TTL and hops are set to 1 and 0 initially, so that the message seems * to come from a neighbouring host and cannot be forwarded. * * When that is done, we will be able to call route_message() and have * all the necessary bookkeeping done for us. */ { struct route_dest dest; gnutella_header_set_muid(&n->header, muid); gnutella_header_set_function(&n->header, GTA_MSG_G2_SEARCH); gnutella_header_set_ttl(&n->header, 1); gnutella_header_set_hops(&n->header, 0); if (!route_message(&n, &dest)) return; /* Already accounted as duplicated, and logged */ } /* * Setup request information so that we can call search_request() * to process our G2 query. */ ZERO(&sri); sri.magic = SEARCH_REQUEST_INFO_MAGIC; /* * Handle the children of /Q2. */ G2_TREE_CHILD_FOREACH(t, c) { enum g2_q2_child ct = TOKENIZE(g2_tree_name(c), g2_q2_children); const char *payload; switch (ct) { case G2_Q2_DN: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == dn) { uint off = 0; /* Not NUL-terminated, need to h_strndup() it */ dn = h_strndup(payload, paylen); if (!query_utf8_decode(dn, &off)) { gnet_stats_count_dropped(n, MSG_DROP_MALFORMED_UTF_8); goto done; /* Drop the query */ } sri.extended_query = dn + off; sri.search_len = paylen - off; /* In bytes */ } break; case G2_Q2_I: if (!has_interest) iflags = g2_node_extract_interest(c); has_interest = TRUE; break; case G2_Q2_MD: payload = g2_tree_node_payload(c, &paylen); if (payload != NULL && NULL == md) { /* Not NUL-terminated, need to h_strndup() it */ md = h_strndup(payload, paylen); } break; case G2_Q2_NAT: sri.flags |= QUERY_F_FIREWALLED; break; case G2_Q2_SZR: /* Size limits */ if (g2_node_extract_size_request(c, &sri.minsize, &sri.maxsize)) sri.size_restrictions = TRUE; break; case G2_Q2_UDP: if (!sri.oob) g2_node_extract_udp(c, &sri, n); break; case G2_Q2_URN: g2_node_extract_urn(c, &sri); break; } } /* * When there is no /Q2/I, return a default set of information. */ if (!has_interest) iflags = G2_Q2_F_DFLT; /* * If there are meta-data, try to intuit which media types there are * looking for. * * The payload is XML looking like "<audio/>" or "<video/>" but there * can be attributes and we don't want to do a full XML parsing there. * Hence we'll base our analysis on simple lexical parsing, which is * why we call a routine to "intuit", not to "extract". * * Also, this is poorer than Gnutella's GGEP "M" because apparently there * can be only one single type, since the XML payload must obey some * kind of schema and there is an audio schema, a video schema, etc... * XML was just a wrong design choice there. */ if (md != NULL) sri.media_types = g2_node_intuit_media_type(md); /* * Validate the return address if OOB hit delivery is configured. */ if (sri.oob && !search_oob_is_allowed(n, &sri)) goto done; /* * Update statistics, as done in search_request_preprocess() for Gnutella. */ if (sri.exv_sha1cnt) { gnet_stats_inc_general(GNR_QUERY_G2_SHA1); if (NULL == dn) { int i; for (i = 0; i < sri.exv_sha1cnt; i++) { search_request_listener_emit(QUERY_SHA1, sha1_base32(&sri.exv_sha1[i].sha1), n->addr, n->port); } } } if (dn != NULL && !is_ascii_string(dn)) gnet_stats_inc_general(GNR_QUERY_G2_UTF8); if (dn != NULL) search_request_listener_emit(QUERY_STRING, dn, n->addr, n->port); if (!search_is_valid(n, 0, &sri)) goto done; /* * Perform the query. */ sri.g2_query = TRUE; sri.partials = booleanize(iflags & G2_Q2_F_PFS); sri.g2_wants_url = booleanize(iflags & G2_Q2_F_URL); sri.g2_wants_alt = booleanize(iflags & G2_Q2_F_A); sri.g2_wants_dn = booleanize(iflags & G2_Q2_F_DN); search_request(n, &sri, NULL); done: HFREE_NULL(dn); HFREE_NULL(md); }
void CServer::handle_request(const char *request) { const char *p; int cmd_len; int ok; logger->log(3, "Got request: %s", request); p = strchr(request,' '); if (p == 0) { p = request + strlen(request); } cmd_len = p - request; while (*p == ' ') { p++; } ok = 0; if (cmd_len == 0) { logger->log(2, "Empty request!"); return; } char *command = new char[cmd_len+1]; memcpy(command, request, cmd_len); command[cmd_len] = 0; try { if (strcmp(command, "SEARCH") == 0) { ok = 1; search_request(p); } if (strcmp(command, "COUNT") == 0) { ok = 1; count_request(); } if (strcmp(command, "DELETEHOST") == 0) { ok = 1; delete_request(p); } if (strcmp(command, "LOAD") == 0) { ok = 1; load_request(p); } if (strcmp(command, "STATUS") == 0) { ok = 1; status_request(); } if (strcmp(command, "HOSTINFO") == 0) { ok = 1; host_info_request(p); } if (strcmp(command, "HOSTSTAT") == 0) { ok = 1; host_stat_request(p); } if (strcmp(command, "CACHEINFO") == 0) { ok = 1; cache_info_request(); } if (strcmp(command, "UPDATEHOSTS") == 0) { ok = 1; update_hosts_request(p); } if (strcmp(command, "SERVERSTAT") == 0) { ok = 1; server_stat_request(); } if (strcmp(command, "DEBUG") == 0) { ok = 1; debug_request(p); } if (strcmp(command, "DUMP") == 0) { ok = 1; dump_state_request(); } if (strcmp(command, "FULLSCAN") == 0) { ok = 1; enable_full_request(p); } if (strcmp(command, "WORDORDER") == 0) { ok = 1; word_order_request(); } if (strcmp(command, "CLEARPAGECACHE") == 0) { ok = 1; clear_pagecache_request(); } if (strcmp(command, "CLEARQUERYCACHE") == 0) { ok = 1; clear_querycache_request(); } if (strcmp(command, "DUMPFILES") == 0) { ok = 1; dump_files_request(p); } if (strcmp(command, "DUMPINDEX") == 0) { ok = 1; dump_index_request(p); } if (!ok) { throw std::runtime_error(std::string("Unrecognized request: ") + request); } } catch (std::runtime_error &e) { logger->log(1, "Error: %s", e.what()); send_string("ERROR"); } delete[] command; m_request_count++; }