RetroShare/libretroshare/src/pqi/p3linkmgr.cc

1451 lines
37 KiB
C++
Raw Normal View History

/*
* libretroshare/src/pqi: p3linkmgr.cc
*
* 3P/PQI network interface for RetroShare.
*
* Copyright 2007-2011 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/p3linkmgr.h"
#include "pqi/authssl.h"
#include "pqi/p3dhtmgr.h" // Only need it for constants.
#include "tcponudp/tou.h"
#include "util/extaddrfinder.h"
#include "util/dnsresolver.h"
#include "util/rsnet.h"
#include "pqi/authgpg.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
const int p3connectzone = 3431;
#include "serialiser/rsconfigitems.h"
#include "pqi/pqinotify.h"
#include "retroshare/rsiface.h"
#include <sstream>
/* Network setup States */
/****
* #define CONN_DEBUG 1
* #define CONN_DEBUG_RESET 1
* #define CONN_DEBUG_TICK 1
***/
/****
* #define P3CONNMGR_NO_TCP_CONNECTIONS 1
***/
/****
* #define P3CONNMGR_NO_AUTO_CONNECTION 1
***/
const uint32_t P3CONNMGR_TCP_DEFAULT_DELAY = 3; /* 2 Seconds? is it be enough! */
const uint32_t P3CONNMGR_UDP_DEFAULT_DELAY = 3; /* 2 Seconds? is it be enough! */
const uint32_t P3CONNMGR_TCP_DEFAULT_PERIOD = 10;
const uint32_t P3CONNMGR_UDP_DEFAULT_PERIOD = 40;
#define MAX_AVAIL_PERIOD 230 //times a peer stay in available state when not connected
#define MIN_RETRY_PERIOD 140
#define MAX_RANDOM_ATTEMPT_OFFSET 6 // seconds.
void printConnectState(std::ostream &out, peerConnectState &peer);
peerConnectAddress::peerConnectAddress()
:delay(0), period(0), type(0), ts(0)
{
sockaddr_clear(&addr);
}
peerAddrInfo::peerAddrInfo()
:found(false), type(0), ts(0)
{
}
peerConnectState::peerConnectState()
:id("unknown"),
gpg_id("unknown"),
netMode(RS_NET_MODE_UNKNOWN), visState(RS_VIS_STATE_STD),
lastcontact(0),
connecttype(0),
lastavailable(0),
lastattempt(0),
name(""), location(""),
state(0), actions(0),
source(0),
inConnAttempt(0)
{
sockaddr_clear(&currentlocaladdr);
sockaddr_clear(&currentserveraddr);
return;
}
std::string textPeerConnectState(peerConnectState &state)
{
std::ostringstream out;
out << "Id: " << state.id << std::endl;
out << "NetMode: " << state.netMode << std::endl;
out << "VisState: " << state.visState << std::endl;
out << "laddr: " << rs_inet_ntoa(state.currentlocaladdr.sin_addr)
<< ":" << ntohs(state.currentlocaladdr.sin_port) << std::endl;
out << "eaddr: " << rs_inet_ntoa(state.currentserveraddr.sin_addr)
<< ":" << ntohs(state.currentserveraddr.sin_port) << std::endl;
std::string output = out.str();
return output;
}
p3LinkMgr::p3LinkMgr()
:mLinkMtx("p3LinkMgr"),mStatusChanged(false)
{
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
/* setup basics of own state */
mOwnState.id = AuthSSL::getAuthSSL()->OwnId();
mOwnState.gpg_id = AuthGPG::getAuthGPG()->getGPGOwnId();
mOwnState.name = AuthGPG::getAuthGPG()->getGPGOwnName();
mOwnState.location = AuthSSL::getAuthSSL()->getOwnLocation();
mOwnState.netMode = RS_NET_MODE_UDP;
// user decided.
//mOwnState.netMode |= RS_NET_MODE_TRY_UPNP;
mAllowTunnelConnection = false;
mDNSResolver = new DNSResolver;
mRetryPeriod = MIN_RETRY_PERIOD;
lastGroupId = 1;
/* setup Banned Ip Address - static for now
*/
struct in_addr bip;
memset(&bip, 0, sizeof(bip));
bip.s_addr = 1;
mBannedIpList.push_back(bip);
}
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr() Startup" << std::endl;
#endif
return;
}
void p3LinkMgr::setTunnelConnection(bool b)
{
bool changed = false;
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
if (mAllowTunnelConnection != b)
changed = true;
mAllowTunnelConnection = b;
}
if (changed)
{
IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
}
}
bool p3LinkMgr::getTunnelConnection()
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
return mAllowTunnelConnection;
}
void p3LinkMgr::tick()
{
netTick();
statusTick();
tickMonitors();
}
bool p3LinkMgr::shutdown() /* blocking shutdown call */
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::shutdown()";
std::cerr << std::endl;
#endif
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
mNetStatus = RS_NET_UNKNOWN;
mNetInitTS = time(NULL);
netStatusReset_locked();
}
netAssistFirewallShutdown();
netAssistConnectShutdown();
return true;
}
void p3LinkMgr::statusTick()
{
/* iterate through peers ...
* if been available for long time ... remove flag
* if last attempt a while - retryConnect.
* etc.
*/
#ifdef CONN_DEBUG_TICK
std::cerr << "p3LinkMgr::statusTick()" << std::endl;
#endif
std::list<std::string> retryIds;
std::list<std::string>::iterator it2;
//std::list<std::string> dummyToRemove;
{
time_t now = time(NULL);
time_t oldavail = now - MAX_AVAIL_PERIOD;
time_t retry = now - mRetryPeriod;
RsStackMutex stack(mLinkMtx); /****** LOCK MUTEX ******/
std::map<std::string, peerConnectState>::iterator it;
for(it = mFriendList.begin(); it != mFriendList.end(); it++)
{
if (it->second.state & RS_PEER_S_CONNECTED)
{
continue;
}
if ((it->second.state & RS_PEER_S_ONLINE) &&
(it->second.lastavailable < oldavail))
{
#ifdef CONN_DEBUG_TICK
std::cerr << "p3LinkMgr::statusTick() ONLINE TIMEOUT for: ";
std::cerr << it->first;
std::cerr << std::endl;
#endif
it->second.state &= (~RS_PEER_S_ONLINE);
}
if (it->second.lastattempt < retry)
{
retryIds.push_back(it->first);
}
}
}
#ifndef P3CONNMGR_NO_AUTO_CONNECTION
for(it2 = retryIds.begin(); it2 != retryIds.end(); it2++)
{
#ifdef CONN_DEBUG_TICK
std::cerr << "p3LinkMgr::statusTick() RETRY TIMEOUT for: ";
std::cerr << *it2;
std::cerr << std::endl;
#endif
/* retry it! */
retryConnect(*it2);
}
#endif
}
/******************************** Network Status *********************************
* Configuration Loading / Saving.
*/
void p3LinkMgr::addMonitor(pqiMonitor *mon)
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
std::list<pqiMonitor *>::iterator it;
it = std::find(clients.begin(), clients.end(), mon);
if (it != clients.end())
{
return;
}
mon->setLinkMgr(this);
clients.push_back(mon);
return;
}
void p3LinkMgr::removeMonitor(pqiMonitor *mon)
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
std::list<pqiMonitor *>::iterator it;
it = std::find(clients.begin(), clients.end(), mon);
if (it == clients.end())
{
return;
}
(*it)->setLinkMgr(NULL);
clients.erase(it);
return;
}
void p3LinkMgr::tickMonitors()
{
bool doStatusChange = false;
std::list<pqipeer> actionList;
std::map<std::string, peerConnectState>::iterator it;
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
if (mStatusChanged)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::tickMonitors() StatusChanged! List:" << std::endl;
#endif
/* 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 << " Id: " << peer.id << " 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
/* notify GUI */
if (peer.actions & RS_PEER_CONNECTED)
{
pqiNotify *notify = getPqiNotify();
if (notify)
{
notify->AddPopupMessage(RS_POPUP_CONNECT, peer.id,"", "Online: ");
notify->AddFeedItem(RS_FEED_ITEM_PEER_CONNECT, peer.id, "", "");
}
}
}
}
/* 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 << " Id: " << peer.id << " 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;
doStatusChange = true;
}
} /****** UNLOCK STACK MUTEX ******/
/* NOTE - clients is accessed without mutex protection!!!!
* At the moment this is okay - as they are only added at the start.
* IF this changes ---- must fix with second Mutex.
*/
if (doStatusChange)
{
#ifdef CONN_DEBUG
std::cerr << "Sending to " << clients.size() << " monitorClients" << std::endl;
#endif
/* send to all monitors */
std::list<pqiMonitor *>::iterator mit;
for(mit = clients.begin(); mit != clients.end(); mit++)
{
(*mit)->statusChange(actionList);
}
}
#ifdef WINDOWS_SYS
///////////////////////////////////////////////////////////
// hack for too many connections
/* notify all monitors */
std::list<pqiMonitor *>::iterator mit;
for(mit = clients.begin(); mit != clients.end(); mit++) {
(*mit)->statusChanged();
}
///////////////////////////////////////////////////////////
#endif
}
const std::string p3LinkMgr::getOwnId()
{
return AuthSSL::getAuthSSL()->OwnId();
}
bool p3LinkMgr::connectAttempt(const std::string &id, struct sockaddr_in &addr,
uint32_t &delay, uint32_t &period, uint32_t &type)
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
it = mFriendList.find(id);
if (it == mFriendList.end())
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectAttempt() FAILED Not in FriendList! id: " << id << std::endl;
#endif
return false;
}
if (it->second.connAddrs.size() < 1)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectAttempt() FAILED No ConnectAddresses id: " << id << std::endl;
#endif
return false;
}
if (it->second.state & RS_PEER_S_CONNECTED)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectAttempt() Already FLAGGED as connected!!!!" << std::endl;
std::cerr << "p3LinkMgr::connectAttempt() But allowing anyway!!!" << std::endl;
#endif
}
it->second.lastattempt = time(NULL);
it->second.inConnAttempt = true;
it->second.currentConnAddrAttempt = it->second.connAddrs.front();
it->second.connAddrs.pop_front();
addr = it->second.currentConnAddrAttempt.addr;
delay = it->second.currentConnAddrAttempt.delay;
period = it->second.currentConnAddrAttempt.period;
type = it->second.currentConnAddrAttempt.type;
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectAttempt() found an address: id: " << id << std::endl;
std::cerr << " laddr: " << rs_inet_ntoa(addr.sin_addr) << " lport: " << ntohs(addr.sin_port) << " delay: " << delay << " period: " << period;
std::cerr << " type: " << type << std::endl;
#endif
if (addr.sin_addr.s_addr == 0 || addr.sin_port == 0) {
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectAttempt() WARNING: address or port is null" << std::endl;
std::cerr << " type: " << type << std::endl;
#endif
}
return true;
}
/****************************
* Update state,
* trigger retry if necessary,
*
* remove from DHT?
*
*/
bool p3LinkMgr::connectResult(const std::string &id, bool success, uint32_t flags, struct sockaddr_in remote_peer_address)
{
bool should_netAssistFriend_false = false ;
bool should_netAssistFriend_true = false ;
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::connectResult() called Connect!: id: " + id);
if (success) {
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::connectResult() called with SUCCESS.");
} else {
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::connectResult() called with FAILED.");
}
if (id == getOwnId()) {
#ifdef CONN_DEBUG
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::connectResult() Failed, connecting to own id: ");
#endif
return false;
}
/* check for existing */
std::map<std::string, peerConnectState>::iterator it;
it = mFriendList.find(id);
if (it == mFriendList.end())
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectResult() Failed, missing Friend " << " id: " << id << std::endl;
#endif
return false;
}
if (success)
{
/* update address (should also come through from DISC) */
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectResult() Connect!: id: " << id << std::endl;
std::cerr << " Success: " << success << " flags: " << flags << std::endl;
#endif
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::connectResult() Success");
/* change state */
it->second.state |= RS_PEER_S_CONNECTED;
it->second.actions |= RS_PEER_CONNECTED;
it->second.lastcontact = time(NULL); /* time of connect */
it->second.connecttype = flags;
/* only update the peer's address if we were in a connect attempt.
* Otherwise, they connected to us, and the address will be a
* random port of their outgoing TCP socket
*
* NB even if we received the connection, the IP address is likely to okay.
*/
//used to send back to the peer it's own ext address
//it->second.currentserveraddr = remote_peer_address;
if ((it->second.inConnAttempt) &&
(it->second.currentConnAddrAttempt.addr.sin_addr.s_addr
== remote_peer_address.sin_addr.s_addr) &&
(it->second.currentConnAddrAttempt.addr.sin_port
== remote_peer_address.sin_port))
{
pqiIpAddress raddr;
raddr.mAddr = remote_peer_address;
raddr.mSeenTime = time(NULL);
raddr.mSrc = 0;
if (isPrivateNet(&(remote_peer_address.sin_addr)))
{
it->second.ipAddrs.updateLocalAddrs(raddr);
it->second.currentlocaladdr = remote_peer_address;
}
else
{
it->second.ipAddrs.updateExtAddrs(raddr);
it->second.currentserveraddr = remote_peer_address;
}
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectResult() adding current peer address in list." << std::endl;
it->second.ipAddrs.printAddrs(std::cerr);
#endif
}
/* remove other attempts */
it->second.inConnAttempt = false;
it->second.connAddrs.clear();
should_netAssistFriend_false = true ;
mStatusChanged = true;
return true;
}
else
{
it->second.inConnAttempt = false;
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::connectResult() Disconnect/Fail: id: " << id << std::endl;
std::cerr << " Success: " << success << " flags: " << flags << std::endl;
#endif
/* 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;
mStatusChanged = true;
it->second.lastcontact = time(NULL); /* time of disconnect */
should_netAssistFriend_true = true ;
}
if (it->second.connAddrs.size() >= 1)
{
it->second.actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
}
}
}
if(should_netAssistFriend_true)
netAssistFriend(id,true) ;
if(should_netAssistFriend_false)
netAssistFriend(id,false) ;
return true;
}
/******************************** Feedback ...... *********************************
* From various sources
*/
void p3LinkMgr::peerStatus(std::string id, const pqiIpAddrSet &addrs,
uint32_t type, uint32_t flags, uint32_t source)
{
/* HACKED UP FIX ****/
std::map<std::string, peerConnectState>::iterator it;
bool isFriend = true;
bool newAddrs;
time_t now = time(NULL);
peerAddrInfo details;
details.type = type;
details.found = true;
details.addrs = addrs;
details.ts = now;
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
{
/* Log */
std::ostringstream out;
out << "p3LinkMgr::peerStatus()" << " id: " << id;
out << " type: " << type << " flags: " << flags;
out << " source: " << source;
out << std::endl;
addrs.printAddrs(out);
rslog(RSL_WARNING, p3connectzone, out.str());
#ifdef CONN_DEBUG
std::cerr << out.str();
#endif
}
/* look up the id */
it = mFriendList.find(id);
if (it == mFriendList.end())
{
/* check Others list */
isFriend = false;
it = mOthersList.find(id);
if (it == mOthersList.end())
{
/* not found - ignore */
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() Peer Not Found - Ignore" << std::endl;
#endif
return;
}
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() Peer is in mOthersList" << std::endl;
#endif
}
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() Current Peer State:" << std::endl;
printConnectState(std::cerr, it->second);
std::cerr << std::endl;
#endif
/* update the status */
/* 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;
/* If we get a info -> then they are online */
it->second.state |= RS_PEER_S_ONLINE;
it->second.lastavailable = now;
/* if we are recieving these - the dht is definitely up.
*/
mNetFlags.mDhtOk = true;
}
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;
if (flags & RS_NET_FLAGS_ONLINE)
{
it->second.actions |= RS_PEER_ONLINE;
it->second.state |= RS_PEER_S_ONLINE;
it->second.lastavailable = now;
mStatusChanged = true;
}
/* not updating VIS status??? */
IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
}
else if (source == RS_CB_PERSON)
{
/* PERSON can tell us about
* 1) online / offline
* 2) connect address
* -> update all!
*/
it->second.source = RS_CB_PERSON;
it->second.peer = details;
it->second.state |= RS_PEER_S_ONLINE;
it->second.lastavailable = now;
/* must be online to recv info (should be connected too!)
* but no need for action as should be connected already
*/
it->second.netMode &= (~RS_NET_MODE_ACTUAL); /* clear actual flags */
if (flags & RS_NET_FLAGS_EXTERNAL_ADDR)
{
it->second.netMode = RS_NET_MODE_EXT;
}
else if (flags & RS_NET_FLAGS_STABLE_UDP)
{
it->second.netMode = RS_NET_MODE_UDP;
}
else
{
it->second.netMode = RS_NET_MODE_UNREACHABLE;
}
/* always update VIS status */
if (flags & RS_NET_FLAGS_USE_DISC)
{
it->second.visState &= (~RS_VIS_STATE_NODISC);
}
else
{
it->second.visState |= RS_VIS_STATE_NODISC;
}
if (flags & RS_NET_FLAGS_USE_DHT)
{
it->second.visState &= (~RS_VIS_STATE_NODHT);
}
else
{
it->second.visState |= RS_VIS_STATE_NODHT;
}
IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
}
/* Determine Reachability (only advisory) */
if (mOwnState.netMode & RS_NET_MODE_UDP)
{
if ((details.type & RS_NET_CONN_UDP_DHT_SYNC) ||
(details.type & RS_NET_CONN_TCP_EXTERNAL))
{
/* reachable! */
it->second.state &= (~RS_PEER_S_UNREACHABLE);
}
else
{
/* unreachable */
it->second.state |= RS_PEER_S_UNREACHABLE;
}
}
else if (mOwnState.netMode & RS_NET_MODE_UNREACHABLE)
{
if (details.type & RS_NET_CONN_TCP_EXTERNAL)
{
/* reachable! */
it->second.state &= (~RS_PEER_S_UNREACHABLE);
}
else
{
/* unreachable */
it->second.state |= RS_PEER_S_UNREACHABLE;
}
}
else
{
it->second.state &= (~RS_PEER_S_UNREACHABLE);
}
if (!isFriend)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() NOT FRIEND " << " id: " << id << std::endl;
#endif
{
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::peerStatus() NO CONNECT (not friend)");
}
return;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() PEER ONLINE ALREADY " << " id: " << id << std::endl;
#endif
{
/* Log */
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::peerStatus() NO CONNECT (already connected!)");
}
return;
}
newAddrs = it->second.ipAddrs.updateAddrs(addrs);
} /****** STACK UNLOCK MUTEX *******/
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus()" << " id: " << id;
std::cerr << " type: " << type << " flags: " << flags;
std::cerr << " source: " << source << std::endl;
std::cerr << " addrs: " << std::endl;
addrs.printAddrs(std::cerr);
std::cerr << std::endl;
#endif
#ifndef P3CONNMGR_NO_AUTO_CONNECTION
#ifndef P3CONNMGR_NO_TCP_CONNECTIONS
if (newAddrs)
{
retryConnectTCP(id);
}
#endif // P3CONNMGR_NO_TCP_CONNECTIONS
#else
#endif // P3CONNMGR_NO_AUTO_CONNECTION
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerStatus() Resulting Peer State:" << std::endl;
printConnectState(std::cerr, it->second);
std::cerr << std::endl;
#endif
}
void p3LinkMgr::peerConnectRequest(std::string id, struct sockaddr_in raddr,
uint32_t source)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerConnectRequest() id: " << id << " raddr: " << rs_inet_ntoa(raddr.sin_addr) << ":" << ntohs(raddr.sin_port);
std::cerr << " source: " << source << std::endl;
#endif
{
/* Log */
std::ostringstream out;
out << "p3LinkMgr::peerConnectRequest() id: " << id << " raddr: " << rs_inet_ntoa(raddr.sin_addr);
out << ":" << ntohs(raddr.sin_port) << " source: " << source;
rslog(RSL_WARNING, p3connectzone, out.str());
}
/******************** TCP PART *****************************/
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::peerConnectRequest() Try TCP first" << std::endl;
#endif
if (source == RS_CB_DHT)
{
std::cerr << "p3LinkMgr::peerConnectRequest() source DHT ==> retryConnectUDP()" << std::endl;
retryConnectUDP(id, raddr);
return;
}
else
{ // IS THIS USED???
std::cerr << "p3LinkMgr::peerConnectRequest() source OTHER ==> retryConnect()" << std::endl;
retryConnect(id);
return;
}
}
/*******************************************************************/
/*******************************************************************/
/*************** External Control ****************/
bool p3LinkMgr::retryConnect(const std::string &id)
{
/* push all available addresses onto the connect addr stack */
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnect() id: " << id << std::endl;
#endif
#ifndef P3CONNMGR_NO_TCP_CONNECTIONS
retryConnectTCP(id);
#endif // P3CONNMGR_NO_TCP_CONNECTIONS
return true;
}
bool p3LinkMgr::retryConnectUDP(const std::string &id, struct sockaddr_in &rUdpAddr)
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
/* push all available addresses onto the connect addr stack */
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() id: " << id << std::endl;
#endif
if (id == getOwnId()) {
#ifdef CONN_DEBUG
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::retryConnectUDP() Failed, connecting to own id: ");
#endif
return false;
}
/* look up the id */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() == (it = mFriendList.find(id)))
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectUDP() Peer is not Friend" << std::endl;
#endif
return false;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectUDP() Peer Already Connected" << std::endl;
#endif
if (it->second.connecttype & RS_NET_CONN_TUNNEL) {
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectUDP() Peer Connected through a tunnel connection, let's try a normal connection." << std::endl;
#endif
} else {
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectUDP() Peer Connected no more connection attempts" << std::endl;
#endif
return false;
}
}
/* Explicit Request to start the UDP connection */
if (isValidNet(&(rUdpAddr.sin_addr)))
{
#ifdef CONN_DEBUG
std::cerr << "Adding udp connection attempt: ";
std::cerr << "Addr: " << rs_inet_ntoa(rUdpAddr.sin_addr);
std::cerr << ":" << ntohs(rUdpAddr.sin_port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr = rUdpAddr;
pca.type = RS_NET_CONN_UDP_PEER_SYNC;
pca.delay = P3CONNMGR_UDP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_UDP_DEFAULT_PERIOD;
addAddressIfUnique(it->second.connAddrs, pca);
}
/* finish it off */
return locked_ConnectAttempt_Complete(&(it->second));
}
bool p3LinkMgr::retryConnectTCP(const std::string &id)
{
RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
/* push all available addresses onto the connect addr stack...
* with the following exceptions:
* - check local address, see if it is the same network as us
- check address age. don't add old ones
*/
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() id: " << id << std::endl;
#endif
if (id == getOwnId()) {
#ifdef CONN_DEBUG
rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::retryConnectTCP() Failed, connecting to own id: ");
#endif
return false;
}
/* look up the id */
std::map<std::string, peerConnectState>::iterator it;
if (mFriendList.end() == (it = mFriendList.find(id)))
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() Peer is not Friend" << std::endl;
#endif
return false;
}
/* if already connected -> done */
if (it->second.state & RS_PEER_S_CONNECTED)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() Peer Already Connected" << std::endl;
#endif
if (it->second.connecttype & RS_NET_CONN_TUNNEL) {
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() Peer Connected through a tunnel connection, let's try a normal connection." << std::endl;
#endif
} else {
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::retryConnectTCP() Peer Connected no more connection attempts" << std::endl;
#endif
return false;
}
}
/* UDP automatically searches -> no need to push start */
locked_ConnectAttempt_CurrentAddresses(&(it->second));
locked_ConnectAttempt_HistoricalAddresses(&(it->second));
locked_ConnectAttempt_AddDynDNS(&(it->second));
locked_ConnectAttempt_AddTunnel(&(it->second));
/* finish it off */
return locked_ConnectAttempt_Complete(&(it->second));
}
#define MAX_TCP_ADDR_AGE (3600 * 24 * 14) // two weeks in seconds.
bool p3LinkMgr::locked_CheckPotentialAddr(struct sockaddr_in *addr, time_t age)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr(";
std::cerr << rs_inet_ntoa(addr->sin_addr);
std::cerr << ":" << ntohs(addr->sin_port);
std::cerr << ", " << age << ")";
std::cerr << std::endl;
#endif
/*
* if it is old - quick rejection
*/
if (age > MAX_TCP_ADDR_AGE)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() REJECTING - TOO OLD";
std::cerr << std::endl;
#endif
return false;
}
bool isValid = isValidNet(&(addr->sin_addr));
// bool isLoopback = isLoopbackNet(&(addr->sin_addr));
// bool isPrivate = isPrivateNet(&(addr->sin_addr));
bool isExternal = isExternalNet(&(addr->sin_addr));
/* if invalid - quick rejection */
if (!isValid)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() REJECTING - INVALID";
std::cerr << std::endl;
#endif
return false;
}
/* if it is on the ban list - ignore */
/* checks - is it the dreaded 1.0.0.0 */
std::list<struct in_addr>::const_iterator it;
for(it = mBannedIpList.begin(); it != mBannedIpList.end(); it++)
{
if (it->s_addr == addr->sin_addr.s_addr)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() REJECTING - ON BANNED IPLIST";
std::cerr << std::endl;
#endif
return false;
}
}
/* if it is an external address, we'll accept it.
* - even it is meant to be a local address.
*/
if (isExternal)
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() ACCEPTING - EXTERNAL";
std::cerr << std::endl;
#endif
return true;
}
/* get here, it is private or loopback
* - can only connect to these addresses if we are on the same subnet.
- check net against our local address.
*/
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() Checking sameNet against: ";
std::cerr << rs_inet_ntoa(mOwnState.currentlocaladdr.sin_addr);
std::cerr << ")";
std::cerr << std::endl;
if (sameNet(&(mOwnState.currentlocaladdr.sin_addr), &(addr->sin_addr)))
{
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() ACCEPTING - PRIVATE & sameNET";
std::cerr << std::endl;
#endif
return true;
}
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_CheckPotentialAddr() REJECTING - PRIVATE & !sameNET";
std::cerr << std::endl;
#endif
/* else it fails */
return false;
}
void p3LinkMgr::locked_ConnectAttempt_CurrentAddresses(peerConnectState *peer)
{
// Just push all the addresses onto the stack.
/* try "current addresses" first */
if (locked_CheckPotentialAddr(&(peer->currentlocaladdr), 0))
{
#ifdef CONN_DEBUG
std::cerr << "Adding tcp connection attempt: ";
std::cerr << "Current Local Addr: " << rs_inet_ntoa(peer->currentlocaladdr.sin_addr);
std::cerr << ":" << ntohs(peer->currentlocaladdr.sin_port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr = peer->currentlocaladdr;
pca.type = RS_NET_CONN_TCP_LOCAL;
pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
addAddressIfUnique(peer->connAddrs, pca);
}
if (locked_CheckPotentialAddr(&(peer->currentserveraddr), 0))
{
#ifdef CONN_DEBUG
std::cerr << "Adding tcp connection attempt: ";
std::cerr << "Current Ext Addr: " << rs_inet_ntoa(peer->currentserveraddr.sin_addr);
std::cerr << ":" << ntohs(peer->currentserveraddr.sin_port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr = peer->currentserveraddr;
pca.type = RS_NET_CONN_TCP_EXTERNAL;
pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
addAddressIfUnique(peer->connAddrs, pca);
}
}
void p3LinkMgr::locked_ConnectAttempt_HistoricalAddresses(peerConnectState *peer)
{
/* now try historical addresses */
/* try local addresses first */
std::list<pqiIpAddress>::iterator ait;
time_t now = time(NULL);
for(ait = peer->ipAddrs.mLocal.mAddrs.begin();
ait != peer->ipAddrs.mLocal.mAddrs.end(); ait++)
{
if (locked_CheckPotentialAddr(&(ait->mAddr), now - ait->mSeenTime))
{
#ifdef CONN_DEBUG
std::cerr << "Adding tcp connection attempt: ";
std::cerr << "Local Addr: " << rs_inet_ntoa(ait->mAddr.sin_addr);
std::cerr << ":" << ntohs(ait->mAddr.sin_port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr = ait->mAddr;
pca.type = RS_NET_CONN_TCP_LOCAL;
pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
addAddressIfUnique(peer->connAddrs, pca);
}
}
for(ait = peer->ipAddrs.mExt.mAddrs.begin();
ait != peer->ipAddrs.mExt.mAddrs.end(); ait++)
{
if (locked_CheckPotentialAddr(&(ait->mAddr), now - ait->mSeenTime))
{
#ifdef CONN_DEBUG
std::cerr << "Adding tcp connection attempt: ";
std::cerr << "Ext Addr: " << rs_inet_ntoa(ait->mAddr.sin_addr);
std::cerr << ":" << ntohs(ait->mAddr.sin_port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr = ait->mAddr;
pca.type = RS_NET_CONN_TCP_EXTERNAL;
pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
addAddressIfUnique(peer->connAddrs, pca);
}
}
}
void p3LinkMgr::locked_ConnectAttempt_AddDynDNS(peerConnectState *peer)
{
/* try dyndns address too */
if (!peer->dyndns.empty())
{
struct in_addr addr;
u_short port = peer->currentserveraddr.sin_port ? peer->currentserveraddr.sin_port : peer->currentlocaladdr.sin_port;
#ifdef CONN_DEBUG
std::cerr << "Looking up DynDNS address" << std::endl;
#endif
if (port)
{
if(mDNSResolver->getIPAddressFromString (std::string(peer->dyndns.c_str()), addr))
{
#ifdef CONN_DEBUG
std::cerr << "Adding tcp connection attempt: ";
std::cerr << "DynDNS Addr: " << rs_inet_ntoa(addr);
std::cerr << ":" << ntohs(port);
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.addr.sin_family = AF_INET;
pca.addr.sin_addr.s_addr = addr.s_addr;
pca.addr.sin_port = port;
pca.type = RS_NET_CONN_TCP_EXTERNAL;
//for the delay, we add a random time and some more time when the friend list is big
pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
pca.ts = time(NULL);
pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
/* check address validity */
if (locked_CheckPotentialAddr(&(pca.addr), 0))
{
addAddressIfUnique(peer->connAddrs, pca);
}
}
}
}
}
void p3LinkMgr::locked_ConnectAttempt_AddTunnel(peerConnectState *peer)
{
if (!(peer->state & RS_PEER_S_CONNECTED) && mAllowTunnelConnection)
{
#ifdef CONN_DEBUG
std::cerr << "Adding TUNNEL Connection Attempt";
std::cerr << std::endl;
#endif
peerConnectAddress pca;
pca.type = RS_NET_CONN_TUNNEL;
pca.ts = time(NULL);
pca.period = 0;
sockaddr_clear(&pca.addr);
addAddressIfUnique(peer->connAddrs, pca);
}
}
bool p3LinkMgr::addAddressIfUnique(std::list<peerConnectAddress> &addrList, peerConnectAddress &pca)
{
/* iterate through the list, and make sure it isn't already
* in the list
*/
std::list<peerConnectAddress>::iterator it;
for(it = addrList.begin(); it != addrList.end(); it++)
{
if ((pca.addr.sin_addr.s_addr == it->addr.sin_addr.s_addr) &&
(pca.addr.sin_port == it->addr.sin_port) &&
(pca.type == it->type))
{
/* already */
return false;
}
}
addrList.push_back(pca);
return true;
}
bool p3LinkMgr::locked_ConnectAttempt_Complete(peerConnectState *peer)
{
/* flag as last attempt to prevent loop */
//add a random perturbation between 0 and 2 sec.
peer->lastattempt = time(NULL) + rand() % MAX_RANDOM_ATTEMPT_OFFSET;
if (peer->inConnAttempt) {
/* -> it'll automatically use the addresses we added */
#ifdef CONN_DEBUG
std::cerr << "p3LinkMgr::locked_ConnectAttempt_Complete() Already in CONNECT ATTEMPT";
std::cerr << std::endl;
std::cerr << "p3LinkMgr::locked_ConnectAttempt_Complete() Remaining ConnAddr Count: " << peer->connAddrs.size();
std::cerr << std::endl;
#endif
return true;
}
/* start a connection attempt */
if (peer->connAddrs.size() > 0)
{
#ifdef CONN_DEBUG
std::ostringstream out;
out << "p3LinkMgr::locked_ConnectAttempt_Complete() Started CONNECT ATTEMPT! " ;
out << std::endl;
out << "p3LinkMgr::locked_ConnectAttempt_Complete() ConnAddr Count: " << peer->connAddrs.size();
rslog(RSL_DEBUG_ALERT, p3connectzone, out.str());
std::cerr << out.str() << std::endl;
#endif
peer->actions |= RS_PEER_CONNECT_REQ;
mStatusChanged = true;
return true;
}
else
{
#ifdef CONN_DEBUG
std::ostringstream out;
out << "p3LinkMgr::locked_ConnectAttempt_Complete() No addr in the connect attempt list. Not suitable for CONNECT ATTEMPT! ";
rslog(RSL_DEBUG_ALERT, p3connectzone, out.str());
std::cerr << out.str() << std::endl;
#endif
return false;
}
return false;
}