mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
1015 lines
26 KiB
C++
1015 lines
26 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* 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/>.
|
|
**************************************************************************/
|
|
|
|
#include "InternetServiceConnector.h"
|
|
#include "server.h"
|
|
#include "../Interface/Server.h"
|
|
#include "../Interface/Mutex.h"
|
|
#include "../Interface/Condition.h"
|
|
#include "../Interface/Database.h"
|
|
#include "../common/data.h"
|
|
#include "../urbackupcommon/InternetServiceIDs.h"
|
|
#include "../urbackupcommon/InternetServicePipe.h"
|
|
#include "../urbackupcommon/CompressedPipe2.h"
|
|
#include "../urbackupcommon/CompressedPipeZstd.h"
|
|
#include "../urbackupcommon/CompressedPipe.h"
|
|
#include "server_settings.h"
|
|
#include "database.h"
|
|
#include "../stringtools.h"
|
|
#include "../cryptoplugin/ICryptoFactory.h"
|
|
#include "serverinterface/login.h"
|
|
|
|
#include <memory.h>
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include "../urbackupcommon/InternetServicePipe2.h"
|
|
|
|
const unsigned int ping_interval=5*60*1000;
|
|
const unsigned int ping_timeout=30000;
|
|
const unsigned int offline_timeout=ping_interval+10000;
|
|
const unsigned int establish_timeout=60000;
|
|
const int64 max_ecdh_key_age = 6 * 60 * 60 * 1000; //6h
|
|
const std::string restore_prefix = "##restore##";
|
|
|
|
std::map<std::string, SClientData> InternetServiceConnector::client_data;
|
|
IMutex *InternetServiceConnector::mutex=NULL;
|
|
IMutex *InternetServiceConnector::onetime_token_mutex=NULL;
|
|
std::map<unsigned int, SOnetimeToken> InternetServiceConnector::onetime_tokens;
|
|
unsigned int InternetServiceConnector::onetime_token_id=0;
|
|
int64 InternetServiceConnector::last_token_remove=0;
|
|
std::vector<std::pair<IECDHKeyExchange*, int64> > InternetServiceConnector::ecdh_key_exchange_buffer;
|
|
std::set<std::string> InternetServiceConnector::internet_expect_endpoint;
|
|
|
|
|
|
extern ICryptoFactory *crypto_fak;
|
|
const size_t pbkdf2_iterations=20000;
|
|
|
|
InternetService::InternetService(BackupServer * backup_server)
|
|
:backup_server(backup_server)
|
|
{
|
|
}
|
|
|
|
ICustomClient* InternetService::createClient()
|
|
{
|
|
return new InternetServiceConnector(backup_server);
|
|
}
|
|
|
|
void InternetService::destroyClient( ICustomClient * pClient)
|
|
{
|
|
delete ((InternetServiceConnector*)pClient);
|
|
}
|
|
|
|
InternetServiceConnector::InternetServiceConnector(BackupServer * backup_server)
|
|
: backup_server(backup_server)
|
|
{
|
|
local_mutex=Server->createMutex();
|
|
ecdh_key_exchange=NULL;
|
|
}
|
|
|
|
InternetServiceConnector::~InternetServiceConnector(void)
|
|
{
|
|
IScopedLock lock(mutex);
|
|
if(!connect_start)
|
|
{
|
|
cleanup_pipes(true);
|
|
}
|
|
Server->destroy(local_mutex);
|
|
|
|
if(ecdh_key_exchange!=NULL
|
|
&& Server->getTimeMS()-ecdh_key_exchange_age<max_ecdh_key_age)
|
|
{
|
|
ecdh_key_exchange_buffer.push_back(std::make_pair(ecdh_key_exchange, ecdh_key_exchange_age));
|
|
}
|
|
else
|
|
{
|
|
Server->destroy(ecdh_key_exchange);
|
|
}
|
|
}
|
|
|
|
void InternetServiceConnector::Init(THREAD_ID pTID, IPipe *pPipe, const std::string& pEndpointName)
|
|
{
|
|
tid=pTID;
|
|
cs=pPipe;
|
|
comm_pipe=cs;
|
|
is_pipe=NULL;
|
|
conn_version=2;
|
|
comp_pipe=NULL;
|
|
connect_start=false;
|
|
do_connect=false;
|
|
stop_connecting=false;
|
|
is_connected=false;
|
|
pinging=false;
|
|
free_connection=false;
|
|
if (internet_expect_endpoint.find(pEndpointName)!=internet_expect_endpoint.end())
|
|
{
|
|
state = ISS_RECEIVE_ENDPOINT;
|
|
endpoint_remaining = 50;
|
|
}
|
|
else
|
|
{
|
|
endpoint_name = pEndpointName;
|
|
state = ISS_AUTH;
|
|
}
|
|
has_timeout=false;
|
|
connection_done_cond=NULL;
|
|
tcpstack.reset();
|
|
tcpstack.setAddChecksum(true);
|
|
tcpstack.setMaxPacketSize(32768);
|
|
client_ping_interval = ping_interval;
|
|
challenge=ServerSettings::generateRandomBinaryKey();
|
|
{
|
|
CWData data;
|
|
data.addChar(ID_ISC_CHALLENGE);
|
|
data.addString(challenge);
|
|
unsigned int capa=0;
|
|
ServerSettings server_settings(Server->getDatabase(pTID, URBACKUPDB_SERVER));
|
|
SSettings *settings=server_settings.getSettings();
|
|
capa|=IPC_ENCRYPTED;
|
|
capa|=IPC_COMPRESSED;
|
|
#ifndef NO_ZSTD_COMPRESSION
|
|
capa |= IPC_COMPRESSED_ZSTD;
|
|
#endif
|
|
|
|
compression_level=settings->internet_compression_level;
|
|
data.addUInt(capa);
|
|
data.addInt(compression_level);
|
|
data.addUInt((unsigned int)pbkdf2_iterations);
|
|
|
|
if(ecdh_key_exchange==NULL)
|
|
{
|
|
IScopedLock lock(mutex);
|
|
if(!ecdh_key_exchange_buffer.empty())
|
|
{
|
|
ecdh_key_exchange = ecdh_key_exchange_buffer[ecdh_key_exchange_buffer.size()-1].first;
|
|
ecdh_key_exchange_age = ecdh_key_exchange_buffer[ecdh_key_exchange_buffer.size() - 1].second;
|
|
ecdh_key_exchange_buffer.pop_back();
|
|
}
|
|
}
|
|
if(ecdh_key_exchange==NULL)
|
|
{
|
|
ecdh_key_exchange = crypto_fak->createECDHKeyExchange();
|
|
ecdh_key_exchange_age = Server->getTimeMS();
|
|
}
|
|
|
|
data.addString(ecdh_key_exchange->getPublicKey());
|
|
|
|
tcpstack.Send(cs, data);
|
|
}
|
|
lastpingtime=Server->getTimeMS();
|
|
}
|
|
|
|
void InternetServiceConnector::cleanup_pipes(bool remove_connection)
|
|
{
|
|
delete is_pipe;
|
|
is_pipe=NULL;
|
|
delete comp_pipe;
|
|
comp_pipe=NULL;
|
|
|
|
if(remove_connection
|
|
&& client_data.find(clientname)!=client_data.end())
|
|
{
|
|
std::vector<InternetServiceConnector*>& spare_connections = client_data[clientname].spare_connections;
|
|
std::vector<InternetServiceConnector*>::iterator it=std::find(spare_connections.begin(), spare_connections.end(), this);
|
|
if(it!=spare_connections.end())
|
|
{
|
|
spare_connections.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool InternetServiceConnector::Run(IRunOtherCallback* run_other)
|
|
{
|
|
if(stop_connecting)
|
|
{
|
|
IScopedLock lock(local_mutex);
|
|
cleanup_pipes(false);
|
|
return false;
|
|
}
|
|
|
|
if(state==ISS_CONNECTING)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(state==ISS_USED)
|
|
{
|
|
if(free_connection)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if( has_timeout )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (state == ISS_RECEIVE_ENDPOINT)
|
|
{
|
|
if (Server->getTimeMS() - lastpingtime > ping_timeout)
|
|
{
|
|
Server->Log("ISS_RECEIVE_ENDPOINT timeout in InternetServiceConnector::Run", LL_DEBUG);
|
|
IScopedLock lock(mutex);
|
|
if (!connect_start)
|
|
{
|
|
has_timeout = true;
|
|
cleanup_pipes(true);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if(do_connect && !pinging && state==ISS_AUTHED )
|
|
{
|
|
CWData data;
|
|
{
|
|
IScopedLock lock(local_mutex);
|
|
data.addChar(ID_ISC_CONNECT);
|
|
data.addChar(target_service);
|
|
state=ISS_CONNECTING;
|
|
}
|
|
tcpstack.Send(comm_pipe, data);
|
|
Server->Log("Connecting to target service...", LL_DEBUG);
|
|
starttime=Server->getTimeMS();
|
|
}
|
|
|
|
if(state==ISS_AUTHED && Server->getTimeMS()-lastpingtime>client_ping_interval && !pinging)
|
|
{
|
|
lastpingtime=Server->getTimeMS();
|
|
pinging=true;
|
|
CWData data;
|
|
data.addChar(ID_ISC_PING);
|
|
tcpstack.Send(comm_pipe, data);
|
|
}
|
|
else if(state==ISS_AUTHED && pinging )
|
|
{
|
|
if(Server->getTimeMS()-lastpingtime>ping_timeout)
|
|
{
|
|
Server->Log("Ping timeout in InternetServiceConnector::Run", LL_DEBUG);
|
|
IScopedLock lock(mutex);
|
|
if(!connect_start)
|
|
{
|
|
has_timeout=true;
|
|
cleanup_pipes(true);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void InternetServiceConnector::ReceivePackets(IRunOtherCallback* run_other)
|
|
{
|
|
if(state==ISS_USED || has_timeout)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (state == ISS_RECEIVE_ENDPOINT)
|
|
{
|
|
char buf[51];
|
|
size_t rc = comm_pipe->Read(buf, endpoint_remaining);
|
|
if (rc == 0)
|
|
{
|
|
IScopedLock lock(mutex);
|
|
if (!connect_start)
|
|
{
|
|
has_timeout = true;
|
|
cleanup_pipes(true);
|
|
}
|
|
return;
|
|
}
|
|
buf[rc] = 0;
|
|
endpoint_remaining -= rc;
|
|
endpoint_name += buf;
|
|
|
|
if (endpoint_remaining == 0)
|
|
{
|
|
state = ISS_AUTH;
|
|
}
|
|
return;
|
|
}
|
|
|
|
std::string ret;
|
|
size_t rc;
|
|
rc=comm_pipe->Read(&ret);
|
|
|
|
if(rc==0
|
|
|| !tcpstack.AddData((char*)ret.c_str(), ret.size()))
|
|
{
|
|
if( state!=ISS_CONNECTING && state!=ISS_USED )
|
|
{
|
|
IScopedLock lock(mutex);
|
|
if(!connect_start)
|
|
{
|
|
has_timeout=true;
|
|
cleanup_pipes(true);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
char *buf;
|
|
size_t packetsize;
|
|
while((buf=tcpstack.getPacket(&packetsize))!=NULL)
|
|
{
|
|
CRData rd(buf, packetsize);
|
|
char id;
|
|
if(rd.getChar(&id))
|
|
{
|
|
switch(state)
|
|
{
|
|
case ISS_AUTH:
|
|
{
|
|
if(id==ID_ISC_AUTH || id==ID_ISC_AUTH_TOKEN
|
|
|| id==ID_ISC_AUTH2 || id==ID_ISC_AUTH_TOKEN2)
|
|
{
|
|
unsigned int iterations=static_cast<unsigned int>(pbkdf2_iterations);
|
|
if(id==ID_ISC_AUTH_TOKEN || id==ID_ISC_AUTH_TOKEN2)
|
|
{
|
|
iterations=1;
|
|
token_auth=true;
|
|
}
|
|
else
|
|
{
|
|
token_auth=false;
|
|
}
|
|
|
|
std::string hmac;
|
|
std::string errmsg;
|
|
std::string client_challenge;
|
|
std::string hmac_key;
|
|
|
|
if(rd.getStr(&clientname) && rd.getStr(&hmac) )
|
|
{
|
|
std::string token;
|
|
bool db_timeout=false;
|
|
if(id==ID_ISC_AUTH_TOKEN || id==ID_ISC_AUTH_TOKEN2)
|
|
{
|
|
unsigned int token_id;
|
|
if(rd.getUInt(&token_id) )
|
|
{
|
|
std::string cname;
|
|
authkey=getOnetimeToken(token_id, &cname);
|
|
if(authkey.empty())
|
|
{
|
|
errmsg="Token not found";
|
|
}
|
|
else if(cname!=clientname)
|
|
{
|
|
errmsg="Wrong token";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errmsg="Missing field -1";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
authkey=getAuthkeyFromDB(clientname, db_timeout);
|
|
}
|
|
|
|
std::string ecdh_pubkey;
|
|
if(id==ID_ISC_AUTH2)
|
|
{
|
|
if(!rd.getStr(&ecdh_pubkey) || ecdh_pubkey.empty())
|
|
{
|
|
errmsg="Missing field -2";
|
|
}
|
|
}
|
|
|
|
if(!rd.getStr(&client_challenge) || client_challenge.size()<32 )
|
|
{
|
|
errmsg="Client challenge missing or not long enough";
|
|
}
|
|
|
|
unsigned int client_iterations = iterations;
|
|
if(id!=ID_ISC_AUTH_TOKEN && id!=ID_ISC_AUTH_TOKEN2)
|
|
{
|
|
rd.getUInt(&client_iterations);
|
|
}
|
|
|
|
if(errmsg.empty() && !authkey.empty())
|
|
{
|
|
std::string salt = challenge+client_challenge;
|
|
|
|
if(id==ID_ISC_AUTH2)
|
|
{
|
|
std::string shared_key = ecdh_key_exchange->getSharedKey(ecdh_pubkey);
|
|
if (salt.empty())
|
|
{
|
|
errmsg = "Generating ECDH shared key failed";
|
|
}
|
|
salt+=shared_key;
|
|
}
|
|
|
|
hmac_key=crypto_fak->generateBinaryPasswordHash(authkey, salt, (std::max)(iterations, client_iterations));
|
|
|
|
std::string hmac_loc=crypto_fak->generateBinaryPasswordHash(hmac_key, challenge, 1);
|
|
if(hmac_loc==hmac
|
|
&& errmsg.empty())
|
|
{
|
|
if(id==ID_ISC_AUTH2 || id==ID_ISC_AUTH_TOKEN2)
|
|
{
|
|
if(id==ID_ISC_AUTH2)
|
|
{
|
|
delete ecdh_key_exchange;
|
|
ecdh_key_exchange=NULL;
|
|
}
|
|
|
|
is_pipe=new InternetServicePipe2(comm_pipe, hmac_key);
|
|
conn_version=2;
|
|
}
|
|
else
|
|
{
|
|
is_pipe=new InternetServicePipe(comm_pipe, hmac_key);
|
|
conn_version=1;
|
|
}
|
|
state=ISS_CAPA;
|
|
comm_pipe=is_pipe;
|
|
}
|
|
else if(errmsg.empty())
|
|
{
|
|
errmsg="Auth failed (Authkey/password wrong)";
|
|
}
|
|
}
|
|
else if(errmsg.empty())
|
|
{
|
|
if(db_timeout)
|
|
{
|
|
errmsg="Database timeout while looking for client";
|
|
}
|
|
else
|
|
{
|
|
if(!hasClient(clientname, db_timeout))
|
|
{
|
|
if(db_timeout)
|
|
{
|
|
errmsg="Database timeout while looking for client";
|
|
}
|
|
else
|
|
{
|
|
if(checkhtml(clientname))
|
|
{
|
|
errmsg="Unknown client ("+clientname+")";
|
|
}
|
|
else
|
|
{
|
|
Server->Log("HTML injection detected", LL_WARNING);
|
|
errmsg="Unknown client";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errmsg = "Empty authentication key";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errmsg="Missing fields";
|
|
}
|
|
|
|
CWData data;
|
|
if(!errmsg.empty())
|
|
{
|
|
if (!token_auth)
|
|
{
|
|
logFailedAuth(clientname, endpoint_name);
|
|
}
|
|
Server->Log("Authentication failed in InternetServiceConnector::ReceivePackets: "+errmsg
|
|
+ (token_auth ? " (token authentication)" :""), LL_INFO);
|
|
data.addChar(ID_ISC_AUTH_FAILED);
|
|
data.addString(errmsg);
|
|
}
|
|
else if(state==ISS_CAPA)
|
|
{
|
|
data.addChar(ID_ISC_AUTH_OK);
|
|
|
|
std::string hmac_loc;
|
|
hmac_loc=crypto_fak->generateBinaryPasswordHash(hmac_key, client_challenge, 1);
|
|
data.addString(hmac_loc);
|
|
|
|
std::string new_token=generateOnetimeToken(clientname);
|
|
data.addString(is_pipe->encrypt(new_token));
|
|
}
|
|
tcpstack.Send(cs, data);
|
|
}
|
|
}break;
|
|
case ISS_CAPA:
|
|
{
|
|
if(id==ID_ISC_CAPA)
|
|
{
|
|
unsigned int capa;
|
|
if( rd.getUInt(&capa) )
|
|
{
|
|
comm_pipe=cs;
|
|
std::string capa_debug_str;
|
|
if(capa & IPC_ENCRYPTED )
|
|
{
|
|
is_pipe->setBackendPipe(comm_pipe);
|
|
comm_pipe=is_pipe;
|
|
capa_debug_str += std::string("encrypted-") + (conn_version==2 ? "v2" : "v1");
|
|
}
|
|
if (capa & IPC_COMPRESSED_ZSTD)
|
|
{
|
|
#ifndef NO_ZSTD_COMPRESSION
|
|
if (conn_version == 2)
|
|
{
|
|
comp_pipe = new CompressedPipeZstd(comm_pipe, compression_level, -1);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Unknown connection version " + convert((int)conn_version) + " in state ISS_CAPA -1", LL_ERROR);
|
|
assert(false);
|
|
}
|
|
|
|
comm_pipe = comp_pipe;
|
|
|
|
if (!capa_debug_str.empty()) capa_debug_str += ", ";
|
|
capa_debug_str += "compressed-zstd";
|
|
#else
|
|
Server->Log("Server does not support zstd compression in ISS_CAPA", LL_ERROR);
|
|
#endif
|
|
}
|
|
else if(capa & IPC_COMPRESSED )
|
|
{
|
|
if(conn_version==1)
|
|
{
|
|
comp_pipe=new CompressedPipe(comm_pipe, compression_level);
|
|
}
|
|
else if(conn_version==2)
|
|
{
|
|
comp_pipe=new CompressedPipe2(comm_pipe, compression_level);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Unknown connection version " + convert((int)conn_version) + " in state ISS_CAPA", LL_ERROR);
|
|
assert(false);
|
|
}
|
|
|
|
comm_pipe=comp_pipe;
|
|
|
|
if (!capa_debug_str.empty()) capa_debug_str += ", ";
|
|
capa_debug_str += std::string("compressed-") + (conn_version == 2 ? "v2" : "v1");
|
|
}
|
|
|
|
|
|
size_t spare_connections_num;
|
|
|
|
bool wakeup_new_client = false;
|
|
{
|
|
IScopedLock lock(mutex);
|
|
SClientData& curr_client_data = client_data[clientname];
|
|
if (curr_client_data.last_seen == -1
|
|
&& backup_server!=NULL)
|
|
{
|
|
wakeup_new_client = true;
|
|
}
|
|
curr_client_data.spare_connections.push_back(this);
|
|
curr_client_data.last_seen=Server->getTimeMS();
|
|
curr_client_data.endpoint_name = endpoint_name;
|
|
|
|
spare_connections_num = curr_client_data.spare_connections.size();
|
|
}
|
|
if (wakeup_new_client)
|
|
backup_server->wakeupNewClient();
|
|
|
|
if (token_auth)
|
|
{
|
|
if (!capa_debug_str.empty()) capa_debug_str += ", ";
|
|
capa_debug_str += "token auth";
|
|
}
|
|
|
|
Server->Log("Authed+capa for client '"+clientname+"' "
|
|
+"("+ capa_debug_str+")"
|
|
+" - "+convert(spare_connections_num)+" spare connections", LL_DEBUG);
|
|
|
|
state=ISS_AUTHED;
|
|
}
|
|
}
|
|
}break;
|
|
case ISS_AUTHED:
|
|
{
|
|
if(id==ID_ISC_PONG)
|
|
{
|
|
pinging=false;
|
|
IScopedLock lock(mutex);
|
|
client_data[clientname].last_seen=Server->getTimeMS();
|
|
}
|
|
}break;
|
|
case ISS_CONNECTING:
|
|
{
|
|
if(id==ID_ISC_CONNECT_OK)
|
|
{
|
|
state=ISS_USED;
|
|
|
|
{
|
|
IScopedLock lock(mutex);
|
|
client_data[clientname].last_seen=Server->getTimeMS();
|
|
}
|
|
|
|
IScopedLock lock(local_mutex);
|
|
is_connected=true;
|
|
if(connection_done_cond!=NULL)
|
|
{
|
|
connection_done_cond->notify_all();
|
|
}
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
delete []buf;
|
|
}
|
|
}
|
|
|
|
void InternetServiceConnector::init_mutex(void)
|
|
{
|
|
mutex=Server->createMutex();
|
|
onetime_token_mutex=Server->createMutex();
|
|
|
|
IDatabase* db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
|
|
if (db != NULL)
|
|
{
|
|
db_results res = db->Read("SELECT value FROM settings_db.settings WHERE key='internet_expect_endpoint' AND clientid=0");
|
|
if (!res.empty())
|
|
{
|
|
std::vector<std::string> toks;
|
|
Tokenize(res[0]["value"], toks, ",;|");
|
|
for (size_t i = 0; i < toks.size(); ++i)
|
|
{
|
|
internet_expect_endpoint.insert(toks[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InternetServiceConnector::destroy_mutex(void)
|
|
{
|
|
Server->destroy(mutex);
|
|
mutex=NULL;
|
|
Server->destroy(onetime_token_mutex);
|
|
}
|
|
|
|
IPipe *InternetServiceConnector::getConnection(const std::string &clientname, char service, int timeoutms)
|
|
{
|
|
|
|
int64 starttime=Server->getTimeMS();
|
|
do
|
|
{
|
|
IScopedLock lock(mutex);
|
|
std::map<std::string, SClientData>::iterator iter=client_data.find(clientname);
|
|
if(iter==client_data.end())
|
|
return NULL;
|
|
|
|
if(iter->second.spare_connections.empty())
|
|
{
|
|
lock.relock(NULL);
|
|
Server->wait(100);
|
|
}
|
|
else
|
|
{
|
|
InternetServiceConnector *isc=iter->second.spare_connections.back();
|
|
|
|
if(!isc->connectStart())
|
|
{
|
|
Server->Log("Connecting on internet connection failed (1). Service="+convert((int)service), LL_DEBUG);
|
|
continue;
|
|
}
|
|
|
|
iter->second.spare_connections.pop_back();
|
|
|
|
lock.relock(NULL);
|
|
|
|
int64 rtime=Server->getTimeMS()-starttime;
|
|
if((int)rtime<timeoutms)
|
|
rtime=timeoutms-rtime;
|
|
else
|
|
rtime=0;
|
|
|
|
if(rtime<100) rtime=100;
|
|
|
|
if(!isc->Connect(service, static_cast<int>(rtime)))
|
|
{
|
|
//Automatically freed
|
|
Server->Log("Connecting on internet connection failed. Service="+convert((int)service), LL_DEBUG);
|
|
}
|
|
else
|
|
{
|
|
IPipe *ret=isc->getISPipe();
|
|
isc->freeConnection(); //deletes ics
|
|
|
|
CompressedPipe *comp_pipe;
|
|
CompressedPipe2 *comp_pipe2;
|
|
#ifndef NO_ZSTD_COMPRESSION
|
|
CompressedPipeZstd* comp_zstd;
|
|
if ((comp_zstd = dynamic_cast<CompressedPipeZstd*>(ret)) != NULL)
|
|
{
|
|
InternetServicePipe2 *isc_pipe2 = dynamic_cast<InternetServicePipe2*>(comp_zstd->getRealPipe());
|
|
if (isc_pipe2 != NULL)
|
|
{
|
|
isc_pipe2->destroyBackendPipeOnDelete(true);
|
|
}
|
|
comp_zstd->destroyBackendPipeOnDelete(true);
|
|
}
|
|
else
|
|
#endif
|
|
if((comp_pipe2 = dynamic_cast<CompressedPipe2*>(ret))!=NULL)
|
|
{
|
|
InternetServicePipe2 *isc_pipe2=dynamic_cast<InternetServicePipe2*>(comp_pipe2->getRealPipe());
|
|
if(isc_pipe2!=NULL)
|
|
{
|
|
isc_pipe2->destroyBackendPipeOnDelete(true);
|
|
}
|
|
comp_pipe2->destroyBackendPipeOnDelete(true);
|
|
}
|
|
else if((comp_pipe = dynamic_cast<CompressedPipe*>(ret) )!=NULL)
|
|
{
|
|
InternetServicePipe *isc_pipe=dynamic_cast<InternetServicePipe*>(comp_pipe->getRealPipe());
|
|
if(isc_pipe!=NULL)
|
|
{
|
|
isc_pipe->destroyBackendPipeOnDelete(true);
|
|
}
|
|
comp_pipe->destroyBackendPipeOnDelete(true);
|
|
}
|
|
else
|
|
{
|
|
InternetServicePipe *isc_pipe=dynamic_cast<InternetServicePipe*>(ret);
|
|
if(isc_pipe!=NULL)
|
|
{
|
|
isc_pipe->destroyBackendPipeOnDelete(true);
|
|
}
|
|
InternetServicePipe2 *isc_pipe2=dynamic_cast<InternetServicePipe2*>(ret);
|
|
if(isc_pipe2!=NULL)
|
|
{
|
|
isc_pipe2->destroyBackendPipeOnDelete(true);
|
|
}
|
|
}
|
|
Server->Log("Established internet connection. Service="+convert((int)service), LL_DEBUG);
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
}while(timeoutms==-1 || Server->getTimeMS()-starttime<(unsigned int)timeoutms);
|
|
|
|
Server->Log("Establishing internet connection failed. Service="+convert((int)service), LL_DEBUG);
|
|
return NULL;
|
|
}
|
|
|
|
bool InternetServiceConnector::wantReceive(void)
|
|
{
|
|
if(has_timeout)
|
|
return false;
|
|
|
|
if(state!=ISS_USED )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool InternetServiceConnector::closeSocket(void)
|
|
{
|
|
if(free_connection)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool InternetServiceConnector::connectStart()
|
|
{
|
|
if(has_timeout)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
connect_start=true;
|
|
return true;
|
|
}
|
|
|
|
bool InternetServiceConnector::Connect(char service, int timems)
|
|
{
|
|
IScopedLock lock(local_mutex);
|
|
|
|
connection_done_cond=Server->createCondition();
|
|
ObjectScope connection_done_cond_scope(connection_done_cond);
|
|
|
|
target_service=service;
|
|
do_connect=true;
|
|
|
|
connection_done_cond->wait(&lock, timems);
|
|
|
|
if(!is_connected)
|
|
{
|
|
connection_done_cond=NULL;
|
|
stop_connecting=true;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
IPipe *InternetServiceConnector::getISPipe(void)
|
|
{
|
|
IScopedLock lock(local_mutex);
|
|
return comm_pipe;
|
|
}
|
|
|
|
void InternetServiceConnector::stopConnecting(void)
|
|
{
|
|
stop_connecting=true;
|
|
}
|
|
|
|
void InternetServiceConnector::freeConnection(void)
|
|
{
|
|
free_connection=true;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string> > InternetServiceConnector::getOnlineClients(void)
|
|
{
|
|
std::vector<std::pair<std::string, std::string> > ret;
|
|
|
|
IScopedLock lock(mutex);
|
|
int64 ct=Server->getTimeMS();
|
|
|
|
if(ct-last_token_remove>30*60*1000)
|
|
{
|
|
removeOldTokens();
|
|
last_token_remove=ct;
|
|
}
|
|
|
|
std::vector<std::string> todel;
|
|
for(std::map<std::string, SClientData>::iterator it=client_data.begin();it!=client_data.end();++it)
|
|
{
|
|
if(!it->second.spare_connections.empty())
|
|
{
|
|
if(ct-it->second.last_seen<offline_timeout)
|
|
{
|
|
ret.push_back(std::make_pair(it->first, it->second.endpoint_name));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ct-it->second.last_seen>=establish_timeout)
|
|
{
|
|
todel.push_back(it->first);
|
|
}
|
|
else if (ct - it->second.last_seen < 10000)
|
|
{
|
|
ret.push_back(std::make_pair(it->first, it->second.endpoint_name));
|
|
}
|
|
}
|
|
}
|
|
|
|
for(size_t i=0;i<todel.size();++i)
|
|
{
|
|
std::map<std::string, SClientData>::iterator it=client_data.find(todel[i]);
|
|
Server->Log("Establish timeout: Deleting internet client \""+it->first+"\"", LL_DEBUG);
|
|
while(!it->second.spare_connections.empty())
|
|
{
|
|
InternetServiceConnector *isc=it->second.spare_connections.back();
|
|
it->second.spare_connections.pop_back();
|
|
isc->connectStart();
|
|
isc->stopConnecting();
|
|
}
|
|
client_data.erase(it);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
std::string InternetServiceConnector::generateOnetimeToken(const std::string &clientname)
|
|
{
|
|
SOnetimeToken token(clientname);
|
|
unsigned int token_id;
|
|
{
|
|
IScopedLock lock(onetime_token_mutex);
|
|
token_id=onetime_token_id++;
|
|
onetime_tokens.insert(std::pair<unsigned int, SOnetimeToken>(token_id, token));
|
|
}
|
|
std::string ret;
|
|
ret.resize(sizeof(unsigned int)+token.token.size());
|
|
token_id=little_endian(token_id);
|
|
memcpy(&ret[0], &token_id, sizeof(unsigned int));
|
|
memcpy(&ret[sizeof(unsigned int)], token.token.data(), token.token.size());
|
|
return ret;
|
|
}
|
|
|
|
std::string InternetServiceConnector::getOnetimeToken(unsigned int id, std::string *cname)
|
|
{
|
|
IScopedLock lock(onetime_token_mutex);
|
|
std::map<unsigned int, SOnetimeToken>::iterator iter=onetime_tokens.find(id);
|
|
if(iter!=onetime_tokens.end())
|
|
{
|
|
*cname=iter->second.clientname;
|
|
std::string token=iter->second.token;
|
|
onetime_tokens.erase(iter);
|
|
return token;
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
std::string InternetServiceConnector::getAuthkeyFromDB(const std::string &clientname, bool &db_timeout)
|
|
{
|
|
IDatabase *db=Server->getDatabase(tid, URBACKUPDB_SERVER);
|
|
|
|
if (next(clientname, 0, restore_prefix))
|
|
{
|
|
IQuery *q = db->Prepare("SELECT value FROM settings_db.settings WHERE key='restore_authkey' AND clientid=0", false);
|
|
int timeoutms = 1000;
|
|
db_results res = q->Read(&timeoutms);
|
|
db->destroyQuery(q);
|
|
if (!res.empty())
|
|
{
|
|
client_ping_interval = 30*1000;
|
|
db_timeout = false;
|
|
return res[0]["value"];
|
|
}
|
|
else if (timeoutms == 1)
|
|
{
|
|
db_timeout = true;
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
|
|
IQuery *q=db->Prepare("SELECT value FROM settings_db.settings WHERE key='internet_authkey' AND clientid=(SELECT id FROM clients WHERE name=?)", false);
|
|
q->Bind(clientname);
|
|
int timeoutms=1000;
|
|
db_results res=q->Read(&timeoutms);
|
|
db->destroyQuery(q);
|
|
if(!res.empty())
|
|
{
|
|
db_timeout=false;
|
|
return (res[0]["value"]);
|
|
}
|
|
else if(timeoutms==1)
|
|
{
|
|
db_timeout=true;
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
|
|
bool InternetServiceConnector::hasClient(const std::string &clientname, bool &db_timeout)
|
|
{
|
|
IDatabase *db=Server->getDatabase(tid, URBACKUPDB_SERVER);
|
|
IQuery *q=db->Prepare("SELECT id FROM clients WHERE name=?", false);
|
|
q->Bind(clientname);
|
|
int timeoutms=1000;
|
|
db_results res=q->Read(&timeoutms);
|
|
db->destroyQuery(q);
|
|
if(timeoutms==1)
|
|
{
|
|
db_timeout=true;
|
|
return false;
|
|
}
|
|
|
|
return !res.empty();
|
|
}
|
|
|
|
void InternetServiceConnector::removeOldTokens(void)
|
|
{
|
|
IScopedLock lock(onetime_token_mutex);
|
|
int64 tt=Server->getTimeMS();
|
|
std::vector<unsigned int> todel;
|
|
for(std::map<unsigned int, SOnetimeToken>::iterator it=onetime_tokens.begin();it!=onetime_tokens.end();++it)
|
|
{
|
|
if(tt-it->second.created>1*60*60*1000)
|
|
{
|
|
todel.push_back(it->first);
|
|
}
|
|
}
|
|
|
|
for(size_t i=0;i<todel.size();++i)
|
|
{
|
|
std::map<unsigned int, SOnetimeToken>::iterator iter=onetime_tokens.find(todel[i]);
|
|
if(iter!=onetime_tokens.end())
|
|
{
|
|
onetime_tokens.erase(iter);
|
|
}
|
|
}
|
|
}
|