Merge pull request #2495 from csoler/v0.6-FriendServer

V0.6 friend server
This commit is contained in:
csoler 2022-01-10 20:14:58 +01:00 committed by GitHub
commit b847caa11b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 3009 additions and 688 deletions

View File

@ -55,6 +55,12 @@ retroshare_service {
retroshare_service.target = retroshare_service retroshare_service.target = retroshare_service
} }
retroshare_friendserver {
SUBDIRS += retroshare_friendserver
retroshare_friendserver.file = retroshare-friendserver/src/retroshare-friendserver.pro
retroshare_friendserver.depends = libretroshare
retroshare_friendserver.target = retroshare_friendserver
}
retroshare_plugins { retroshare_plugins {
SUBDIRS += plugins SUBDIRS += plugins
plugins.file = plugins/plugins.pro plugins.file = plugins/plugins.pro

View File

@ -54,7 +54,12 @@ namespace librs
return Sha1CheckSum(h); return Sha1CheckSum(h);
} }
template<>
HashStream& operator<<(HashStream& u,const std::pair<unsigned char *,uint32_t>& p)
{
EVP_DigestUpdate(u.mdctx,p.first,p.second) ;
return u;
}
template<> template<>
HashStream& operator<<(HashStream& u,const std::string& s) HashStream& operator<<(HashStream& u,const std::string& s)
{ {

View File

@ -0,0 +1,202 @@
/*******************************************************************************
* libretroshare/src/file_sharing: fsclient.cc *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2021 by retroshare team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
******************************************************************************/
#include "pqi/pqithreadstreamer.h"
#include "retroshare/rspeers.h"
#include "fsclient.h"
#include "pqi/pqifdbin.h"
#include "pqi/pqiproxy.h"
bool FsClient::requestFriends(const std::string& address,uint16_t port,
const std::string& proxy_address,uint16_t proxy_port,
uint32_t reqs,std::map<std::string,bool>& friend_certificates)
{
// send our own certificate to publish and expects response frmo the server , decrypts it and reutnrs friend list
RsFriendServerClientPublishItem *pitem = new RsFriendServerClientPublishItem();
pitem->n_requested_friends = reqs;
std::string pgp_base64_string,pgp_base64_checksum,short_invite;
rsPeers->GetPGPBase64StringAndCheckSum(rsPeers->getGPGOwnId(),pgp_base64_string,pgp_base64_checksum);
if(!rsPeers->getShortInvite(short_invite,RsPeerId(),RetroshareInviteFlags::RADIX_FORMAT | RetroshareInviteFlags::DNS))
{
RsErr() << "Cannot request own short invite! Something's very wrong." ;
return false;
}
pitem->pgp_public_key_b64 = pgp_base64_string;
pitem->short_invite = short_invite;
std::list<RsItem*> response;
sendItem(address,port,proxy_address,proxy_port,pitem,response);
// now decode the response
friend_certificates.clear();
for(auto item:response)
{
// auto *encrypted_response_item = dynamic_cast<RsFriendServerEncryptedServerResponseItem*>(item);
// if(!encrypted_response_item)
// {
// delete item;
// continue;
// }
// For now, also handle unencrypted response items. Will be disabled in production
auto *response_item = dynamic_cast<RsFriendServerServerResponseItem*>(item);
if(response_item)
handleServerResponse(response_item);
delete item;
}
return friend_certificates.size();
}
void FsClient::handleServerResponse(RsFriendServerServerResponseItem *item)
{
std::cerr << "Received a response item from server: " << std::endl;
std::cerr << *item << std::endl;
// for(const auto& it:response_item->friend_invites)
// friend_certificates.insert(it);
}
bool FsClient::sendItem(const std::string& server_address,uint16_t server_port,
const std::string& proxy_address,uint16_t proxy_port,
RsItem *item,std::list<RsItem*>& response)
{
// open a connection
RsDbg() << "Sending item to friend server at \"" << server_address << ":" << server_port << " through proxy " << proxy_address << ":" << proxy_port;
int CreateSocket = 0;
char dataReceived[1024];
struct sockaddr_in ipOfServer;
memset(dataReceived, '0' ,sizeof(dataReceived));
if((CreateSocket = socket(AF_INET, SOCK_STREAM, 0))< 0)
{
printf("Socket not created \n");
return 1;
}
ipOfServer.sin_family = AF_INET;
ipOfServer.sin_port = htons(proxy_port);
ipOfServer.sin_addr.s_addr = inet_addr(proxy_address.c_str());
if(connect(CreateSocket, (struct sockaddr *)&ipOfServer, sizeof(ipOfServer))<0)
{
printf("Connection to proxy failed due to port and ip problems, or proxy is not available\n");
return false;
}
// Now connect to the proxy
int ret=0;
pqiproxyconnection proxy;
proxy.setRemoteAddress(server_address);
proxy.setRemotePort(server_port);
while(1 != (ret = proxy.proxy_negociate_connection(CreateSocket)))
if(ret < 0)
{
RsErr() << "FriendServer client: Connection problem to the proxy!" ;
return false;
}
else
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// Serialise the item and send it.
FsSerializer *fss = new FsSerializer;
RsSerialiser *rss = new RsSerialiser(); // deleted by ~pqistreamer()
rss->addSerialType(fss);
RsFdBinInterface *bio = new RsFdBinInterface(CreateSocket,true); // deleted by ~pqistreamer()
pqithreadstreamer p(this,rss,RsPeerId(),bio,BIN_FLAGS_READABLE | BIN_FLAGS_WRITEABLE | BIN_FLAGS_NO_CLOSE);
p.start();
uint32_t ss;
p.SendItem(item,ss);
RsDbg() << "Item sent. Waiting for response..." ;
// Now attempt to read and deserialize anything that comes back from that connexion until it gets closed by the server.
while(true)
{
p.tick(); // ticks bio
RsItem *item = GetItem();
#ifdef DEBUG_FSCLIENT
RsDbg() << "Ticking for response...";
#endif
if(item)
{
response.push_back(item);
std::cerr << "Got a response item: " << std::endl;
std::cerr << *item << std::endl;
RsDbg() << "End of transmission. " ;
break;
}
else
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
RsDbg() << " Stopping/killing pqistreamer" ;
p.fullstop();
RsDbg() << " Closing socket." ;
close(CreateSocket);
CreateSocket=0;
RsDbg() << " Exiting loop." ;
return true;
}
bool FsClient::RecvItem(RsItem *item)
{
mIncomingItems.push_back(item);
return true;
}
RsItem *FsClient::GetItem()
{
if(mIncomingItems.empty())
return nullptr;
RsItem *item = mIncomingItems.front();
mIncomingItems.pop_front();
return item;
}

View File

@ -0,0 +1,54 @@
/*******************************************************************************
* libretroshare/src/file_sharing: fsclient.h *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2021 by retroshare team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
******************************************************************************/
#include <string>
#include "fsitem.h"
#include "pqi/pqi_base.h"
// This class runs a client connection to the friend server. It opens a socket at each connection.
class FsClient: public PQInterface
{
public:
FsClient() :PQInterface(RsPeerId()) {}
bool requestFriends(const std::string& address, uint16_t port,
const std::string &proxy_address, uint16_t proxy_port,
uint32_t reqs, std::map<std::string,bool>& friend_certificates);
protected:
// Implements PQInterface
bool RecvItem(RsItem *item) override;
int SendItem(RsItem *) override { RsErr() << "FsClient::SendItem() called although it should not." ; return 0;}
RsItem *GetItem() override;
private:
bool sendItem(const std::string &server_address, uint16_t server_port,
const std::string &proxy_address, uint16_t proxy_port,
RsItem *item, std::list<RsItem *> &response);
void handleServerResponse(RsFriendServerServerResponseItem *item);
std::list<RsItem*> mIncomingItems;
};

View File

@ -0,0 +1,185 @@
/*******************************************************************************
* libretroshare/src/file_sharing: fsitem.h *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2021 by retroshare team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
******************************************************************************/
#pragma once
#include "serialiser/rsserial.h"
#include "serialiser/rsserializer.h"
#include "rsitems/rsitem.h"
#include "serialiser/rstlvbinary.h"
#include "rsitems/rsserviceids.h"
#include "rsitems/itempriorities.h"
const uint8_t RS_PKT_SUBTYPE_FS_CLIENT_PUBLISH = 0x01 ;
const uint8_t RS_PKT_SUBTYPE_FS_CLIENT_REMOVE = 0x02 ;
const uint8_t RS_PKT_SUBTYPE_FS_SERVER_RESPONSE = 0x03 ;
const uint8_t RS_PKT_SUBTYPE_FS_SERVER_ENCRYPTED_RESPONSE = 0x04 ;
const uint8_t RS_PKT_SUBTYPE_FS_SERVER_STATUS = 0x05 ;
class RsFriendServerItem: public RsItem
{
public:
RsFriendServerItem(uint8_t item_subtype) : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_TYPE_FRIEND_SERVER,item_subtype)
{
setPriorityLevel(QOS_PRIORITY_DEFAULT) ;
}
virtual ~RsFriendServerItem() {}
virtual void clear() override {}
};
class RsFriendServerClientPublishItem: public RsFriendServerItem
{
public:
RsFriendServerClientPublishItem() : RsFriendServerItem(RS_PKT_SUBTYPE_FS_CLIENT_PUBLISH) {}
void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
{
RS_SERIAL_PROCESS(n_requested_friends);
RS_SERIAL_PROCESS(short_invite);
RS_SERIAL_PROCESS(pgp_public_key_b64);
}
virtual void clear() override
{
pgp_public_key_b64.clear();
short_invite.clear();
n_requested_friends=0;
}
// specific members for that item
uint32_t n_requested_friends;
std::string short_invite;
std::string pgp_public_key_b64;
};
class RsFriendServerStatusItem: public RsFriendServerItem
{
public:
RsFriendServerStatusItem() : RsFriendServerItem(RS_PKT_SUBTYPE_FS_SERVER_STATUS) {}
void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
{
RS_SERIAL_PROCESS(status);
}
enum ConnectionStatus: uint8_t
{
UNKNOWN = 0x00,
END_OF_TRANSMISSION = 0x01
};
// specific members for that item
ConnectionStatus status;
};
class RsFriendServerClientRemoveItem: public RsFriendServerItem
{
public:
RsFriendServerClientRemoveItem() : RsFriendServerItem(RS_PKT_SUBTYPE_FS_CLIENT_REMOVE) {}
void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
{
RS_SERIAL_PROCESS(peer_id);
RS_SERIAL_PROCESS(nonce);
}
// Peer ID for the peer to remove.
RsPeerId peer_id;
// Nonce that was returned by the server after the last client request. Should match in order to proceed. This prevents
// a malicious actor from removing peers from the server. Since the nonce is sent through Tor tunnels, it cannot be known by
// anyone else than the client.
uint64_t nonce;
};
class RsFriendServerEncryptedServerResponseItem: public RsFriendServerItem
{
public:
RsFriendServerEncryptedServerResponseItem() : RsFriendServerItem(RS_PKT_SUBTYPE_FS_SERVER_ENCRYPTED_RESPONSE) {}
void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
{
RsTypeSerializer::RawMemoryWrapper prox(bin_data, bin_len);
RsTypeSerializer::serial_process(j, ctx, prox, "data");
}
virtual void clear() override
{
free(bin_data);
bin_len = 0;
bin_data = nullptr;
}
//
void *bin_data;
uint32_t bin_len;
};
class RsFriendServerServerResponseItem: public RsFriendServerItem
{
public:
RsFriendServerServerResponseItem() : RsFriendServerItem(RS_PKT_SUBTYPE_FS_SERVER_RESPONSE) {}
void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
{
RS_SERIAL_PROCESS(nonce);
RS_SERIAL_PROCESS(friend_invites);
}
virtual void clear() override
{
friend_invites.clear();
nonce = 0;
}
// specific members for that item
uint64_t nonce;
std::map<std::string,bool> friend_invites;
};
struct FsSerializer : RsServiceSerializer
{
FsSerializer(RsSerializationFlags flags = RsSerializationFlags::NONE): RsServiceSerializer(RS_SERVICE_TYPE_FRIEND_SERVER, flags) {}
virtual RsItem *create_item(uint16_t service_id,uint8_t item_sub_id) const
{
if(service_id != static_cast<uint16_t>(RsServiceType::FRIEND_SERVER))
return nullptr;
switch(item_sub_id)
{
case RS_PKT_SUBTYPE_FS_CLIENT_REMOVE: return new RsFriendServerClientRemoveItem();
case RS_PKT_SUBTYPE_FS_CLIENT_PUBLISH: return new RsFriendServerClientPublishItem();
case RS_PKT_SUBTYPE_FS_SERVER_RESPONSE: return new RsFriendServerServerResponseItem();
case RS_PKT_SUBTYPE_FS_SERVER_STATUS: return new RsFriendServerStatusItem();
case RS_PKT_SUBTYPE_FS_SERVER_ENCRYPTED_RESPONSE: return new RsFriendServerEncryptedServerResponseItem();
default:
RsErr() << "Unknown subitem type " << item_sub_id << " in FsSerialiser" ;
return nullptr;
}
}
};

View File

@ -0,0 +1,123 @@
#include <cmath>
#include "fsmanager.h"
#include "fsclient.h"
RsFriendServer *rsFriendServer = nullptr;
static const rstime_t MIN_DELAY_BETWEEN_FS_REQUESTS = 30;
static const rstime_t MAX_DELAY_BETWEEN_FS_REQUESTS = 3600;
static const uint32_t DEFAULT_FRIENDS_TO_REQUEST = 10;
static const std::string DEFAULT_PROXY_ADDRESS = "127.0.0.1";
static const uint16_t DEFAULT_FRIEND_SERVER_PORT = 2017;
static const uint16_t DEFAULT_PROXY_PORT = 9050;
FriendServerManager::FriendServerManager()
{
mLastFriendReqestCampain = 0;
mFriendsToRequest = DEFAULT_FRIENDS_TO_REQUEST;
mProxyAddress = DEFAULT_PROXY_ADDRESS;
mProxyPort = DEFAULT_PROXY_PORT;
mServerPort = DEFAULT_FRIEND_SERVER_PORT;
}
void FriendServerManager::startServer()
{
if(!isRunning())
{
std::cerr << "Starting Friend Server Manager." << std::endl;
RsTickingThread::start() ;
}
}
void FriendServerManager::stopServer()
{
if(isRunning() && !shouldStop())
{
std::cerr << "Stopping Friend Server Manager." << std::endl;
RsTickingThread::askForStop() ;
}
}
void FriendServerManager::checkServerAddress_async(const std::string& addr,uint16_t, const std::function<void (const std::string& address,bool result_status)>& callback)
{
#warning TODO
std::this_thread::sleep_for(std::chrono::seconds(1));
callback(addr,true);
}
void FriendServerManager::setServerAddress(const std::string& addr,uint16_t port)
{
mServerAddress = addr;
mServerPort = port;
}
void FriendServerManager::setProxyAddress(const std::string& addr,uint16_t port)
{
mProxyAddress = addr;
mProxyPort = port;
}
void FriendServerManager::setFriendsToRequest(uint32_t n)
{
mFriendsToRequest = n;
}
void FriendServerManager::threadTick()
{
std::cerr << "Ticking FriendServerManager..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
if(mServerAddress.empty())
{
RsErr() << "No friend server address has been setup. This is probably a bug.";
return;
}
// Check for requests. Compute how much to wait based on how many friends we have already
std::vector<RsPgpId> friends;
rsPeers->getPgpFriendList(friends);
// log-scale interpolation of the delay between two requests.
if(mFriendsToRequest == 0 || mFriendsToRequest < friends.size())
{
RsErr() << "No friends to request! This is unexpected. Returning." << std::endl;
return;
}
// This formula makes RS wait much longuer between two requests to the server when the number of friends is close the
// wanted number
// Delay for 0 friends: 30 secs.
// Delay for 1 friends: 30 secs.
// Delay for 2 friends: 32 secs.
// Delay for 3 friends: 35 secs.
// Delay for 4 friends: 44 secs.
// Delay for 5 friends: 66 secs.
// Delay for 6 friends: 121 secs.
// Delay for 7 friends: 258 secs.
// Delay for 8 friends: 603 secs.
// Delay for 9 friends: 1466 secs.
RsDbg() << friends.size() << " friends already, " << mFriendsToRequest << " friends to request";
double s = (friends.size() < mFriendsToRequest)? ( (mFriendsToRequest - friends.size())/(double)mFriendsToRequest) : 1.0;
rstime_t delay_for_request = MIN_DELAY_BETWEEN_FS_REQUESTS + (int)floor(exp(-1*s + log(MAX_DELAY_BETWEEN_FS_REQUESTS)*(1.0-s)));
std::cerr << "Delay for " << friends.size() << " friends: " << delay_for_request << " secs." << std::endl;
rstime_t now = time(nullptr);
if(mLastFriendReqestCampain + delay_for_request < now)
{
mLastFriendReqestCampain = now;
std::cerr << "Requesting new friends to friend server..." << std::endl;
std::map<std::string,bool> friend_certificates;
FsClient().requestFriends(mServerAddress,mServerPort,mProxyAddress,mProxyPort,mFriendsToRequest,friend_certificates); // blocking call
std::cerr << "Got the following list of friend certificates:" << std::endl;
for(const auto& it:friend_certificates)
std::cerr << it.first << " : " << it.second << std::endl;
}
}

View File

@ -0,0 +1,51 @@
#include <map>
#include "util/rsthreads.h"
#include "retroshare/rsfriendserver.h"
#include "retroshare/rspeers.h"
struct FriendServerPeerInfo
{
enum FriendServerPeerStatus: uint8_t
{
UNKNOWN = 0x00,
LOCALLY_ACCEPTED = 0x01,
HAS_ACCEPTED_ME = 0x02,
ALREADY_CONNECTED = 0x03
};
uint32_t status ;
rstime_t received_TS;
};
class FriendServerManager: public RsFriendServer, public RsTickingThread
{
public:
FriendServerManager();
virtual void startServer() override ;
virtual void stopServer() override ;
virtual void checkServerAddress_async(const std::string& addr,uint16_t, const std::function<void (const std::string& address,bool result_status)>& callback) override ;
virtual void setServerAddress(const std::string&,uint16_t) override ;
virtual void setProxyAddress(const std::string&,uint16_t) override ;
virtual void setFriendsToRequest(uint32_t) override ;
virtual uint32_t friendsToRequest() override { return mFriendsToRequest ; }
virtual uint16_t friendsServerPort() override { return mServerPort ; }
virtual std::string friendsServerAddress() override { return mServerAddress ; }
protected:
virtual void threadTick() override;
private:
uint32_t mFriendsToRequest;
rstime_t mLastFriendReqestCampain;
// encode the current list of friends obtained through the friendserver and their status
std::map<RsPeerId, FriendServerPeerInfo> mPeers;
std::string mServerAddress ;
uint16_t mServerPort;
std::string mProxyAddress ;
uint16_t mProxyPort;
};

View File

@ -391,6 +391,7 @@ HEADERS += pqi/authssl.h \
pqi/pqissl.h \ pqi/pqissl.h \
pqi/pqissllistener.h \ pqi/pqissllistener.h \
pqi/pqisslpersongrp.h \ pqi/pqisslpersongrp.h \
pqi/pqiproxy.h \
pqi/pqisslproxy.h \ pqi/pqisslproxy.h \
pqi/pqistore.h \ pqi/pqistore.h \
pqi/pqistreamer.h \ pqi/pqistreamer.h \
@ -559,6 +560,7 @@ SOURCES += pqi/authgpg.cc \
pqi/pqissl.cc \ pqi/pqissl.cc \
pqi/pqissllistener.cc \ pqi/pqissllistener.cc \
pqi/pqisslpersongrp.cc \ pqi/pqisslpersongrp.cc \
pqi/pqiproxy.cc \
pqi/pqisslproxy.cc \ pqi/pqisslproxy.cc \
pqi/pqistore.cc \ pqi/pqistore.cc \
pqi/pqistreamer.cc \ pqi/pqistreamer.cc \
@ -825,6 +827,22 @@ wikipoos {
rsitems/rswikiitems.cc \ rsitems/rswikiitems.cc \
} }
# Friend server
rs_efs {
DEFINES *= RS_EMBEDED_FRIEND_SERVER
HEADERS += friend_server/fsclient.h \
friend_server/fsitem.h \
friend_server/fsmanager.h \
retroshare/rsfriendserver.h
SOURCES += friend_server/fsclient.cc \
friend_server/fsmanager.cc
}
# The Wire
gxsthewire { gxsthewire {
DEFINES *= RS_USE_WIRE DEFINES *= RS_USE_WIRE

View File

@ -1642,7 +1642,10 @@ bool OpenPGPSDKHandler::locked_syncPublicKeyring()
#else #else
if(-1 == stat64(_pubring_path.c_str(), &buf)) if(-1 == stat64(_pubring_path.c_str(), &buf))
#endif #endif
{
RsErr() << "OpenPGPSDKHandler::syncDatabase(): can't stat file " << _pubring_path << ". Can't sync public keyring." ; RsErr() << "OpenPGPSDKHandler::syncDatabase(): can't stat file " << _pubring_path << ". Can't sync public keyring." ;
buf.st_mtime = 0;
}
if(_pubring_last_update_time < buf.st_mtime) if(_pubring_last_update_time < buf.st_mtime)
{ {

View File

@ -357,12 +357,13 @@ bool PGPHandler::locked_syncTrustDatabase()
librs::util::ConvertUtf8ToUtf16(_trustdb_path, wfullname); librs::util::ConvertUtf8ToUtf16(_trustdb_path, wfullname);
if(-1 == _wstati64(wfullname.c_str(), &buf)) if(-1 == _wstati64(wfullname.c_str(), &buf))
#else #else
if(-1 == stat64(_trustdb_path.c_str(), &buf)) if(-1 == stat64(_trustdb_path.c_str(), &buf))
#endif #endif
{ {
RsErr() << "PGPHandler::syncDatabase(): can't stat file " << _trustdb_path << ". Will force write it." ; RsErr() << "PGPHandler::syncDatabase(): can't stat file " << _trustdb_path << ". Will force write it." ;
_trustdb_changed = true ; // we force write of trust database if it does not exist. _trustdb_changed = true ; // we force write of trust database if it does not exist.
} buf.st_mtime = 0;
}
if(_trustdb_last_update_time < buf.st_mtime) if(_trustdb_last_update_time < buf.st_mtime)
{ {

View File

@ -21,6 +21,7 @@
*******************************************************************************/ *******************************************************************************/
#include <stdint.h> #include <stdint.h>
#include <util/radix64.h> #include <util/radix64.h>
#include <crypto/hashstream.h>
#include "pgpkeyutil.h" #include "pgpkeyutil.h"
#include <iostream> #include <iostream>
@ -181,6 +182,59 @@ uint32_t PGPKeyManagement::compute24bitsCRC(unsigned char *octets, size_t len)
return crc & 0xFFFFFFL; return crc & 0xFFFFFFL;
} }
bool PGPKeyManagement::parsePGPPublicKey(const unsigned char *keydata, size_t keylen, PGPKeyInfo& info)
{
#ifdef DEBUG_PGPUTIL
std::cerr << "Total size: " << keylen << std::endl;
#endif
unsigned char *data = (unsigned char*)keydata;
uint8_t packet_tag;
uint32_t packet_length ;
PGPKeyParser::read_packetHeader(data,packet_tag,packet_length) ;
#ifdef DEBUG_PGPUTIL
std::cerr << "Packet tag : " << (int)packet_tag << ", length=" << packet_length << std::endl;
#endif
if(packet_tag != PGPKeyParser::PGP_PACKET_TAG_PUBLIC_KEY)
{
std::cerr << "(EE) Parsing error in PGP public key. Expected a public key tag (6). Found " << (int)packet_tag << " instead." << std::endl;
return false;
}
librs::crypto::HashStream H(librs::crypto::HashStream::SHA1);
H << (uint8_t)0x99; // RFC_4880
std::cerr << "Packet length = " << packet_length << std::endl;
H << (uint8_t)(packet_length >> 8);
H << (uint8_t)(packet_length);
H << std::make_pair(data,packet_length) ;
auto hash = H.hash();
memcpy(info.fingerprint, hash.toByteArray(),hash.SIZE_IN_BYTES);
data += packet_length;
// Read user ID.
PGPKeyParser::read_packetHeader(data,packet_tag,packet_length) ;
if(packet_tag != PGPKeyParser::PGP_PACKET_TAG_USER_ID)
{
std::cerr << "(EE) Parsing error in PGP public key. Expected a user ID key tag (13). Found " << (int)packet_tag << " instead." << std::endl;
return false;
}
info.user_id.clear();
for(uint32_t i=0;i<packet_length;++i)
info.user_id += (char)(data[i]);
return true ;
}
bool PGPKeyManagement::parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info) bool PGPKeyManagement::parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info)
{ {
unsigned char *data = (unsigned char *)signature ; unsigned char *data = (unsigned char *)signature ;

View File

@ -81,6 +81,16 @@ public:
uint8_t hash_algorithm ; uint8_t hash_algorithm ;
}; };
class PGPKeyInfo
{
public:
PGPKeyInfo() {}
std::string user_id;
unsigned char fingerprint[20];
};
// This class handles GPG keys. For now we only clean them from signatures, but // This class handles GPG keys. For now we only clean them from signatures, but
// in the future, we might cache them to avoid unnecessary calls to gpgme. // in the future, we might cache them to avoid unnecessary calls to gpgme.
// //
@ -107,6 +117,8 @@ class PGPKeyManagement
static uint32_t compute24bitsCRC(unsigned char *data,size_t len) ; static uint32_t compute24bitsCRC(unsigned char *data,size_t len) ;
static bool parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info) ; static bool parseSignature(const unsigned char *signature, size_t sign_len, PGPSignatureInfo& info) ;
static bool parsePGPPublicKey(const unsigned char *keydata, size_t keylen, PGPKeyInfo& info);
}; };
// This class handles the parsing of PGP packet headers under various (old and new) formats. // This class handles the parsing of PGP packet headers under various (old and new) formats.
@ -126,7 +138,7 @@ class PGPKeyParser
static uint64_t read_KeyID(unsigned char *& data) ; static uint64_t read_KeyID(unsigned char *& data) ;
static uint32_t read_125Size(unsigned char *& data) ; static uint32_t read_125Size(unsigned char *& data) ;
static uint32_t read_partialBodyLength(unsigned char *& data) ; static uint32_t read_partialBodyLength(unsigned char *& data) ;
static void read_packetHeader(unsigned char *& data,uint8_t& packet_tag,uint32_t& packet_length) ; static void read_packetHeader(unsigned char *&data, uint8_t& packet_tag, uint32_t& packet_length) ;
// These functions write, and indicate how many bytes where written. // These functions write, and indicate how many bytes where written.
// //

View File

@ -28,6 +28,7 @@
#include <retroshare/rspeers.h> #include <retroshare/rspeers.h>
#include <util/radix64.h> #include <util/radix64.h>
#include <pgp/pgpkeyutil.h> #include <pgp/pgpkeyutil.h>
#include <pgp/pgphandler.h>
#include "rscertificate.h" #include "rscertificate.h"
#include "util/rsstring.h" #include "util/rsstring.h"
#include "util/stacktrace.h" #include "util/stacktrace.h"
@ -619,6 +620,140 @@ bool RsCertificate::cleanRadix64(const std::string& instr,std::string& str,uint3
return true ; return true ;
} }
bool RsCertificate::decodeRadix64ShortInvite(const std::string& rsInvite, RsPeerDetails& details, uint32_t& err_code)
{
err_code = 0;
std::vector<uint8_t> bf = Radix64::decode(rsInvite);
size_t size = bf.size();
unsigned char* buf = bf.data();
size_t total_s = 0;
bool CRC_ok = false ; // not checked yet
while(total_s < size)
{
RsShortInviteFieldType ptag = RsShortInviteFieldType(buf[0]);
buf = &buf[1];
unsigned char *buf2 = buf;
uint32_t s = 0;
try { s = PGPKeyParser::read_125Size(buf); }
catch (...)
{
err_code = CERTIFICATE_PARSING_ERROR_SIZE_ERROR;
return false;
}
total_s += 1 + ( reinterpret_cast<size_t>(buf) - reinterpret_cast<size_t>(buf2) );
if(total_s > size)
{
err_code = CERTIFICATE_PARSING_ERROR_SIZE_ERROR;
return false;
}
Dbg3() << __PRETTY_FUNCTION__ << " Read ptag: "
<< static_cast<uint32_t>(ptag)
<< ", size " << s << ", total_s = " << total_s
<< ", expected total = " << size << std::endl;
switch(ptag)
{
case RsShortInviteFieldType::SSL_ID:
details.id = RsPeerId::fromBufferUnsafe(buf) ;
break;
case RsShortInviteFieldType::PEER_NAME:
details.name = std::string((char*)buf,s);
break;
case RsShortInviteFieldType::PGP_FINGERPRINT:
details.fpr = RsPgpFingerprint::fromBufferUnsafe(buf);
details.gpg_id = PGPHandler::pgpIdFromFingerprint(details.fpr);
break;
case RsShortInviteFieldType::LOCATOR:
{
std::string locatorStr((char*)buf,s);
details.ipAddressList.push_back(locatorStr);
}
break;
case RsShortInviteFieldType::DNS_LOCATOR:
details.extPort = (((int)buf[0]) << 8) + buf[1];
details.dyndns = std::string((char*)&buf[2],s-2);
break;
case RsShortInviteFieldType::LOC4_LOCATOR:
{
uint32_t t4Addr = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
sockaddr_in tLocalAddr;
tLocalAddr.sin_addr.s_addr = t4Addr;
details.localAddr = rs_inet_ntoa(tLocalAddr.sin_addr);
details.localPort = (((uint32_t)buf[4])<<8) + (uint32_t)buf[5];
}
break;
case RsShortInviteFieldType::EXT4_LOCATOR:
{
uint32_t t4Addr = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
sockaddr_in tExtAddr;
tExtAddr.sin_addr.s_addr = t4Addr;
details.extAddr = rs_inet_ntoa(tExtAddr.sin_addr);
details.extPort = (((uint32_t)buf[4])<<8) + (uint32_t)buf[5];
}
break;
case RsShortInviteFieldType::HIDDEN_LOCATOR:
details.hiddenType = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
details.hiddenNodePort = (((uint32_t)buf[4]) << 8)+ (uint32_t)buf[5];
details.isHiddenNode = true;
details.hiddenNodeAddress = std::string((char*)&buf[6],s-6);
break;
case RsShortInviteFieldType::CHECKSUM:
{
if(s != 3 || total_s+3 != size) // make sure the checksum is the last section
{
err_code = CERTIFICATE_PARSING_ERROR_INVALID_CHECKSUM_SECTION;
return false;
}
uint32_t computed_crc = PGPKeyManagement::compute24bitsCRC(bf.data(),size-5);
uint32_t certificate_crc = static_cast<uint32_t>( buf[0] + (buf[1] << 8) + (buf[2] << 16) );
if(computed_crc != certificate_crc)
{
err_code = CERTIFICATE_PARSING_ERROR_CHECKSUM_ERROR;
return false;
}
CRC_ok = true;
break;
}
}
buf = &buf[s];
total_s += s;
}
if(details.id.isNull())
{
err_code = CERTIFICATE_PARSING_ERROR_MISSING_LOCATION_ID;
return false;
}
if(!CRC_ok)
{
err_code = CERTIFICATE_PARSING_ERROR_CHECKSUM_ERROR;
return false;
}
return true;
}

View File

@ -36,6 +36,21 @@ struct RsPeerDetails;
class RsCertificate class RsCertificate
{ {
public: public:
enum class RsShortInviteFieldType : uint8_t
{
SSL_ID = 0x00,
PEER_NAME = 0x01,
LOCATOR = 0x02,
PGP_FINGERPRINT = 0x03,
CHECKSUM = 0x04,
/* The following will be deprecated, and ported to LOCATOR when generic transport layer will be implemented */
HIDDEN_LOCATOR = 0x90,
DNS_LOCATOR = 0x91,
EXT4_LOCATOR = 0x92, // external IPv4 address
LOC4_LOCATOR = 0x93 // local IPv4 address
};
typedef enum { RS_CERTIFICATE_OLD_FORMAT, RS_CERTIFICATE_RADIX, RS_CERTIFICATE_SHORT_RADIX } Format; typedef enum { RS_CERTIFICATE_OLD_FORMAT, RS_CERTIFICATE_RADIX, RS_CERTIFICATE_SHORT_RADIX } Format;
/** /**
@ -64,6 +79,8 @@ public:
~RsCertificate(); ~RsCertificate();
static bool decodeRadix64ShortInvite(const std::string& short_invite_b64,RsPeerDetails& det,uint32_t& error_code);
/// Convert to certificate radix string /// Convert to certificate radix string
std::string toStdString() const; std::string toStdString() const;

View File

@ -280,7 +280,8 @@ public:
* Sends data to a prescribed location (implementation dependent) * Sends data to a prescribed location (implementation dependent)
*@param data what will be sent *@param data what will be sent
*@param len the size of data pointed to in memory *@param len the size of data pointed to in memory
*/ *@returns total number of bytes actually sent
*/
virtual int senddata(void *data, int len) = 0; virtual int senddata(void *data, int len) = 0;
/** /**

View File

@ -236,6 +236,7 @@ int RsFdBinInterface::readline(void *data, int len)
return 0; return 0;
} }
int RsFdBinInterface::readdata(void *data, int len) int RsFdBinInterface::readdata(void *data, int len)
{ {
// read incoming bytes in the buffer // read incoming bytes in the buffer

View File

@ -0,0 +1,469 @@
#include "util/rsdebug.h"
#include "util/rsnet.h"
#include "pqi/pqiproxy.h"
//#define PROXY_DEBUG 1
int pqiproxyconnection::proxy_negociate_connection(int sockfd)
{
int ret = 0;
switch(mProxyState)
{
case PROXY_STATE_INIT:
ret = Proxy_Send_Method(sockfd); // checks basic conn, sends Method when able.
break;
case PROXY_STATE_WAITING_METHOD_RESPONSE:
ret = Proxy_Send_Address(sockfd); // waits for Method Response, send Address when able.
break;
case PROXY_STATE_WAITING_SOCKS_RESPONSE:
ret = Proxy_Connection_Complete(sockfd); // wait for ACK.
if(ret < 1)
break;
case PROXY_STATE_CONNECTION_COMPLETE:
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() COMPLETED";
std::cerr << std::endl;
#endif
return 1;
case PROXY_STATE_FAILED:
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() FAILED";
std::cerr << std::endl;
#endif
return -1;
}
if(ret < 0)
return -1;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() IN PROGRESS";
std::cerr << std::endl;
#endif
// In Progress.
return 0;
}
int pqiproxyconnection::Proxy_Send_Method(int sockfd)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Basic complete, sending Method";
std::cerr << std::endl;
#endif
/* send hello to proxy server */
char method_hello_data[3] = { 0x05, 0x01, 0x00 }; // [ Ver | nMethods (1) | No Auth Method ]
int sent = send(sockfd, method_hello_data, 3, 0);
if (sent != 3)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Send Failure";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Send Method Okay";
std::cerr << std::endl;
#endif
mProxyState = PROXY_STATE_WAITING_METHOD_RESPONSE;
return 1;
}
int pqiproxyconnection::Proxy_Method_Response(int sockfd)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response()";
std::cerr << std::endl;
#endif
/* get response from proxy server */
char method_response[2];
/*
first it was:
int recvd = recv(sockfd, method_response, 2, MSG_WAITALL);
this does not work on windows, because the socket is in nonblocking mode
the winsock reference says about the recv function and MSG_WAITALL:
"Note that if the underlying transport does not support MSG_WAITALL,
or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP."
now it is a two step process:
int recvd = recv(sockfd, method_response, 2, MSG_PEEK); // test how many bytes are in the input queue
if (enaugh bytes available){
recvd = recv(sockfd, method_response, 2, 0);
}
this does not work on windows:
if ((recvd == -1) && (errno == EAGAIN)) return TRY_AGAIN_LATER;
instead have to do:
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK)) return TRY_AGAIN_LATER;
*/
// test how many bytes can be read from the queue
int recvd = recv(sockfd, method_response, 2, MSG_PEEK);
if (recvd != 2)
{
#ifdef WINDOWS_SYS
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() EAGAIN";
std::cerr << std::endl;
#endif
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() recv error peek";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, method_response, 2, 0);
if (recvd != 2)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() recv error";
std::cerr << std::endl;
#endif
return -1;
}
// does it make sense?
if (method_response[0] != 0x05)
{
// Error.
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x05. Is: ";
std::cerr << (uint32_t) method_response[0];
std::cerr << std::endl;
#endif
return -1;
}
if (method_response[1] != 0x00)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x00. Is: ";
std::cerr << (uint32_t) method_response[1];
std::cerr << std::endl;
#endif
// Error.
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() Response Okay";
std::cerr << std::endl;
#endif
return 1;
}
#define MAX_SOCKS_REQUEST_LEN 262 // 4 + 1 + 255 + 2.
int pqiproxyconnection::Proxy_Send_Address(int sockfd)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Checking Method Response";
std::cerr << std::endl;
#endif
// Check Method Response.
int ret = Proxy_Method_Response(sockfd);
if (ret != 1)
{
return ret; // Method Response not complete.
}
char socks_request[MAX_SOCKS_REQUEST_LEN] = {
0x05, // SOCKS VERSION.
0x01, // CONNECT (Tor doesn't support BIND or UDP).
0x00, // RESERVED.
0x03, // ADDRESS TYPE (Domain Name)
0x00, // Length of Domain name... the rest is variable so can't hard code it!
};
/* get the length of the domain name, pack so we can't overflow uint8_t */
uint8_t len = mDomainAddress.length();
socks_request[4] = len;
for(int i = 0; i < len; i++)
socks_request[5 + i] = mDomainAddress[i];
/* now add the port, being careful with packing */
uint16_t net_port = htons(mRemotePort);
socks_request[5 + len] = ((uint8_t *) &net_port)[0];
socks_request[5 + len + 1] = ((uint8_t *) &net_port)[1];
int pkt_len = 5 + len + 2;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Sending String: ";
for(int i = 0; i < pkt_len; i++)
std::cerr << (uint32_t) socks_request[i];
std::cerr << std::endl;
#endif
int sent = send(sockfd, socks_request, pkt_len, 0);
if (sent != pkt_len)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Send Error";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Sent Okay";
std::cerr << std::endl;
#endif
mProxyState = PROXY_STATE_WAITING_SOCKS_RESPONSE;
return 1;
}
int pqiproxyconnection::Proxy_Connection_Complete(int sockfd)
{
/* get response from proxy server */
/* response is similar format to request - with variable length data */
char socks_response[MAX_SOCKS_REQUEST_LEN];
// test how many bytes can be read
int recvd = recv(sockfd, socks_response, 5, MSG_PEEK);
if (recvd != 5)
{
#ifdef WINDOWS_SYS
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() EAGAIN";
std::cerr << std::endl;
#endif
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error peek";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, socks_response, 5, 0);
if (recvd != 5)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error";
std::cerr << std::endl;
#endif
return -1;
}
// error checking.
if (socks_response[0] != 0x05)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[0] != 0x05. is: ";
std::cerr << (uint32_t) socks_response[0];
std::cerr << std::endl;
#endif
// error.
return -1;
}
if (socks_response[1] != 0x00)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[1] != 0x00. is: ";
std::cerr << (uint32_t) socks_response[1];
std::cerr << std::endl;
#endif
// connection failed.
return -1;
}
int address_bytes = 0;
switch(socks_response[3]) // Address Type.
{
case 0x01:
// IPv4 4 address bytes.
address_bytes = 4;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv4 Address Type";
std::cerr << std::endl;
#endif
break;
case 0x04:
// IPv6 16 address bytes.
address_bytes = 16;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv6 Address Type";
std::cerr << std::endl;
#endif
break;
case 0x03:
// Variable address bytes - specified in next byte.
address_bytes = 1 + socks_response[4];
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() Domain Address Type. len: " << address_bytes;
std::cerr << std::endl;
#endif
break;
default:
// unknown error.
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR Unknown Address Type";
std::cerr << std::endl;
#endif
return -1;
break;
}
// test how many bytes can be read
recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, MSG_PEEK); // address_bytes - 1 + 2...
if (recvd != address_bytes + 1)
{
#ifdef WINDOWS_SYS
if((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2) (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR EAGAIN at end.";
std::cerr << std::endl;
#endif
// Waiting - shouldn't happen.
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR recving(2)";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2)";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, 0); // address_bytes - 1 + 2...
if (recvd != address_bytes + 1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error (2)";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() Received String: ";
for(int i = 0; i < 4 + address_bytes + 2; i++)
std::cerr << (uint32_t) socks_response[i];
std::cerr << std::endl;
#endif
// should print address.
// if we get here - connection is good!.
mProxyState = PROXY_STATE_CONNECTION_COMPLETE;
return 1;
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* libretroshare/src/pqi: pqiproxy.h *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2004-2013 by Robert Fernie. *
* Copyright 2004-2021 by retroshare team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include <string>
class pqiproxyconnection
{
public:
enum ProxyState: uint8_t {
PROXY_STATE_FAILED = 0x00,
PROXY_STATE_INIT = 0x01,
PROXY_STATE_WAITING_METHOD_RESPONSE = 0x02,
PROXY_STATE_WAITING_SOCKS_RESPONSE = 0x03,
PROXY_STATE_CONNECTION_COMPLETE = 0x04
};
pqiproxyconnection() : mProxyState(PROXY_STATE_INIT) {}
/*!
* \brief proxy_negotiate_connection
* Negotiate the connection with the proxy that is connected with openned socket sockfd. The caller needs to
* connect the socket *before* trying to call proxy_negotiate_connection(). The function must be called as many times as
* necessary until it returns 1 (success) or -1 (error) in which case the socket needs to be closed.
* \return
* -1 : error. The socket must be closed as soon as possible.
* 0 : in progress. The function needs to be called again asap.
* 1 : proxy connection is fully negociated. Client can send data to the socket.
*/
int proxy_negociate_connection(int sockfd);
void setRemotePort(uint16_t v) { mRemotePort = v; }
void setRemoteAddress(const std::string& s) { mDomainAddress = s; }
ProxyState proxyConnectionState() const { return mProxyState ; }
void proxy_init() { mProxyState = PROXY_STATE_INIT; }
private:
ProxyState mProxyState;
std::string mDomainAddress;
uint16_t mRemotePort;
// These are the internal steps in setting up the Proxy Connection.
int Proxy_Send_Method(int sockfd);
int Proxy_Method_Response(int sockfd);
int Proxy_Send_Address(int sockfd);
int Proxy_Connection_Complete(int sockfd);
};

View File

@ -38,12 +38,6 @@ static struct RsLog::logInfo pqisslproxyzoneInfo = {RsLog::Default, "pqisslproxy
// #define PROXY_DEBUG 1 // #define PROXY_DEBUG 1
// #define PROXY_DEBUG_LOG 1 // #define PROXY_DEBUG_LOG 1
#define PROXY_STATE_FAILED 0
#define PROXY_STATE_INIT 1
#define PROXY_STATE_WAITING_METHOD_RESPONSE 2
#define PROXY_STATE_WAITING_SOCKS_RESPONSE 3
#define PROXY_STATE_CONNECTION_COMPLETE 4
pqisslproxy::pqisslproxy(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm) pqisslproxy::pqisslproxy(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm)
:pqissl(l, parent, lm) :pqissl(l, parent, lm)
{ {
@ -74,7 +68,7 @@ int pqisslproxy::Initiate_Connection()
rslog(RSL_DEBUG_BASIC, pqisslproxyzone, rslog(RSL_DEBUG_BASIC, pqisslproxyzone,
"pqisslproxy::Initiate_Connection() Connection to Proxy"); "pqisslproxy::Initiate_Connection() Connection to Proxy");
/* init proxy state */ /* init proxy state */
mProxyState = PROXY_STATE_INIT; proxy_init();
/* call standard Init_Conn() */ /* call standard Init_Conn() */
return pqissl::Initiate_Connection(); return pqissl::Initiate_Connection();
@ -84,499 +78,31 @@ int pqisslproxy::Initiate_Connection()
/********* VERY DIFFERENT **********/ /********* VERY DIFFERENT **********/
int pqisslproxy::Basic_Connection_Complete() int pqisslproxy::Basic_Connection_Complete()
{ {
rslog(RSL_DEBUG_BASIC, pqisslproxyzone, rslog(RSL_DEBUG_BASIC, pqisslproxyzone,
"pqisslproxy::Basic_Connection_Complete()..."); "pqisslproxy::Basic_Connection_Complete()...");
#ifdef PROXY_DEBUG #ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() STATE: " << mProxyState; std::cerr << "pqisslproxy::Basic_Connection_Complete() STATE: " << mProxyState;
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
if (CheckConnectionTimeout()) if (CheckConnectionTimeout())
{
// calls reset.
return -1;
}
int ret = 0;
switch(mProxyState)
{
case PROXY_STATE_INIT:
ret = Proxy_Send_Method(); // checks basic conn, sends Method when able.
break;
case PROXY_STATE_WAITING_METHOD_RESPONSE:
ret = Proxy_Send_Address(); // waits for Method Response, send Address when able.
break;
case PROXY_STATE_WAITING_SOCKS_RESPONSE:
ret = Proxy_Connection_Complete(); // wait for ACK.
break;
case PROXY_STATE_CONNECTION_COMPLETE:
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() COMPLETED";
std::cerr << std::endl;
#endif
return 1;
case PROXY_STATE_FAILED:
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() FAILED";
std::cerr << std::endl;
#endif
reset_locked();
return -1;
}
if (ret < 0)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() FAILED(2)";
std::cerr << std::endl;
#endif
reset_locked();
return -1; // FAILURE.
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Basic_Connection_Complete() IN PROGRESS";
std::cerr << std::endl;
#endif
// In Progress.
return 0;
}
int pqisslproxy::Proxy_Send_Method()
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Checking pqissl::Basic_Connection_Complete()";
std::cerr << std::endl;
#endif
int ret = pqissl::Basic_Connection_Complete();
if (ret != 1)
{
return ret; // basic connection not complete.
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Basic complete, sending Method";
std::cerr << std::endl;
#endif
/* send hello to proxy server */
char method_hello_data[3] = { 0x05, 0x01, 0x00 }; // [ Ver | nMethods (1) | No Auth Method ]
int sent = send(sockfd, method_hello_data, 3, 0);
if (sent != 3)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Send Failure";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Method() Send Method Okay";
std::cerr << std::endl;
#endif
mProxyState = PROXY_STATE_WAITING_METHOD_RESPONSE;
return 1;
}
int pqisslproxy::Proxy_Method_Response()
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response()";
std::cerr << std::endl;
#endif
/* get response from proxy server */
char method_response[2];
/*
first it was:
int recvd = recv(sockfd, method_response, 2, MSG_WAITALL);
this does not work on windows, because the socket is in nonblocking mode
the winsock reference says about the recv function and MSG_WAITALL:
"Note that if the underlying transport does not support MSG_WAITALL,
or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP."
now it is a two step process:
int recvd = recv(sockfd, method_response, 2, MSG_PEEK); // test how many bytes are in the input queue
if (enaugh bytes available){
recvd = recv(sockfd, method_response, 2, 0);
}
this does not work on windows:
if ((recvd == -1) && (errno == EAGAIN)) return TRY_AGAIN_LATER;
instead have to do:
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK)) return TRY_AGAIN_LATER;
*/
// test how many bytes can be read from the queue
int recvd = recv(sockfd, method_response, 2, MSG_PEEK);
if (recvd != 2)
{
#ifdef WINDOWS_SYS
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() EAGAIN";
std::cerr << std::endl;
#endif
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() recv error peek";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, method_response, 2, 0);
if (recvd != 2)
{ {
#ifdef PROXY_DEBUG // calls reset.
std::cerr << "pqisslproxy::Proxy_Method_Response() recv error";
std::cerr << std::endl;
#endif
return -1; return -1;
} }
// does it make sense? int ret;
if (method_response[0] != 0x05)
{
// Error. if(proxyConnectionState() == PROXY_STATE_INIT && 1!=(ret=pqissl::Basic_Connection_Complete()))
#ifdef PROXY_DEBUG return ret; // basic connection not complete.
std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x05. Is: ";
std::cerr << (uint32_t) method_response[0];
std::cerr << std::endl;
#endif
return -1;
}
if (method_response[1] != 0x00) ret = proxy_negociate_connection(sockfd);
{
#ifdef PROXY_DEBUG if(ret < 0)
std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x00. Is: "; reset_locked();
std::cerr << (uint32_t) method_response[1];
std::cerr << std::endl;
#endif
// Error.
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Method_Response() Response Okay";
std::cerr << std::endl;
#endif
return 1;
}
#define MAX_SOCKS_REQUEST_LEN 262 // 4 + 1 + 255 + 2.
int pqisslproxy::Proxy_Send_Address()
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Checking Method Response";
std::cerr << std::endl;
#endif
// Check Method Response.
int ret = Proxy_Method_Response();
if (ret != 1)
{
return ret; // Method Response not complete.
}
char socks_request[MAX_SOCKS_REQUEST_LEN] =
{ 0x05, // SOCKS VERSION.
0x01, // CONNECT (Tor doesn't support BIND or UDP).
0x00, // RESERVED.
0x03, // ADDRESS TYPE (Domain Name)
0x00, // Length of Domain name... the rest is variable so can't hard code it!
};
/* get the length of the domain name, pack so we can't overflow uint8_t */
uint8_t len = mDomainAddress.length();
socks_request[4] = len;
for(int i = 0; i < len; i++)
{
socks_request[5 + i] = mDomainAddress[i];
}
/* now add the port, being careful with packing */
uint16_t net_port = htons(mRemotePort);
socks_request[5 + len] = ((uint8_t *) &net_port)[0];
socks_request[5 + len + 1] = ((uint8_t *) &net_port)[1];
int pkt_len = 5 + len + 2;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Sending String: ";
for(int i = 0; i < pkt_len; i++)
std::cerr << (uint32_t) socks_request[i];
std::cerr << std::endl;
#endif
int sent = send(sockfd, socks_request, pkt_len, 0);
if (sent != pkt_len)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Send Error";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Send_Address() Sent Okay";
std::cerr << std::endl;
#endif
mProxyState = PROXY_STATE_WAITING_SOCKS_RESPONSE;
return 1;
}
int pqisslproxy::Proxy_Connection_Complete()
{
/* get response from proxy server */
/* response is similar format to request - with variable length data */
char socks_response[MAX_SOCKS_REQUEST_LEN];
// test how many bytes can be read
int recvd = recv(sockfd, socks_response, 5, MSG_PEEK);
if (recvd != 5)
{
#ifdef WINDOWS_SYS
if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() EAGAIN";
std::cerr << std::endl;
#endif
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error peek";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, socks_response, 5, 0);
if (recvd != 5)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error";
std::cerr << std::endl;
#endif
return -1;
}
// error checking.
if (socks_response[0] != 0x05)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[0] != 0x05. is: ";
std::cerr << (uint32_t) socks_response[0];
std::cerr << std::endl;
#endif
// error.
return -1;
}
if (socks_response[1] != 0x00)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[1] != 0x00. is: ";
std::cerr << (uint32_t) socks_response[1];
std::cerr << std::endl;
#endif
// connection failed.
return -1;
}
int address_bytes = 0;
switch(socks_response[3]) // Address Type.
{
case 0x01:
// IPv4 4 address bytes.
address_bytes = 4;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv4 Address Type";
std::cerr << std::endl;
#endif
break;
case 0x04:
// IPv6 16 address bytes.
address_bytes = 16;
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv6 Address Type";
std::cerr << std::endl;
#endif
break;
case 0x03:
// Variable address bytes - specified in next byte.
address_bytes = 1 + socks_response[4];
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() Domain Address Type. len: " << address_bytes;
std::cerr << std::endl;
#endif
break;
default:
// unknown error.
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR Unknown Address Type";
std::cerr << std::endl;
#endif
return -1;
break;
}
// test how many bytes can be read
recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, MSG_PEEK); // address_bytes - 1 + 2...
if (recvd != address_bytes + 1)
{
#ifdef WINDOWS_SYS
if((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2) (windows)";
std::cerr << std::endl;
#endif
return 0;
}
#endif
if ((recvd == -1) && (errno == EAGAIN))
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR EAGAIN at end.";
std::cerr << std::endl;
#endif
// Waiting - shouldn't happen.
return 0;
}
else if (recvd == -1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR recving(2)";
std::cerr << std::endl;
#endif
return -1;
}
else
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2)";
std::cerr << std::endl;
#endif
return 0;
}
}
// read the bytes
recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, 0); // address_bytes - 1 + 2...
if (recvd != address_bytes + 1)
{
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error (2)";
std::cerr << std::endl;
#endif
return -1;
}
#ifdef PROXY_DEBUG
std::cerr << "pqisslproxy::Proxy_Connection_Complete() Received String: ";
for(int i = 0; i < 4 + address_bytes + 2; i++)
std::cerr << (uint32_t) socks_response[i];
std::cerr << std::endl;
#endif
// should print address.
// if we get here - connection is good!.
mProxyState = PROXY_STATE_CONNECTION_COMPLETE;
return 1;
return ret;
} }
bool pqisslproxy::connect_parameter(uint32_t type, const std::string &value) bool pqisslproxy::connect_parameter(uint32_t type, const std::string &value)
@ -591,7 +117,7 @@ bool pqisslproxy::connect_parameter(uint32_t type, const std::string &value)
rs_sprintf(out, "pqisslproxy::connect_parameter() Peer: %s DOMAIN_ADDRESS: %s", PeerId().toStdString().c_str(), value.c_str()); rs_sprintf(out, "pqisslproxy::connect_parameter() Peer: %s DOMAIN_ADDRESS: %s", PeerId().toStdString().c_str(), value.c_str());
rslog(RSL_WARNING, pqisslproxyzone, out); rslog(RSL_WARNING, pqisslproxyzone, out);
#endif #endif
mDomainAddress = value; setRemoteAddress(value);
#ifdef PROXY_DEBUG #ifdef PROXY_DEBUG
std::cerr << out << std::endl; std::cerr << out << std::endl;
#endif #endif
@ -614,7 +140,7 @@ bool pqisslproxy::connect_parameter(uint32_t type, uint32_t value)
#ifdef PROXY_DEBUG_LOG #ifdef PROXY_DEBUG_LOG
rslog(RSL_WARNING, pqisslproxyzone, out); rslog(RSL_WARNING, pqisslproxyzone, out);
#endif #endif
mRemotePort = value; setRemotePort(value);
#ifdef PROXY_DEBUG #ifdef PROXY_DEBUG
std::cerr << out << std::endl; std::cerr << out << std::endl;
#endif #endif

View File

@ -24,6 +24,7 @@
// operating system specific network header. // operating system specific network header.
#include "pqi/pqinetwork.h" #include "pqi/pqinetwork.h"
#include "pqi/pqiproxy.h"
#include <string> #include <string>
#include <map> #include <map>
@ -39,40 +40,27 @@
* fns declared here are different -> all others are identical. * fns declared here are different -> all others are identical.
*/ */
class pqisslproxy: public pqissl class pqisslproxy: public pqissl, public pqiproxyconnection
{ {
public: public:
pqisslproxy(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm); pqisslproxy(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm);
virtual ~pqisslproxy(); virtual ~pqisslproxy();
// NetInterface. Is the same. // NetInterface. Is the same.
// BinInterface. Is the same. // BinInterface. Is the same.
virtual bool connect_parameter(uint32_t type, const std::string &value); virtual bool connect_parameter(uint32_t type, const std::string &value);
virtual bool connect_parameter(uint32_t type, uint32_t value); virtual bool connect_parameter(uint32_t type, uint32_t value);
protected: protected:
//Initiate is the same - except it uses the Proxy Address rather than the Peer Address. //Initiate is the same - except it uses the Proxy Address rather than the Peer Address.
// minor tweaks to setup data state. // minor tweaks to setup data state.
virtual int Initiate_Connection(); virtual int Initiate_Connection();
// The real overloading is done in Basic Connection Complete. // The real overloading is done in Basic Connection Complete.
// Instead of just checking for an open socket, we need to communicate with the SOCKS5 proxy. // Instead of just checking for an open socket, we need to communicate with the SOCKS5 proxy.
virtual int Basic_Connection_Complete(); virtual int Basic_Connection_Complete();
// These are the internal steps in setting up the Proxy Connection.
virtual int Proxy_Send_Method();
virtual int Proxy_Method_Response();
virtual int Proxy_Send_Address();
virtual int Proxy_Connection_Complete();
private:
uint32_t mProxyState;
std::string mDomainAddress;
uint16_t mRemotePort;
}; };
#endif // MRK_PQI_SSL_PROXY_HEADER #endif // MRK_PQI_SSL_PROXY_HEADER

View File

@ -357,6 +357,7 @@ int pqistreamer::status()
// this method is overloaded by pqiqosstreamer // this method is overloaded by pqiqosstreamer
void pqistreamer::locked_storeInOutputQueue(void *ptr,int,int) void pqistreamer::locked_storeInOutputQueue(void *ptr,int,int)
{ {
RsDbg() << "Storing packet " << std::hex << ptr << std::dec << " in outqueue.";
mOutPkts.push_back(ptr); mOutPkts.push_back(ptr);
} }
@ -683,7 +684,7 @@ int pqistreamer::handleoutgoing_locked()
outSentBytes_locked(mPkt_wpending_size); // this is the only time where we know exactly what was sent. outSentBytes_locked(mPkt_wpending_size); // this is the only time where we know exactly what was sent.
#ifdef DEBUG_TRANSFERS #ifdef DEBUG_TRANSFERS
std::cerr << "pqistreamer::handleoutgoing_locked() Sent Packet len: " << mPkt_wpending_size << " @ " << RsUtil::AccurateTimeString(); std::cerr << "pqistreamer::handleoutgoing_locked() Sent Packet len: " << mPkt_wpending_size << " @ " << getCurrentTS();
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
@ -693,7 +694,7 @@ int pqistreamer::handleoutgoing_locked()
mPkt_wpending = NULL; mPkt_wpending = NULL;
mPkt_wpending_size = 0 ; mPkt_wpending_size = 0 ;
sent = true; sent = true;
} }
} }
#ifdef DEBUG_PQISTREAMER #ifdef DEBUG_PQISTREAMER
@ -802,11 +803,14 @@ start_packet_read:
if(!memcmp(block,PACKET_SLICING_PROBE_BYTES,8)) if(!memcmp(block,PACKET_SLICING_PROBE_BYTES,8))
{ {
mAcceptsPacketSlicing = !DISABLE_PACKET_SLICING; mAcceptsPacketSlicing = !DISABLE_PACKET_SLICING;
#ifdef DEBUG_PACKET_SLICING #ifdef DEBUG_PACKET_SLICING
std::cerr << "(II) Enabling packet slicing!" << std::endl; std::cerr << "(II) Enabling packet slicing!" << std::endl;
#endif #endif
} mReading_state = reading_state_initial ; // restart at state 1.
mFailed_read_attempts = 0 ;
return 0;
}
} }
continue_packet: continue_packet:
{ {
@ -1430,8 +1434,12 @@ void *pqistreamer::locked_pop_out_data(uint32_t /*max_slice_size*/, uint32_t &si
{ {
res = *(mOutPkts.begin()); res = *(mOutPkts.begin());
mOutPkts.pop_front(); mOutPkts.pop_front();
// In pqistreamer, we do not split outgoing packets. For now only pqiQoSStreamer supports packet slicing.
size = getRsItemSize(res);
#ifdef DEBUG_TRANSFERS #ifdef DEBUG_TRANSFERS
std::cerr << "pqistreamer::locked_pop_out_data() getting next pkt from mOutPkts queue"; std::cerr << "pqistreamer::locked_pop_out_data() getting next pkt " << std::hex << res << std::dec << " from mOutPkts queue";
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
} }

View File

@ -31,8 +31,8 @@ public:
pqithreadstreamer(PQInterface *parent, RsSerialiser *rss, const RsPeerId& peerid, BinInterface *bio_in, int bio_flagsin); pqithreadstreamer(PQInterface *parent, RsSerialiser *rss, const RsPeerId& peerid, BinInterface *bio_in, int bio_flagsin);
// from pqistreamer // from pqistreamer
virtual bool RecvItem(RsItem *item); virtual bool RecvItem(RsItem *item) override;
virtual int tick(); virtual int tick() override;
protected: protected:
void threadTick() override; /// @see RsTickingThread void threadTick() override; /// @see RsTickingThread

View File

@ -0,0 +1,39 @@
#include <functional>
#include <thread>
#include "util/rstime.h"
// The Friend Server component of Retroshare automatically adds/removes some friends so that the
//
// The current strategy is:
//
// - if total nb of friends < S
// request new friends to the FS
// - if total nb of friends >= S
// do not request anymore (and unpublish the key), but keep the friends already here
//
// Possible states:
// - not started
// - maintain friend list
// - actively request friends
//
// The friend server internally keeps track of which friends have been added using the friend server.
// It's important to keep the ones that are already connected because they may count on us.
// Friends supplied by the FS who never connected for a few days should be removed automatically.
class RsFriendServer
{
public:
virtual void startServer() =0;
virtual void stopServer() =0;
virtual void checkServerAddress_async(const std::string& addr,uint16_t, const std::function<void (const std::string& address,bool result_status)>& callback) =0;
virtual void setServerAddress(const std::string&,uint16_t) =0;
virtual void setProxyAddress(const std::string&,uint16_t) =0;
virtual void setFriendsToRequest(uint32_t) =0;
virtual uint32_t friendsToRequest() =0;
virtual uint16_t friendsServerPort() =0;
virtual std::string friendsServerAddress() =0;
};
extern RsFriendServer *rsFriendServer;

View File

@ -168,6 +168,15 @@ struct t_RsGenericIdType
return ret; return ret;
} }
inline Id_t operator^ (const Id_t& fp) const
{
Id_t ret;
for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i)
ret.bytes[i] = bytes[i] ^ fp.bytes[i];
return ret;
}
inline bool isNull() const inline bool isNull() const
{ {
for(uint32_t i=0; i < SIZE_IN_BYTES; ++i) for(uint32_t i=0; i < SIZE_IN_BYTES; ++i)

View File

@ -54,7 +54,8 @@ enum class RsServiceType : uint16_t
BANLIST = 0x0101, BANLIST = 0x0101,
STATUS = 0x0102, STATUS = 0x0102,
NXS = 0x0200, FRIEND_SERVER = 0x0103,
NXS = 0x0200,
GXSID = 0x0211, GXSID = 0x0211,
PHOTO = 0x0212, PHOTO = 0x0212,
WIKI = 0x0213, WIKI = 0x0213,
@ -115,6 +116,8 @@ RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_DISTANT_CHAT =
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_GXS_TUNNEL = 0x0028; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_GXS_TUNNEL = 0x0028;
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101;
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_STATUS = 0x0102; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_STATUS = 0x0102;
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_FRIEND_SERVER = 0x0103;
/// Rs Network Exchange Service /// Rs Network Exchange Service
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_NXS = 0x0200; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_TYPE_NXS = 0x0200;
RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_GXSID = 0x0211; RS_DEPRECATED_FOR(RsServiceType) const uint16_t RS_SERVICE_GXS_TYPE_GXSID = 0x0211;

View File

@ -48,6 +48,7 @@
#include "pqi/authssl.h" #include "pqi/authssl.h"
typedef RsCertificate::RsShortInviteFieldType RsShortInviteFieldType; // locally in this file to avoid renaming everything.
RsPeers *rsPeers = NULL; RsPeers *rsPeers = NULL;
@ -1162,22 +1163,6 @@ bool p3Peers::GetPGPBase64StringAndCheckSum(
return true; return true;
} }
enum class RsShortInviteFieldType : uint8_t
{
SSL_ID = 0x00,
PEER_NAME = 0x01,
LOCATOR = 0x02,
PGP_FINGERPRINT = 0x03,
CHECKSUM = 0x04,
/* The following will be deprecated, and ported to LOCATOR when generic
* trasport layer will be implemented */
HIDDEN_LOCATOR = 0x90,
DNS_LOCATOR = 0x91,
EXT4_LOCATOR = 0x92, // external IPv4 address
LOC4_LOCATOR = 0x93 // local IPv4 address
};
static void addPacketHeader(RsShortInviteFieldType ptag, size_t size, unsigned char *& buf, uint32_t& offset, uint32_t& buf_size) static void addPacketHeader(RsShortInviteFieldType ptag, size_t size, unsigned char *& buf, uint32_t& offset, uint32_t& buf_size)
{ {
// Check that the buffer has sufficient size. If not, increase it. // Check that the buffer has sufficient size. If not, increase it.
@ -1373,136 +1358,23 @@ bool p3Peers::getShortInvite(std::string& invite, const RsPeerId& _sslId, Retros
bool p3Peers::parseShortInvite(const std::string& inviteStrUrl, RsPeerDetails& details, uint32_t &err_code ) bool p3Peers::parseShortInvite(const std::string& inviteStrUrl, RsPeerDetails& details, uint32_t &err_code )
{ {
if(inviteStrUrl.empty()) if(inviteStrUrl.empty())
{ {
RsErr() << __PRETTY_FUNCTION__ << " can't parse empty invite" RsErr() << __PRETTY_FUNCTION__ << " can't parse empty invite"
<< std::endl; << std::endl;
return false; return false;
} }
std::string rsInvite = inviteStrUrl; std::string rsInvite = inviteStrUrl;
RsUrl inviteUrl(inviteStrUrl); RsUrl inviteUrl(inviteStrUrl);
if(inviteUrl.hasQueryK("rsInvite")) if(inviteUrl.hasQueryK("rsInvite"))
rsInvite = *inviteUrl.getQueryV("rsInvite"); rsInvite = *inviteUrl.getQueryV("rsInvite");
std::vector<uint8_t> bf = Radix64::decode(rsInvite); if(!RsCertificate::decodeRadix64ShortInvite(rsInvite, details, err_code))
size_t size = bf.size(); return false;
unsigned char* buf = bf.data(); // Also check if the PGP key is available. If so, add it in the PeerDetails:
size_t total_s = 0;
bool CRC_ok = false ; // not checked yet
while(total_s < size)
{
RsShortInviteFieldType ptag = RsShortInviteFieldType(buf[0]);
buf = &buf[1];
unsigned char *buf2 = buf;
uint32_t s = 0;
try { s = PGPKeyParser::read_125Size(buf); }
catch (...)
{
err_code = CERTIFICATE_PARSING_ERROR_SIZE_ERROR;
return false;
}
total_s += 1 + ( reinterpret_cast<size_t>(buf) - reinterpret_cast<size_t>(buf2) );
if(total_s > size)
{
err_code = CERTIFICATE_PARSING_ERROR_SIZE_ERROR;
return false;
}
Dbg3() << __PRETTY_FUNCTION__ << " Read ptag: "
<< static_cast<uint32_t>(ptag)
<< ", size " << s << ", total_s = " << total_s
<< ", expected total = " << size << std::endl;
switch(ptag)
{
case RsShortInviteFieldType::SSL_ID:
details.id = RsPeerId::fromBufferUnsafe(buf) ;
break;
case RsShortInviteFieldType::PEER_NAME:
details.name = std::string((char*)buf,s);
break;
case RsShortInviteFieldType::PGP_FINGERPRINT:
details.fpr = RsPgpFingerprint::fromBufferUnsafe(buf);
details.gpg_id = PGPHandler::pgpIdFromFingerprint(details.fpr);
break;
case RsShortInviteFieldType::LOCATOR:
{
std::string locatorStr((char*)buf,s);
details.ipAddressList.push_back(locatorStr);
}
break;
case RsShortInviteFieldType::DNS_LOCATOR:
details.extPort = (((int)buf[0]) << 8) + buf[1];
details.dyndns = std::string((char*)&buf[2],s-2);
break;
case RsShortInviteFieldType::LOC4_LOCATOR:
{
uint32_t t4Addr = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
sockaddr_in tLocalAddr;
tLocalAddr.sin_addr.s_addr = t4Addr;
details.localAddr = rs_inet_ntoa(tLocalAddr.sin_addr);
details.localPort = (((uint32_t)buf[4])<<8) + (uint32_t)buf[5];
}
break;
case RsShortInviteFieldType::EXT4_LOCATOR:
{
uint32_t t4Addr = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
sockaddr_in tExtAddr;
tExtAddr.sin_addr.s_addr = t4Addr;
details.extAddr = rs_inet_ntoa(tExtAddr.sin_addr);
details.extPort = (((uint32_t)buf[4])<<8) + (uint32_t)buf[5];
}
break;
case RsShortInviteFieldType::HIDDEN_LOCATOR:
details.hiddenType = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3];
details.hiddenNodePort = (((uint32_t)buf[4]) << 8)+ (uint32_t)buf[5];
details.isHiddenNode = true;
details.hiddenNodeAddress = std::string((char*)&buf[6],s-6);
break;
case RsShortInviteFieldType::CHECKSUM:
{
if(s != 3 || total_s+3 != size) // make sure the checksum is the last section
{
err_code = CERTIFICATE_PARSING_ERROR_INVALID_CHECKSUM_SECTION;
return false;
}
uint32_t computed_crc = PGPKeyManagement::compute24bitsCRC(bf.data(),size-5);
uint32_t certificate_crc = static_cast<uint32_t>( buf[0] + (buf[1] << 8) + (buf[2] << 16) );
if(computed_crc != certificate_crc)
{
err_code = CERTIFICATE_PARSING_ERROR_CHECKSUM_ERROR;
return false;
}
CRC_ok = true;
break;
}
}
buf = &buf[s];
total_s += s;
}
// now check if the PGP key is available. If so, add it in the PeerDetails:
RsPeerDetails pgp_det ; RsPeerDetails pgp_det ;
if(getGPGDetails(PGPHandler::pgpIdFromFingerprint(details.fpr),pgp_det) && pgp_det.fpr == details.fpr) if(getGPGDetails(PGPHandler::pgpIdFromFingerprint(details.fpr),pgp_det) && pgp_det.fpr == details.fpr)
@ -1519,23 +1391,13 @@ bool p3Peers::parseShortInvite(const std::string& inviteStrUrl, RsPeerDetails& d
else else
details.skip_pgp_signature_validation = true; details.skip_pgp_signature_validation = true;
if(!CRC_ok)
{
err_code = CERTIFICATE_PARSING_ERROR_CHECKSUM_ERROR;
return false;
}
if(details.gpg_id.isNull()) if(details.gpg_id.isNull())
{ {
err_code = CERTIFICATE_PARSING_ERROR_MISSING_PGP_FINGERPRINT; err_code = CERTIFICATE_PARSING_ERROR_MISSING_PGP_FINGERPRINT;
return false; return false;
} }
if(details.id.isNull()) err_code = CERTIFICATE_PARSING_ERROR_NO_ERROR;
{ return true;
err_code = CERTIFICATE_PARSING_ERROR_MISSING_LOCATION_ID;
return false;
}
err_code = CERTIFICATE_PARSING_ERROR_NO_ERROR;
return true;
} }
bool p3Peers::acceptInvite( const std::string& invite, bool p3Peers::acceptInvite( const std::string& invite,

View File

@ -52,6 +52,10 @@
#include "rsserver/rsloginhandler.h" #include "rsserver/rsloginhandler.h"
#include "rsserver/rsaccounts.h" #include "rsserver/rsaccounts.h"
#ifdef RS_EMBEDED_FRIEND_SERVER
#include "friend_server/fsmanager.h"
#endif
#include <list> #include <list>
#include <string> #include <string>
@ -1175,6 +1179,11 @@ int RsServer::StartupRetroShare()
serviceCtrl->setServiceServer(pqih) ; serviceCtrl->setServiceServer(pqih) ;
#ifdef RS_EMBEDED_FRIEND_SERVER
// setup friend server
rsFriendServer = new FriendServerManager();
#endif
/****** New Ft Server **** !!! */ /****** New Ft Server **** !!! */
ftServer *ftserver = new ftServer(mPeerMgr, serviceCtrl); ftServer *ftserver = new ftServer(mPeerMgr, serviceCtrl);
ftserver->setConfigDirectory(RsAccounts::AccountDirectory()); ftserver->setConfigDirectory(RsAccounts::AccountDirectory());
@ -1936,6 +1945,12 @@ int RsServer::StartupRetroShare()
std::string RsInit::executablePath() std::string RsInit::executablePath()
{ {
if(rsInitConfig->mainExecutablePath.empty())
{
RsErr() << "Main executable path not set! Plz call RsInit::InitRetroShare(conf) with conf.main_executable_path = argv[0]";
assert(false);
}
return rsInitConfig->mainExecutablePath; return rsInitConfig->mainExecutablePath;
} }
bool RsInit::startAutoTor() bool RsInit::startAutoTor()

View File

@ -142,6 +142,8 @@ std::string TorManager::torDataDirectory() const
void TorManager::setTorDataDirectory(const std::string &path) void TorManager::setTorDataDirectory(const std::string &path)
{ {
assert(RsDirUtil::checkCreateDirectory(std::string(path)));
d->dataDir = path; d->dataDir = path;
if (!d->dataDir.empty() && !ByteArray(d->dataDir).endsWith('/')) if (!d->dataDir.empty() && !ByteArray(d->dataDir).endsWith('/'))

View File

@ -0,0 +1,414 @@
#include "util/rsdebug.h"
#include "util/rsprint.h"
#include "util/rsdir.h"
#include "util/rsbase64.h"
#include "util/radix64.h"
#include "pgp/pgpkeyutil.h"
#include "pgp/rscertificate.h"
#include "pgp/openpgpsdkhandler.h"
#include "friendserver.h"
#include "friend_server/fsitem.h"
static const rstime_t MAXIMUM_PEER_INACTIVE_DELAY = 600;
static const rstime_t DELAY_BETWEEN_TWO_AUTOWASH = 60;
static const rstime_t DELAY_BETWEEN_TWO_DEBUG_PRINT = 10;
static const uint32_t MAXIMUM_PEERS_TO_REQUEST = 10;
void FriendServer::threadTick()
{
// Listen to the network interface, capture incoming data etc.
RsItem *item;
while(nullptr != (item = mni->GetItem()))
{
RsFriendServerItem *fsitem = dynamic_cast<RsFriendServerItem*>(item);
if(!fsitem)
{
RsErr() << "Received an item of the wrong type!" ;
continue;
}
std::cerr << "Received item: " << std::endl << *fsitem << std::endl;
switch(fsitem->PacketSubType())
{
case RS_PKT_SUBTYPE_FS_CLIENT_PUBLISH: handleClientPublish(dynamic_cast<RsFriendServerClientPublishItem*>(fsitem));
break;
case RS_PKT_SUBTYPE_FS_CLIENT_REMOVE: handleClientRemove(dynamic_cast<RsFriendServerClientRemoveItem*>(fsitem));
break;
default: ;
}
delete item;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
static rstime_t last_autowash_TS = time(nullptr);
rstime_t now = time(nullptr);
if(last_autowash_TS + DELAY_BETWEEN_TWO_AUTOWASH < now)
{
last_autowash_TS = now;
autoWash();
}
static rstime_t last_debugprint_TS = time(nullptr);
if(last_debugprint_TS + DELAY_BETWEEN_TWO_DEBUG_PRINT < now)
{
last_debugprint_TS = now;
debugPrint();
}
}
void FriendServer::handleClientPublish(const RsFriendServerClientPublishItem *item)
{
// We always respond with exactly one item, be it an error item or a list of friends to connect to.
try
{
RsDbg() << "Received a client publish item from " << item->PeerId() << ":";
RsDbg() << *item ;
// First of all, read PGP key and short invites, parse them, and check that they contain the same information
std::map<RsPeerId,PeerInfo>::iterator pi = handleIncomingClientData(item->pgp_public_key_b64,item->short_invite);
// No need to test for it==mCurrentClients.end() because it will be directly caught by the exception handling below even before.
// Respond with a list of potential friends
RsDbg() << "Sending response item to " << item->PeerId() ;
RsFriendServerServerResponseItem *sr_item = new RsFriendServerServerResponseItem;
std::map<RsPeerId,RsPgpFingerprint> friends;
sr_item->nonce = pi->second.last_nonce;
sr_item->friend_invites = computeListOfFriendInvites(item->n_requested_friends,pi->first,friends);
sr_item->PeerId(item->PeerId());
// Update the have_added_as_friend for the list of each peer. We do that before sending because sending destroys
// the item.
for(const auto& pid:friends)
{
auto& p(mCurrentClientPeers[pid.first]);
p.have_added_this_peer[computePeerDistance(p.pgp_fingerprint, pi->second.pgp_fingerprint)] = pi->first;
}
// Now encrypt the item with the public PGP key of the destination. This prevents the wrong person to request for
// someone else's data.
#warning TODO
// Send the item.
mni->SendItem(sr_item);
// Update the list of closest peers for all peers currently in the database.
updateClosestPeers(pi->first,pi->second.pgp_fingerprint);
}
catch(std::exception& e)
{
RsErr() << "ERROR: " << e.what() ;
RsFriendServerStatusItem *status_item = new RsFriendServerStatusItem;
status_item->status = RsFriendServerStatusItem::END_OF_TRANSMISSION;
status_item->PeerId(item->PeerId());
mni->SendItem(status_item);
return;
}
}
std::map<std::string, bool> FriendServer::computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map<RsPeerId,RsPgpFingerprint>& friends)
{
// Strategy: we want to return the same set of friends for a given PGP profile key.
// Still, using some closest distance strategy, the n-closest peers for profile A is not the
// same set than the n-closest peers for profile B. We have multiple options:
//
// Option 1:
//
// (1) for each profile, keep the list of n-closest peers updated (when a new peer if added/removed all lists are updated)
//
// When a peer asks for k friends, read from (1), until the number of collected peers
// reaches the requested value. Then when a peer receives a connection request, ask the friend server if the
// peer has been sent your own cert.
//
// Option 2:
//
// (1) for each profile, keep the list of n-closest peers updated (when a new peer if added/removed all lists are updated)
// (2) for each profile, keep the list of which peers have been sent this profile already
//
// When a peer asks for k friends, read from (2) first, then (1), until the number of collected peers
// reaches the requested value.
//
// So we choose Option 2.
std::map<std::string,bool> res;
auto add_from = [&res,&friends,nb_reqs_invites,this](bool added,const std::map<PeerInfo::PeerDistance,RsPeerId>& lst) -> bool
{
for(const auto& pid:lst)
{
const auto p = mCurrentClientPeers.find(pid.second);
res.insert(std::make_pair(p->second.short_certificate,added));
friends.insert(std::make_pair(p->first,p->second.pgp_fingerprint));
if(res.size() >= nb_reqs_invites)
return true;
}
return false;
};
const auto& pinfo(mCurrentClientPeers[pid]);
// First add from peers who already added the current peer as friend, and leave if we already have enough
if(add_from(true,pinfo.have_added_this_peer))
return res;
add_from(false,pinfo.closest_peers);
return res;
}
std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64)
{
// 1 - Check that the incoming data is sound.
RsDbg() << " Checking item data...";
std::string error_string;
std::vector<uint8_t> key_binary_data ;
if(RsBase64::decode(pgp_public_key_b64,key_binary_data))
throw std::runtime_error(" Cannot decode client pgp public key: \"" + pgp_public_key_b64 + "\". Wrong format??");
RsDbg() << " Parsing public key:" ;
PGPKeyInfo received_key_info;
if(!PGPKeyManagement::parsePGPPublicKey(key_binary_data.data(),key_binary_data.size(),received_key_info))
throw std::runtime_error("Cannot parse received pgp public key.") ;
RsDbg() << " Issuer : \"" << received_key_info.user_id << "\"" ;
RsDbg() << " Fingerprint: " << RsPgpFingerprint::fromBufferUnsafe(received_key_info.fingerprint) ;
RsDbg() << " Parsing short invite:" ;
RsPeerDetails shortInviteDetails;
uint32_t errorCode = 0;
if(short_invite_b64.empty() || !RsCertificate::decodeRadix64ShortInvite(short_invite_b64, shortInviteDetails,errorCode ))
throw std::runtime_error("Could not parse short certificate. Error = " + RsUtil::NumberToString(errorCode));
RsDbg() << " Fingerprint: " << shortInviteDetails.fpr ;
RsDbg() << " Peer ID: " << shortInviteDetails.id ;
if(shortInviteDetails.fpr != RsPgpFingerprint::fromBufferUnsafe(received_key_info.fingerprint))
throw std::runtime_error("Fingerpring from short invite and public key are different! Very unexpected! Message will be ignored.");
// 3 - if the key is not already here, add it to keyring.
{
RsPgpFingerprint fpr_test;
if(mPgpHandler->isPgpPubKeyAvailable(RsPgpId::fromBufferUnsafe(received_key_info.fingerprint+12)))
RsDbg() << " PGP Key is already into keyring.";
else
{
RsPgpId pgp_id;
if(!mPgpHandler->LoadCertificateFromBinaryData(key_binary_data.data(),key_binary_data.size(), pgp_id, error_string))
throw std::runtime_error("Cannot load client's pgp public key into keyring: " + error_string) ;
RsDbg() << " Public key added to keyring.";
RsDbg() << " Sync-ing the PGP keyring on disk";
mPgpHandler->syncDatabase();
}
}
// All good.
// Store/update the peer info
auto& pi(mCurrentClientPeers[shortInviteDetails.id]);
pi.short_certificate = short_invite_b64;
pi.last_connection_TS = time(nullptr);
pi.pgp_fingerprint = shortInviteDetails.fpr;
while(pi.last_nonce == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
pi.last_nonce = RsRandom::random_u64();
return mCurrentClientPeers.find(shortInviteDetails.id);
}
void FriendServer::handleClientRemove(const RsFriendServerClientRemoveItem *item)
{
RsDbg() << "Received a client remove item:" << *item ;
auto it = mCurrentClientPeers.find(item->peer_id);
if(it == mCurrentClientPeers.end())
{
RsErr() << " ERROR: Client " << item->peer_id << " is not known to the server." ;
return;
}
if(it->second.last_nonce != item->nonce)
{
RsErr() << " ERROR: Client supplied a nonce " << std::hex << item->nonce << std::dec << " that is not correct (expected "
<< std::hex << it->second.last_nonce << std::dec << ")";
return;
}
RsDbg() << " Nonce is correct: " << std::hex << item->nonce << std::dec << ". Removing peer " << item->peer_id ;
removePeer(item->peer_id);
}
void FriendServer::removePeer(const RsPeerId& peer_id)
{
auto it = mCurrentClientPeers.find(peer_id);
if(it != mCurrentClientPeers.end())
mCurrentClientPeers.erase(it);
for(auto& it:mCurrentClientPeers)
{
// Also remove that peer from all n-closest lists
for(auto pit(it.second.closest_peers.begin());pit!=it.second.closest_peers.end();)
if(pit->second == peer_id)
{
RsDbg() << " Removing from n-closest peers of peer " << pit->first ;
auto tmp(pit);
++tmp;
it.second.closest_peers.erase(pit);
pit=tmp;
}
else
++pit;
// Also remove that peer from peers that have accepted each peer
for(auto fit(it.second.have_added_this_peer.begin());fit!=it.second.have_added_this_peer.end();)
if(fit->second == peer_id)
{
RsDbg() << " Removing from have_added_as_friend peers of peer " << fit->first ;
auto tmp(fit);
++tmp;
it.second.closest_peers.erase(fit);
fit=tmp;
}
else
++fit;
}
}
PeerInfo::PeerDistance FriendServer::computePeerDistance(const RsPgpFingerprint& p1,const RsPgpFingerprint& p2)
{
std::cerr << "Computing peer distance: p1=" << p1 << " p2=" << p2 << " p1^p2=" << (p1^p2) << " distance=" << ((p1^p2)^mRandomPeerBias) << std::endl;
return (p1 ^ p2)^mRandomPeerBias;
}
FriendServer::FriendServer(const std::string& base_dir,const std::string& listening_address,uint16_t listening_port)
: mListeningAddress(listening_address),mListeningPort(listening_port)
{
RsDbg() << "Creating friend server." ;
mBaseDirectory = base_dir;
// Create a PGP Handler
std::string pgp_public_keyring_path = RsDirUtil::makePath(base_dir,"pgp_public_keyring") ;
std::string pgp_lock_path = RsDirUtil::makePath(base_dir,"pgp_lock") ;
std::string pgp_private_keyring_path = RsDirUtil::makePath(base_dir,"pgp_private_keyring") ; // not used.
std::string pgp_trustdb_path = RsDirUtil::makePath(base_dir,"pgp_trustdb") ; // not used.
mPgpHandler = new OpenPGPSDKHandler(pgp_public_keyring_path,pgp_private_keyring_path,pgp_trustdb_path,pgp_lock_path);
// Random bias. Should be cryptographically safe.
mRandomPeerBias = RsPgpFingerprint::random();
}
void FriendServer::run()
{
// 1 - create network interface.
mni = new FsNetworkInterface(mListeningAddress,mListeningPort);
mni->start();
while(!shouldStop()) { threadTick() ; }
}
void FriendServer::autoWash()
{
rstime_t now = time(nullptr);
RsDbg() << "autoWash..." ;
std::list<RsPeerId> to_remove;
for(std::map<RsPeerId,PeerInfo>::iterator it(mCurrentClientPeers.begin());it!=mCurrentClientPeers.end();++it)
if(it->second.last_connection_TS + MAXIMUM_PEER_INACTIVE_DELAY < now)
{
RsDbg() << "Removing client peer " << it->first << " because it's inactive for more than " << MAXIMUM_PEER_INACTIVE_DELAY << " seconds." ;
to_remove.push_back(it->first);
}
for(auto peer_id:to_remove)
removePeer(peer_id);
RsDbg() << "done." ;
}
void FriendServer::updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr)
{
for(auto& it:mCurrentClientPeers)
if(it.first != pid)
{
PeerInfo::PeerDistance d = computePeerDistance(fpr,it.second.pgp_fingerprint);
it.second.closest_peers.insert(std::make_pair(d,pid));
if(it.second.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
it.second.closest_peers.erase(std::prev(it.second.closest_peers.end()));
}
}
void FriendServer::debugPrint()
{
RsDbg() << "========== FriendServer statistics ============";
RsDbg() << " Base directory: "<< mBaseDirectory;
RsDbg() << " Random peer bias: "<< mRandomPeerBias;
RsDbg() << " Network interface: ";
RsDbg() << " Max peers in n-closest list: " << MAXIMUM_PEERS_TO_REQUEST;
RsDbg() << " Current active peers: " << mCurrentClientPeers.size() ;
rstime_t now = time(nullptr);
for(const auto& it:mCurrentClientPeers)
{
RsDbg() << " " << it.first << ": nonce=" << std::hex << it.second.last_nonce << std::dec << " fpr: " << it.second.pgp_fingerprint << ", last contact: " << now - it.second.last_connection_TS << " secs ago.";
RsDbg() << " Closest peers:" ;
for(const auto& pit:it.second.closest_peers)
RsDbg() << " " << pit.second << " distance=" << pit.first ;
RsDbg() << " Have added this peer:" ;
for(const auto& pit:it.second.have_added_this_peer)
RsDbg() << " " << pit.second << " distance=" << pit.first ;
}
RsDbg() << "===============================================";
}

View File

@ -0,0 +1,91 @@
/*
* RetroShare Friend Server
* Copyright (C) 2021-2021 retroshare team <retroshare.project@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "util/rsthreads.h"
#include "pqi/pqistreamer.h"
#include "pgp/pgphandler.h"
#include "network.h"
class RsFriendServerClientRemoveItem;
class RsFriendServerClientPublishItem;
struct PeerInfo
{
typedef RsPgpFingerprint PeerDistance;
RsPgpFingerprint pgp_fingerprint;
std::string short_certificate;
rstime_t last_connection_TS;
uint64_t last_nonce;
std::map<PeerDistance,RsPeerId> closest_peers;
std::map<PeerDistance,RsPeerId> have_added_this_peer;
};
class FriendServer : public RsTickingThread
{
public:
FriendServer(const std::string& base_directory,const std::string& listening_address,uint16_t listening_port);
private:
// overloads RsTickingThread
virtual void threadTick() override;
virtual void run() override;
// Own algorithmics
void handleClientRemove(const RsFriendServerClientRemoveItem *item);
void handleClientPublish(const RsFriendServerClientPublishItem *item);
// Updates for each peer in the database, the list of closest peers w.r.t. some arbitrary distance.
void updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr);
// removes a single peer from all lists.
void removePeer(const RsPeerId& peer_id);
// Adds the incoming peer data to the list of current clients and returns the
std::map<RsPeerId,PeerInfo>::iterator handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64);
// Computes the appropriate list of short invites to send to a given peer.
std::map<std::string, bool> computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map<RsPeerId,RsPgpFingerprint>& friends);
// Compute the distance between peers using the random bias (It's not really a distance though. I'm not sure about the triangular inequality).
PeerInfo::PeerDistance computePeerDistance(const RsPgpFingerprint &p1, const RsPgpFingerprint &p2);
void autoWash();
void debugPrint();
// Local members
FsNetworkInterface *mni;
PGPHandler *mPgpHandler;
std::string mBaseDirectory;
RsPgpFingerprint mRandomPeerBias;
std::map<RsPeerId, PeerInfo> mCurrentClientPeers;
std::string mListeningAddress;
uint16_t mListeningPort;
};

View File

@ -0,0 +1,298 @@
/*
* RetroShare Friend Server
* Copyright (C) 2021-2021 retroshare team <retroshare.project@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "util/rsnet.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
#include "pqi/pqithreadstreamer.h"
#include "pqi/pqifdbin.h"
#include "network.h"
#include "friend_server/fsitem.h"
FsNetworkInterface::FsNetworkInterface(const std::string& listening_address,uint16_t listening_port)
: PQInterface(RsPeerId()),mFsNiMtx(std::string("FsNetworkInterface")),mListeningAddress(listening_address),mListeningPort(listening_port)
{
RS_STACK_MUTEX(mFsNiMtx);
mClintListn = 0;
mClintListn = socket(AF_INET, SOCK_STREAM, 0); // creating socket
int flags = fcntl(mClintListn, F_GETFL);
fcntl(mClintListn, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in ipOfServer;
memset(&ipOfServer, '0', sizeof(ipOfServer));
assert(mListeningPort > 1024);
ipOfServer.sin_family = AF_INET;
ipOfServer.sin_port = htons(mListeningPort); // this is the port number of running server
int addr[4];
if(sscanf(listening_address.c_str(),"%d.%d.%d.%d",&addr[0],&addr[1],&addr[2],&addr[3]) != 4)
throw std::runtime_error("Cannot parse a proper IPv4 address in \""+listening_address+"\"");
for(int i=0;i<4;++i)
if(addr[i] < 0 || addr[i] > 255)
throw std::runtime_error("Cannot parse a proper IPv4 address in \""+listening_address+"\"");
ipOfServer.sin_addr.s_addr = htonl( (addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3] );
if(bind(mClintListn, (struct sockaddr*)&ipOfServer , sizeof(ipOfServer)) < 0)
{
RsErr() << "Error while binding: errno=" << errno ;
return;
}
if(listen(mClintListn , 40) < 0)
{
RsErr() << "Error while calling listen: errno=" << errno ;
return;
}
RsDbg() << "Network interface now listening for TCP on " << sockaddr_storage_tostring( *(sockaddr_storage*)&ipOfServer) ;
}
FsNetworkInterface::~FsNetworkInterface()
{
RS_STACK_MUTEX(mFsNiMtx);
for(auto& it:mConnections)
{
delete it.second.pqi_thread;
std::cerr << "Releasing socket " << it.second.socket << std::endl;
close(it.second.socket);
}
std::cerr << "Releasing listening socket " << mClintListn << std::endl;
close(mClintListn);
}
void FsNetworkInterface::threadTick()
{
// 1 - check for new connections
checkForNewConnections();
// 2 - tick all streamers
std::list<RsPeerId> to_close;
RS_STACK_MUTEX(mFsNiMtx);
for(auto& it:mConnections)
if(it.second.bio->isactive())
it.second.pqi_thread->tick();
else
to_close.push_back(it.first);
for(const auto& pid:to_close)
locked_closeConnection(pid);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
static RsPeerId makePeerId(int t)
{
unsigned char s[RsPeerId::SIZE_IN_BYTES];
memset(s,0,sizeof(s));
*reinterpret_cast<int*>(&s) = t;
return RsPeerId::fromBufferUnsafe(s);
}
bool FsNetworkInterface::checkForNewConnections()
{
// look for incoming data
struct sockaddr addr;
socklen_t addr_len = sizeof(sockaddr);
int clintConnt = accept(mClintListn, &addr, &addr_len); // accept is a blocking call!
if(clintConnt < 0)
{
if(errno == EWOULDBLOCK)
;//RsErr()<< "Incoming connection with nothing to read!" << std::endl;
else
RsErr()<< "Error when accepting connection." << std::endl;
return false;
}
RsDbg() << "Got incoming connection from " << sockaddr_storage_tostring( *(sockaddr_storage*)&addr);
// Make the socket non blocking so that we can read from it and return if nothing comes
int flags = fcntl(clintConnt, F_GETFL);
fcntl(clintConnt, F_SETFL, flags | O_NONBLOCK);
// Create connection info
ConnectionData c;
c.socket = clintConnt;
c.client_address = addr;
RsPeerId pid = makePeerId(clintConnt);
// Setup a pqistreamer to deserialize whatever comes from this connection
RsSerialiser *rss = new RsSerialiser ;
rss->addSerialType(new FsSerializer) ;
RsFdBinInterface *bio = new RsFdBinInterface(clintConnt,true);
auto pqi = new pqithreadstreamer(this,rss, pid, bio,BIN_FLAGS_READABLE | BIN_FLAGS_WRITEABLE);
c.pqi_thread = pqi;
c.bio = bio;
pqi->start();
RS_STACK_MUTEX(mFsNiMtx);
mConnections[pid] = c;
return true;
}
bool FsNetworkInterface::RecvItem(RsItem *item)
{
RS_STACK_MUTEX(mFsNiMtx);
auto it = mConnections.find(item->PeerId());
if(it == mConnections.end())
{
RsErr() << "Receiving an item for peer ID " << item->PeerId() << " but no connection is known for that peer." << std::endl;
delete item;
return false;
}
it->second.incoming_items.push_back(item);
return true;
}
RsItem *FsNetworkInterface::GetItem()
{
RS_STACK_MUTEX(mFsNiMtx);
for(auto& it:mConnections)
{
if(!it.second.incoming_items.empty())
{
RsItem *item = it.second.incoming_items.front();
it.second.incoming_items.pop_front();
return item;
}
}
return nullptr;
}
int FsNetworkInterface::SendItem(RsItem *item)
{
RS_STACK_MUTEX(mFsNiMtx);
const auto& it = mConnections.find(item->PeerId());
if(it == mConnections.end())
{
RsErr() << "Cannot send item to peer " << item->PeerId() << ": no pending sockets available." ;
delete item;
return 0;
}
uint32_t ss;
return it->second.pqi_thread->SendItem(item,ss);
}
void FsNetworkInterface::closeConnection(const RsPeerId& peer_id)
{
RS_STACK_MUTEX(mFsNiMtx);
locked_closeConnection(peer_id);
}
void FsNetworkInterface::locked_closeConnection(const RsPeerId& peer_id)
{
RsDbg() << "Closing connection to virtual peer " << peer_id ;
const auto& it = mConnections.find(peer_id);
if(it == mConnections.end())
{
RsErr() << " Cannot close connection to peer " << peer_id << ": no pending sockets available." ;
return;
}
if(!it->second.incoming_items.empty())
{
RsErr() << " Trying to close an incoming connection with incoming items still pending! The items will be lost:" << std::endl;
for(auto& item:it->second.incoming_items)
{
RsErr() << *item;
delete item;
}
it->second.incoming_items.clear();
}
// Close the socket and delete everything.
close(it->second.socket);
it->second.pqi_thread->fullstop();
it->second.bio->close();
delete it->second.pqi_thread;
mConnections.erase(it);
}
void FsNetworkInterface::debugPrint()
{
RsDbg() << " " << mClintListn ; // listening socket
RsDbg() << " Connections: " << mConnections.size() ;
for(auto& it:mConnections)
RsDbg() << " " << it.first << ": from \"" << sockaddr_storage_tostring(*(sockaddr_storage*)(&it.second.client_address)) << "\", socket=" << it.second.socket ;
std::map<RsPeerId,ConnectionData> mConnections;
}

View File

@ -0,0 +1,86 @@
/*
* RetroShare Friend Server
* Copyright (C) 2021-2021 retroshare team <retroshare.project@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "util/rsthreads.h"
#include "pqi/pqi_base.h"
#include "retroshare/rspeers.h"
class pqithreadstreamer;
class RsFdBinInterface;
struct ConnectionData
{
sockaddr client_address;
int socket;
pqithreadstreamer *pqi_thread;
RsFdBinInterface *bio;
std::list<RsItem*> incoming_items;
};
// This class handles multiple connections to the server and supplies RsItem elements
class FsNetworkInterface: public RsTickingThread, public PQInterface
{
public:
FsNetworkInterface(const std::string& listening_address,uint16_t listening_port) ;
virtual ~FsNetworkInterface() ;
// basic functionality
void debugPrint();
// Implements PQInterface
bool RecvItem(RsItem *item) override;
int SendItem(RsItem *item) override;
RsItem *GetItem() override;
void closeConnection(const RsPeerId& peer_id);
// Implements RsTickingThread
void threadTick() override;
protected:
bool checkForNewConnections();
void locked_closeConnection(const RsPeerId& peer_id);
private:
RsMutex mFsNiMtx;
void initListening();
void stopListening();
int mClintListn ; // listening socket
std::map<RsPeerId,ConnectionData> mConnections;
std::string mListeningAddress;
uint16_t mListeningPort;
};

View File

@ -0,0 +1,106 @@
/*
* RetroShare Service
* Copyright (C) 2021-2021 retroshare team <retroshare.project@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include "util/stacktrace.h"
#include "util/rsdir.h"
#include "util/argstream.h"
#include "util/rstime.h"
#include "util/rsdebug.h"
#include "retroshare/rstor.h"
#include "retroshare/rsinit.h"
#include "friendserver.h"
// debug
#include "friend_server/fsitem.h"
#include "friend_server/fsclient.h"
int main(int argc, char* argv[])
{
RsInfo() << "\n" <<
"+================================================================+\n"
"| o---o o |\n"
"| \\ / - Retroshare Friend Server - / \\ |\n"
"| o o---o |\n"
"+================================================================+"
<< std::endl << std::endl;
//RsControl::earlyInitNotificationSystem();
std::string base_directory = "FSData";
argstream as(argc,argv);
as >> parameter( 'c',"base-dir", base_directory, "set base directory to store data files (keys, etc)", false )
>> help( 'h', "help", "Display this Help" );
as.defaultErrorHandling(true, true);
RsConfigOptions conf;
conf.main_executable_path = argv[0];
RsInit::InitRsConfig();
RsInit::InitRetroShare(conf);
// Create the base directory if needed
if(!RsDirUtil::checkCreateDirectory(base_directory))
{
RsErr() << "Cannot create base directory \"" << base_directory << "\". Check permissions, paths, etc." ;
return 1;
}
// Create/start TorManager
RsTor::setTorDataDirectory(RsDirUtil::makePath(base_directory,"tor"));
RsTor::setHiddenServiceDirectory(RsDirUtil::makePath(base_directory,"hidden_service"));
if(! RsTor::start() || RsTor::hasError())
{
RsErr() << "Tor cannot be started on your system: " << RsTor::errorMessage() ;
return 1 ;
}
std::string service_id;
while(RsTor::torStatus() != RsTorStatus::READY || RsTor::getHiddenServiceStatus(service_id) != RsTorHiddenServiceStatus::ONLINE)
std::this_thread::sleep_for(std::chrono::seconds(1));
std::string onion_address,service_target_address;
uint16_t service_port,target_port;
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,target_port) ;
RsDbg() << "Tor properly started: " ;
RsDbg() << " Hidden service address: " << onion_address << ":" << service_port;
RsDbg() << " Target address : " << service_target_address << ":" << target_port;
// Now start the real thing.
FriendServer fs(base_directory,service_target_address,target_port);
fs.start();
while(fs.isRunning())
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}

View File

@ -0,0 +1,43 @@
# RetroShare service qmake build script
#
# Copyright (C) 2021-2021, retroshare team <retroshare.project@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
# SPDX-License-Identifier: AGPL-3.0-or-later
!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri")
TARGET = retroshare-friendserver
!include("../../libretroshare/src/use_libretroshare.pri"):error("Including")
SOURCES += retroshare-friendserver.cc \
friendserver.cc \
network.cc
HEADERS += friendserver.h \
network.h \
fsitem.h
################################# Linux ##########################################
unix {
target.path = "$${RS_BIN_DIR}"
INSTALLS += target
}
################################### COMMON stuff ##################################

View File

@ -0,0 +1,171 @@
/*******************************************************************************
* gui/FriendServer.cpp *
* *
* Copyright (c) 2021 Retroshare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QTimer>
#include <QMovie>
#include <QTcpSocket>
#include "retroshare/rsfriendserver.h"
#include "retroshare/rstor.h"
#include "util/qtthreadsutils.h"
#include "gui/common/FilesDefs.h"
#include "FriendServerControl.h"
#include <iostream>
#define ICON_STATUS_UNKNOWN ":/images/ledoff1.png"
#define ICON_STATUS_OK ":/images/ledon1.png"
/** Constructor */
FriendServerControl::FriendServerControl(QWidget *parent)
: QWidget(parent)
{
/* Invoke the Qt Designer generated object setup routine */
setupUi(this);
if(!rsFriendServer)
{
setEnabled(false);
return;
}
mConnectionCheckTimer = new QTimer;
// init values
torServerFriendsToRequest_SB->setValue(rsFriendServer->friendsToRequest());
torServerAddress_LE->setText(QString::fromStdString(rsFriendServer->friendsServerAddress().c_str()));
torServerPort_SB->setValue(rsFriendServer->friendsServerPort());
// connect slignals/slots
QObject::connect(friendServerOnOff_CB,SIGNAL(toggled(bool)),this,SLOT(onOnOffClick(bool)));
QObject::connect(torServerFriendsToRequest_SB,SIGNAL(valueChanged(int)),this,SLOT(onFriendsToRequestChanged(int)));
QObject::connect(torServerAddress_LE,SIGNAL(textChanged(const QString&)),this,SLOT(onOnionAddressEdit(const QString&)));
QObject::connect(mConnectionCheckTimer,SIGNAL(timeout()),this,SLOT(checkServerAddress()));
mCheckingServerMovie = new QMovie(":/images/loader/circleball-16.gif");
serverStatusCheckResult_LB->setMovie(mCheckingServerMovie);
updateFriendServerStatusIcon(false);
updateTorProxyInfo();
}
void FriendServerControl::updateTorProxyInfo()
{
std::string friend_proxy_address;
uint16_t friend_proxy_port;
RsTor::getProxyServerInfo(friend_proxy_address,friend_proxy_port);
torProxyPort_SB->setValue(friend_proxy_port);
torProxyAddress_LE->setText(QString::fromStdString(friend_proxy_address));
}
FriendServerControl::~FriendServerControl()
{
delete mCheckingServerMovie;
delete mConnectionCheckTimer;
}
void FriendServerControl::onOnOffClick(bool b)
{
if(b)
rsFriendServer->startServer();
else
rsFriendServer->stopServer();
}
void FriendServerControl::onOnionPortEdit(int)
{
#warning TODO
// // Setup timer to auto-check the friend server address
//
// mConnectionCheckTimer->setSingleShot(true);
// mConnectionCheckTimer->setInterval(5000); // check in 5 secs unless something is changed in the mean time.
//
// mConnectionCheckTimer->start();
//
// if(mCheckingServerMovie->fileName() != QString(":/images/loader/circleball-16.gif" ))
// {
// mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
// mCheckingServerMovie->start();
// }
rsFriendServer->setServerAddress(torServerAddress_LE->text().toStdString(),torServerPort_SB->value());
rsFriendServer->setProxyAddress(torProxyAddress_LE->text().toStdString(),torProxyPort_SB->value());
}
void FriendServerControl::onOnionAddressEdit(const QString&)
{
// Setup timer to auto-check the friend server address
// mConnectionCheckTimer->setSingleShot(true);
// mConnectionCheckTimer->setInterval(5000); // check in 5 secs unless something is changed in the mean time.
//
// mConnectionCheckTimer->start();
//
// if(mCheckingServerMovie->fileName() != QString(":/images/loader/circleball-16.gif" ))
// {
// mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
// mCheckingServerMovie->start();
// }
rsFriendServer->setServerAddress(torServerAddress_LE->text().toStdString(),torServerPort_SB->value());
rsFriendServer->setProxyAddress(torProxyAddress_LE->text().toStdString(),torProxyPort_SB->value());
}
void FriendServerControl::checkServerAddress()
{
rsFriendServer->checkServerAddress_async(torServerAddress_LE->text().toStdString(),torServerPort_SB->value(),
[this](const std::string& address,bool test_result)
{
if(test_result)
rsFriendServer->setServerAddress(address,1729);
RsQThreadUtils::postToObject( [=]() { updateFriendServerStatusIcon(test_result); },this);
}
);
}
void FriendServerControl::onNbFriendsToRequestsChanged(int n)
{
rsFriendServer->setFriendsToRequest(n);
}
void FriendServerControl::updateFriendServerStatusIcon(bool ok)
{
mCheckingServerMovie->stop();
if(ok)
{
torServerStatus_LB->setToolTip(tr("Friend server is currently reachable.")) ;
mCheckingServerMovie->setFileName(ICON_STATUS_OK);
}
else
{
torServerStatus_LB->setToolTip(tr("The proxy is not enabled or broken.\nAre all services up and running fine??\nAlso check your ports!")) ;
mCheckingServerMovie->setFileName(ICON_STATUS_UNKNOWN);
}
mCheckingServerMovie->start();
}

View File

@ -0,0 +1,48 @@
/*******************************************************************************
* gui/NetworkView.h *
* *
* Copyright (c) 2008 Robert Fernie <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include <QGraphicsScene>
#include "ui_FriendServerControl.h"
class FriendServerControl : public QWidget, public Ui::FriendServerControl
{
Q_OBJECT
public:
FriendServerControl(QWidget *parent = 0);
virtual ~FriendServerControl();
protected slots:
void onOnOffClick(bool b);
void onOnionAddressEdit(const QString&);
void onOnionPortEdit(int);
void onNbFriendsToRequestsChanged(int n);
void updateTorProxyInfo();
private:
void checkServerAddress();
void updateFriendServerStatusIcon(bool ok);
QTimer *mConnectionCheckTimer;
QMovie *mCheckingServerMovie;
};

View File

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FriendServerControl</class>
<widget class="QWidget" name="FriendServerControl">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>620</width>
<height>499</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="friendServerOnOff_CB">
<property name="text">
<string>On/Off</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Friends to request: </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="torServerFriendsToRequest_SB">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>15</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Server onion address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="torServerAddress_LE">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>.onion</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="torServerPort_SB">
<property name="minimum">
<number>1025</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="value">
<number>2017</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="serverStatusCheckResult_LB">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="torServerStatus_LB">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Tor proxy address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="torProxyAddress_LE">
<property name="text">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="torProxyPort_SB">
<property name="minimum">
<number>1025</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>9050</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>380</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -44,6 +44,9 @@
#include "NetworkView.h" #include "NetworkView.h"
#include "NetworkDialog.h" #include "NetworkDialog.h"
#include "gui/common/NewFriendList.h" #include "gui/common/NewFriendList.h"
#ifdef RS_EMBEDED_FRIEND_SERVER
#include "gui/FriendServerControl.h"
#endif
#include "gui/Identity/IdDialog.h" #include "gui/Identity/IdDialog.h"
/* Images for Newsfeed icons */ /* Images for Newsfeed icons */
//#define IMAGE_NEWSFEED "" //#define IMAGE_NEWSFEED ""
@ -88,6 +91,9 @@ FriendsDialog::FriendsDialog(QWidget *parent) : MainPage(parent)
ui.avatar->setFrameType(AvatarWidget::STATUS_FRAME); ui.avatar->setFrameType(AvatarWidget::STATUS_FRAME);
ui.tabWidget->setTabPosition(QTabWidget::North); ui.tabWidget->setTabPosition(QTabWidget::North);
#ifdef RS_EMBEDED_FRIEND_SERVER
ui.tabWidget->addTab(friendServerControl = new FriendServerControl(),QIcon(IMAGE_PEERS), tr("Friend Server"));
#endif
ui.tabWidget->addTab(networkView = new NetworkView(),QIcon(IMAGE_NETWORK2), tr("Network graph")); ui.tabWidget->addTab(networkView = new NetworkView(),QIcon(IMAGE_NETWORK2), tr("Network graph"));
ui.tabWidget->addTab(networkDialog = new NetworkDialog(),QIcon(IMAGE_PEERS), tr("Keyring")); ui.tabWidget->addTab(networkDialog = new NetworkDialog(),QIcon(IMAGE_PEERS), tr("Keyring"));

View File

@ -30,6 +30,7 @@ class NetworkDialog;
class NetworkView; class NetworkView;
class IdDialog; class IdDialog;
class CirclesDialog; class CirclesDialog;
class FriendServerControl;
class FriendsDialog : public MainPage class FriendsDialog : public MainPage
{ {
@ -64,6 +65,7 @@ public:
NetworkDialog *networkDialog ; NetworkDialog *networkDialog ;
NetworkView *networkView ; NetworkView *networkView ;
FriendServerControl *friendServerControl ;
IdDialog *idDialog; IdDialog *idDialog;

View File

@ -1085,6 +1085,13 @@ DEFINES *= CHANNELS_FRAME_CATCHER
} }
# Embedded Friend Server
rs_efs {
SOURCES += gui/FriendServerControl.cpp
HEADERS += gui/FriendServerControl.h
FORMS += gui/FriendServerControl.ui
}
# BELOW IS GXS Unfinished Services. # BELOW IS GXS Unfinished Services.

View File

@ -51,6 +51,11 @@ retroshare_plugins:CONFIG -= no_retroshare_plugins
CONFIG *= retroshare_service CONFIG *= retroshare_service
no_retroshare_service:CONFIG -= retroshare_service no_retroshare_service:CONFIG -= retroshare_service
# To disable RetroShare FriendServer append the following assignation to
# qmake command line "CONFIG+=no_rs_friendserver"
CONFIG *= retroshare_friendserver
no_rs_friendserver:CONFIG -= retroshare_friendserver
# To disable SQLCipher support append the following assignation to qmake # To disable SQLCipher support append the following assignation to qmake
# command line "CONFIG+=no_sqlcipher" # command line "CONFIG+=no_sqlcipher"
CONFIG *= sqlcipher CONFIG *= sqlcipher