RetroShare/libretroshare/src/services/p3disc.cc
csoler fc8dfcf65b Merged branch v0.5-OpenPGP into trunk:
User-level changes:
==================
- libgpgme is not used anymore; it is replaced by a built-in piece of code called OpenPGP-SDK 
  (http://openpgp.nominet.org.uk/cgi-bin/trac.cgi) that was improved to be used by RetroShare
  for handling PGP keys.

- the gnupg keyring is not used anymore. Now, RetroShare has it's own gpg keyring, shared by all instances.
  On linux it's located in ~/.retroshare/pgp/. A lock system prevents multiple locations to read/write keyrings
  simultaneously.

- the trust database from gnupg is not documented, so RetroShare cannot import it. This comes from the fact that
  the GPG standard (RFC4880) asks explicitly not to export trust information. So RetroShare has it's own 
  trust DB shared by locations. This means you need to re-trust people. Sorry for that!

- at start, if no keyring is found, RS will propose to copy the gnupg keyring to use your existing keys. Clicking on 
  "OK" will do the copy, and you should find back all existing locations, except for DSA keys.

- locations for which the suitable keypair is not in the keyring will not be displayed in the login window
- locations for which the suitable keypair is not a RSA/RSA key will not be displayed. RetroShare does not
  support DSA/Elgamal keypairs yet.

- a key import/export exchange function has been added in the certificate creation window (you go there from the login
  window by clicking on "manage keys/locations". This allows to easily create a new location with the same pgp key on
  another computer. To obtain a suitable keypair using gnupg, you need to concatenate the encrypted private key and the 
  public key into an ascii file. This can be done using:
  		gpg -a --export-secret-keys [your ID] > mykey.asc
		gpg -a --export [your ID] >> mykey.asc

- importing a key with subkeys in not yet possible. Please remove subkeys before importing.

- The code has been tested for a reasonnable amount of time, but it's not possible to prevent some new bugs 
  to appear. Please report them asap supplying: call-stacks if possible, and terminal output. In particular,
  openpgp has some assert()'s that should not be triggered unless RetroShare is calling it in an improper way.

Internal changes
================
- a specific component, PGPHandler, takes care of the interface between openpgp-sdk and RetroShare
  openpgp-sdk is c-code, with it's own memory management, which has been kept well separated from 
  RetroShare.

- GPG Ids are now a specific class (not a std::string anymore) for code consistency reasons. As strings are
  still used in many places, this requires a few conversions. In particular, AuthGPG takes strings as
  function params and calls GPGHandler with the proper PGPIdType class. In the future, RetroShare should
  only use PGPIdType. The same will be done for SSL ids.

- signature cleaning is still handled by the Retroshare built-in function, not by openpgp, but we will 
  do this later.

Still to do
===========
- DSA needs subkey handling, since the encryption is performed by a Elgamal subkey. Not sure this will be done.
- GPGIds/SSLIds cleaning (meaning replace strings by appropriate types). Lots of confusion throughout the code in retroshare-gui in particular.
- key removal from keyring. This is a challenge to keep locations synchronised.



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@5293 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2012-07-13 21:53:39 +00:00

1297 lines
36 KiB
C++

/*
* libretroshare/src/services: p3disc.cc
*
* Services for RetroShare.
*
* Copyright 2004-2008 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 2 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 "retroshare@lunamutt.com".
*
*/
#include "retroshare/rsiface.h"
#include "retroshare/rspeers.h"
#include "services/p3disc.h"
#include "pqi/p3peermgr.h"
#include "pqi/p3linkmgr.h"
#include "pqi/p3netmgr.h"
#include "pqi/authssl.h"
#include "pqi/authgpg.h"
#include <iostream>
#include <errno.h>
#include <cmath>
const uint32_t AUTODISC_LDI_SUBTYPE_PING = 0x01;
const uint32_t AUTODISC_LDI_SUBTYPE_RPLY = 0x02;
#include "util/rsdebug.h"
#include "util/rsprint.h"
#include "util/rsversion.h"
const int pqidisczone = 2482;
//static int convertTDeltaToTRange(double tdelta);
//static int convertTRangeToTDelta(int trange);
// Operating System specific includes.
#include "pqi/pqinetwork.h"
/* DISC FLAGS */
const uint32_t P3DISC_FLAGS_USE_DISC = 0x0001;
const uint32_t P3DISC_FLAGS_USE_DHT = 0x0002;
const uint32_t P3DISC_FLAGS_EXTERNAL_ADDR = 0x0004;
const uint32_t P3DISC_FLAGS_STABLE_UDP = 0x0008;
const uint32_t P3DISC_FLAGS_PEER_ONLINE = 0x0010;
const uint32_t P3DISC_FLAGS_OWN_DETAILS = 0x0020;
const uint32_t P3DISC_FLAGS_PEER_TRUSTS_ME = 0x0040;
const uint32_t P3DISC_FLAGS_ASK_VERSION = 0x0080;
/*****
* #define P3DISC_DEBUG 1
****/
//#define P3DISC_DEBUG 1
/******************************************************************************************
****************************** NEW DISCOVERY *******************************************
******************************************************************************************
*****************************************************************************************/
p3disc::p3disc(p3PeerMgr *pm, p3LinkMgr *lm, p3NetMgr *nm, pqipersongrp *pqih)
:p3Service(RS_SERVICE_TYPE_DISC),
p3Config(CONFIG_TYPE_P3DISC),
mPeerMgr(pm), mLinkMgr(lm), mNetMgr(nm),
mPqiPersonGrp(pqih), mDiscMtx("p3disc")
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
addSerialType(new RsDiscSerialiser());
mLastSentHeartbeatTime = time(NULL);
mDiscEnabled = true;
//add own version to versions map
versions[AuthSSL::getAuthSSL()->OwnId()] = RsUtil::retroshareVersion();
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::p3disc() setup";
std::cerr << std::endl;
#endif
return;
}
int p3disc::tick()
{
//send a heartbeat to all connected peers
time_t hbTime;
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
hbTime = mLastSentHeartbeatTime;
}
if (time(NULL) - hbTime > HEARTBEAT_REPEAT_TIME)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::tick() sending heartbeat to all peers" << std::endl;
#endif
std::list<std::string> peers;
std::list<std::string>::const_iterator pit;
mLinkMgr->getOnlineList(peers);
for (pit = peers.begin(); pit != peers.end(); ++pit)
{
sendHeartbeat(*pit);
}
/* check our Discovery flag */
peerState detail;
mPeerMgr->getOwnNetStatus(detail);
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
mDiscEnabled = (!(detail.visState & RS_VIS_STATE_NODISC));
mLastSentHeartbeatTime = time(NULL);
}
return handleIncoming();
}
int p3disc::handleIncoming()
{
RsItem *item = NULL;
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::handleIncoming()" << std::endl;
#endif
int nhandled = 0;
// While messages read
while(NULL != (item = recvItem()))
{
RsDiscAskInfo *inf = NULL;
RsDiscReply *dri = NULL;
RsDiscVersion *dvi = NULL;
RsDiscHeartbeat *dta = NULL;
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::handleIncoming() Received Message!" << std::endl;
item -> print(std::cerr);
std::cerr << std::endl;
#endif
// if discovery reply then respond if haven't already.
if (NULL != (dri = dynamic_cast<RsDiscReply *> (item)))
{
recvDiscReply(dri);
}
else if (NULL != (dvi = dynamic_cast<RsDiscVersion *> (item)))
{
recvPeerVersionMsg(dvi);
nhandled++;
delete item;
}
else if (NULL != (inf = dynamic_cast<RsDiscAskInfo *> (item))) /* Ping */
{
recvAskInfo(inf);
nhandled++;
delete item;
}
else if (NULL != (dta = dynamic_cast<RsDiscHeartbeat *> (item)))
{
recvHeartbeatMsg(dta);
nhandled++ ;
delete item;
}
else
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::handleIncoming() Unknown Received Message!" << std::endl;
item -> print(std::cerr);
std::cerr << std::endl;
#endif
delete item;
}
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::handleIncoming() finished." << std::endl;
#endif
return nhandled;
}
/************* from pqiMonitor *******************/
void p3disc::statusChange(const std::list<pqipeer> &plist)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::statusChange()" << std::endl;
#endif
std::list<pqipeer>::const_iterator pit;
/* if any have switched to 'connected' then we notify */
for(pit = plist.begin(); pit != plist.end(); pit++)
{
if ((pit->state & RS_PEER_S_FRIEND) && (pit->actions & RS_PEER_CONNECTED))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::statusChange() Starting Disc with: " << pit->id << std::endl;
#endif
sendOwnVersion(pit->id);
sendAllInfoToJustConnectedPeer(pit->id);
sendJustConnectedPeerInfoToAllPeer(pit->id);
}
else if (!(pit->state & RS_PEER_S_FRIEND) && (pit->actions & RS_PEER_MOVED))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::statusChange() Removing Friend: " << pit->id << std::endl;
#endif
removeFriend(pit->id);
}
else if ((pit->state & RS_PEER_S_FRIEND) && (pit->actions & RS_PEER_NEW))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::statusChange() Adding Friend: " << pit->id << std::endl;
#endif
askInfoToAllPeers(pit->id);
}
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::statusChange() finished." << std::endl;
#endif
return;
}
void p3disc::sendAllInfoToJustConnectedPeer(const std::string &id)
{
/* get a peer lists */
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() id: " << id << std::endl;
#endif
std::list<std::string> friendIds;
std::list<std::string>::iterator it;
std::set<std::string> gpgIds;
std::set<std::string>::iterator git;
/* We send our full friends list - if we have Discovery Enabled */
if (mDiscEnabled)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() Discovery Enabled, sending Friend List" << std::endl;
#endif
mLinkMgr->getFriendList(friendIds);
/* send them a list of all friend's details */
for(it = friendIds.begin(); it != friendIds.end(); it++)
{
/* get details */
peerState detail;
if (!mPeerMgr->getFriendNetStatus(*it, detail))
{
/* major error! */
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() No Info, Skipping: " << *it;
std::cerr << std::endl;
#endif
continue;
}
if (!(detail.visState & RS_VIS_STATE_NODISC))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() Adding GPGID: " << detail.gpg_id;
std::cerr << " (SSLID: " << *it << ")";
std::cerr << std::endl;
#endif
gpgIds.insert(detail.gpg_id);
}
else
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() DISC OFF for GPGID: " << detail.gpg_id;
std::cerr << " (SSLID: " << *it << ")";
std::cerr << std::endl;
#endif
}
}
}
//add own info, this info is sent whether discovery is enabled - or not.
gpgIds.insert(rsPeers->getGPGOwnId());
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
/* refresh with new list */
std::list<std::string> &idList = mSendIdList[id];
idList.clear();
for(git = gpgIds.begin(); git != gpgIds.end(); git++)
{
idList.push_back(*git);
}
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendAllInfoToJustConnectedPeer() finished." << std::endl;
#endif
}
void p3disc::sendJustConnectedPeerInfoToAllPeer(const std::string &connectedPeerId)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() connectedPeerId : " << connectedPeerId << std::endl;
#endif
/* only ask info if discovery is on */
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
if (!mDiscEnabled)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() Disc Disabled => NULL OP" << std::endl;
#endif
return;
}
}
/* get details */
peerState detail;
if (!mPeerMgr->getFriendNetStatus(connectedPeerId, detail))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() No NetStatus => FAILED" << std::endl;
#endif
return;
}
if (detail.visState & RS_VIS_STATE_NODISC)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() Peer Disc Discable => NULL OP" << std::endl;
#endif
return;
}
std::string gpg_connectedPeerId = rsPeers->getGPGId(connectedPeerId);
std::list<std::string> onlineIds;
/* get a peer lists */
rsPeers->getOnlineList(onlineIds);
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
/* append gpg id's of all friend's to the sending list */
std::list<std::string>::iterator it;
for (it = onlineIds.begin(); it != onlineIds.end(); it++)
{
std::list<std::string> &idList = mSendIdList[*it];
if (std::find(idList.begin(), idList.end(), gpg_connectedPeerId) == idList.end())
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() adding to queue for: ";
std::cerr << *it << std::endl;
#endif
idList.push_back(gpg_connectedPeerId);
}
else
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendJustConnectedPeerInfoToAllPeer() already in queue for: ";
std::cerr << *it << std::endl;
#endif
}
}
}
}
bool isDummyFriend(const std::string &id)
{
bool ret = (id.substr(0,5) == "dummy");
return ret;
}
/* (dest (to), source (cert)) */
RsDiscReply *p3disc::createDiscReply(const std::string &to, const std::string &about)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() called. Sending details of: " << about << " to: " << to << std::endl;
#endif
std::string aboutGpgId = rsPeers->getGPGId(about);
if (aboutGpgId.empty()) {
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() no info about this id" << std::endl;
#endif
return NULL;
}
// Construct a message
RsDiscReply *di = new RsDiscReply();
// Fill the message
// Set Target as input cert.
di -> PeerId(to);
di -> aboutId = aboutGpgId;
// set the ip addresse list.
std::list<std::string> sslChilds;
rsPeers->getAssociatedSSLIds(aboutGpgId, sslChilds);
bool shouldWeSendGPGKey = false;//the GPG key is send only if we've got a valid friend with DISC enabled
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
if (!mDiscEnabled)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Disc Disabled, removing all friend SSL Ids";
std::cerr << std::endl;
#endif
sslChilds.clear();
}
}
std::list<std::string>::iterator it;
for (it = sslChilds.begin(); it != sslChilds.end(); it++)
{
/* skip dummy ones - until they are removed fully */
if (isDummyFriend(*it))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Skipping Dummy Child SSL Id:" << *it;
std::cerr << std::endl;
#endif
continue;
}
peerState detail;
if (!mPeerMgr->getFriendNetStatus(*it, detail)
|| detail.visState & RS_VIS_STATE_NODISC)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Skipping cos No Details or NODISC flag id: " << *it;
std::cerr << std::endl;
#endif
continue;
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Found Child SSL Id:" << *it;
std::cerr << std::endl;
#endif
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Adding Child SSL Id Details";
std::cerr << std::endl;
#endif
shouldWeSendGPGKey = true;
RsPeerNetItem rsPeerNetItem ;
rsPeerNetItem.clear();
rsPeerNetItem.pid = detail.id;
rsPeerNetItem.gpg_id = detail.gpg_id;
rsPeerNetItem.location = detail.location;
rsPeerNetItem.netMode = detail.netMode;
rsPeerNetItem.visState = detail.visState;
rsPeerNetItem.lastContact = detail.lastcontact;
rsPeerNetItem.currentlocaladdr = detail.localaddr;
rsPeerNetItem.currentremoteaddr = detail.serveraddr;
rsPeerNetItem.dyndns = detail.dyndns;
detail.ipAddrs.mLocal.loadTlv(rsPeerNetItem.localAddrList);
detail.ipAddrs.mExt.loadTlv(rsPeerNetItem.extAddrList);
di->rsPeerList.push_back(rsPeerNetItem);
}
//send own details
if (about == rsPeers->getGPGOwnId())
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() Adding Own Id Details";
std::cerr << std::endl;
#endif
peerState detail;
if (mPeerMgr->getOwnNetStatus(detail))
{
shouldWeSendGPGKey = true;
RsPeerNetItem rsPeerNetItem ;
rsPeerNetItem.clear();
rsPeerNetItem.pid = detail.id;
rsPeerNetItem.gpg_id = detail.gpg_id;
rsPeerNetItem.location = detail.location;
rsPeerNetItem.netMode = detail.netMode;
rsPeerNetItem.visState = detail.visState;
rsPeerNetItem.lastContact = time(NULL);
rsPeerNetItem.currentlocaladdr = detail.localaddr;
rsPeerNetItem.currentremoteaddr = detail.serveraddr;
rsPeerNetItem.dyndns = detail.dyndns;
detail.ipAddrs.mLocal.loadTlv(rsPeerNetItem.localAddrList);
detail.ipAddrs.mExt.loadTlv(rsPeerNetItem.extAddrList);
di->rsPeerList.push_back(rsPeerNetItem);
}
}
if (!shouldWeSendGPGKey) {
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::createDiscReply() GPG key should not be send, no friend with disc on found about it." << std::endl;
#endif
// cleanup!
delete di;
return NULL;
}
return di;
}
void p3disc::sendOwnVersion(std::string to)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendOwnVersion() Sending rs version to: " << to << std::endl;
#endif
/* only ask info if discovery is on */
if (!mDiscEnabled)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendOwnVersion() Disc Disabled => NULL OP" << std::endl;
#endif
return;
}
RsDiscVersion *di = new RsDiscVersion();
di->PeerId(to);
di->version = RsUtil::retroshareVersion();
/* send the message */
sendItem(di);
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::sendOwnVersion() finished." << std::endl;
#endif
}
void p3disc::sendHeartbeat(std::string to)
{
{
std::string out = "p3disc::sendHeartbeat() to : " + to;
#ifdef P3DISC_DEBUG
std::cerr << out << std::endl;
#endif
rslog(RSL_WARNING, pqidisczone, out);
}
RsDiscHeartbeat *di = new RsDiscHeartbeat();
di->PeerId(to);
/* send the message */
sendItem(di);
#ifdef P3DISC_DEBUG
std::cerr << "Sent tick Message" << std::endl;
#endif
}
void p3disc::askInfoToAllPeers(std::string about)
{
#ifdef P3DISC_DEBUG
std::cerr <<"p3disc::askInfoToAllPeers() about " << about << std::endl;
#endif
// We Still Ask even if Disc isn't Enabled... if they want to give us the info ;)
peerState connectState;
if (!mPeerMgr->getFriendNetStatus(about, connectState) || (connectState.visState & RS_VIS_STATE_NODISC))
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::askInfoToAllPeers() friend disc is off, don't send the request." << std::endl;
#endif
return;
}
std::string aboutGpgId = rsPeers->getGPGId(about);
if (aboutGpgId == "")
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::askInfoToAllPeers() no gpg id found" << std::endl;
#endif
}
std::list<std::string> onlineIds;
std::list<std::string>::iterator it;
rsPeers->getOnlineList(onlineIds);
/* ask info to trusted friends */
for(it = onlineIds.begin(); it != onlineIds.end(); it++)
{
RsDiscAskInfo *di = new RsDiscAskInfo();
di->PeerId(*it);
di->gpg_id = aboutGpgId;
sendItem(di);
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::askInfoToAllPeers() question sent to : " << *it << std::endl;
#endif
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::askInfoToAllPeers() finished." << std::endl;
#endif
}
void p3disc::recvPeerDetails(RsDiscReply *item, const std::string &certGpgId)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvPeerFriendMsg() From: " << item->PeerId() << " About " << item->aboutId << std::endl;
#endif
if (certGpgId.empty())
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvPeerFriendMsg() gpg cert Id is empty - cert not transmitted" << std::endl;
#endif
}
else if (item->aboutId == "" || item->aboutId != certGpgId)
{
std::cerr << "p3disc::recvPeerFriendMsg() Error : about id is not the same as gpg id." << std::endl;
return;
}
bool should_notify_discovery = false ;
std::string item_gpg_id = rsPeers->getGPGId(item->PeerId()) ;
for (std::list<RsPeerNetItem>::iterator pit = item->rsPeerList.begin(); pit != item->rsPeerList.end(); pit++)
{
if(isDummyFriend(pit->pid))
{
continue;
}
bool new_info = false;
addDiscoveryData(item->PeerId(), pit->pid,item_gpg_id,
item->aboutId, pit->currentlocaladdr, pit->currentremoteaddr, 0, time(NULL),new_info);
if(new_info)
should_notify_discovery = true ;
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvPeerFriendMsg() Peer Config Item:" << std::endl;
pit->print(std::cerr, 10);
std::cerr << std::endl;
#endif
if (pit->pid != rsPeers->getOwnId())
{
// Apparently, the connect manager won't add a friend if the gpg id is not
// trusted. However, this should be tested here for consistency and security
// in case of modifications in mConnMgr.
//
// Check if already friend.
if(AuthGPG::getAuthGPG()->isGPGAccepted(pit->gpg_id) || pit->gpg_id == AuthGPG::getAuthGPG()->getGPGOwnId())
{
if (!mPeerMgr->isFriend(pit->pid))
{
// Add with no disc by default. If friend already exists, it will do nothing
// NO DISC is important - otherwise, we'll just enter a nasty loop,
// where every addition triggers requests, then they are cleaned up, and readded...
// This way we get their addresses, but don't advertise them until we get a
// connection.
#ifdef P3DISC_DEBUG
std::cerr << "--> Adding to friends list " << pit->pid << " - " << pit->gpg_id << std::endl;
#endif
mPeerMgr->addFriend(pit->pid, pit->gpg_id, pit->netMode, RS_VIS_STATE_NODISC, 0);
}
}
/* skip if not one of our peers */
if (!mPeerMgr->isFriend(pit->pid))
{
/* THESE ARE OUR FRIEND OF FRIENDS ... pass this information along to NetMgr & DHT...
* as we can track FOF and use them as potential Proxies / Relays
*/
/* add into NetMgr and non-search, so we can detect connect attempts */
mNetMgr->netAssistFriend(pit->pid,false);
/* inform NetMgr that we know this peer */
mNetMgr->netAssistKnownPeer(pit->pid, pit->currentremoteaddr,
NETASSIST_KNOWN_PEER_FOF | NETASSIST_KNOWN_PEER_OFFLINE);
continue;
}
if (item->PeerId() == pit->pid)
{
#ifdef P3DISC_DEBUG
std::cerr << "Info sent by the peer itself -> updating self info:" << std::endl;
std::cerr << " -> current local addr = " << pit->currentlocaladdr << std::endl;
std::cerr << " -> current remote addr = " << pit->currentremoteaddr << std::endl;
std::cerr << " -> visState = " << std::hex << pit->visState << std::dec;
std::cerr << " -> network mode: " << pit->netMode << std::endl;
std::cerr << " -> location: " << pit->location << std::endl;
std::cerr << std::endl;
#endif
// When the peer sends his own list of IPs, the info replaces the existing info, because the
// peer is the primary source of his own IPs.
mPeerMgr->setNetworkMode(pit->pid, pit->netMode);
mPeerMgr->setLocation(pit->pid, pit->location);
mPeerMgr->setLocalAddress(pit->pid, pit->currentlocaladdr);
mPeerMgr->setExtAddress(pit->pid, pit->currentremoteaddr);
mPeerMgr->setVisState(pit->pid, pit->visState);
if (pit->dyndns != "")
mPeerMgr->setDynDNS(pit->pid, pit->dyndns);
}
// always update historical address list... this should be enough to let us connect.
pqiIpAddrSet addrsFromPeer;
addrsFromPeer.mLocal.extractFromTlv(pit->localAddrList);
addrsFromPeer.mExt.extractFromTlv(pit->extAddrList);
#ifdef P3DISC_DEBUG
std::cerr << "Setting address list to peer " << pit->pid << ", to be:" << std::endl ;
addrsFromPeer.printAddrs(std::cerr);
std::cerr << std::endl;
#endif
mPeerMgr->updateAddressList(pit->pid, addrsFromPeer);
}
#ifdef P3DISC_DEBUG
else
{
std::cerr << "Skipping info about own id " << pit->pid << std::endl ;
}
#endif
}
rsicontrol->getNotify().notifyListChange(NOTIFY_LIST_NEIGHBOURS, NOTIFY_TYPE_MOD);
if(should_notify_discovery)
rsicontrol->getNotify().notifyDiscInfoChanged();
/* cleanup (handled by caller) */
}
void p3disc::recvPeerVersionMsg(RsDiscVersion *item)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvPeerVersionMsg() From: " << item->PeerId();
std::cerr << std::endl;
#endif
// dont need protection
versions[item->PeerId()] = item->version;
return;
}
void p3disc::recvHeartbeatMsg(RsDiscHeartbeat *item)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvHeartbeatMsg() From: " << item->PeerId();
std::cerr << std::endl;
#endif
mPqiPersonGrp->tagHeartbeatRecvd(item->PeerId());
return;
}
void p3disc::recvAskInfo(RsDiscAskInfo *item)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvAskInfo() From: " << item->PeerId();
std::cerr << std::endl;
#endif
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
/* only provide info if discovery is on */
if (!mDiscEnabled)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvAskInfo() Disc Disabled => NULL OP";
std::cerr << std::endl;
#endif
return;
}
std::list<std::string> &idList = mSendIdList[item->PeerId()];
if (std::find(idList.begin(), idList.end(), item->gpg_id) == idList.end()) {
idList.push_back(item->gpg_id);
}
}
void p3disc::recvDiscReply(RsDiscReply *dri)
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::recvDiscReply() From: " << dri->PeerId() << " About: " << dri->aboutId;
std::cerr << std::endl;
#endif
/* search pending item and remove it, when already exist */
std::list<RsDiscReply*>::iterator it;
for (it = mPendingDiscReplyInList.begin(); it != mPendingDiscReplyInList.end(); it++)
{
if ((*it)->PeerId() == dri->PeerId() && (*it)->aboutId == dri->aboutId)
{
delete (*it);
mPendingDiscReplyInList.erase(it);
break;
}
}
// add item to list for later process
if(mDiscEnabled || dri->aboutId == rsPeers->getGPGId(dri->PeerId()))
mPendingDiscReplyInList.push_back(dri); // no delete
else
delete dri ;
}
void p3disc::removeFriend(std::string /*ssl_id*/)
{
}
/*************************************************************************************/
/* AuthGPGService */
/*************************************************************************************/
AuthGPGOperation *p3disc::getGPGOperation()
{
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
/* process disc reply in list */
if (mPendingDiscReplyInList.empty() == false) {
RsDiscReply *item = mPendingDiscReplyInList.front();
return new AuthGPGOperationLoadOrSave(true, item->aboutId, item->certGPG, item);
}
}
/* process disc reply out list */
std::string destId;
std::string srcId;
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
while (!mSendIdList.empty())
{
std::map<std::string, std::list<std::string> >::iterator it = mSendIdList.begin();
if (!it->second.empty() && mLinkMgr->isOnline(it->first)) {
std::string gpgId = it->second.front();
it->second.pop_front();
destId = it->first;
srcId = gpgId;
break;
} else {
/* peer is not online anymore ... try next */
mSendIdList.erase(it);
}
}
}
if (!destId.empty() && !srcId.empty()) {
RsDiscReply *item = createDiscReply(destId, srcId);
if (item) {
return new AuthGPGOperationLoadOrSave(false, item->aboutId, "", item);
}
}
return NULL;
}
void p3disc::setGPGOperation(AuthGPGOperation *operation)
{
AuthGPGOperationLoadOrSave *loadOrSave = dynamic_cast<AuthGPGOperationLoadOrSave*>(operation);
if (loadOrSave) {
if (loadOrSave->m_load) {
/* search in pending in list */
RsDiscReply *item = NULL;
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
std::list<RsDiscReply*>::iterator it;
it = std::find(mPendingDiscReplyInList.begin(), mPendingDiscReplyInList.end(), loadOrSave->m_userdata);
if (it != mPendingDiscReplyInList.end()) {
item = *it;
mPendingDiscReplyInList.erase(it);
}
}
if (item) {
recvPeerDetails(item, loadOrSave->m_certGpgId);
delete item;
}
} else {
RsDiscReply *item = (RsDiscReply*) loadOrSave->m_userdata;
if (item)
{
// It is okay to send an empty certificate! - This is to reduce the network load at connection time.
// Hopefully, we'll get the stripped down certificates working soon!... even then still be okay to send null.
#if 0
if (loadOrSave->m_certGpg.empty())
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::setGPGOperation() don't send details because the gpg cert is not good" << std::endl;
#endif
delete item;
return;
}
#endif
/* for Relay Connections (and other slow ones) we don't want to
* to waste bandwidth sending certificates. So don't add it.
*/
uint32_t linkType = mLinkMgr->getLinkType(item->PeerId());
if ((linkType & RS_NET_CONN_SPEED_TRICKLE) ||
(linkType & RS_NET_CONN_SPEED_LOW))
{
std::cerr << "p3disc::setGPGOperation() Send DiscReply Packet to: ";
std::cerr << item->PeerId();
std::cerr << " without Certificate (low bandwidth)" << std::endl;
}
else
{
// Attaching Certificate.
item->certGPG = loadOrSave->m_certGpg;
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::setGPGOperation() About to Send Message:" << std::endl;
item->print(std::cerr, 5);
#endif
// Send off message
sendItem(item);
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::cbkGPGOperationSave() discovery reply sent." << std::endl;
#endif
}
}
return;
}
/* ignore other operations */
}
/*************************************************************************************/
/* Storing Network Graph */
/*************************************************************************************/
int p3disc::addDiscoveryData(const std::string& fromId, const std::string& aboutId,const std::string& from_gpg_id,const std::string& about_gpg_id, const struct sockaddr_in& laddr, const struct sockaddr_in& raddr, uint32_t flags, time_t ts,bool& new_info)
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
new_info = false ;
gpg_neighbors[from_gpg_id].insert(about_gpg_id) ;
#ifdef P3DISC_DEBUG
std::cerr << "Adding discovery data " << fromId << " - " << aboutId << std::endl ;
#endif
/* Store Network information */
std::map<std::string, autoneighbour>::iterator it;
if (neighbours.end() == (it = neighbours.find(aboutId)))
{
/* doesn't exist */
autoneighbour an;
/* an data */
an.id = aboutId;
an.validAddrs = false;
an.discFlags = 0;
an.ts = 0;
neighbours[aboutId] = an;
it = neighbours.find(aboutId);
new_info = true ;
}
/* it always valid */
/* just update packet */
autoserver as;
as.id = fromId;
as.localAddr = laddr;
as.remoteAddr = raddr;
as.discFlags = flags;
as.ts = ts;
bool authDetails = (as.id == it->second.id);
/* KEY decision about address */
if ((authDetails) ||
((!(it->second.authoritative)) && (as.ts > it->second.ts)))
{
/* copy details to an */
it->second.authoritative = authDetails;
it->second.ts = as.ts;
it->second.validAddrs = true;
it->second.localAddr = as.localAddr;
it->second.remoteAddr = as.remoteAddr;
it->second.discFlags = as.discFlags;
}
if(it->second.neighbour_of.find(fromId) == it->second.neighbour_of.end())
{
(it->second).neighbour_of[fromId] = as;
new_info =true ;
}
/* do we update network address info??? */
return 1;
}
/*************************************************************************************/
/* Extracting Network Graph Details */
/*************************************************************************************/
bool p3disc::potentialproxies(const std::string& id, std::list<std::string> &proxyIds)
{
/* find id -> and extract the neighbour_of ids */
if(id == rsPeers->getOwnId()) // SSL id // This is treated appart, because otherwise we don't receive any disc info about us
return rsPeers->getFriendList(proxyIds) ;
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
std::map<std::string, autoneighbour>::iterator it;
std::map<std::string, autoserver>::iterator sit;
if (neighbours.end() == (it = neighbours.find(id)))
{
return false;
}
for(sit = it->second.neighbour_of.begin(); sit != it->second.neighbour_of.end(); sit++)
{
proxyIds.push_back(sit->first);
}
return true;
}
bool p3disc::potentialGPGproxies(const std::string& gpg_id, std::list<std::string> &proxyGPGIds)
{
/* find id -> and extract the neighbour_of ids */
if(gpg_id == rsPeers->getGPGOwnId()) // SSL id // This is treated appart, because otherwise we don't receive any disc info about us
return rsPeers->getGPGAcceptedList(proxyGPGIds) ;
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
std::map<std::string, std::set<std::string> >::iterator it = gpg_neighbors.find(gpg_id) ;
if(it == gpg_neighbors.end())
return false;
for(std::set<std::string>::const_iterator sit(it->second.begin()); sit != it->second.end(); ++sit)
proxyGPGIds.push_back(*sit);
return true;
}
void p3disc::getversions(std::map<std::string, std::string> &versions)
{
versions = this->versions;
}
void p3disc::getWaitingDiscCount(unsigned int *sendCount, unsigned int *recvCount)
{
if (sendCount == NULL && recvCount == NULL) {
/* Nothing to do */
return;
}
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
if (sendCount) {
*sendCount = 0;
std::map<std::string, std::list<std::string> >::iterator it;
for (it = mSendIdList.begin(); it != mSendIdList.end(); it++) {
*sendCount += it->second.size();
}
}
if (recvCount) {
*recvCount = mPendingDiscReplyInList.size();
}
}
#ifdef UNUSED_CODE
int p3disc::idServers()
{
RsStackMutex stack(mDiscMtx); /********** STACK LOCKED MTX ******/
std::map<std::string, autoneighbour>::iterator nit;
std::map<std::string, autoserver>::iterator sit;
int cts = time(NULL);
std::string out = "::::AutoDiscovery Neighbours::::\n";
for(nit = neighbours.begin(); nit != neighbours.end(); nit++)
{
out += "Neighbour: " + (nit->second).id + "\n";
rs_sprintf_append(out, "-> LocalAddr: %s:%u\n", rs_inet_ntoa(nit->second.localAddr.sin_addr).c_str(), ntohs(nit->second.localAddr.sin_port));
rs_sprintf_append(out, "-> RemoteAddr: %s:%u\n", rs_inet_ntoa(nit->second.remoteAddr.sin_addr).c_str(), ntohs(nit->second.remoteAddr.sin_port));
rs_sprintf_append(out, " Last Contact: %ld sec ago\n", cts - (nit->second.ts));
rs_sprintf_append(out, " -->DiscFlags: 0x%x\n", nit->second.discFlags);
for(sit = (nit->second.neighbour_of).begin();
sit != (nit->second.neighbour_of).end(); sit++)
{
out += "\tConnected via: " + (sit->first) + "\n";
rs_sprintf_append(out, "\t\tLocalAddr: %s:%u\n", rs_inet_ntoa(sit->second.localAddr.sin_addr).c_str(), ntohs(sit->second.localAddr.sin_port));
rs_sprintf_append(out, "\t\tRemoteAddr: %s:%u\n", rs_inet_ntoa(sit->second.remoteAddr.sin_addr).c_str(), ntohs(sit->second.remoteAddr.sin_port));
rs_sprintf_append(out, "\t\tLast Contact: %ld sec ago\n", cts - (sit->second.ts));
rs_sprintf_append(out, "\t\tDiscFlags: 0x%x\n", sit->second.discFlags);
}
}
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::idServers()" << std::endl;
std::cerr << out;
#endif
return 1;
}
#endif
// tdelta -> trange.
// -inf...<0 0 (invalid)
// 0.. <9 1
// 9...<99 2
// 99...<999 3
// 999...<9999 4
// etc...
//int convertTDeltaToTRange(double tdelta)
//{
// if (tdelta < 0)
// return 0;
// int trange = 1 + (int) log10(tdelta + 1.0);
// return trange;
//
//}
// trange -> tdelta
// -inf...0 -1 (invalid)
// 1 8
// 2 98
// 3 998
// 4 9998
// etc...
//int convertTRangeToTDelta(int trange)
//{
// if (trange <= 0)
// return -1;
//
// return (int) (pow(10.0, trange) - 1.5); // (int) xxx98.5 -> xxx98
//}
// -----------------------------------------------------------------------------------//
// -------------------------------- Config functions ------------------------------ //
// -----------------------------------------------------------------------------------//
//
RsSerialiser *p3disc::setupSerialiser()
{
RsSerialiser *rss = new RsSerialiser ;
rss->addSerialType(new RsGeneralConfigSerialiser());
return rss ;
}
bool p3disc::saveList(bool& cleanup, std::list<RsItem*>& /*lst*/)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::saveList() called" << std::endl;
#endif
cleanup = true ;
// DON'T KNOW WHY SSL IDS were saved -> the results are never used
#if 0
// Now save config for network digging strategies
RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ;
std::map<std::string, time_t>::iterator mapIt;
for (mapIt = deletedSSLFriendsIds.begin(); mapIt != deletedSSLFriendsIds.end(); mapIt++)
{
RsTlvKeyValue kv;
kv.key = mapIt->first;
rs_sprintf(kv.value, "%ld", mapIt->second);
vitem->tlvkvs.pairs.push_back(kv) ;
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::saveList() saving : " << mapIt->first << " ; " << mapIt->second << std::endl ;
#endif
}
lst.push_back(vitem);
#endif
return true ;
}
bool p3disc::loadList(std::list<RsItem*>& load)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::loadList() Item Count: " << load.size() << std::endl;
#endif
RsStackMutex stack(mDiscMtx); /****** STACK LOCK MUTEX *******/
/* load the list of accepted gpg keys */
std::list<RsItem *>::iterator it;
for(it = load.begin(); it != load.end(); it++)
{
// DON'T KNOW WHY SSL IDS were saved -> the results are never used
#if 0
RsConfigKeyValueSet *vitem = dynamic_cast<RsConfigKeyValueSet *>(*it);
if(vitem)
{
#ifdef P3DISC_DEBUG
std::cerr << "p3disc::loadList() General Variable Config Item:" << std::endl;
vitem->print(std::cerr, 10);
std::cerr << std::endl;
#endif
std::list<RsTlvKeyValue>::iterator kit;
for(kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); kit++)
{
std::istringstream instream(kit->value); // please do not use std::istringstream
time_t deleted_time_t;
instream >> deleted_time_t;
deletedSSLFriendsIds[kit->key] = deleted_time_t;
}
}
#endif
delete (*it);
}
return true;
}