mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
9976b80566
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@2017 b45a01b8-16f6-495d-af2f-9b41ad6348cc
567 lines
16 KiB
C++
567 lines
16 KiB
C++
/*
|
|
* "$Id: pqissl.cc,v 1.28 2007-03-17 19:32:59 rmf24 Exp $"
|
|
*
|
|
* 3P/PQI network interface for RetroShare.
|
|
*
|
|
* Copyright 2004-2006 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/pqissltunnel.h"
|
|
#include "pqi/pqinetwork.h"
|
|
|
|
#include "services/p3tunnel.h"
|
|
|
|
#include "util/rsnet.h"
|
|
#include "util/rsdebug.h"
|
|
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include <sstream>
|
|
|
|
const int pqisslzone = 37714;
|
|
|
|
#define TUNNEL_WAITING_NOT 0
|
|
#define TUNNEL_WAITING_DELAY 1
|
|
#define TUNNEL_WAITING_SPAM_PING 2
|
|
#define TUNNEL_WAITING_PING_RETURN 3
|
|
|
|
|
|
#define TUNNEL_PASSIVE 0x00
|
|
#define TUNNEL_ACTIVE 0x01
|
|
|
|
#define TUNNEL_START_CONNECTION_DELAY 1
|
|
#define TUNNEL_PING_TIMEOUT 6
|
|
#define TUNNEL_REPEAT_PING_TIME 2
|
|
#define TUNNEL_TIMEOUT_AFTER_RESET 30
|
|
|
|
#define TUNNEL_TRY_OTHER_CONNECTION_INTERVAL 190 //let's try a normal tcp or udp connection every 190 sec
|
|
|
|
//const int TUNNEL_LOCAL_FLAG = 0x01;
|
|
//const int TUNNEL_REMOTE_FLAG = 0x02;
|
|
//const int TUNNEL_UDP_FLAG = 0x02;
|
|
//
|
|
//static const int PQISSL_MAX_READ_ZERO_COUNT = 20;
|
|
//static const int PQISSL_SSL_CONNECT_TIMEOUT = 30;
|
|
|
|
/********** PQI SSL STUFF ******************************************
|
|
*
|
|
* A little note on the notifyEvent(FAILED)....
|
|
*
|
|
* this is called from
|
|
* (1) reset if needed!
|
|
* (2) Determine_Remote_Address (when all options have failed).
|
|
*
|
|
* reset() is only called when a TCP/SSL connection has been
|
|
* established, and there is an error. If there is a failed TCP
|
|
* connection, then an alternative address can be attempted.
|
|
*
|
|
* reset() is called from
|
|
* (1) destruction.
|
|
* (2) disconnect()
|
|
* (3) bad waiting state.
|
|
*
|
|
* // TCP/or SSL connection already established....
|
|
* (5) pqissltunnel::SSL_Connection_Complete() <- okay -> cos we made a TCP connection already.
|
|
* (6) pqissltunnel::accept() <- okay cos something went wrong.
|
|
* (7) moretoread()/cansend() <- okay cos
|
|
*
|
|
*/
|
|
|
|
pqissltunnel::pqissltunnel(PQInterface *parent, p3ConnectMgr *cm)
|
|
:NetBinInterface(parent, parent->PeerId()), mConnMgr(cm)
|
|
{
|
|
active = false;
|
|
waiting = TUNNEL_WAITING_NOT;
|
|
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqissltunnel for PeerId: " << PeerId();
|
|
rslog(RSL_ALERT, pqisslzone, out.str());
|
|
}
|
|
|
|
// if (!(AuthSSL::getAuthSSL()->isAuthenticated(PeerId()))) {
|
|
// rslog(RSL_ALERT, pqisslzone,
|
|
// "pqissltunnel::Warning Certificate Not Approved!");
|
|
// rslog(RSL_ALERT, pqisslzone,
|
|
// "\t pqissltunnel will not initialise....");
|
|
// }
|
|
mP3tunnel = mConnMgr->getP3tunnel();
|
|
current_data_offset = 0;
|
|
curent_data_packet.length = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
pqissltunnel::~pqissltunnel() {
|
|
rslog(RSL_ALERT, pqisslzone,
|
|
"pqissltunnel::~pqissltunnel -> destroying pqissl");
|
|
//stoplistening();
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
|
|
/********** Implementation of NetInterface *************************/
|
|
|
|
int pqissltunnel::connect(struct sockaddr_in raddr) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::connect() called : " << PeerId() << std::endl;
|
|
#endif
|
|
last_normal_connection_attempt_time = time(NULL);
|
|
mConnectTS = time(NULL);
|
|
resetTime = time(NULL) - TUNNEL_TIMEOUT_AFTER_RESET;
|
|
waiting = TUNNEL_WAITING_DELAY;
|
|
return 0;
|
|
}
|
|
|
|
// tells pqilistener to listen for us.
|
|
int pqissltunnel::listen()
|
|
{
|
|
//no use
|
|
return 0;
|
|
}
|
|
|
|
int pqissltunnel::stoplistening()
|
|
{
|
|
//no use
|
|
return 1;
|
|
}
|
|
|
|
int pqissltunnel::disconnect()
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::disconnect() called : " << PeerId() << std::endl;
|
|
#endif
|
|
return reset();
|
|
}
|
|
|
|
/* BinInterface version of reset() for pqistreamer */
|
|
int pqissltunnel::close()
|
|
{
|
|
return reset();
|
|
}
|
|
|
|
// put back on the listening queue.
|
|
int pqissltunnel::reset()
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::reset() called : " << PeerId() << std::endl;
|
|
#endif
|
|
|
|
if (active)
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::reset() Reset Required because tunnel was activated !" << std::endl;
|
|
std::cerr << "pqissltunnel::reset() Will Attempt notifyEvent(FAILED)" << std::endl;
|
|
#endif
|
|
waiting = TUNNEL_WAITING_NOT;
|
|
active = false;
|
|
resetTime = time(NULL);
|
|
// clean up the streamer
|
|
if (parent()) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::reset() notifyEvent(FAILED)" << std::endl;
|
|
#endif
|
|
parent() -> notifyEvent(this, NET_CONNECT_FAILED);
|
|
}
|
|
} else {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::reset() Reset not required because tunnel was not activated !" << std::endl;
|
|
#endif
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int pqissltunnel::getConnectAddress(struct sockaddr_in &raddr) {
|
|
sockaddr_clear(&raddr);
|
|
return 0;
|
|
}
|
|
|
|
bool pqissltunnel::connect_parameter(uint32_t type, uint32_t value)
|
|
{
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqissltunnel::connect_parameter() (not used) Peer: " << PeerId();
|
|
out << " type: " << type << "value: " << value;
|
|
rslog(RSL_DEBUG_ALL, pqisslzone, out.str());
|
|
}
|
|
|
|
if (type == NET_PARAM_CONNECT_DELAY)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqissltunnel::connect_parameter() (not used) Peer: " << PeerId();
|
|
out << " DELAY: " << value;
|
|
rslog(RSL_WARNING, pqisslzone, out.str());
|
|
|
|
return true;
|
|
}
|
|
else if (type == NET_PARAM_CONNECT_TIMEOUT)
|
|
{
|
|
std::ostringstream out;
|
|
out << "pqissltunnel::connect_parameter() (not used) Peer: " << PeerId();
|
|
out << " TIMEOUT: " << value;
|
|
rslog(RSL_WARNING, pqisslzone, out.str());
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/********** End of Implementation of NetInterface ******************/
|
|
/********** Implementation of BinInterface **************************
|
|
* Only status() + tick() are here ... as they are really related
|
|
* to the NetInterface, and not the BinInterface,
|
|
*
|
|
*/
|
|
|
|
/* returns ...
|
|
* -1 if inactive.
|
|
* 0 if connecting.
|
|
* 1 if connected.
|
|
*/
|
|
|
|
int pqissltunnel::status()
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::status() called." << std::endl;
|
|
#endif
|
|
|
|
if (active) {
|
|
std::cerr << " active: " << std::endl;
|
|
// print out connection.
|
|
std::cerr << "dest : " << PeerId();
|
|
std::cerr << "relay : " << relayPeerId;
|
|
std::cerr << std::endl;
|
|
}
|
|
else {
|
|
std::cerr << " Waiting for connection!" << std::endl;
|
|
}
|
|
|
|
if (active) {
|
|
return 1;
|
|
} else if (waiting > 0) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int pqissltunnel::tick()
|
|
{
|
|
if (active && ((time(NULL) - last_normal_connection_attempt_time) > TUNNEL_TRY_OTHER_CONNECTION_INTERVAL)) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::tick() attempt to connect through a normal tcp or udp connection." << std::endl;
|
|
#endif
|
|
last_normal_connection_attempt_time = time(NULL);
|
|
mConnMgr->retryConnect(parent()->PeerId());
|
|
}
|
|
|
|
if (active && ((time(NULL) - last_ping_send_time) > TUNNEL_REPEAT_PING_TIME)) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::tick() sending a ping." << std::endl;
|
|
#endif
|
|
last_ping_send_time = time(NULL);;
|
|
mP3tunnel->pingTunnelConnection(relayPeerId, parent()->PeerId());
|
|
}
|
|
|
|
if (active && ((time(NULL) - last_packet_time) > TUNNEL_PING_TIMEOUT)) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::tick() no packet received since PING_RECEIVE_TIME_OUT. Connection is broken." << std::endl;
|
|
#endif
|
|
reset();
|
|
}
|
|
// continue existing connection attempt.
|
|
if (!active)
|
|
{
|
|
// if we are waiting.. continue the connection (only)
|
|
if (waiting > 0)
|
|
{
|
|
std::cerr << "pqissltunnel::tick() Continuing Connection Attempt!" << std::endl;
|
|
ConnectAttempt();
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/********** End of Implementation of BinInterface ******************/
|
|
/********** Internals of Tunnel Connection ****************************/
|
|
int pqissltunnel::ConnectAttempt()
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::ConnectAttempt() called." << std::endl;
|
|
#endif
|
|
switch(waiting)
|
|
{
|
|
case TUNNEL_WAITING_NOT:
|
|
|
|
active = true; /* we're starting this one */
|
|
std::cerr << "pqissltunnel::ConnectAttempt() STATE = Not Waiting." << std::endl;
|
|
|
|
case TUNNEL_WAITING_DELAY:
|
|
|
|
std::cerr << "pqissltunnel::ConnectAttempt() STATE = Waiting Delay, starting connection" << std::endl;
|
|
|
|
if ((time(NULL) - mConnectTS) > TUNNEL_START_CONNECTION_DELAY) {
|
|
waiting = TUNNEL_WAITING_SPAM_PING;
|
|
}
|
|
|
|
break;
|
|
|
|
case TUNNEL_WAITING_SPAM_PING:
|
|
|
|
std::cerr << "pqissltunnel::ConnectAttempt() STATE = Waiting for spamming ping." << std::endl;
|
|
|
|
Spam_Ping();
|
|
waiting = TUNNEL_WAITING_PING_RETURN;
|
|
break;
|
|
|
|
case TUNNEL_WAITING_PING_RETURN:
|
|
if ((time(NULL) - mConnectTS) < TUNNEL_PING_TIMEOUT) {
|
|
std::cerr << "pqissltunnel::ConnectAttempt() STATE = Waiting for ping reply." << std::endl;
|
|
} else {
|
|
std::cerr << "pqissltunnel::ConnectAttempt() no ping reply during imparing time. Connection failed." << std::endl;
|
|
waiting = TUNNEL_WAITING_NOT;
|
|
active = false;
|
|
// clean up the streamer
|
|
if (parent()) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::reset() Reset not required because tunnel was not activated !" << std::endl;
|
|
#endif
|
|
parent() -> notifyEvent(this, NET_CONNECT_FAILED);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
std::cerr << "pqissltunnel::ConnectAttempt() STATE = Unknown - Reset" << std::endl;
|
|
|
|
reset();
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void pqissltunnel::Spam_Ping()
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::Spam_Ping() starting to spam ping tunnel packet." << std::endl;
|
|
#endif
|
|
std::list<std::string> peers;
|
|
mConnMgr->getOnlineList(peers);
|
|
std::list<std::string>::iterator it = peers.begin();
|
|
while (it != peers.end()) {
|
|
//send a ping to the destination through the relay
|
|
if (*it != parent()->PeerId()) {
|
|
std::cerr << "sending ping with relay id : " << *it << std::endl;
|
|
mP3tunnel->pingTunnelConnection(*it, parent()->PeerId());
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void pqissltunnel::addIncomingPacket(void* encoded_data, int encoded_data_length) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::addIncomingPacket() called." << std::endl;
|
|
std::cerr << "pqissltunnel::addIncomingPacket() getRsItemSize(encoded_data) : " << getRsItemSize(encoded_data) << std::endl;
|
|
#endif
|
|
last_packet_time = time(NULL);
|
|
|
|
data_with_length data_w_l;
|
|
data_w_l.data = (void*)malloc(encoded_data_length) ;
|
|
memcpy(data_w_l.data, encoded_data, encoded_data_length);
|
|
data_w_l.length = encoded_data_length;
|
|
data_packet_queue.push_front(data_w_l);
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::addIncomingPacket() getRsItemSize(data_w_l.data) : " << getRsItemSize(data_w_l.data) << std::endl;
|
|
#endif
|
|
}
|
|
|
|
void pqissltunnel::IncommingPingPacket(std::string incRelayPeerId) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::IncommingPingPacket() called with incRelayPeerId : " << incRelayPeerId << std::endl;
|
|
#endif
|
|
|
|
if ((time(NULL) - resetTime) <= TUNNEL_TIMEOUT_AFTER_RESET) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::IncommingPingPacket() a reset occured, don't activate the connection." << std::endl;
|
|
#endif
|
|
return;
|
|
}
|
|
last_packet_time = time(NULL);
|
|
|
|
std::string message = "pqissltunnel::IncommingPingPacket() mConnMgr->isOnline(parent()->PeerId() : ";
|
|
if (mConnMgr->isOnline(parent()->PeerId())) {
|
|
message += "true";
|
|
} else {
|
|
message += "false";
|
|
}
|
|
rslog(RSL_DEBUG_BASIC, pqisslzone, message);
|
|
|
|
if (active || mConnMgr->isOnline(parent()->PeerId())) {
|
|
//connection is already active, or peer is already online don't do nothing
|
|
return;
|
|
}
|
|
//TODO : check if cert is in order before accepting
|
|
|
|
//activate connection
|
|
waiting = TUNNEL_WAITING_NOT;
|
|
active = true;
|
|
relayPeerId = incRelayPeerId;
|
|
|
|
if (parent())
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cerr << "pqissltunnel::IncommingPingPacket() Notify the pqiperson.... (Both Connect/Receive)" << parent()->PeerId() <<std::endl;
|
|
#endif
|
|
rslog(RSL_DEBUG_BASIC, pqisslzone, "pqissltunnel::IncommingPingPacket() Notify the pqiperson.... (Both Connect/Receive)");
|
|
parent() -> notifyEvent(this, NET_CONNECT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
/********** Implementation of BinInterface **************************
|
|
* All the rest of the BinInterface.
|
|
*
|
|
*/
|
|
|
|
int pqissltunnel::senddata(void *data, int len)
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cout << "pqissltunnel::senddata() called" << std::endl ;
|
|
#endif
|
|
if (!active) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cout << "pqissltunnel::senddata() connection is not active" << std::endl ;
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
//create RsTunnelDataItem
|
|
RsTunnelDataItem *item = new RsTunnelDataItem;
|
|
item->destPeerId = parent()->PeerId();
|
|
item->relayPeerId = relayPeerId;
|
|
item->sourcePeerId = mConnMgr->getOwnId();
|
|
item->PeerId(relayPeerId);
|
|
item->connection_accepted = 1;
|
|
|
|
int oulen;
|
|
if (!AuthSSL::getAuthSSL()->encrypt(item->encoded_data, oulen, data, len, parent()->PeerId())) {
|
|
std::cerr << "pqissltunnel::readdata() problem while crypting packet, ignoring it." << std::endl;
|
|
return -1;
|
|
}
|
|
item->encoded_data_len = oulen;
|
|
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cout << "pqissltunnel::senddata() sending item (Putting it into queue)" << std::endl ;
|
|
#endif
|
|
mP3tunnel->sendItem(item);
|
|
|
|
return oulen;
|
|
}
|
|
|
|
int pqissltunnel::readdata(void *data, int len)
|
|
{
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cout << "pqissltunnel::readdata() called" << std::endl ;
|
|
#endif
|
|
//let's see if we got a new packet to read
|
|
if (current_data_offset >= curent_data_packet.length) {
|
|
//current packet has finished reading, let's pop out a new packet if available
|
|
if (data_packet_queue.size() ==0) {
|
|
//no more to read
|
|
return -1;
|
|
} else {
|
|
//let's read a new packet
|
|
current_data_offset = 0;
|
|
//decrypt one packet from the queue and put it into the current data packet.
|
|
if (!AuthSSL::getAuthSSL()->decrypt(curent_data_packet.data, curent_data_packet.length, data_packet_queue.back().data, data_packet_queue.back().length)) {
|
|
std::cerr << "pqissltunnel::readdata() problem while decrypting packet, ignoring it." << std::endl;
|
|
curent_data_packet.length = 0;
|
|
return -1;
|
|
}
|
|
data_packet_queue.pop_back();
|
|
}
|
|
}
|
|
|
|
if (current_data_offset < curent_data_packet.length) {
|
|
#ifdef DEBUG_PQISSL_TUNNEL
|
|
std::cout << "pqissltunnel::readdata() reading..." << std::endl ;
|
|
std::cout << "pqissltunnel::readdata() len : " << len << std::endl ;
|
|
std::cout << "pqissltunnel::readdata() current_data_offset : " << current_data_offset << std::endl ;
|
|
std::cout << "pqissltunnel::readdata() curent_data_packet.length : " << curent_data_packet.length << std::endl ;
|
|
std::cerr << "pqissltunnel::readdata() getRsItemSize(curent_data_packet.data) : " << getRsItemSize(curent_data_packet.data) << std::endl;
|
|
#endif
|
|
|
|
//read from packet
|
|
memcpy(data, (void*)((unsigned long int)curent_data_packet.data+(unsigned long int)current_data_offset), len);
|
|
current_data_offset += len;
|
|
|
|
return len;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// dummy function currently.
|
|
int pqissltunnel::netstatus()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int pqissltunnel::isactive()
|
|
{
|
|
return active;
|
|
}
|
|
|
|
bool pqissltunnel::moretoread()
|
|
{
|
|
//let's see if we got an old packet or a new packet to read
|
|
if (current_data_offset >= curent_data_packet.length && data_packet_queue.size() ==0) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool pqissltunnel::cansend()
|
|
{
|
|
if (!mConnMgr->isOnline(relayPeerId)) {
|
|
reset();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string pqissltunnel::gethash()
|
|
{
|
|
std::string dummyhash;
|
|
return dummyhash;
|
|
}
|
|
|
|
/********** End of Implementation of BinInterface ******************/
|