/* This Work is under GPL provided with this work. This work is based on the java implementation of the Kademlia protocol. Kademlia: Peer-to-peer routing based on the XOR metric Copyright (C) 2002 Petar Maymounkov [petar@post.harvard.edu] http://kademlia.scs.cs.nyu.edu and This work is bassed on translation from Barry Dunne to C++ Copyright (C)2003 Barry Dunne (http://www.emule-project.net) */ #include "cygwin.h" #include "opcodes.h" #include "kademlia.h" #include "cHash.h" #include "cPeer.h" #include "cBucket.h" #include "cSearch.h" #include "cZone.h" #include "sTag.h" class TagList; class CTag; extern class cZone overnet; extern class cKademlia *kademlia; cSearch:: cSearch() {{{ m_created = time(NULL); m_searchTerms = NULL; }}} cSearch::~cSearch() {{{ if (m_searchTerms != NULL) free(m_searchTerms); cPeerList::const_iterator it; for (it = m_delete.begin(); it != m_delete.end(); it++) delete *it; m_responded.clear(); m_possible.clear(); }}} void cSearch::go(void) {{{ // Start with a lot of possible contacts, this is a fallback in case search stalls due to dead contacts if (m_possible.empty()) overnet.getClosestTo(m_target, 50, &m_possible); size_t count = 3; // Take top 3 possible if (m_possible.size() < count) count = m_possible.size(); cPeerMap::iterator it; for (size_t i=0; isecond; m_tried[it->first] = c; // Move to tried m_possible.erase(it); // and remove it from possible sendFindValue(m_target, c->ip, c->udp); // Send request } }}} void cSearch::jumpStart(void) {{{ if (m_possible.empty()) return; if (!m_responded.empty()) { // Remove any obsolete possible contacts cHash best = m_responded.begin()->first; cPeerMap::iterator it = m_possible.begin(); while (it != m_possible.end()) { cPeerMap::iterator next = it; next++; if (it->first < best) m_possible.erase(it); it = next; } } if (m_possible.empty()) return; class cPeer *c = m_possible.begin()->second; m_tried[m_possible.begin()->first] = c; // add to tried m_possible.erase(m_possible.begin()); // delete from possible sendFindValue(m_target, c->ip, c->udp); // Send request }}} void cSearch::sendFindValue (const uint8_t *target, uint32_t ip, uint16_t port) {{{ struct in_addr IP; IP.s_addr = ip; size_t LEN, len = LEN = 19; unsigned char BUF[19], *buf = BUF; ADD_U1 (&buf, &len, OP_EDONKEYHEADER); ADD_U1 (&buf, &len, OVERNET_REQUEST); ADD_U1 (&buf, &len, ((m_type == NODE)?OVERNET_FIND_NODE:OVERNET_FIND_VALUE)); ADD_HASH (&buf, &len, target); if (kademlia != NULL) kademlia->send_Raw(IP, port, BUF, LEN); }}} void cSearch::processResponse (const uint8_t *target, uint32_t fromIP, uint16_t fromPort, cPeerList *results) {{{ cPeerList::iterator response; // Remember the contacts to be deleted when finished for (response = results->begin(); response != results->end(); response++) if (m_type == NODE) {{{ // Not interested in responses for FIND_NODE, will be added to contacts by udp listener return; }}} cPeerMap::const_iterator tried; bool returnedCloser = false;; for (tried = m_tried.begin(); tried != m_tried.end(); tried++) { // Find the person who sent this cHash fromDistance = tried->first; cPeer *from = tried->second; m_responded[fromDistance] = from; // Add to list of people who responded if (from->ip != fromIP ) continue; if (from->udp != fromPort) continue; returnedCloser = false; for (response = results->begin(); response != results->end(); response++) { // Loop through their responses cPeer *c = *response; if (c->type != 0) continue; // Only interested in type 0 cHash distance = *(c->get_Hash()); xor_128(distance.hash, target); if (m_tried.count(distance) > 0) continue; // Ignore if already tried if (distance >= fromDistance) break; // If response is closer than 'from' returnedCloser = true; bool top = false; // If in top 3 responses if (m_best.size() < 3) top = true; else {{{ cPeerMap::iterator it = m_best.end(); it--; if (distance < it->first) { m_best.erase(it); m_best[distance] = c; top = true; } }}} if (top) { m_tried[distance] = c; // Add to tried sendFindValue(m_target, c->ip, c->udp); // Send request } else m_possible[distance] = c; // Add to possible break; } if (!returnedCloser) // Ask 'from' for the file if closest if(kademlia!=NULL) { struct in_addr ip; ip.s_addr = from->ip; kademlia->send_Raw(ip, from->udp, m_searchTerms, m_lenSearchTerms); class sFile *file = forHash(m_target); if (file != NULL) { kademlia->send_Publish(file->hash, file->size, file->Name()); } } } }}}