mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-06 13:18:06 -05:00
d6938721b0
Made the query more robust, by only saying a query is SUCCESSFUL if we have recvd a reply from peer. NB: These changes are yet to be tested! git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-netupgrade@4439 b45a01b8-16f6-495d-af2f-9b41ad6348cc
1100 lines
28 KiB
C++
1100 lines
28 KiB
C++
|
|
/*
|
|
* bitdht/bdquery.cc
|
|
*
|
|
* BitDHT: An Flexible DHT library.
|
|
*
|
|
* Copyright 2010 by Robert Fernie
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License Version 3 as published by the Free Software Foundation.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA.
|
|
*
|
|
* Please report all bugs and problems to "bitdht@lunamutt.com".
|
|
*
|
|
*/
|
|
|
|
|
|
#include "bitdht/bdquery.h"
|
|
#include "util/bdnet.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
|
|
/**
|
|
* #define DEBUG_QUERY 1
|
|
**/
|
|
|
|
#define EXPECTED_REPLY 10 // Speed up queries
|
|
#define QUERY_IDLE_RETRY_PEER_PERIOD 300 // 5min = (mFns->bdNodesPerBucket() * 30)
|
|
#define MAX_QUERY_IDLE_PERIOD 900 // 15min.
|
|
|
|
|
|
/************************************************************
|
|
* bdQuery logic:
|
|
* 1) as replies come in ... maintain list of M closest peers to ID.
|
|
* 2) select non-queried peer from list, and query.
|
|
* 3) halt when we have asked all M closest peers about the ID.
|
|
*
|
|
* Flags can be set to disguise the target of the search.
|
|
* This involves
|
|
*/
|
|
|
|
bdQuery::bdQuery(const bdNodeId *id, std::list<bdId> &startList, uint32_t queryFlags,
|
|
bdDhtFunctions *fns)
|
|
{
|
|
/* */
|
|
mId = *id;
|
|
mFns = fns;
|
|
|
|
time_t now = time(NULL);
|
|
std::list<bdId>::iterator it;
|
|
for(it = startList.begin(); it != startList.end(); it++)
|
|
{
|
|
bdPeer peer;
|
|
peer.mLastSendTime = 0;
|
|
peer.mLastRecvTime = 0;
|
|
peer.mPeerFlags = 0;
|
|
peer.mFoundTime = now;
|
|
peer.mPeerId = *it;
|
|
|
|
bdMetric dist;
|
|
|
|
mFns->bdDistance(&mId, &(peer.mPeerId.id), &dist);
|
|
|
|
mClosest.insert(std::pair<bdMetric, bdPeer>(dist, peer));
|
|
|
|
|
|
}
|
|
|
|
mState = BITDHT_QUERY_QUERYING;
|
|
mQueryFlags = queryFlags;
|
|
mQueryTS = now;
|
|
mSearchTime = 0;
|
|
mClosestListSize = (int) (1.5 * mFns->bdNodesPerBucket());
|
|
mPotPeerCleanTS = now;
|
|
|
|
mQueryIdlePeerRetryPeriod = QUERY_IDLE_RETRY_PEER_PERIOD;
|
|
mRequiredPeerFlags = BITDHT_PEER_STATUS_DHT_ENGINE_VERSION; // XXX to update later.
|
|
|
|
/* setup the limit of the search
|
|
* by default it is setup to 000000 = exact match
|
|
*/
|
|
bdZeroNodeId(&mLimit);
|
|
}
|
|
|
|
bool bdQuery::result(std::list<bdId> &answer)
|
|
{
|
|
/* get all the matches to our query */
|
|
std::multimap<bdMetric, bdPeer>::iterator sit, eit;
|
|
sit = mClosest.begin();
|
|
eit = mClosest.upper_bound(mLimit);
|
|
int i = 0;
|
|
for(; sit != eit; sit++)
|
|
{
|
|
if ((sit->second).mLastRecvTime != 0)
|
|
{
|
|
answer.push_back(sit->second.mPeerId);
|
|
i++;
|
|
}
|
|
}
|
|
return (i > 0);
|
|
}
|
|
|
|
|
|
int bdQuery::nextQuery(bdId &id, bdNodeId &targetNodeId)
|
|
{
|
|
if ((mState != BITDHT_QUERY_QUERYING) && !(mQueryFlags & BITDHT_QFLAGS_DO_IDLE))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Query is done\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* search through through list, find closest not queried */
|
|
time_t now = time(NULL);
|
|
|
|
/* update IdlePeerRetry */
|
|
if ((now - mQueryTS) / 2 > mQueryIdlePeerRetryPeriod)
|
|
{
|
|
mQueryIdlePeerRetryPeriod = (now-mQueryTS) / 2;
|
|
if (mQueryIdlePeerRetryPeriod > MAX_QUERY_IDLE_PERIOD)
|
|
{
|
|
mQueryIdlePeerRetryPeriod = MAX_QUERY_IDLE_PERIOD;
|
|
}
|
|
}
|
|
|
|
bool notFinished = false;
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
int i = 0;
|
|
for(it = mClosest.begin(); it != mClosest.end(); it++, i++)
|
|
{
|
|
bool queryPeer = false;
|
|
|
|
/* if never queried */
|
|
if (it->second.mLastSendTime == 0)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Found non-sent peer. queryPeer = true : ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
std::cerr << std::endl;
|
|
#endif
|
|
queryPeer = true;
|
|
}
|
|
|
|
/* re-request every so often */
|
|
if ((!queryPeer) && (mQueryFlags & BITDHT_QFLAGS_DO_IDLE) &&
|
|
(now - it->second.mLastSendTime > mQueryIdlePeerRetryPeriod))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Found out-of-date. queryPeer = true : ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
std::cerr << std::endl;
|
|
#endif
|
|
queryPeer = true;
|
|
}
|
|
|
|
/* expecting every peer to be up-to-date is too hard...
|
|
* enough just to have received lists from each
|
|
* - replacement policy will still work.
|
|
*
|
|
* Need to wait at least EXPECTED_REPLY, to make sure their answers are pinged
|
|
*/
|
|
|
|
if (((it->second.mLastRecvTime == 0) || (now - it->second.mLastRecvTime < EXPECTED_REPLY)) &&
|
|
(i < mFns->bdNodesPerBucket()))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Never Received @Idx(%d) notFinished = true: ", i);
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
std::cerr << std::endl;
|
|
#endif
|
|
notFinished = true;
|
|
}
|
|
|
|
if (queryPeer)
|
|
{
|
|
id = it->second.mPeerId;
|
|
it->second.mLastSendTime = now;
|
|
|
|
if (mQueryFlags & BITDHT_QFLAGS_DISGUISE)
|
|
{
|
|
/* calc Id mid point between Target and Peer */
|
|
bdNodeId midRndId;
|
|
mFns->bdRandomMidId(&mId, &(id.id), &midRndId);
|
|
|
|
targetNodeId = midRndId;
|
|
}
|
|
else
|
|
{
|
|
targetNodeId = mId;
|
|
}
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Querying Peer: ");
|
|
mFns->bdPrintId(std::cerr, &id);
|
|
std::cerr << std::endl;
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* allow query to run for a minimal amount of time
|
|
* This is important as startup - when we might not have any peers.
|
|
* Probably should be handled elsewhere.
|
|
*/
|
|
time_t age = now - mQueryTS;
|
|
|
|
if (age < BITDHT_MIN_QUERY_AGE)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() under Min Time: Query not finished / No Query\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (age > BITDHT_MAX_QUERY_AGE)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() over Max Time: Query force to Finish\n");
|
|
#endif
|
|
/* fall through and stop */
|
|
}
|
|
else if ((mClosest.size() < mFns->bdNodesPerBucket()) || (notFinished))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() notFinished | !size(): Query not finished / No Query\n");
|
|
#endif
|
|
/* not full yet... */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "NextQuery() Finished\n");
|
|
#endif
|
|
|
|
/* if we get here - query finished */
|
|
if (mState == BITDHT_QUERY_QUERYING)
|
|
{
|
|
/* store query time */
|
|
mSearchTime = now - mQueryTS;
|
|
}
|
|
|
|
/* cleanup PotentialPeers before doing the final State */;
|
|
removeOldPotentialPeers();
|
|
/* check if we found the node */
|
|
if (mClosest.size() > 0)
|
|
{
|
|
if (((mClosest.begin()->second).mPeerId.id == mId) &&
|
|
((mClosest.begin()->second).mLastRecvTime != 0))
|
|
{
|
|
mState = BITDHT_QUERY_SUCCESS;
|
|
}
|
|
else if ((mPotentialPeers.begin()->second).mPeerId.id == mId)
|
|
{
|
|
mState = BITDHT_QUERY_PEER_UNREACHABLE;
|
|
}
|
|
else
|
|
{
|
|
mState = BITDHT_QUERY_FOUND_CLOSEST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mState = BITDHT_QUERY_FAILURE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int bdQuery::addClosestPeer(const bdId *id, uint32_t mode)
|
|
{
|
|
bdMetric dist;
|
|
time_t ts = time(NULL);
|
|
|
|
mFns->bdDistance(&mId, &(id->id), &dist);
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "bdQuery::addPeer(");
|
|
mFns->bdPrintId(std::cerr, id);
|
|
fprintf(stderr, ", %u)\n", mode);
|
|
#endif
|
|
|
|
std::multimap<bdMetric, bdPeer>::iterator it, sit, eit;
|
|
sit = mClosest.lower_bound(dist);
|
|
eit = mClosest.upper_bound(dist);
|
|
int i = 0;
|
|
int actualCloser = 0;
|
|
int toDrop = 0;
|
|
// switched end condition to upper_bound to provide stability for NATTED peers.
|
|
// we will favour the older entries!
|
|
for(it = mClosest.begin(); it != eit; it++, i++, actualCloser++)
|
|
{
|
|
time_t sendts = ts - it->second.mLastSendTime;
|
|
bool hasSent = (it->second.mLastSendTime != 0);
|
|
bool hasReply = (it->second.mLastRecvTime >= it->second.mLastSendTime);
|
|
if ((hasSent) && (!hasReply) && (sendts > EXPECTED_REPLY))
|
|
{
|
|
i--; /* dont count this one */
|
|
toDrop++;
|
|
}
|
|
}
|
|
// Counts where we are.
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Searching.... %di = %d - %d peers closer than this one\n", i, actualCloser, toDrop);
|
|
#endif
|
|
|
|
|
|
if (i > mClosestListSize - 1)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Distance to far... dropping\n");
|
|
#endif
|
|
/* drop it */
|
|
return 0;
|
|
}
|
|
|
|
for(it = sit; it != eit; it++, i++)
|
|
{
|
|
/* full id check */
|
|
if (mFns->bdSimilarId(id, &(it->second.mPeerId)))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Peer Already here!\n");
|
|
#endif
|
|
if (mode)
|
|
{
|
|
/* also update port from incoming id, as we have definitely recved from it */
|
|
if (mFns->bdUpdateSimilarId(&(it->second.mPeerId), id))
|
|
{
|
|
/* updated it... must be Unstable */
|
|
it->second.mExtraFlags |= BITDHT_PEER_EXFLAG_UNSTABLE;
|
|
}
|
|
}
|
|
if (mode & BITDHT_PEER_STATUS_RECV_NODES)
|
|
{
|
|
/* only update recvTime if sendTime > checkTime.... (then its our query) */
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Updating LastRecvTime\n");
|
|
#endif
|
|
it->second.mLastRecvTime = ts;
|
|
it->second.mPeerFlags |= mode;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Peer not in Query\n");
|
|
#endif
|
|
/* firstly drop unresponded (bit ugly - but hard structure to extract from) */
|
|
int j;
|
|
for(j = 0; j < toDrop; j++)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Dropping Peer that dont reply\n");
|
|
#endif
|
|
bool removed = false;
|
|
for(it = mClosest.begin(); it != mClosest.end(); ++it)
|
|
{
|
|
time_t sendts = ts - it->second.mLastSendTime;
|
|
bool hasSent = (it->second.mLastSendTime != 0);
|
|
bool hasReply = (it->second.mLastRecvTime >= it->second.mLastSendTime);
|
|
|
|
if ((hasSent) && (!hasReply) && (sendts > EXPECTED_REPLY))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Dropped: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
mClosest.erase(it);
|
|
removed = true;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* trim it back */
|
|
while(mClosest.size() > (uint32_t) (mClosestListSize - 1))
|
|
{
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
it = mClosest.end();
|
|
if (!mClosest.empty())
|
|
{
|
|
it--;
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Removing Furthest Peer: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
mClosest.erase(it);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "bdQuery::addPeer(): Closer Peer!: ");
|
|
mFns->bdPrintId(std::cerr, id);
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
/* add it in */
|
|
bdPeer peer;
|
|
peer.mPeerId = *id;
|
|
peer.mPeerFlags = mode;
|
|
peer.mLastSendTime = 0;
|
|
peer.mLastRecvTime = 0;
|
|
peer.mFoundTime = ts;
|
|
|
|
if (mode & BITDHT_PEER_STATUS_RECV_NODES)
|
|
{
|
|
peer.mLastRecvTime = ts;
|
|
}
|
|
|
|
mClosest.insert(std::pair<bdMetric, bdPeer>(dist, peer));
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
********************************* Add Peer Interface *************************************
|
|
*******************************************************************************************/
|
|
|
|
/**** These functions are called by bdNode to add peers to the query
|
|
* They add/update the three sets of lists.
|
|
*
|
|
* int bdQuery::addPeer(const bdId *id, uint32_t mode)
|
|
* Proper message from a peer.
|
|
*
|
|
* int bdQuery::addPotentialPeer(const bdId *id, const bdId *src, uint32_t srcmode)
|
|
* This returns 1 if worthy of pinging, 0 if to ignore.
|
|
*/
|
|
|
|
#define PEER_MESSAGE 0
|
|
#define FIND_NODE_RESPONSE 1
|
|
|
|
int bdQuery::addPeer(const bdId *id, uint32_t mode)
|
|
{
|
|
addClosestPeer(id, mode);
|
|
updatePotentialPeer(id, mode, PEER_MESSAGE);
|
|
updateProxy(id, mode);
|
|
return 1;
|
|
}
|
|
|
|
int bdQuery::addPotentialPeer(const bdId *id, const bdId *src, uint32_t srcmode)
|
|
{
|
|
// is it a Potential Proxy? Always Check This.
|
|
addProxy(id, src, srcmode);
|
|
|
|
int worthy = worthyPotentialPeer(id);
|
|
int shouldPing = 0;
|
|
if (worthy)
|
|
{
|
|
shouldPing = updatePotentialPeer(id, 0, FIND_NODE_RESPONSE);
|
|
}
|
|
return shouldPing;
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
********************************* Closest Peer ********************************************
|
|
*******************************************************************************************/
|
|
|
|
|
|
/*******************************************************************************************
|
|
******************************** Potential Peer *******************************************
|
|
*******************************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*******
|
|
* Potential Peers are a list of the closest answers to our queries.
|
|
* Lots of these peers will not be reachable.... so will only exist in this list.
|
|
* They will also never have there PeerFlags set ;(
|
|
*
|
|
*/
|
|
|
|
|
|
/*** utility functions ***/
|
|
|
|
int bdQuery::worthyPotentialPeer(const bdId *id)
|
|
{
|
|
bdMetric dist;
|
|
mFns->bdDistance(&mId, &(id->id), &dist);
|
|
|
|
#ifdef DEBUG_QUERY
|
|
std::cerr << "bdQuery::worthyPotentialPeer(";
|
|
mFns->bdPrintId(std::cerr, id);
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
/* we check if this is a worthy potential peer....
|
|
* if it is already in mClosest -> false. old peer.
|
|
* if it is > mClosest.rbegin() -> false. too far way.
|
|
*/
|
|
int retval = 1;
|
|
|
|
std::multimap<bdMetric, bdPeer>::iterator it, sit, eit;
|
|
sit = mClosest.lower_bound(dist);
|
|
eit = mClosest.upper_bound(dist);
|
|
|
|
/* check if outside range, & bucket is full */
|
|
if ((sit == mClosest.end()) && (mClosest.size() >= mFns->bdNodesPerBucket()))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Peer to far away for Potential\n");
|
|
#endif
|
|
return 0; /* too far way */
|
|
}
|
|
|
|
|
|
for(it = sit; it != eit; it++)
|
|
{
|
|
if (mFns->bdSimilarId(id, &(it->second.mPeerId)))
|
|
{
|
|
// Not updating Full Peer Id here... as inspection function.
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Peer already in mClosest\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1; /* either within mClosest Range (but not there!), or there aren't enough peers */
|
|
}
|
|
|
|
|
|
/*****
|
|
*
|
|
* mLastSendTime ... is the last FIND_NODE_RESPONSE that we returned 1. (indicating to PING).
|
|
* mLastRecvTime ... is the last time we received an updatei about/from them
|
|
*
|
|
* The update is dependent on the flags passed in the function call. (saves duplicate code).
|
|
*
|
|
*
|
|
* XXX IMPORTANT TO DECIDE WHAT IS RETURNED HERE.
|
|
* original algorithm return 0 if exists in potential peers, 1 if unknown.
|
|
* This is used to limit the number of pings to non-responding potentials.
|
|
*
|
|
* MUST think about this. Need to install HISTORY tracking again. to look at the statistics.
|
|
*
|
|
* It is important that the potential Peers list extends all the way back to == mClosest().end().
|
|
* Otherwise we end up with [TARGET] .... [ POTENTIAL ] ..... [ CLOSEST ] ......
|
|
* and the gap between POT and CLOSEST will get hammered with pings.
|
|
*
|
|
*/
|
|
|
|
#define MIN_PING_POTENTIAL_PERIOD 300
|
|
|
|
int bdQuery::updatePotentialPeer(const bdId *id, uint32_t mode, uint32_t addType)
|
|
{
|
|
bdMetric dist;
|
|
time_t now = time(NULL);
|
|
|
|
mFns->bdDistance(&mId, &(id->id), &dist);
|
|
|
|
std::multimap<bdMetric, bdPeer>::iterator it, sit, eit;
|
|
sit = mPotentialPeers.lower_bound(dist);
|
|
eit = mPotentialPeers.upper_bound(dist);
|
|
|
|
bool found = false;
|
|
for(it = sit; it != eit; it++)
|
|
{
|
|
if (mFns->bdSimilarId(id, &(it->second.mPeerId)))
|
|
{
|
|
found = true;
|
|
|
|
it->second.mPeerFlags |= mode;
|
|
it->second.mLastRecvTime = now;
|
|
if (addType == FIND_NODE_RESPONSE)
|
|
{
|
|
// We could lose peers here by not updating port... but should be okay.
|
|
if (now - it->second.mLastSendTime > MIN_PING_POTENTIAL_PERIOD)
|
|
{
|
|
it->second.mLastSendTime = now;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (mode)
|
|
{
|
|
/* also update port from incoming id, as we have definitely recved from it */
|
|
if (mFns->bdUpdateSimilarId(&(it->second.mPeerId), id))
|
|
{
|
|
/* updated it... must be Unstable */
|
|
it->second.mExtraFlags |= BITDHT_PEER_EXFLAG_UNSTABLE;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Removing this check - so that we can have varying length PotentialPeers.
|
|
// Peer will always be added, then probably removed straight away.
|
|
|
|
#if 0
|
|
/* check if outside range, & bucket is full */
|
|
if ((sit == mPotentialPeers.end()) && (mPotentialPeers.size() >= mFns->bdNodesPerBucket()))
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Peer to far away for Potential\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* add it in */
|
|
bdPeer peer;
|
|
peer.mPeerId = *id;
|
|
peer.mPeerFlags = mode;
|
|
peer.mFoundTime = now;
|
|
peer.mLastRecvTime = now;
|
|
peer.mLastSendTime = 0;
|
|
if (addType == FIND_NODE_RESPONSE)
|
|
{
|
|
peer.mLastSendTime = now;
|
|
}
|
|
|
|
mPotentialPeers.insert(std::pair<bdMetric, bdPeer>(dist, peer));
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Flagging as Potential Peer!\n");
|
|
#endif
|
|
|
|
trimPotentialPeers_toClosest();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int bdQuery::trimPotentialPeers_FixedLength()
|
|
{
|
|
/* trim it back */
|
|
while(mPotentialPeers.size() > (uint32_t) (mFns->bdNodesPerBucket()))
|
|
{
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
it = mPotentialPeers.end();
|
|
it--; // must be more than 1 peer here?
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Removing Furthest Peer: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
mPotentialPeers.erase(it);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
int bdQuery::trimPotentialPeers_toClosest()
|
|
{
|
|
if (mPotentialPeers.size() <= (uint32_t) (mFns->bdNodesPerBucket()))
|
|
return 1;
|
|
|
|
std::multimap<bdMetric, bdPeer>::reverse_iterator it;
|
|
it = mClosest.rbegin();
|
|
bdMetric lastClosest = it->first;
|
|
|
|
/* trim it back */
|
|
while(mPotentialPeers.size() > (uint32_t) (mFns->bdNodesPerBucket()))
|
|
{
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
it = mPotentialPeers.end();
|
|
it--; // must be more than 1 peer here?
|
|
if (lastClosest < it->first)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Removing Furthest Peer: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
mPotentialPeers.erase(it);
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* as Potential Peeers are to determine if a peer is CLOSEST or UNREACHABLE
|
|
* we need to drop ones that we haven't heard about in ages.
|
|
*
|
|
* only do this in IDLE mode...
|
|
* The timeout period is dependent on our RetryPeriod.
|
|
*/
|
|
|
|
#define POT_PEER_CLEAN_PERIOD 60
|
|
#define POT_PEER_RECV_TIMEOUT_PERIOD (mQueryIdlePeerRetryPeriod + EXPECTED_REPLY)
|
|
|
|
int bdQuery::removeOldPotentialPeers()
|
|
{
|
|
if (!(mQueryFlags & BITDHT_QFLAGS_DO_IDLE))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
time_t now = time(NULL);
|
|
if (now - mPotPeerCleanTS < POT_PEER_CLEAN_PERIOD)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
mPotPeerCleanTS = now;
|
|
|
|
/* painful loop */
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
for(it = mPotentialPeers.begin(); it != mPotentialPeers.end();)
|
|
{
|
|
/* which timestamp do we care about? */
|
|
if (now - it->second.mLastRecvTime > POT_PEER_RECV_TIMEOUT_PERIOD)
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
std::cerr << "bdQuery::removeOldPotentialPeers() removing: ";
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
std::multimap<bdMetric, bdPeer>::iterator it2 = it;
|
|
++it2 ;
|
|
mPotentialPeers.erase(it);
|
|
it = it2 ;
|
|
|
|
// Unfortunately have to start again... as pointers invalidated.
|
|
//it = mPotentialPeers.begin();
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
return 1 ;
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************************
|
|
******************************** Potential Proxies ****************************************
|
|
*******************************************************************************************/
|
|
|
|
/********
|
|
* Potential Proxies. a list of peers that have returned our target in response to a query.
|
|
*
|
|
* We are particularly interested in peers with specific flags...
|
|
* But all the peers have been pinged already by the time they reach this list.
|
|
* So there are two options:
|
|
* 1) Track everythings mode history - which is a waste of resources.
|
|
* 2) Store the list, and ping later.
|
|
*
|
|
* We will store these in two lists: Flags & Unknown.
|
|
* we keep the most recent of each, and move around as required.
|
|
*
|
|
* we could also check the Closest/PotentialPeer lists to grab the flags,
|
|
* for an unknown peer?
|
|
*
|
|
* All Functions manipulating PotentialProxies are here.
|
|
* We need several functions:
|
|
*
|
|
* For Extracting Proxies.
|
|
bool bdQuery::proxies(std::list<bdId> &answer)
|
|
bool bdQuery::potentialProxies(std::list<bdId> &answer)
|
|
*
|
|
* For Adding/Updating Proxies.
|
|
int bdQuery::addProxy(const bdId *id, const bdId *src, uint32_t srcmode)
|
|
int bdQuery::updateProxy(const bdId *id, uint32_t mode)
|
|
*
|
|
*/
|
|
|
|
/*** Two Functions to extract Proxies... ***/
|
|
bool bdQuery::proxies(std::list<bdId> &answer)
|
|
{
|
|
/* get all the matches to our query */
|
|
std::list<bdPeer>::iterator it;
|
|
int i = 0;
|
|
for(it = mProxiesFlagged.begin(); it != mProxiesFlagged.end(); it++, i++)
|
|
{
|
|
answer.push_back(it->mPeerId);
|
|
}
|
|
return (i > 0);
|
|
}
|
|
|
|
bool bdQuery::potentialProxies(std::list<bdId> &answer)
|
|
{
|
|
/* get all the matches to our query */
|
|
std::list<bdPeer>::iterator it;
|
|
int i = 0;
|
|
for(it = mProxiesUnknown.begin(); it != mProxiesUnknown.end(); it++, i++)
|
|
{
|
|
answer.push_back(it->mPeerId);
|
|
}
|
|
return (i > 0);
|
|
}
|
|
|
|
|
|
|
|
int bdQuery::addProxy(const bdId *id, const bdId *src, uint32_t srcmode)
|
|
{
|
|
bdMetric dist;
|
|
time_t now = time(NULL);
|
|
|
|
mFns->bdDistance(&mId, &(id->id), &dist);
|
|
|
|
/* finally if it is an exact match, add as potential proxy */
|
|
int bucket = mFns->bdBucketDistance(&dist);
|
|
if ((bucket != 0) || (src == NULL))
|
|
{
|
|
/* not a potential proxy */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Bucket = 0, Have Potential Proxy!\n");
|
|
#endif
|
|
|
|
bool found = false;
|
|
if (updateProxyList(src, srcmode, mProxiesUnknown))
|
|
{
|
|
found = true;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
if (updateProxyList(src, srcmode, mProxiesFlagged))
|
|
{
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
/* if we get here. its not in the list */
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Adding Source to Proxy List:\n");
|
|
#endif
|
|
bdPeer peer;
|
|
peer.mPeerId = *src;
|
|
peer.mPeerFlags = srcmode;
|
|
peer.mLastSendTime = 0;
|
|
peer.mLastRecvTime = now;
|
|
peer.mFoundTime = now;
|
|
|
|
/* add it in */
|
|
if ((srcmode & mRequiredPeerFlags) == mRequiredPeerFlags)
|
|
{
|
|
mProxiesFlagged.push_front(peer);
|
|
}
|
|
else
|
|
{
|
|
mProxiesUnknown.push_front(peer);
|
|
}
|
|
}
|
|
|
|
|
|
trimProxies();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int bdQuery::updateProxy(const bdId *id, uint32_t mode)
|
|
{
|
|
if (!updateProxyList(id, mode, mProxiesUnknown))
|
|
{
|
|
updateProxyList(id, mode, mProxiesFlagged);
|
|
}
|
|
|
|
trimProxies();
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**** Utility functions that do all the work! ****/
|
|
|
|
int bdQuery::updateProxyList(const bdId *id, uint32_t mode, std::list<bdPeer> &searchProxyList)
|
|
{
|
|
std::list<bdPeer>::iterator it;
|
|
for(it = searchProxyList.begin(); it != searchProxyList.end(); it++)
|
|
{
|
|
if (mFns->bdSimilarId(id, &(it->mPeerId)))
|
|
{
|
|
/* found it ;( */
|
|
#ifdef DEBUG_QUERY
|
|
std::cerr << "bdQuery::updateProxyList() Found peer, updating";
|
|
std::cerr << std::endl;
|
|
#endif
|
|
|
|
time_t now = time(NULL);
|
|
if (mode)
|
|
{
|
|
/* also update port from incoming id, as we have definitely recved from it */
|
|
if (mFns->bdUpdateSimilarId(&(it->mPeerId), id))
|
|
{
|
|
/* updated it... must be Unstable */
|
|
it->mExtraFlags |= BITDHT_PEER_EXFLAG_UNSTABLE;
|
|
}
|
|
}
|
|
it->mPeerFlags |= mode;
|
|
it->mLastRecvTime = now;
|
|
|
|
/* now move it to the front of required list...
|
|
* note this could be exactly the same list as &searchProxyList, or a different one!
|
|
*/
|
|
|
|
bdPeer peer = *it;
|
|
it = searchProxyList.erase(it);
|
|
|
|
if ((peer.mPeerFlags & mRequiredPeerFlags) == mRequiredPeerFlags)
|
|
{
|
|
mProxiesFlagged.push_front(peer);
|
|
}
|
|
else
|
|
{
|
|
mProxiesUnknown.push_front(peer);
|
|
}
|
|
|
|
return 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define MAX_POTENTIAL_PROXIES 10
|
|
|
|
int bdQuery::trimProxies()
|
|
{
|
|
|
|
/* drop excess Potential Proxies */
|
|
while(mProxiesUnknown.size() > MAX_POTENTIAL_PROXIES)
|
|
{
|
|
mProxiesUnknown.pop_back();
|
|
}
|
|
|
|
while(mProxiesFlagged.size() > MAX_POTENTIAL_PROXIES)
|
|
{
|
|
mProxiesFlagged.pop_back();
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
******************************** Potential Proxies ****************************************
|
|
*******************************************************************************************/
|
|
|
|
|
|
|
|
/* print query.
|
|
*/
|
|
|
|
int bdQuery::printQuery()
|
|
{
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "bdQuery::printQuery()\n");
|
|
#endif
|
|
|
|
time_t ts = time(NULL);
|
|
fprintf(stderr, "Query for: ");
|
|
mFns->bdPrintNodeId(std::cerr, &mId);
|
|
fprintf(stderr, " Query State: %d", mState);
|
|
fprintf(stderr, " Query Age %ld secs", ts-mQueryTS);
|
|
if (mState >= BITDHT_QUERY_FAILURE)
|
|
{
|
|
fprintf(stderr, " Search Time: %d secs", mSearchTime);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
#ifdef DEBUG_QUERY
|
|
fprintf(stderr, "Closest Available Peers:\n");
|
|
std::multimap<bdMetric, bdPeer>::iterator it;
|
|
for(it = mClosest.begin(); it != mClosest.end(); it++)
|
|
{
|
|
fprintf(stderr, "Id: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, " Bucket: %d ", mFns->bdBucketDistance(&(it->first)));
|
|
fprintf(stderr," Found: %ld ago", ts-it->second.mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-it->second.mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-it->second.mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
fprintf(stderr, "\nClosest Potential Peers:\n");
|
|
for(it = mPotentialPeers.begin(); it != mPotentialPeers.end(); it++)
|
|
{
|
|
fprintf(stderr, "Id: ");
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, " Bucket: %d ", mFns->bdBucketDistance(&(it->first)));
|
|
fprintf(stderr," Found: %ld ago", ts-it->second.mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-it->second.mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-it->second.mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
std::list<bdPeer>::iterator lit;
|
|
fprintf(stderr, "\nProxies Flagged:\n");
|
|
for(lit = mProxiesFlagged.begin(); lit != mProxiesFlagged.end(); lit++)
|
|
{
|
|
fprintf(stderr, "ProxyId: ");
|
|
mFns->bdPrintId(std::cerr, &(lit->mPeerId));
|
|
fprintf(stderr," Found: %ld ago", ts-lit->mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-lit->mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-lit->mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
fprintf(stderr, "\nProxies Unknown:\n");
|
|
for(lit = mProxiesUnknown.begin(); lit != mProxiesUnknown.end(); lit++)
|
|
{
|
|
fprintf(stderr, "ProxyId: ");
|
|
mFns->bdPrintId(std::cerr, &(lit->mPeerId));
|
|
fprintf(stderr," Found: %ld ago", ts-lit->mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-lit->mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-lit->mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
#else
|
|
// shortened version.
|
|
fprintf(stderr, "Closest Available Peer: ");
|
|
std::multimap<bdMetric, bdPeer>::iterator it = mClosest.begin();
|
|
if (it != mClosest.end())
|
|
{
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, " Bucket: %d ", mFns->bdBucketDistance(&(it->first)));
|
|
fprintf(stderr," Flags: %x", it->second.mPeerFlags);
|
|
fprintf(stderr," Found: %ld ago", ts-it->second.mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-it->second.mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-it->second.mLastRecvTime);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "Closest Potential Peer: ");
|
|
it = mPotentialPeers.begin();
|
|
if (it != mPotentialPeers.end())
|
|
{
|
|
mFns->bdPrintId(std::cerr, &(it->second.mPeerId));
|
|
fprintf(stderr, " Bucket: %d ", mFns->bdBucketDistance(&(it->first)));
|
|
fprintf(stderr," Flags: %x", it->second.mPeerFlags);
|
|
fprintf(stderr," Found: %ld ago", ts-it->second.mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-it->second.mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-it->second.mLastRecvTime);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
std::list<bdPeer>::iterator lit;
|
|
fprintf(stderr, "Flagged Proxies:\n");
|
|
for(lit = mProxiesFlagged.begin(); lit != mProxiesFlagged.end(); lit++)
|
|
{
|
|
fprintf(stderr, "ProxyId: ");
|
|
mFns->bdPrintId(std::cerr, &(lit->mPeerId));
|
|
fprintf(stderr," Flags: %x", it->second.mPeerFlags);
|
|
fprintf(stderr," Found: %ld ago", ts-lit->mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-lit->mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-lit->mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
fprintf(stderr, "Potential Proxies:\n");
|
|
for(lit = mProxiesUnknown.begin(); lit != mProxiesUnknown.end(); lit++)
|
|
{
|
|
fprintf(stderr, "ProxyId: ");
|
|
mFns->bdPrintId(std::cerr, &(lit->mPeerId));
|
|
fprintf(stderr," Flags: %x", it->second.mPeerFlags);
|
|
fprintf(stderr," Found: %ld ago", ts-lit->mFoundTime);
|
|
fprintf(stderr," LastSent: %ld ago", ts-lit->mLastSendTime);
|
|
fprintf(stderr," LastRecv: %ld ago", ts-lit->mLastRecvTime);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************************* Remote Query **************************************/
|
|
bdRemoteQuery::bdRemoteQuery(bdId *id, bdNodeId *query, bdToken *transId, uint32_t query_type)
|
|
:mId(*id), mQuery(*query), mTransId(*transId), mQueryType(query_type)
|
|
{
|
|
mQueryTS = time(NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
|