Implemented turtle tunneling. Not debugged yet 8-).

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@1127 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2009-04-13 20:26:13 +00:00
parent 2f7fb3690f
commit e2ea2ec75b
2 changed files with 727 additions and 91 deletions

View File

@ -34,6 +34,9 @@
#include "pqi/p3connmgr.h"
#include "pqi/pqinotify.h"
#include "serialiser/rstlvbase.h"
#include "serialiser/rsbaseserial.h"
#include "p3turtle.h"
#include <iostream>
@ -50,8 +53,6 @@
/* TURTLE FLAGS */
#define P3TURTLE_DEBUG 1
p3turtle::p3turtle(p3ConnectMgr *cm) :p3Service(RS_SERVICE_TYPE_TURTLE), mConnMgr(cm)
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
@ -63,7 +64,6 @@ p3turtle::p3turtle(p3ConnectMgr *cm) :p3Service(RS_SERVICE_TYPE_TURTLE), mConnMg
int p3turtle::tick()
{
handleIncoming(); // handle incoming packets
// handleOutgoing(); // handle outgoing packets
// Clean every 10 sec.
time_t now = time(NULL) ;
@ -81,61 +81,47 @@ void p3turtle::autoWash()
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
#ifdef P3TURTLE_DEBUG
std::cerr << "Calling autowash." << std::endl ;
#endif
// look for tunnels and stored temportary info that have not been used for a while.
}
// -----------------------------------------------------------------------------------//
// -------------------------------- Helper functions ------------------------------ //
// -----------------------------------------------------------------------------------//
//
uint32_t p3turtle::generateRandomRequestId()
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
return rand() ;
}
TurtleRequestId p3turtle::turtleSearch(const std::string& string_to_match)
uint32_t p3turtle::generatePersonalFilePrint(const TurtleFileHash& hash)
{
// generate a new search id.
TurtleRequestId id = generateRandomRequestId() ;
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
// Form a request packet that simulates a request from us.
//
RsTurtleSearchRequestItem *item = new RsTurtleSearchRequestItem ;
// whatever cooking from the file hash and OwnId that cannot be recovered.
// The only important thing is that the saem hash produces the same tunnel
// id.
#ifdef P3TURTLE_DEBUG
std::cerr << "performing search. OwnId = " << mConnMgr->getOwnId() << std::endl ;
#endif
while(mConnMgr->getOwnId() == "")
std::string buff(hash + mConnMgr->getOwnId()) ;
uint32_t res = 0 ;
uint32_t decal = 0 ;
for(uint i=0;i<buff.length();++i)
{
std::cerr << "... waitting for connect manager to form own id." << std::endl ;
#ifdef WIN32
Sleep(1000) ;
#else
sleep(1) ;
#endif
res += 7*buff[i] + decal ;
decal = decal*44497+15641+(res%86243) ;
}
item->PeerId(mConnMgr->getOwnId()) ;
item->match_string = string_to_match ;
item->request_id = id ;
item->depth = 0 ;
// send it
handleSearchRequest(item) ;
delete item ;
return id ;
return res ;
}
void p3turtle::turtleDownload(const std::string& file_hash)
{
pqiNotify *notify = getPqiNotify();
if (notify)
notify->AddSysMessage(0, RS_SYS_WARNING, std::string("Unimplemented"),std::string("turtle download is not yet implemented. Sorry"));
}
// -----------------------------------------------------------------------------------//
// -------------------------------- Global routing. -------------------------------- //
// -----------------------------------------------------------------------------------//
//
int p3turtle::handleIncoming()
{
#ifdef P3TURTLE_DEBUG
@ -160,6 +146,12 @@ int p3turtle::handleIncoming()
case RS_TURTLE_SUBTYPE_SEARCH_RESULT : handleSearchResult(dynamic_cast<RsTurtleSearchResultItem *>(item)) ;
break ;
case RS_TURTLE_SUBTYPE_OPEN_TUNNEL : handleTunnelRequest(dynamic_cast<RsTurtleOpenTunnelItem *>(item)) ;
break ;
case RS_TURTLE_SUBTYPE_TUNNEL_OK : handleTunnelResult(dynamic_cast<RsTurtleTunnelOkItem *>(item)) ;
break ;
// Here will also come handling of file transfer requests, tunnel digging/closing, etc.
default:
std::cerr << "p3turtle::handleIncoming: Unknown packet subtype " << item->PacketSubType() << std::endl ;
@ -170,6 +162,10 @@ int p3turtle::handleIncoming()
return nhandled;
}
// -----------------------------------------------------------------------------------//
// -------------------------------- Search handling. ------------------------------- //
// -----------------------------------------------------------------------------------//
//
void p3turtle::handleSearchRequest(RsTurtleSearchRequestItem *item)
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
@ -183,7 +179,7 @@ void p3turtle::handleSearchRequest(RsTurtleSearchRequestItem *item)
// If the item contains an already handled search request, give up. This
// happens when the same search request gets relayed by different peers
//
if(requests_origins.find(item->request_id) != requests_origins.end())
if(_search_requests_origins.find(item->request_id) != _search_requests_origins.end())
{
#ifdef P3TURTLE_DEBUG
std::cerr << " This is a bouncing request. Ignoring and deleting it." << std::endl ;
@ -194,7 +190,7 @@ void p3turtle::handleSearchRequest(RsTurtleSearchRequestItem *item)
// This is a new request. Let's add it to the request map, and forward it to
// open peers.
requests_origins[item->request_id] = item->PeerId() ;
_search_requests_origins[item->request_id] = item->PeerId() ;
// If it's not for us, perform a local search. If something found, forward the search result back.
@ -287,12 +283,12 @@ void p3turtle::handleSearchResult(RsTurtleSearchResultItem *item)
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
// Find who actually sent the corresponding request.
//
std::map<TurtleRequestId,TurtlePeerId>::const_iterator it = requests_origins.find(item->request_id) ;
std::map<TurtleRequestId,TurtlePeerId>::const_iterator it = _search_requests_origins.find(item->request_id) ;
#ifdef P3TURTLE_DEBUG
std::cerr << "Received search result:" << std::endl ;
item->print(std::cerr,0) ;
#endif
if(it == requests_origins.end())
if(it == _search_requests_origins.end())
{
// This is an error: how could we receive a search result corresponding to a search item we
// have forwarded but that it not in the list ??
@ -323,6 +319,10 @@ void p3turtle::handleSearchResult(RsTurtleSearchResultItem *item)
}
}
// -----------------------------------------------------------------------------------//
// ------------------------------------- IO --------------------------------------- //
// -----------------------------------------------------------------------------------//
//
std::ostream& RsTurtleSearchRequestItem::print(std::ostream& o, uint16_t)
{
o << "Search request:" << std::endl ;
@ -349,17 +349,225 @@ std::ostream& RsTurtleSearchResultItem::print(std::ostream& o, uint16_t)
return o ;
}
void p3turtle::returnSearchResult(RsTurtleSearchResultItem *item)
std::ostream& RsTurtleOpenTunnelItem::print(std::ostream& o, uint16_t)
{
o << "Open Tunnel:" << std::endl ;
o << " Peer id : " << PeerId() << std::endl ;
o << " Partial tId: " << (void *)partial_tunnel_id << std::endl ;
o << " Req. Id : " << (void *)request_id << std::endl ;
o << " Depth : " << depth << std::endl ;
o << " Hash : " << file_hash << std::endl ;
return o ;
}
std::ostream& RsTurtleTunnelOkItem::print(std::ostream& o, uint16_t)
{
o << "Tunnel Ok:" << std::endl ;
o << " Peer id : " << PeerId() << std::endl ;
o << " tunnel id : " << (void*)tunnel_id << std::endl ;
o << " Req. Id : " << (void *)request_id << std::endl ;
return o ;
}
// -----------------------------------------------------------------------------------//
// -------------------------------- Search handling. ------------------------------- //
// -----------------------------------------------------------------------------------//
//
void p3turtle::diggTunnel(const TurtleFileHash& hash)
{
// just cout for now, but it should be notified to the gui
#ifdef P3TURTLE_DEBUG
std::cerr << " Returning result for search request " << item->request_id << " upwards." << std::endl ;
std::cerr << "performing tunnel request. OwnId = " << mConnMgr->getOwnId() << std::endl ;
#endif
while(mConnMgr->getOwnId() == "")
{
std::cerr << "... waitting for connect manager to form own id." << std::endl ;
#ifdef WIN32
Sleep(1000) ;
#else
sleep(1) ;
#endif
}
TurtleRequestId id = generateRandomRequestId() ;
// Form a tunnel request packet that simulates a request from us.
//
RsTurtleOpenTunnelItem *item = new RsTurtleOpenTunnelItem ;
item->PeerId(mConnMgr->getOwnId()) ;
item->file_hash = hash ;
item->request_id = id ;
item->partial_tunnel_id = generatePersonalFilePrint(hash) ;
item->depth = 0 ;
// send it
handleTunnelRequest(item) ;
delete item ;
}
void p3turtle::handleTunnelRequest(RsTurtleOpenTunnelItem *item)
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
#ifdef P3TURTLE_DEBUG
std::cerr << "Received tunnel request from peer " << item->PeerId() << ": " << std::endl ;
item->print(std::cerr,0) ;
#endif
// If the item contains an already handled tunnel request, give up. This
// happens when the same tunnel request gets relayed by different peers
//
if(_tunnel_requests_origins.find(item->request_id) != _tunnel_requests_origins.end())
{
#ifdef P3TURTLE_DEBUG
std::cerr << " This is a bouncing request. Ignoring and deleting item." << std::endl ;
#endif
return ;
}
// This is a new request. Let's add it to the request map, and forward it to
// open peers.
_tunnel_requests_origins[item->request_id] = item->PeerId() ;
// If it's not for us, perform a local search. If something found, forward the search result back.
if(item->PeerId() != mConnMgr->getOwnId())
{
#ifdef P3TURTLE_DEBUG
std::cerr << " Request not from us. Performing local search" << std::endl ;
#endif
if((_sharing_strategy != SHARE_FRIENDS_ONLY || item->depth < 2) && performLocalHashSearch(item->file_hash))
{
#ifdef P3TURTLE_DEBUG
std::cerr << " Local hash found. Sending tunnel ok to origin (" << item->PeerId() << ")." << std::endl ;
#endif
// Send back tunnel ok to the same guy
//
RsTurtleTunnelOkItem *res_item = new RsTurtleTunnelOkItem ;
res_item->request_id = item->request_id ;
res_item->tunnel_id = item->partial_tunnel_id ^ generatePersonalFilePrint(item->file_hash) ;
res_item->PeerId(item->PeerId()) ;
sendItem(res_item) ;
// Note in the tunnels list that we have an ending tunnel here.
TurtleTunnel tt ;
tt.local_src = item->PeerId() ;
tt.local_dst = mConnMgr->getOwnId() ; // this means us
tt.time_stamp = time(NULL) ;
_local_tunnels[res_item->tunnel_id] = tt ;
// We return straight, because when something is found, there's no need to digg a tunnel further.
return ;
}
#ifdef P3TURTLE_DEBUG
else
std::cerr << " No hash found locally, or local file not allowed for distant peers. Forwarding. " << std::endl ;
#endif
}
// If search depth not too large, also forward this search request to all other peers.
//
if(item->depth < TURTLE_MAX_SEARCH_DEPTH)
{
std::list<std::string> onlineIds ;
mConnMgr->getOnlineList(onlineIds);
#ifdef P3TURTLE_DEBUG
std::cerr << " Forwarding tunnel request: Looking for online peers" << std::endl ;
#endif
rsicontrol->getNotify().notifyTurtleSearchResult(item->request_id,item->result) ;
for(std::list<std::string>::const_iterator it(onlineIds.begin());it!=onlineIds.end();++it)
if(*it != item->PeerId())
{
#ifdef P3TURTLE_DEBUG
std::cerr << " Forwarding request to peer = " << *it << std::endl ;
#endif
// Copy current item and modify it.
RsTurtleOpenTunnelItem *fwd_item = new RsTurtleOpenTunnelItem(*item) ;
++(fwd_item->depth) ; // increase tunnel depth
fwd_item->PeerId(*it) ;
sendItem(fwd_item) ;
}
}
#ifdef P3TURTLE_DEBUG
else
std::cout << " Dropping this item, as tunnel depth is " << item->depth << std::endl ;
#endif
}
void p3turtle::handleTunnelResult(RsTurtleTunnelOkItem *item)
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
// Find who actually sent the corresponding turtle tunnel request.
//
std::map<TurtleTunnelRequestId,TurtlePeerId>::const_iterator it = _tunnel_requests_origins.find(item->request_id) ;
#ifdef P3TURTLE_DEBUG
std::cerr << "Received tunnel result:" << std::endl ;
item->print(std::cerr,0) ;
#endif
if(it == _tunnel_requests_origins.end())
{
// This is an error: how could we receive a tunnel result corresponding to a tunnel item we
// have forwarded but that it not in the list ??
std::cerr << __PRETTY_FUNCTION__ << ": tunnel result has no peer direction!" << std::endl ;
delete item ;
return ;
}
// store tunnel info.
if(_local_tunnels.find(item->tunnel_id) != _local_tunnels.end())
std::cerr << "Tunnel already there. This is an error !!" << std::endl ;
TurtleTunnel& tunnel(_local_tunnels[item->tunnel_id]) ;
tunnel.local_src = it->second ;
tunnel.local_dst = item->PeerId() ;
tunnel.time_stamp = time(NULL) ;
#ifdef P3TURTLE_DEBUG
std::cerr << " storing tunnel info. src=" << tunnel.local_src << ", dst=" << tunnel.local_dst << ", id=" << item->tunnel_id << std::endl ;
#endif
// Is this result's target actually ours ?
if(it->second == mConnMgr->getOwnId())
{
#ifdef P3TURTLE_DEBUG
std::cerr << " Tunnel starting point. Storing id=" << item->tunnel_id << " for hash (unknown) and tunnel request id " << it->second << std::endl;
#endif
// Tunnel is ending here. Add it to the list of tunnels for the given hash.
// a connexion between the file hash and the tunnel id is missing at this point.
//_file_hashes_tunnels.insert(item->tunnel_id) ;
}
else
{ // Nope, forward it back.
#ifdef P3TURTLE_DEBUG
std::cerr << " Forwarding result back to " << it->second << std::endl;
#endif
RsTurtleTunnelOkItem *fwd_item = new RsTurtleTunnelOkItem(*item) ; // copy the item
fwd_item->PeerId(it->second) ;
sendItem(fwd_item) ;
}
}
// -----------------------------------------------------------------------------------//
// ------------------------------ Tunnel maintenance. ------------------------------ //
// -----------------------------------------------------------------------------------//
//
/************* from pqiMonitor *******************/
void p3turtle::statusChange(const std::list<pqipeer> &plist)
{
@ -383,10 +591,120 @@ void p3turtle::statusChange(const std::list<pqipeer> &plist)
}
#endif
}
// -----------------------------------------------------------------------------------//
// ------------------------------ IO with libretroshare ----------------------------//
// -----------------------------------------------------------------------------------//
//
void p3turtle::performLocalSearch(const std::string& s,std::list<TurtleFileInfo>& result)
{
/* call to core */
std::list<FileDetail> initialResults;
std::list<std::string> words ;
#include "serialiser/rstlvbase.h"
#include "serialiser/rsbaseserial.h"
// to do: split search string into words.
words.push_back(s) ;
// now, search!
rsFiles->SearchKeywords(words, initialResults,DIR_FLAGS_LOCAL);
result.clear() ;
for(std::list<FileDetail>::const_iterator it(initialResults.begin());it!=initialResults.end();++it)
{
TurtleFileInfo i ;
i.hash = it->hash ;
i.size = it->size ;
i.name = it->name ;
result.push_back(i) ;
}
}
TurtleRequestId p3turtle::turtleSearch(const std::string& string_to_match)
{
// generate a new search id.
TurtleRequestId id = generateRandomRequestId() ;
// Form a request packet that simulates a request from us.
//
RsTurtleSearchRequestItem *item = new RsTurtleSearchRequestItem ;
#ifdef P3TURTLE_DEBUG
std::cerr << "performing search. OwnId = " << mConnMgr->getOwnId() << std::endl ;
#endif
while(mConnMgr->getOwnId() == "")
{
std::cerr << "... waitting for connect manager to form own id." << std::endl ;
#ifdef WIN32
Sleep(1000) ;
#else
sleep(1) ;
#endif
}
item->PeerId(mConnMgr->getOwnId()) ;
item->match_string = string_to_match ;
item->request_id = id ;
item->depth = 0 ;
// send it
handleSearchRequest(item) ;
delete item ;
return id ;
}
void p3turtle::turtleDownload(const std::string& file_hash)
{
RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/
if(_file_hashes_tunnels.find(file_hash) != _file_hashes_tunnels.end()) // download already asked.
{
#ifdef P3TURTLE_DEBUG
std::cerr << "p3turtle: File hash " << file_hash << " already in pool. Returning." << std::endl ;
#endif
return ;
}
// No tunnels at start, but this triggers digging new tunnels.
//
_file_hashes_tunnels[file_hash] = std::list<TurtleTunnelId>() ;
// also should send associated request to the file transfer module.
// todo!
}
void p3turtle::returnSearchResult(RsTurtleSearchResultItem *item)
{
// just cout for now, but it should be notified to the gui
#ifdef P3TURTLE_DEBUG
std::cerr << " Returning result for search request " << item->request_id << " upwards." << std::endl ;
#endif
rsicontrol->getNotify().notifyTurtleSearchResult(item->request_id,item->result) ;
}
bool p3turtle::performLocalHashSearch(const TurtleFileHash& hash)
{
FileInfo info ;
return rsFiles->FileDetails(hash, RS_FILE_HINTS_LOCAL, info);
}
// -----------------------------------------------------------------------------------//
// -------------------------------- Serialization. --------------------------------- //
// -----------------------------------------------------------------------------------//
//
//
// ---------------------------------- Packet sizes -----------------------------------//
//
uint32_t RsTurtleSearchRequestItem::serial_size()
{
uint32_t s = 0 ;
@ -418,6 +736,32 @@ uint32_t RsTurtleSearchResultItem::serial_size()
return s ;
}
uint32_t RsTurtleOpenTunnelItem::serial_size()
{
uint32_t s = 0 ;
s += 8 ; // header
s += GetTlvStringSize(file_hash) ; // file hash
s += 4 ; // tunnel request id
s += 4 ; // partial tunnel id
s += 2 ; // depth
return s ;
}
uint32_t RsTurtleTunnelOkItem::serial_size()
{
uint32_t s = 0 ;
s += 8 ; // header
s += 4 ; // tunnel id
s += 4 ; // tunnel request id
return s ;
}
//
// ---------------------------------- Serialization ----------------------------------//
//
RsItem *RsTurtleSerialiser::deserialise(void *data, uint32_t *size)
{
// look what we have...
@ -549,7 +893,6 @@ bool RsTurtleSearchResultItem::serialize(void *data,uint32_t& pktsize)
return ok;
}
RsTurtleSearchResultItem::RsTurtleSearchResultItem(void *data,uint32_t pktsize)
: RsTurtleItem(RS_TURTLE_SUBTYPE_SEARCH_RESULT)
{
@ -589,29 +932,122 @@ RsTurtleSearchResultItem::RsTurtleSearchResultItem(void *data,uint32_t pktsize)
throw std::runtime_error("Unknown error while deserializing.") ;
}
void p3turtle::performLocalSearch(const std::string& s,std::list<TurtleFileInfo>& result)
bool RsTurtleOpenTunnelItem::serialize(void *data,uint32_t& pktsize)
{
/* call to core */
std::list<FileDetail> initialResults;
std::list<std::string> words ;
uint32_t tlvsize = serial_size();
uint32_t offset = 0;
// to do: split search string into words.
words.push_back(s) ;
// now, search!
rsFiles->SearchKeywords(words, initialResults,DIR_FLAGS_LOCAL);
if (pktsize < tlvsize)
return false; /* not enough space */
result.clear() ;
pktsize = tlvsize;
for(std::list<FileDetail>::const_iterator it(initialResults.begin());it!=initialResults.end();++it)
bool ok = true;
ok &= setRsItemHeader(data,tlvsize,PacketId(), tlvsize);
/* skip the header */
offset += 8;
/* add mandatory parts first */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_HASH_SHA1, file_hash); // file hash
ok &= setRawUInt32(data, tlvsize, &offset, request_id);
ok &= setRawUInt32(data, tlvsize, &offset, partial_tunnel_id);
ok &= setRawUInt16(data, tlvsize, &offset, depth);
if (offset != tlvsize)
{
TurtleFileInfo i ;
i.hash = it->hash ;
i.size = it->size ;
i.name = it->name ;
result.push_back(i) ;
ok = false;
#ifdef RSSERIAL_DEBUG
std::cerr << "RsTurtleOpenTunnelItem::serialiseTransfer() Size Error! " << std::endl;
#endif
}
return ok;
}
RsTurtleOpenTunnelItem::RsTurtleOpenTunnelItem(void *data,uint32_t pktsize)
: RsTurtleItem(RS_TURTLE_SUBTYPE_OPEN_TUNNEL)
{
#ifdef P3TURTLE_DEBUG
std::cerr << " type = open tunnel" << std::endl ;
#endif
uint32_t offset = 8; // skip the header
uint32_t rssize = getRsItemSize(data);
/* add mandatory parts first */
bool ok = true ;
ok &= GetTlvString(data, pktsize, &offset, TLV_TYPE_STR_HASH_SHA1, file_hash); // file hash
ok &= getRawUInt32(data, pktsize, &offset, &request_id);
ok &= getRawUInt32(data, pktsize, &offset, &partial_tunnel_id) ;
ok &= getRawUInt16(data, pktsize, &offset, &depth);
#ifdef P3TURTLE_DEBUG
std::cerr << " reuqest_id=" << (void*)request_id << ", partial_id=" << (void*)partial_tunnel_id << ", depth=" << depth << ", hash=" << file_hash << std::endl ;
#endif
if (offset != rssize)
throw std::runtime_error("RsTurtleOpenTunnelItem::() error while deserializing.") ;
if (!ok)
throw std::runtime_error("RsTurtleOpenTunnelItem::() unknown error while deserializing.") ;
}
bool RsTurtleTunnelOkItem::serialize(void *data,uint32_t& pktsize)
{
uint32_t tlvsize = serial_size();
uint32_t offset = 0;
if (pktsize < tlvsize)
return false; /* not enough space */
pktsize = tlvsize;
bool ok = true;
ok &= setRsItemHeader(data,tlvsize,PacketId(), tlvsize);
/* skip the header */
offset += 8;
/* add mandatory parts first */
ok &= setRawUInt32(data, tlvsize, &offset, tunnel_id);
ok &= setRawUInt32(data, tlvsize, &offset, request_id);
if (offset != tlvsize)
{
ok = false;
#ifdef RSSERIAL_DEBUG
std::cerr << "RsTurtleTunnelOkItem::serialiseTransfer() Size Error! " << std::endl;
#endif
}
return ok;
}
RsTurtleTunnelOkItem::RsTurtleTunnelOkItem(void *data,uint32_t pktsize)
: RsTurtleItem(RS_TURTLE_SUBTYPE_TUNNEL_OK)
{
#ifdef P3TURTLE_DEBUG
std::cerr << " type = tunnel ok" << std::endl ;
#endif
uint32_t offset = 8; // skip the header
uint32_t rssize = getRsItemSize(data);
/* add mandatory parts first */
bool ok = true ;
ok &= getRawUInt32(data, pktsize, &offset, &tunnel_id) ;
ok &= getRawUInt32(data, pktsize, &offset, &request_id);
#ifdef P3TURTLE_DEBUG
std::cerr << " reuqest_id=" << (void*)request_id << ", tunnel_id=" << (void*)tunnel_id << std::endl ;
#endif
if (offset != rssize)
throw std::runtime_error("RsTurtleTunnelOkItem::() error while deserializing.") ;
if (!ok)
throw std::runtime_error("RsTurtleTunnelOkItem::() unknown error while deserializing.") ;
}

View File

@ -23,6 +23,8 @@
*
*/
//====================================== General setup of the router ===================================//
//
// This class implements the turtle hopping router. It basically serves as
// - a cache of turtle tunnels which are the communicating ways between distant peers.
// - turtle tunnels are either end-point tunnels, or transitory points, in which case items are just
@ -39,6 +41,108 @@
// - name // name of the file found
// - search request id. //
//
// - when downloading:
// - for a given hash, a set of starting tunnels is maintained. Transitory
// tunnels are also maintained for other hashes as requested by distant
// peers.
//
//============================================= Operations =============================================//
//
// A download session works as follows:
// Initiation:
// 1 - the user searches for files (turtle search), and selects one and clicks download.
// 2 - In parallel:
// - the ft module gets a request, and searches for peers to provide this using its search modules.
// - the turtle router is informed that a turtle download will happen with the given hash, so
// it initiates tunnels for this hash.
// In a loop:
// 3 - the ft module asks the hash to the turtle searchModule, and sends file requests to the pqi
// interface of this module.
// 4 - the turtle pqi interface forwards these requests to the turtle router, which sends them to
// the correct peers, selecting randomly among all the possible tunnels for this hash.
// 5 - when a file data packet gets back, the turtle router forwards it back to the file transfer module.
//
//================================ connexion to the file transfer module ===============================//
//
// The turtle router should provide the ft module with the necessary interface for asking files, and
// retreiving data:
// - a search module that responds with a given fake peer id for hash request for which it has tunnels.
// - a pqi interface to ask for file data
// - p3turtle sends back file data packets to the file transfer module
//
//========================================== Tunnel usage rules ========================================//
//
// Tunnels should be used according to their capacity. This is an unresolved problem as for now.
//
//======================================= Tunnel maintenance rules =====================================//
//
// P3turtle should derive from pqihandler, just as p3disc, so that newly connected peers should trigger
// asking for new tunnels, and disconnecting peers should produce a close tunnel packet.
//
// - when a peer A connects:
// - initiate new tunnels for all active file hashes (go through the list of hashes) by
// asking to A, for the same hash and the same source. Only report tunnels for which the destination
// endpoint is different, which should not happen in fact, because of bouncing gards.
//
// - when a peer A disconnects.
// - close tunnels whose destination is beyond A by sending a close request backward.
// - close tunnels whose source is beyond A by sending a forward close request.
//
// - when receive open tunnel from A
// - check whether it's a bouncing request. If yes, give up.
// - check hash against local files.
// if > 0
// return tunnel ok item. No need to go forward, as sub tunnels are not useful.
// else
// forward request to peers, notting source and hashes.
//
// - when receive tunnel ok from A
// - no need to check whether we already have this tunnel, as bouncing gards prevent this.
// - leave a trace for the tunnel, and send (forward) backward.
//
// - when receive close tunnel from A
// - if I am the endpoint
// - locally close the tunnel.
// - respond with tunnel closed.
// - otherwise, block the tunnel, and forward close tunnel to tunnel destination.
//
// - when receive tunnel closed from A
// - locally close the tunnel
// - forward back
//
// Ids management:
// - tunnel ids should be identical for requests between 2 same peers for the same file hash.
// - tunnel requests ids do not need to be identical.
// So:
// - when issuing an open tunnel order,
// - a random request id is generated and used for packet routing
// - a partial tunnel id is build, which is unique to the pair (source,file hash)
// - when tunnel_ok is sent back, the tunnel id is completed so that it is unique to the
// triplet (source, destination, file hash).
//
// For these needs, tunnels are represented by:
// - their file hash. Each tunnel is only designed for transferring a single and same file.
// - their local endpoints id. These are the ids of the peers in direction to the source and destination.
// - the tunnel id, which is unique to the triple hash+global source+global destination.
// - there is a difference between source and destination in tunnels. The source is the file asker, the
// destination is the file provider. This helps sorting tunnels.
// - a timestamp, used for cleaning unused tunnels.
//
// The turtle router has:
// - a list of search requests and where to bounce them back.
// - a list of tunnel digging requests and where to bounce them, back.
// - a list of active file hashes, for which is should constantly maintain tunnels.
// - a list of active tunnels, some being transitory, some being endpoints.
//
// Turtle router entries:
// - a function for performing turtle search
// - a function for downloading files.
//
// Questions:
// - should tunnels be re-used ? nope. The only useful case would be when two peers are exchanging files, which happens quite rarely.
// - at a given moment, there is at most 1 tunnel for a given triplet (hash, source, destination).
#ifndef MRK_PQI_TURTLE_H
#define MRK_PQI_TURTLE_H
@ -67,6 +171,10 @@ static const int TURTLE_MAX_SEARCH_DEPTH = 6 ;
typedef std::string TurtlePeerId ;
typedef std::string TurtleFileHash ;
typedef std::string TurtleFileName ;
typedef TurtleRequestId TurtleSearchRequestId ;
typedef uint32_t TurtleTunnelRequestId ;
typedef uint32_t TurtleTunnelId ;
class RsTurtleItem: public RsItem
{
@ -88,7 +196,7 @@ class RsTurtleSearchResultItem: public RsTurtleItem
uint16_t depth ;
uint8_t peer_id[16]; // peer id. This will eventually be obfuscated in some way.
TurtleRequestId request_id ; // randomly generated request id.
TurtleSearchRequestId request_id ; // randomly generated request id.
std::list<TurtleFileInfo> result ;
virtual std::ostream& print(std::ostream& o, uint16_t) ;
@ -115,6 +223,69 @@ class RsTurtleSearchRequestItem: public RsTurtleItem
virtual uint32_t serial_size() ;
};
class RsTurtleOpenTunnelItem: public RsTurtleItem
{
public:
RsTurtleOpenTunnelItem() : RsTurtleItem(RS_TURTLE_SUBTYPE_OPEN_TUNNEL) {}
RsTurtleOpenTunnelItem(void *data,uint32_t size) ; // deserialization
TurtleFileHash file_hash ; // hash to match
uint32_t request_id ; // randomly generated request id.
uint32_t partial_tunnel_id ; // uncomplete tunnel id. Will be completed at destination.
uint16_t depth ; // Used for limiting search depth.
virtual std::ostream& print(std::ostream& o, uint16_t) ;
protected:
virtual bool serialize(void *data,uint32_t& size) ;
virtual uint32_t serial_size() ;
};
class RsTurtleTunnelOkItem: public RsTurtleItem
{
public:
RsTurtleTunnelOkItem() : RsTurtleItem(RS_TURTLE_SUBTYPE_TUNNEL_OK) {}
RsTurtleTunnelOkItem(void *data,uint32_t size) ; // deserialization
uint32_t tunnel_id ; // id of the tunnel. Should be identical for a tunnel between two same peers for the same hash.
uint32_t request_id ; // randomly generated request id corresponding to the intial request.
virtual std::ostream& print(std::ostream& o, uint16_t) ;
protected:
virtual bool serialize(void *data,uint32_t& size) ;
virtual uint32_t serial_size() ;
};
class RsTurtleCloseTunnelItem: public RsTurtleItem
{
public:
RsTurtleCloseTunnelItem() : RsTurtleItem(RS_TURTLE_SUBTYPE_CLOSE_TUNNEL) {}
RsTurtleCloseTunnelItem(void *data,uint32_t size) ; // deserialization
uint32_t tunnel_id ; // id of the tunnel to close.
virtual std::ostream& print(std::ostream& o, uint16_t) ;
protected:
virtual bool serialize(void *data,uint32_t& size) ;
virtual uint32_t serial_size() ;
};
class RsTurtleTunnelClosedItem: public RsTurtleItem
{
public:
RsTurtleTunnelClosedItem() : RsTurtleItem(RS_TURTLE_SUBTYPE_TUNNEL_CLOSED) {}
RsTurtleTunnelClosedItem(void *data,uint32_t size) ; // deserialization
uint32_t tunnel_id ; // id of the tunnel to close.
virtual std::ostream& print(std::ostream& o, uint16_t) ;
protected:
virtual bool serialize(void *data,uint32_t& size) ;
virtual uint32_t serial_size() ;
};
// Class responsible for serializing/deserializing all turtle items.
//
class RsTurtleSerialiser: public RsSerialType
@ -136,9 +307,9 @@ class RsTurtleSerialiser: public RsSerialType
class TurtleTunnel
{
public:
TurtlePeerId in ; // where packets come from
TurtlePeerId out ; // where packets should go
uint32_t time_stamp ; // last time the tunnel was actually used. Used for cleaning old tunnels.
TurtlePeerId local_src ; // where packets come from. Direction to the source.
TurtlePeerId local_dst ; // where packets should go. Direction to the destination.
uint32_t time_stamp ; // last time the tunnel was actually used. Used for cleaning old tunnels.
};
class p3turtle: public p3Service, public pqiMonitor, public RsTurtle
@ -150,11 +321,16 @@ class p3turtle: public p3Service, public pqiMonitor, public RsTurtle
// the request id, which will be further used by the gui to store results
// as they come back.
//
virtual TurtleRequestId turtleSearch(const std::string& string_to_match) ;
virtual TurtleSearchRequestId turtleSearch(const std::string& string_to_match) ;
// Launches a complete download file operation: diggs one or more
// Initiates tunnel handling for the given file hash.
// tunnels. Launches an exception if an error occurs during the
// initialization process.
// initialization process. The turtle router itself does not initiate downloads,
// it only maintains tunnels for the given hash. The download should be
// driven by the file transfer module. Maybe this function can do the whole thing:
// - initiate tunnel handling
// - send the file request to the file transfer module
// - populate the file transfer module with the adequate pqi interface and search module.
//
virtual void turtleDownload(const std::string& file_hash) ;
@ -166,34 +342,58 @@ class p3turtle: public p3Service, public pqiMonitor, public RsTurtle
/************* from pqiMonitor *******************/
// Handles incoming and outgoing packets, sort search requests and
// forward info upward.
// This function does many things:
// - It handles incoming and outgoing packets
// - it sorts search requests and forwards search results upward.
// - it cleans unused (tunnel+search) requests.
// - it maintains the pool of tunnels, for each request file hash.
//
virtual int tick();
private:
uint32_t generateRandomRequestId() ;
void autoWash() ;
//--------------------------- Admin/Helper functions -------------------------//
uint32_t generatePersonalFilePrint(const TurtleFileHash&) ; /// Generates a cyphered combination of ownId() and file hash
uint32_t generateRandomRequestId() ; /// Generates a random uint32_t number.
/* Network Input */
int handleIncoming();
// int handleOutgoing();
void autoWash() ; /// Auto cleaning of unused tunnels, search requests and tunnel requests.
// Performs a search calling local cache and search structure.
void performLocalSearch(const std::string& s,std::list<TurtleFileInfo>& result) ;
//------------------------------ Tunnel handling -----------------------------//
void handleSearchRequest(RsTurtleSearchRequestItem *item);
void diggTunnel(const TurtleFileHash& hash) ; /// initiates tunnels from here to any peers having the given file hash
//----------------------------- Routing functions ----------------------------//
int handleIncoming(); /// Main routing function
void handleSearchRequest(RsTurtleSearchRequestItem *item); /// specific routing functions for handling particular packets.
void handleSearchResult(RsTurtleSearchResultItem *item);
void handleTunnelRequest(RsTurtleOpenTunnelItem *item);
void handleTunnelResult(RsTurtleTunnelOkItem *item);
// returns a search result upwards (possibly to the gui)
//------ Functions connecting the turtle router to other components.----------//
// Performs a search calling local cache and search structure.
void performLocalSearch(const std::string& match_string,std::list<TurtleFileInfo>& result) ;
// Returns a search result upwards (possibly to the gui)
void returnSearchResult(RsTurtleSearchResultItem *item) ;
// Returns true if the file with given hash is hosted locally.
bool performLocalHashSearch(const TurtleFileHash& hash) ;
//--------------------------- Local variables --------------------------------//
/* data */
p3ConnectMgr *mConnMgr;
RsMutex mTurtleMtx;
std::map<TurtleRequestId,TurtlePeerId> requests_origins ; // keeps trace of who emmitted a given request
std::map<TurtleFileHash,TurtleTunnel> file_tunnels ; // stores adequate tunnels for each file hash.
std::map<TurtleSearchRequestId,TurtlePeerId> _search_requests_origins ; /// keeps trace of who emmitted a given search request
std::map<TurtleTunnelRequestId,TurtlePeerId> _tunnel_requests_origins ; /// keeps trace of who emmitted a tunnel request
std::map<TurtleFileHash,std::list<TurtleTunnelId> > _file_hashes_tunnels ; /// stores adequate tunnels for each file hash locally asked
std::map<TurtleTunnelId,TurtleTunnel > _local_tunnels ; /// local tunnels, stored by ids (Either transiting or ending).
time_t _last_clean_time ;
};