RetroShare/libretroshare/src/pqi/p3connmgr.cc

1824 lines
39 KiB
C++
Raw Normal View History

/*
* libretroshare/src/pqi: p3connmgr.cc
*
* 3P/PQI network interface for RetroShare.
*
* Copyright 2007-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 "pqi/p3connmgr.h"
#include "tcponudp/tou.h"
#include "util/rsprint.h"
/* Network setup States */
const uint32_t RS_NET_UNKNOWN = 0x0001;
const uint32_t RS_NET_UPNP_INIT = 0x0002;
const uint32_t RS_NET_UPNP_SETUP = 0x0003;
const uint32_t RS_NET_UDP_SETUP = 0x0004;
const uint32_t RS_NET_DONE = 0x0005;
/* Stun modes (TODO) */
const uint32_t RS_STUN_DHT = 0x0001;
const uint32_t RS_STUN_DONE = 0x0002;
const uint32_t RS_STUN_LIST_MIN = 100;
const uint32_t MAX_UPNP_INIT = 10; /* seconds UPnP timeout */
#define CONN_DEBUG 1
p3ConnectMgr::p3ConnectMgr(p3AuthMgr *am)
:p3Config(CONFIG_TYPE_PEERS, "peers.cfg"),
mAuthMgr(am), mDhtMgr(NULL), mUpnpMgr(NULL), mNetStatus(RS_NET_UNKNOWN),
mStunStatus(0), mStatusChanged(false)
{
mUpnpAddrValid = false;
mStunAddrValid = false;
/* setup basics of own state */
if (am)
{
ownState.id = mAuthMgr->OwnId();
ownState.name = mAuthMgr->getName(ownState.id);
}
return;
}
peerConnectAddress::peerConnectAddress()
:type(0), ts(0)
{
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = 0;
addr.sin_port = 0;
}
peerAddrInfo::peerAddrInfo()
:found(false), type(0), ts(0)
{
laddr.sin_family = AF_INET;
laddr.sin_addr.s_addr = 0;
laddr.sin_port = 0;
raddr.sin_family = AF_INET;
raddr.sin_addr.s_addr = 0;
raddr.sin_port = 0;
}
peerConnectState::peerConnectState()
:id("unknown"), name("nameless"), state(0), actions(0),
netMode(RS_NET_MODE_UNKNOWN), visState(RS_VIS_STATE_STD),
source(0),
inConnAttempt(0), connAttemptTS(0),
lc_timestamp(0), lr_timestamp(0),
nc_timestamp(0), nc_timeintvl(0)
{
lastaddr.sin_family = AF_INET;
lastaddr.sin_addr.s_addr = 0;
lastaddr.sin_port = 0;
localaddr.sin_family = AF_INET;
localaddr.sin_addr.s_addr = 0;
localaddr.sin_port = 0;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = 0;
serveraddr.sin_port = 0;
return;
}
/***** Framework / initial implementation for a connection manager.
*
* This needs a state machine for Initialisation.
*
* Network state:
* RS_NET_UNKNOWN
* RS_NET_EXT_UNKNOWN * forwarded port (but Unknown Ext IP) *
* RS_NET_EXT_KNOWN * forwarded port with known IP/Port. *
*
* RS_NET_UPNP_CHECK * checking for UPnP *
* RS_NET_UPNP_KNOWN * confirmed UPnP ext Ip/port *
*
* RS_NET_UDP_UNKNOWN * not Ext/UPnP - to determine Ext IP/Port *
* RS_NET_UDP_KNOWN * have Stunned for Ext Addr *
*
* Transitions:
*
* RS_NET_UNKNOWN -(config)-> RS_NET_EXT_UNKNOWN
* RS_NET_UNKNOWN -(config)-> RS_NET_UPNP_UNKNOWN
* RS_NET_UNKNOWN -(config)-> RS_NET_UDP_UNKNOWN
*
* RS_NET_EXT_UNKNOWN -(DHT(ip)/Stun)-> RS_NET_EXT_KNOWN
*
* RS_NET_UPNP_UNKNOWN -(Upnp)-> RS_NET_UPNP_KNOWN
* RS_NET_UPNP_UNKNOWN -(timout/Upnp)-> RS_NET_UDP_UNKNOWN
*
* RS_NET_UDP_UNKNOWN -(stun)-> RS_NET_UDP_KNOWN
*
*
* STUN state:
* RS_STUN_INIT * done nothing *
* RS_STUN_DHT * looking up peers *
* RS_STUN_DONE * found active peer and stunned *
*
*
* Steps.
*******************************************************************
* (1) Startup.
* - UDP port setup.
* - DHT setup.
* - Get Stun Keys -> add to DHT.
* - Feedback from DHT -> ask UDP to stun.
*
* (1) determine Network mode.
* If external Port.... Done:
* (2)
*******************************************************************
* Stable operation:
* (1) tick and check peers.
* (2) handle callback.
* (3) notify of new/failed connections.
*
*
*/
void p3ConnectMgr::netStartup()
{
/* startup stuff */
/* StunInit gets a list of peers, and asks the DHT to find them...
* This is needed for all systems so startup straight away
*/
loadConfiguration();
netDhtInit();
netUdpInit();
netStunInit();
/* decide which net setup mode we're going into
*/
connMtx.lock(); /* LOCK MUTEX */
mNetInitTS = time(NULL);
switch(ownState.netMode)
{
case RS_NET_MODE_UPNP:
mNetStatus = RS_NET_UPNP_INIT;
break;
case RS_NET_MODE_EXT: /* v similar to UDP */
case RS_NET_MODE_UDP:
default:
mNetStatus = RS_NET_UDP_SETUP;
break;
}
connMtx.unlock(); /* UNLOCK MUTEX */
}
void p3ConnectMgr::tick()
{
netTick();
tickMonitors();
}
void p3ConnectMgr::netTick()
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick()" << std::endl;
#endif
connMtx.lock(); /* LOCK MUTEX */
uint32_t netStatus = mNetStatus;
connMtx.unlock(); /* UNLOCK MUTEX */
switch(netStatus)
{
case RS_NET_UNKNOWN:
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick() STATUS: UNKNOWN" << std::endl;
#endif
/* RS_NET_UNKNOWN -(config)-> RS_NET_EXT_INIT
* RS_NET_UNKNOWN -(config)-> RS_NET_UPNP_INIT
* RS_NET_UNKNOWN -(config)-> RS_NET_UDP_INIT
*/
netStartup();
break;
case RS_NET_UPNP_INIT:
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick() STATUS: UPNP_INIT" << std::endl;
#endif
netUpnpInit();
break;
case RS_NET_UPNP_SETUP:
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick() STATUS: UPNP_SETUP" << std::endl;
#endif
netUpnpCheck();
break;
case RS_NET_UDP_SETUP:
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick() STATUS: UDP_SETUP" << std::endl;
#endif
netUdpCheck();
break;
case RS_NET_DONE:
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netTick() STATUS: DONE" << std::endl;
#endif
default:
break;
}
return;
}
void p3ConnectMgr::netUdpInit()
{
connMtx.lock(); /* LOCK MUTEX */
struct sockaddr_in iaddr = ownState.localaddr;
connMtx.unlock(); /* UNLOCK MUTEX */
/* open our udp port */
tou_init((struct sockaddr *) &iaddr, sizeof(iaddr));
}
void p3ConnectMgr::netDhtInit()
{
connMtx.lock(); /* LOCK MUTEX */
uint32_t vs = ownState.visState;
connMtx.unlock(); /* UNLOCK MUTEX */
mDhtMgr->setDhtOn(!(vs & RS_VIS_STATE_NODHT));
}
void p3ConnectMgr::netUpnpInit()
{
uint16_t eport, iport;
connMtx.lock(); /* LOCK MUTEX */
/* get the ports from the configuration */
mNetStatus = RS_NET_UPNP_SETUP;
connMtx.unlock(); /* UNLOCK MUTEX */
mUpnpMgr->setInternalPort(iport);
mUpnpMgr->setExternalPort(eport);
mUpnpMgr->enableUPnP(true);
}
void p3ConnectMgr::netUpnpCheck()
{
/* grab timestamp */
connMtx.lock(); /* LOCK MUTEX */
time_t delta = time(NULL) - mNetInitTS;
connMtx.unlock(); /* UNLOCK MUTEX */
struct sockaddr_in extAddr;
int upnpState = mUpnpMgr->getUPnPActive();
if ((upnpState < 0) ||
((upnpState == 0) && (delta > MAX_UPNP_INIT)))
{
/* fallback to UDP startup */
connMtx.lock(); /* LOCK MUTEX */
mUpnpAddrValid = false;
mNetStatus = RS_NET_UDP_SETUP;
ownState.netMode = RS_NET_MODE_UDP; /* UPnP Failed us! */
connMtx.unlock(); /* UNLOCK MUTEX */
}
else if ((upnpState > 0) &&
mUpnpMgr->getExternalAddress(extAddr))
{
/* switch to UDP startup */
connMtx.lock(); /* LOCK MUTEX */
mUpnpAddrValid = true;
mUpnpExtAddr = extAddr;
mNetStatus = RS_NET_UDP_SETUP;
connMtx.unlock(); /* UNLOCK MUTEX */
}
}
void p3ConnectMgr::netUdpCheck()
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::netUdpCheck()" << std::endl;
#endif
if (stunCheck())
{
bool extValid = false;
struct sockaddr_in extAddr;
uint32_t mode = 0;
connMtx.lock(); /* LOCK MUTEX */
mNetStatus = RS_NET_DONE;
/* get the addr from the configuration */
struct sockaddr_in iaddr = ownState.localaddr;
if (mUpnpAddrValid)
{
extValid = true;
extAddr = mUpnpExtAddr;
}
else if (mStunAddrValid)
{
extValid = true;
extAddr = mStunExtAddr;
}
if (extValid)
{
ownState.serveraddr = extAddr;
mode = RS_NET_CONN_TCP_LOCAL | RS_NET_CONN_UDP_DHT_SYNC;
if ((ownState.netMode == RS_NET_MODE_UPNP) ||
(ownState.netMode == RS_NET_MODE_EXT))
{
mode |= RS_NET_CONN_TCP_EXTERNAL;
}
}
connMtx.unlock(); /* UNLOCK MUTEX */
if (extValid)
{
mDhtMgr->setExternalInterface(iaddr, extAddr, mode);
}
else
{
mDhtMgr->setExternalInterface(iaddr, extAddr, RS_NET_MODE_ERROR);
}
}
}
/******************************* UDP MAINTAINANCE ********************************
* Interaction with the UDP is mainly for determining the External Port.
*
*/
bool p3ConnectMgr::udpInternalAddress(struct sockaddr_in iaddr)
{
return false;
}
bool p3ConnectMgr::udpExtAddressCheck()
{
/* three possibilities:
* (1) not found yet.
* (2) Found!
* (3) bad udp (port switching).
*/
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::udpExtAddressCheck()" << std::endl;
#endif
if (tou_extaddr((struct sockaddr *) &addr, &len))
{
/* update UDP information */
connMtx.lock(); /* LOCK MUTEX */
mStunExtAddr = addr;
mStunAddrValid = true;
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::udpExtAddressCheck() Got ";
std::cerr << " addr: " << inet_ntoa(mStunExtAddr.sin_addr);
std::cerr << " port: " << ntohs(mStunExtAddr.sin_port);
std::cerr << std::endl;
#endif
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
return false;
}
void p3ConnectMgr::udpStunPeer(std::string id, struct sockaddr_in &addr)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::udpStunPeer()" << std::endl;
#endif
/* add it into udp stun list */
tou_stunpeer((struct sockaddr *) &addr, sizeof(addr), id.c_str());
}
/********************************** STUN SERVERS ***********************************
* We maintain a list of stun servers. This is initialised with a set of random keys.
*
* This is gradually rolled over with time. We update with friends/friends of friends,
* and the lists that they provide (part of AutoDisc).
*
* max 100 entries?
*/
void p3ConnectMgr::netStunInit()
{
stunInit();
}
void p3ConnectMgr::stunInit()
{
connMtx.lock(); /* LOCK MUTEX */
/* push stun list to DHT */
std::list<std::string>::iterator it;
for(it = mStunList.begin(); it != mStunList.end(); it++)
{
mDhtMgr->addStun(*it);
}
mStunStatus = RS_STUN_DHT;
connMtx.unlock(); /* UNLOCK MUTEX */
}
bool p3ConnectMgr::stunCheck()
{
/* check if we've got a Stun result */
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunCheck()" << std::endl;
#endif
if (udpExtAddressCheck())
{
/* set external UDP address */
mDhtMgr->doneStun();
connMtx.lock(); /* LOCK MUTEX */
mStunStatus = RS_STUN_DONE;
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
return false;
}
void p3ConnectMgr::stunStatus(std::string id, struct sockaddr_in addr, uint32_t flags)
{
std::cerr << "p3ConnectMgr::stunStatus()";
std::cerr << " id: " << RsUtil::BinToHex(id) << " addr: " << inet_ntoa(addr.sin_addr);
std::cerr << " port: " << ntohs(addr.sin_port);
std::cerr << std::endl;
connMtx.lock(); /* LOCK MUTEX */
bool stillStunning = (mStunStatus == RS_STUN_DHT);
connMtx.unlock(); /* UNLOCK MUTEX */
if (stillStunning)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunStatus() Sending to UDP" << std::endl;
#endif
/* push to the UDP */
udpStunPeer(id, addr);
}
/* push to the stunCollect */
stunCollect(id, addr, flags);
}
/* FLAGS
ONLINE
EXT
UPNP
UDP
FRIEND
FRIEND_OF_FRIEND
OTHER
*/
void p3ConnectMgr::stunCollect(std::string id, struct sockaddr_in addr, uint32_t flags)
{
/* if peer is online - move to the top */
connMtx.lock(); /* LOCK MUTEX */
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunCollect() id: " << RsUtil::BinToHex(id) << std::endl;
#endif
std::list<std::string>::iterator it;
it = std::find(mStunList.begin(), mStunList.end(), id);
if (it == mStunList.end())
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunCollect() Id not in List" << std::endl;
#endif
/* add it in:
* if FRIEND / ONLINE or if list is short.
*/
if ((flags & RS_STUN_ONLINE) || (flags & RS_STUN_FRIEND)
|| (mStunList.size() < RS_STUN_LIST_MIN))
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunCollect() Id added to Front" << std::endl;
#endif
/* push to the front */
mStunList.push_front(id);
}
}
else
{
/* if they're online ... move to the front
*/
if (flags & RS_STUN_ONLINE)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::stunCollect() Id moved to Front" << std::endl;
#endif
/* move to front */
mStunList.erase(it);
mStunList.push_front(id);
}
}
connMtx.unlock(); /* UNLOCK MUTEX */
}
/******************************** Network Status *********************************
* Configuration Loading / Saving.
*/
void p3ConnectMgr::addMonitor(pqiMonitor *mon)
{
/*
*/
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
std::list<pqiMonitor *>::iterator it;
it = std::find(clients.begin(), clients.end(), mon);
if (it != clients.end())
{
return;
}
mon->setConnectionMgr(this);
clients.push_back(mon);
return;
}
void p3ConnectMgr::removeMonitor(pqiMonitor *mon)
{
/*
*/
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
std::list<pqiMonitor *>::iterator it;
it = std::find(clients.begin(), clients.end(), mon);
if (it == clients.end())
{
return;
}
(*it)->setConnectionMgr(NULL);
clients.erase(it);
return;
}
void p3ConnectMgr::tickMonitors()
{
std::list<pqipeer> actionList;
std::map<std::string, peerConnectState>::iterator it;
if (mStatusChanged)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::tickMonitors() StatusChanged! List:" << std::endl;
#endif
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
/* assemble list */
for(it = mFriendList.begin(); it != mFriendList.end(); it++)
{
if (it->second.actions)
{
/* add in */
pqipeer peer;
peer.id = it->second.id;
peer.name = it->second.name;
peer.state = it->second.state;
peer.actions = it->second.actions;
/* reset action */
it->second.actions = 0;
actionList.push_back(peer);
#ifdef CONN_DEBUG
std::cerr << "Friend: " << peer.name;
std::cerr << " Id: " << peer.id;
std::cerr << " State: " << peer.state;
if (peer.state & RS_PEER_S_FRIEND)
std::cerr << " S:RS_PEER_S_FRIEND";
if (peer.state & RS_PEER_S_ONLINE)
std::cerr << " S:RS_PEER_S_ONLINE";
if (peer.state & RS_PEER_S_CONNECTED)
std::cerr << " S:RS_PEER_S_CONNECTED";
std::cerr << " Actions: " << peer.actions;
if (peer.actions & RS_PEER_NEW)
std::cerr << " A:RS_PEER_NEW";
if (peer.actions & RS_PEER_MOVED)
std::cerr << " A:RS_PEER_MOVED";
if (peer.actions & RS_PEER_CONNECTED)
std::cerr << " A:RS_PEER_CONNECTED";
if (peer.actions & RS_PEER_DISCONNECTED)
std::cerr << " A:RS_PEER_DISCONNECTED";
if (peer.actions & RS_PEER_CONNECT_REQ)
std::cerr << " A:RS_PEER_CONNECT_REQ";
std::cerr << std::endl;
#endif
}
}
/* do the Others as well! */
for(it = mOthersList.begin(); it != mOthersList.end(); it++)
{
if (it->second.actions)
{
/* add in */
pqipeer peer;
peer.id = it->second.id;
peer.name = it->second.name;
peer.state = it->second.state;
peer.actions = it->second.actions;
/* reset action */
it->second.actions = 0;
#ifdef CONN_DEBUG
std::cerr << "Other: " << peer.name;
std::cerr << " Id: " << peer.id;
std::cerr << " State: " << peer.state;
if (peer.state & RS_PEER_S_FRIEND)
std::cerr << " S:RS_PEER_S_FRIEND";
if (peer.state & RS_PEER_S_ONLINE)
std::cerr << " S:RS_PEER_S_ONLINE";
if (peer.state & RS_PEER_S_CONNECTED)
std::cerr << " S:RS_PEER_S_CONNECTED";
std::cerr << " Actions: " << peer.actions;
if (peer.actions & RS_PEER_NEW)
std::cerr << " A:RS_PEER_NEW";
if (peer.actions & RS_PEER_MOVED)
std::cerr << " A:RS_PEER_MOVED";
if (peer.actions & RS_PEER_CONNECTED)
std::cerr << " A:RS_PEER_CONNECTED";
if (peer.actions & RS_PEER_DISCONNECTED)
std::cerr << " A:RS_PEER_DISCONNECTED";
if (peer.actions & RS_PEER_CONNECT_REQ)
std::cerr << " A:RS_PEER_CONNECT_REQ";
std::cerr << std::endl;
#endif
actionList.push_back(peer);
}
}
mStatusChanged = false;
#ifdef CONN_DEBUG
std::cerr << "Sending to " << clients.size() << " monitorClients";
std::cerr << std::endl;
#endif
/* send to all monitors */
std::list<pqiMonitor *>::iterator mit;
for(mit = clients.begin(); mit != clients.end(); mit++)
{
(*mit)->statusChange(actionList);
}
}
}
const std::string p3ConnectMgr::getOwnId()
{
if (mAuthMgr)
{
return mAuthMgr->OwnId();
}
else
{
std::string nullStr;
return nullStr;
}
}
bool p3ConnectMgr::getOwnNetStatus(peerConnectState &state)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
state = ownState;
return true;
}
bool p3ConnectMgr::isFriend(std::string id)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
return (mFriendList.end() != mFriendList.find(id));
}
bool p3ConnectMgr::getFriendNetStatus(std::string id, peerConnectState &state)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
it = mFriendList.find(id);
if (it == mFriendList.end())
{
return false;
}
state = it->second;
return true;
}
bool p3ConnectMgr::getOthersNetStatus(std::string id, peerConnectState &state)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
it = mOthersList.find(id);
if (it == mOthersList.end())
{
return false;
}
state = it->second;
return true;
}
void p3ConnectMgr::getOnlineList(std::list<std::string> &peers)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
for(it = mFriendList.begin(); it != mFriendList.end(); it++)
{
if (it->second.state & RS_PEER_S_ONLINE)
{
peers.push_back(it->first);
}
}
return;
}
void p3ConnectMgr::getFriendList(std::list<std::string> &peers)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
for(it = mFriendList.begin(); it != mFriendList.end(); it++)
{
peers.push_back(it->first);
}
return;
}
void p3ConnectMgr::getOthersList(std::list<std::string> &peers)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
for(it = mOthersList.begin(); it != mOthersList.end(); it++)
{
peers.push_back(it->first);
}
return;
}
bool p3ConnectMgr::connectAttempt(std::string id, struct sockaddr_in &addr, uint32_t &type)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
it = mFriendList.find(id);
if (it == mFriendList.end())
{
return false;
}
if (it->second.connAddrs.size() < 1)
{
return false;
}
it->second.inConnAttempt = true;
it->second.currentConnAddr = it->second.connAddrs.front();
it->second.connAddrs.pop_front();
addr = it->second.currentConnAddr.addr;
type = it->second.currentConnAddr.type;
return true;
}
/****************************
* Update state,
* trigger retry if necessary,
*
* remove from DHT?
*
*/
bool p3ConnectMgr::connectResult(std::string id, bool success, uint32_t flags)
{
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
it = mFriendList.find(id);
if (it == mFriendList.end())
{
return false;
}
it->second.inConnAttempt = false;
if (success)
{
/* remove other attempts */
it->second.connAddrs.clear();
mDhtMgr->dropPeer(id);
/* update address */
/* change state */
it->second.state |= RS_PEER_S_CONNECTED;
it->second.actions |= RS_PEER_CONNECTED;
mStatusChanged = true;
return true;
}
/* if currently connected -> flag as failed */
if (it->second.state & RS_PEER_S_CONNECTED)
{
it->second.state ^= RS_PEER_S_CONNECTED;
it->second.actions |= RS_PEER_DISCONNECTED;
mDhtMgr->findPeer(id);
if (it->second.visState & RS_VIS_STATE_NODHT)
{
/* hidden from DHT world */
}
else
{
mDhtMgr->findPeer(id);
}
}
if (it->second.connAddrs.size() < 1)
{
return true;
}
it->second.actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
return true;
}
/******************************** Feedback ...... *********************************
* From various sources
*/
void p3ConnectMgr::peerStatus(std::string id,
struct sockaddr_in laddr, struct sockaddr_in raddr,
uint32_t type, uint32_t mode, uint32_t source)
{
std::cerr << "p3ConnectMgr::peerStatus()";
std::cerr << " id: " << id;
std::cerr << " laddr: " << inet_ntoa(laddr.sin_addr);
std::cerr << " lport: " << ntohs(laddr.sin_port);
std::cerr << " raddr: " << inet_ntoa(raddr.sin_addr);
std::cerr << " rport: " << ntohs(raddr.sin_port);
std::cerr << " type: " << type;
std::cerr << " mode: " << mode;
std::cerr << " source: " << source;
std::cerr << std::endl;
/* look up the id */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
bool isFriend = true;
it = mFriendList.find(id);
if (it == mFriendList.end())
{
/* check Others list */
isFriend = false;
it = mOthersList.find(id);
if (it == mOthersList.end())
{
/* not found - ignore */
std::cerr << "p3ConnectMgr::peerStatus() Peer Not Found - Ignore";
std::cerr << std::endl;
return;
}
std::cerr << "p3ConnectMgr::peerStatus() Peer is in mOthersList";
std::cerr << std::endl;
}
/* update the status */
peerAddrInfo details;
details.type = type;
details.found = true;
details.laddr = laddr;
details.raddr = raddr;
details.ts = time(NULL);
/* if source is DHT */
if (source == RS_CB_DHT)
{
/* DHT can tell us about
* 1) connect type (UDP/TCP/etc)
* 2) local/external address
*/
it->second.source = RS_CB_DHT;
it->second.dht = details;
}
else if (source == RS_CB_DISC)
{
/* DISC can tell us about
* 1) connect type (UDP/TCP/etc)
* 2) local/external addresses
*/
it->second.source = RS_CB_DISC;
it->second.disc = details;
}
else if (source == RS_CB_PERSON)
{
/* PERSON can tell us about
* 1) online / offline
* 2) connect address
*/
it->second.source = RS_CB_PERSON;
it->second.peer = details;
}
if (!isFriend)
{
return;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
return;
}
/* are the addresses different? */
time_t now = time(NULL);
/* add in attempts ... local(TCP), remote(TCP)
* udp must come from notify
*/
/* if address is same -> try local */
if ((!isValidNet(&(details.laddr.sin_addr))) &&
(sameNet(&(ownState.localaddr.sin_addr), &(details.laddr.sin_addr))))
{
/* add the local address */
peerConnectAddress pca;
pca.ts = now;
pca.type = RS_NET_CONN_TCP_LOCAL;
pca.addr = details.laddr;
it->second.connAddrs.push_front(pca);
}
if ((details.type & RS_NET_CONN_TCP_EXTERNAL) &&
(!isValidNet(&(details.raddr.sin_addr))))
{
/* add the remote address */
peerConnectAddress pca;
pca.ts = now;
pca.type = RS_NET_CONN_TCP_EXTERNAL;
pca.addr = details.raddr;
it->second.connAddrs.push_front(pca);
}
if (it->second.inConnAttempt)
{
/* -> it'll automatically use the addresses */
return;
}
/* start a connection attempt */
it->second.actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
}
void p3ConnectMgr::peerConnectRequest(std::string id, uint32_t type)
{
std::cerr << "p3ConnectMgr::peerConnectRequest()";
std::cerr << " id: " << id;
std::cerr << " type: " << type;
std::cerr << std::endl;
/* look up the id */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
bool isFriend = true;
it = mFriendList.find(id);
if (it == mFriendList.end())
{
/* check Others list */
isFriend = false;
it = mOthersList.find(id);
if (it == mOthersList.end())
{
/* not found - ignore */
std::cerr << "p3ConnectMgr::peerStatus() Peer Not Found - Ignore";
std::cerr << std::endl;
return;
}
std::cerr << "p3ConnectMgr::peerStatus() Peer is in mOthersList";
std::cerr << std::endl;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
return;
}
time_t now = time(NULL);
/* this is a UDP connection request (DHT only for the moment!) */
if (isValidNet(&(it->second.dht.raddr.sin_addr)))
{
/* add the remote address */
peerConnectAddress pca;
pca.ts = now;
pca.type = RS_NET_CONN_UDP_DHT_SYNC;
pca.addr = it->second.dht.raddr;
/* add to the start of list -> so handled next! */
//it->second.connAddrs.push_front(pca);
/* push to the back ... TCP ones should be tried first */
it->second.connAddrs.push_back(pca);
}
if (it->second.inConnAttempt)
{
/* -> it'll automatically use the addresses */
return;
}
/* start a connection attempt */
it->second.actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
}
//void p3ConnectMgr::stunStatus(std::string id, struct sockaddr_in addr)
/*******************************************************************/
/*******************************************************************/
bool p3ConnectMgr::addFriend(std::string id)
{
/* so three possibilities
* (1) already exists as friend -> do nothing.
* (2) is in others list -> move over.
* (3) is non-existant -> create new one.
*/
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() " << id;
std::cerr << std::endl;
#endif
connMtx.lock(); /* LOCK MUTEX */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() != mFriendList.find(id))
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() Already Exists";
std::cerr << std::endl;
#endif
/* (1) already exists */
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
/* check with the AuthMgr if its authorised */
if (!mAuthMgr->isAuthenticated(id))
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() Failed Authentication";
std::cerr << std::endl;
#endif
/* no auth */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
/* check if it is in others */
if (mOthersList.end() != (it = mOthersList.find(id)))
{
/* (2) in mOthersList -> move over */
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() Move from Others";
std::cerr << std::endl;
#endif
mFriendList[id] = it->second;
mOthersList.erase(it);
it = mFriendList.find(id);
/* setup state */
it->second.state = RS_PEER_S_FRIEND;
it->second.actions = RS_PEER_NEW;
mStatusChanged = true;
/* add peer to DHT (if not dark) */
if (it->second.visState & RS_VIS_STATE_NODHT)
{
/* hidden from DHT world */
}
else
{
mDhtMgr->findPeer(id);
}
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
/* get details from AuthMgr */
pqiAuthDetails detail;
if (!mAuthMgr->getDetails(id, detail))
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() Failed to get Details";
std::cerr << std::endl;
#endif
/* ERROR: no details */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addFriend() Creating New Entry";
std::cerr << std::endl;
#endif
/* create a new entry */
peerConnectState pstate;
pstate.id = id;
pstate.name = detail.name;
pstate.state = RS_PEER_S_FRIEND;
pstate.actions = RS_PEER_NEW;
pstate.visState = RS_VIS_STATE_STD;
pstate.netMode = RS_NET_MODE_UNKNOWN;
/* addr & timestamps -> auto cleared */
mFriendList[id] = pstate;
mStatusChanged = true;
/* expect it to be a standard DHT */
mDhtMgr->findPeer(id);
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
bool p3ConnectMgr::removeFriend(std::string id)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::removeFriend() " << id;
std::cerr << std::endl;
#endif
mDhtMgr->dropPeer(id);
connMtx.lock(); /* LOCK MUTEX */
/* move to othersList */
bool success = false;
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() != (it = mFriendList.find(id)))
{
peerConnectState peer = it->second;
mFriendList.erase(it);
peer.state &= (~RS_PEER_S_FRIEND);
peer.state &= (~RS_PEER_S_CONNECTED);
peer.state &= (~RS_PEER_S_ONLINE);
peer.actions = RS_PEER_MOVED;
peer.inConnAttempt = false;
mOthersList[id] = peer;
mStatusChanged = true;
success = true;
}
connMtx.unlock(); /* UNLOCK MUTEX */
return success;
}
bool p3ConnectMgr::addNeighbour(std::string id)
{
#ifdef CONN_DEBUG
std::cerr << "p3ConnectMgr::addNeighbour() " << id;
std::cerr << std::endl;
#endif
/* so three possibilities
* (1) already exists as friend -> do nothing.
* (2) already in others list -> do nothing.
* (3) is non-existant -> create new one.
*/
connMtx.lock(); /* LOCK MUTEX */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() == mFriendList.find(id))
{
/* (1) already exists */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
if (mOthersList.end() == mOthersList.find(id))
{
/* (2) already exists */
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
/* check with the AuthMgr if its valid */
if (!mAuthMgr->isValid(id))
{
/* no auth */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
/* get details from AuthMgr */
pqiAuthDetails detail;
if (!mAuthMgr->getDetails(id, detail))
{
/* no details */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
/* create a new entry */
peerConnectState pstate;
pstate.id = id;
pstate.name = detail.name;
pstate.state = 0;
pstate.actions = 0; //RS_PEER_NEW;
pstate.visState = RS_VIS_STATE_STD;
pstate.netMode = RS_NET_MODE_UNKNOWN;
/* addr & timestamps -> auto cleared */
mOthersList[id] = pstate;
// Nothing to notify anyone about... as no new information
//mStatusChanged = true;
connMtx.unlock(); /* UNLOCK MUTEX */
return true;
}
/*******************************************************************/
/*******************************************************************/
/*************** External Control ****************/
bool p3ConnectMgr::retryConnect(std::string id)
{
/* push addresses onto stack */
std::cerr << "p3ConnectMgr::retryConnect()";
std::cerr << " id: " << id;
std::cerr << std::endl;
/* look up the id */
std::map<std::string, peerConnectState>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
if (mFriendList.end() == (it = mFriendList.find(id)))
{
std::cerr << "p3ConnectMgr::retryConnect() Peer is not Friend";
std::cerr << std::endl;
return false;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
std::cerr << "p3ConnectMgr::retryConnect() Peer Already Connected";
std::cerr << std::endl;
return true;
}
/* are the addresses different? */
time_t now = time(NULL);
std::list<peerConnectAddress>::iterator cit;
/* add in attempts ... local(TCP), remote(TCP)
*/
/* if address is same -> try local */
if ((isValidNet(&(it->second.localaddr.sin_addr))) &&
(sameNet(&(ownState.localaddr.sin_addr),
&(it->second.localaddr.sin_addr))))
{
std::cerr << "p3ConnectMgr::retryConnect() Local Address Valid: ";
std::cerr << inet_ntoa(it->second.localaddr.sin_addr);
std::cerr << ":" << ntohs(it->second.localaddr.sin_port);
std::cerr << std::endl;
bool localExists = false;
if ((it->second.inConnAttempt) &&
(it->second.currentConnAddr.type == RS_NET_CONN_TCP_LOCAL))
{
localExists = true;
}
for(cit = it->second.connAddrs.begin();
(!localExists) && (cit != it->second.connAddrs.begin()); cit++)
{
if (cit->type == RS_NET_CONN_TCP_LOCAL)
{
localExists = true;
}
}
/* check if there is a local one on there already */
if (!localExists)
{
std::cerr << "p3ConnectMgr::retryConnect() Adding Local Addr to Queue";
std::cerr << std::endl;
/* add the local address */
peerConnectAddress pca;
pca.ts = now;
pca.type = RS_NET_CONN_TCP_LOCAL;
pca.addr = it->second.localaddr;
it->second.connAddrs.push_front(pca);
}
else
{
std::cerr << "p3ConnectMgr::retryConnect() Local Addr already in Queue";
std::cerr << std::endl;
}
}
/* otherwise try external ... (should check flags) */
if ((isValidNet(&(it->second.serveraddr.sin_addr))) && (1))
// (it->second.netMode & RS_NET_CONN_TCP_EXTERNAL))
{
std::cerr << "p3ConnectMgr::retryConnect() Ext Address Valid (+EXT Flag): ";
std::cerr << inet_ntoa(it->second.serveraddr.sin_addr);
std::cerr << ":" << ntohs(it->second.serveraddr.sin_port);
std::cerr << std::endl;
bool remoteExists = false;
if ((it->second.inConnAttempt) &&
(it->second.currentConnAddr.type == RS_NET_CONN_TCP_EXTERNAL))
{
remoteExists = true;
}
for(cit = it->second.connAddrs.begin();
(!remoteExists) && (cit != it->second.connAddrs.begin()); cit++)
{
if (cit->type == RS_NET_CONN_TCP_EXTERNAL)
{
remoteExists = true;
}
}
/* check if there is a local one on there already */
if (!remoteExists)
{
std::cerr << "p3ConnectMgr::retryConnect() Adding Ext Addr to Queue";
std::cerr << std::endl;
/* add the remote address */
peerConnectAddress pca;
pca.ts = now;
pca.type = RS_NET_CONN_TCP_EXTERNAL;
pca.addr = it->second.serveraddr;
it->second.connAddrs.push_front(pca);
}
else
{
std::cerr << "p3ConnectMgr::retryConnect() Ext Addr already in Queue";
std::cerr << std::endl;
}
}
if (it->second.inConnAttempt)
{
/* -> it'll automatically use the addresses */
return true;
}
/* start a connection attempt */
it->second.actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
return true;
}
bool p3ConnectMgr::setLocalAddress(std::string id, struct sockaddr_in addr)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
if (id == mAuthMgr->OwnId())
{
ownState.localaddr = addr;
return true;
}
/* check if it is a friend */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() == (it = mFriendList.find(id)))
{
if (mOthersList.end() == (it = mOthersList.find(id)))
{
return false;
}
}
/* "it" points to peer */
it->second.localaddr = addr;
return true;
}
bool p3ConnectMgr::setExtAddress(std::string id, struct sockaddr_in addr)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
if (id == mAuthMgr->OwnId())
{
ownState.serveraddr = addr;
return true;
}
/* check if it is a friend */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() == (it = mFriendList.find(id)))
{
if (mOthersList.end() == (it = mOthersList.find(id)))
{
return false;
}
}
/* "it" points to peer */
it->second.serveraddr = addr;
return true;
}
bool p3ConnectMgr::setNetworkMode(std::string id, uint32_t netMode)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
return false;
}
bool p3ConnectMgr::getUPnPState()
{
return mUpnpMgr->getUPnPActive();
}
bool p3ConnectMgr::getUPnPEnabled()
{
return mUpnpMgr->getUPnPEnabled();
}
bool p3ConnectMgr::getDHTEnabled()
{
return mDhtMgr->getDhtOn();
}
/*******************************************************************/
bool p3ConnectMgr::checkNetAddress()
{
std::list<std::string> addrs = getLocalInterfaces();
std::list<std::string>::iterator it;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
bool found = false;
for(it = addrs.begin(); (!found) && (it != addrs.end()); it++)
{
if ((*it) == inet_ntoa(ownState.localaddr.sin_addr))
{
found = true;
}
}
/* check that we didn't catch 0.0.0.0 - if so go for prefered */
if ((found) && (ownState.localaddr.sin_addr.s_addr == 0))
{
found = false;
}
if (!found)
{
ownState.localaddr.sin_addr = getPreferredInterface();
}
if ((isPrivateNet(&(ownState.localaddr.sin_addr))) ||
(isLoopbackNet(&(ownState.localaddr.sin_addr))))
{
/* firewalled */
//own_cert -> Firewalled(true);
}
else
{
//own_cert -> Firewalled(false);
}
int port = ntohs(ownState.localaddr.sin_port);
if ((port < PQI_MIN_PORT) || (port > PQI_MAX_PORT))
{
ownState.localaddr.sin_port = htons(PQI_DEFAULT_PORT);
}
/* if localaddr = serveraddr, then ensure that the ports
* are the same (modify server)... this mismatch can
* occur when the local port is changed....
*/
if (ownState.localaddr.sin_addr.s_addr ==
ownState.serveraddr.sin_addr.s_addr)
{
ownState.serveraddr.sin_port =
ownState.localaddr.sin_port;
}
// ensure that address family is set, otherwise windows Barfs.
ownState.localaddr.sin_family = AF_INET;
ownState.serveraddr.sin_family = AF_INET;
ownState.lastaddr.sin_family = AF_INET;
return 1;
}
/************************* p3config functions **********************/
/*******************************************************************/
/* Key Functions to be overloaded for Full Configuration */
/**** TODO ****/
RsSerialiser *p3ConnectMgr::setupSerialiser()
{
RsSerialiser *rss = new RsSerialiser();
//rss->addSerialType(new RsConfigSerialiser());
return rss;
}
std::list<RsItem *> p3ConnectMgr::saveList(bool &cleanup)
{
/* create a list of current peers */
std::list<RsItem *> saveData;
cleanup = true;
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
return saveData;
}
bool p3ConnectMgr::loadList(std::list<RsItem *> load)
{
/* load the list of peers */
std::list<RsItem *>::iterator it;
for(it = load.begin(); it != load.end(); it++)
{
connMtx.lock(); /* LOCK MUTEX */
connMtx.unlock(); /* UNLOCK MUTEX */
delete (*it);
}
return true;
}
/*******************************************************************/
#if 0
/******************************** Load/Save Config *********************************
* Configuration Loading / Saving.
*/
void p3ConnectMgr::saveConfiguration()
{
return;
}
void p3ConnectMgr::loadConfiguration()
{
/* open the config file....
* load:
* (1) a list of friends
* (2) a list of stun peers
*/
return;
}
std::string configFile = configMgr->getConfigDir();
configFile += "rsnet.state";
/* open file */
while(NULL != (item = config.read()))
{
switch(item->SubType())
{
case OWN_CONFIG:
{
RsPeerConfigItem *peeritem = (RsPeerConfigItem *) item;
setupConfig(peeritem);
break;
}
case PEER_CONFIG:
{
RsPeerConfigItem *peeritem = (RsPeerConfigItem *) item;
addPeer(peeritem);
break;
}
case STUNLIST:
{
RsStunConfigItem *slitem = (RsStunConfigItem *) item;
/* add to existing list */
for(it = slitem.begin();
it = slitem.end(); it++)
{
mStunList.push_back(*it);
}
break;
}
default:
break;
}
delete item;
}
}
void p3ConnectMgr::setupOwnNetConfig(RsPeerConfigItem *item)
{
return;
}
#endif