Ability to connect via http connect proxy to internet server

This commit is contained in:
Martin 2019-03-18 15:53:30 +01:00
parent 0e37e77148
commit c63aafeb9a
17 changed files with 913 additions and 32 deletions

View File

@ -304,6 +304,7 @@
<ClCompile Include="OutputStream.cpp" />
<ClCompile Include="PipeThrottler.cpp" />
<ClCompile Include="Query.cpp" />
<ClCompile Include="SChannelPipe.cpp" />
<ClCompile Include="SelectThread.cpp" />
<ClCompile Include="Server.cpp" />
<ClCompile Include="ServerWin32.cpp" />
@ -352,6 +353,7 @@
<ClInclude Include="OutputStream.h" />
<ClInclude Include="PipeThrottler.h" />
<ClInclude Include="Query.h" />
<ClInclude Include="SChannelPipe.h" />
<ClInclude Include="SelectThread.h" />
<ClInclude Include="Server.h" />
<ClInclude Include="ServiceAcceptor.h" />

View File

@ -159,6 +159,9 @@
<ClCompile Include="Mutex_std.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SChannelPipe.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AcceptThread.h">
@ -374,5 +377,8 @@
<ClInclude Include="Condition_std.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SChannelPipe.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -143,6 +143,7 @@ public:
virtual void StartCustomStreamService(IService *pService, std::string pServiceName, unsigned short pPort, int pMaxClientsPerThread=-1, BindTarget bindTarget=BindTarget_All)=0;
virtual IPipe* ConnectStream(std::string pServer, unsigned short pPort, unsigned int pTimeoutms=0)=0;
virtual IPipe* ConnectSslStream(const std::string& pServer, unsigned short pPort, unsigned int pTimeoutms) = 0;
virtual IPipe *PipeFromSocket(SOCKET pSocket)=0;
virtual void DisconnectStream(IPipe *pipe)=0;
virtual std::string LookupHostname(const std::string& pIp)=0;

560
SChannelPipe.cpp Normal file
View File

@ -0,0 +1,560 @@
#include "SChannelPipe.h"
#include "Server.h"
#include "stringtools.h"
#include <ntsecapi.h>
#include <sspi.h>
#include <schnlsp.h>
PSecurityFunctionTableW SChannelPipe::sec = NULL;
SChannelPipe::SChannelPipe(CStreamPipe * bpipe)
: bpipe(bpipe), has_cred_handle(false),
has_ctxt_handle(false), decbuf_pos(0),
sendbuf_pos(0), last_flush_time(0),
has_error(false)
{
}
SChannelPipe::~SChannelPipe()
{
if (has_cred_handle)
{
sec->FreeCredentialsHandle(&cred_handle);
}
if (has_ctxt_handle)
{
sec->DeleteSecurityContext(&ctxt_handle);
}
delete bpipe;
}
bool SChannelPipe::ssl_connect(const std::string& p_hostname, int timeoutms)
{
hostname = p_hostname;
int64 starttime = Server->getTimeMS();
SCHANNEL_CRED cred_data = {};
cred_data.dwVersion = SCHANNEL_CRED_VERSION;
cred_data.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN;
cred_data.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
HRESULT res = sec->AcquireCredentialsHandleW(NULL, UNISP_NAME_W,
SECPKG_CRED_OUTBOUND, NULL, &cred_data, NULL, NULL, &cred_handle, &time_stamp);
if (res != SEC_E_OK)
{
Server->Log("AcquireCredentialsHandleW failed with result " + convert((int64)res), LL_ERROR);
return false;
}
has_cred_handle = true;
SecBuffer outbuf = {};
outbuf.BufferType = SECBUFFER_EMPTY;
SecBufferDesc outbuf_desc;
outbuf_desc.ulVersion = SECBUFFER_VERSION;
outbuf_desc.cBuffers = 1;
outbuf_desc.pBuffers = &outbuf;
std::wstring hostname_w = Server->ConvertToWchar(hostname);
unsigned long flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_STREAM;
unsigned long ret_flags = 0;
res = sec->InitializeSecurityContextW(&cred_handle, NULL, const_cast<wchar_t*>(hostname_w.c_str()), flags,
0, 0, NULL, 0, &ctxt_handle, &outbuf_desc, &ret_flags, &time_stamp);
if (res != SEC_I_CONTINUE_NEEDED)
{
Server->Log("InitializeSecurityContextW failed with result " + convert((int64)res), LL_ERROR);
return false;
}
has_ctxt_handle = true;
if (flags != ret_flags)
{
Server->Log("Setting security context flags failed " + convert((int64)flags) + "!=" + convert((int64)ret_flags), LL_ERROR);
return false;
}
if (!bpipe->Write(reinterpret_cast<char*>(outbuf.pvBuffer), outbuf.cbBuffer, timeoutms, true))
{
return false;
}
last_flush_time = Server->getTimeMS();
int64 passed_time = last_flush_time - starttime;
int64 remaining_time = timeoutms == -1 ? -1 : (passed_time < timeoutms ? (timeoutms - passed_time) : 0);
return ssl_connect_negotiate(static_cast<int>(remaining_time), true);
}
bool SChannelPipe::ssl_connect_negotiate(int timeoutms, bool do_read)
{
const size_t encbuf_size_incr = 4096;
bool connected = false;
int64 starttime = Server->getTimeMS();
unsigned long flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
ISC_REQ_STREAM;
unsigned long ret_flags;
std::wstring hostname_w = Server->ConvertToWchar(hostname);
int64 passed_time;
encbuf_pos = 0;
while (timeoutms == -1
|| (passed_time = Server->getTimeMS() - starttime)<timeoutms)
{
if (timeoutms == -1)
passed_time = Server->getTimeMS() - starttime;
if (do_read)
{
do_read = false;
if (!bpipe->isReadable(timeoutms == -1 ? timeoutms : (timeoutms - passed_time)))
return false;
if (encbuf.size() - encbuf_pos < encbuf_size_incr)
{
encbuf.resize(encbuf.size() + encbuf_size_incr);
}
size_t read = bpipe->Read(&encbuf[encbuf_pos], encbuf_size_incr, 0);
if (read == 0)
return false;
encbuf_pos += read;
}
SecBuffer inbuf2[2] = {};
inbuf2[0].BufferType = SECBUFFER_TOKEN;
inbuf2[0].cbBuffer = encbuf_pos;
inbuf2[0].pvBuffer = encbuf.data();
inbuf2[1].BufferType = SECBUFFER_EMPTY;
SecBufferDesc inbuf2_desc;
inbuf2_desc.cBuffers = 2;
inbuf2_desc.pBuffers = inbuf2;
inbuf2_desc.ulVersion = SECBUFFER_VERSION;
SecBuffer outbuf[3] = {};
outbuf[0].BufferType = SECBUFFER_TOKEN;
outbuf[1].BufferType = SECBUFFER_ALERT;
outbuf[2].BufferType = SECBUFFER_EMPTY;
SecBufferDesc outbuf_desc;
outbuf_desc.cBuffers = 3;
outbuf_desc.pBuffers = outbuf;
outbuf_desc.ulVersion = SECBUFFER_VERSION;
HRESULT res = sec->InitializeSecurityContextW(&cred_handle, &ctxt_handle,
const_cast<wchar_t*>(hostname_w.c_str()), flags, 0, 0, &inbuf2_desc,
0, NULL, &outbuf_desc, &ret_flags, &time_stamp);
if (res == SEC_E_INCOMPLETE_MESSAGE)
{
do_read = true;
continue;
}
else
{
if (inbuf2[1].BufferType == SECBUFFER_EXTRA
&& inbuf2[1].cbBuffer>0)
{
encbuf.erase(encbuf.begin(), encbuf.begin() + (encbuf_pos - inbuf2[1].cbBuffer));
encbuf_pos -= encbuf_pos - inbuf2[1].cbBuffer;
if (res == SEC_I_CONTINUE_NEEDED)
{
continue;
}
}
else
{
encbuf.clear();
encbuf_pos = 0;
}
}
if (res == SEC_E_OK
|| res == SEC_I_CONTINUE_NEEDED)
{
bool has_error = false;
for (size_t i = 0; i < outbuf_desc.cBuffers; ++i)
{
SecBuffer& buf = outbuf_desc.pBuffers[i];
if (buf.BufferType == SECBUFFER_TOKEN
&& buf.cbBuffer > 0
&& !has_error)
{
passed_time = Server->getTimeMS() - starttime;
int64 remaining_time = timeoutms == -1 ? -1 : (passed_time < timeoutms ? (timeoutms - passed_time) : 0);
if (!bpipe->Write(reinterpret_cast<char*>(buf.pvBuffer), buf.cbBuffer, static_cast<int>(remaining_time), true))
{
has_error = true;
}
}
if (buf.pvBuffer != NULL)
sec->FreeContextBuffer(buf.pvBuffer);
}
if (has_error)
return false;
if (res == SEC_E_OK)
{
connected = true;
break;
}
else
{
do_read = true;
}
}
else
{
return false;
}
}
if (connected)
{
HRESULT res = sec->QueryContextAttributesW(&ctxt_handle, SECPKG_ATTR_STREAM_SIZES, &stream_sizes);
if (res != SEC_E_OK)
{
return false;
}
header_buf.resize(stream_sizes.cbHeader);
trailer_buf.resize(stream_sizes.cbTrailer);
}
return connected;
}
void SChannelPipe::init()
{
HMODULE mod = LoadLibraryW(L"secur32.dll");
if (mod == NULL)
{
Server->Log("Error loading libarary secur32.dll. Errno: " + convert((int64)GetLastError()), LL_ERROR);
return;
}
INIT_SECURITY_INTERFACE_W init_sec = reinterpret_cast<INIT_SECURITY_INTERFACE_W>(GetProcAddress(mod, "InitSecurityInterfaceW"));
if (init_sec == NULL)
{
Server->Log("Error getting proc InitSecurityInterfaceW in secur32.dll. Errno: " + convert((int64)GetLastError()), LL_ERROR);
return;
}
sec = init_sec();
}
size_t SChannelPipe::Read(char * buffer, size_t bsize, int timeoutms)
{
const size_t encbuf_size_incr = 1024;
if (has_error)
return 0;
if (decbuf_pos>0)
{
size_t toread = (std::min)(decbuf_pos, bsize);
memcpy(buffer, decbuf.data(), toread);
decbuf.erase(decbuf.begin(), decbuf.begin() + toread);
decbuf_pos -= toread;
return toread;
}
int64 starttime = Server->getTimeMS();
if (encbuf.size() - encbuf_pos < bsize)
{
encbuf.resize(encbuf.size() + bsize);
}
size_t read = bpipe->Read(&encbuf[encbuf_pos], bsize, timeoutms);
if (read == 0)
return 0;
encbuf_pos += read;
size_t orig_bsize = bsize;
HRESULT res = SEC_E_OK;
while ( (res == SEC_E_OK || res== SEC_E_INCOMPLETE_MESSAGE)
&& encbuf_pos > 0 && bsize>0)
{
if (res == SEC_E_INCOMPLETE_MESSAGE)
{
if (encbuf.size() - encbuf_pos < encbuf_size_incr)
{
encbuf.resize(encbuf.size() + encbuf_size_incr);
}
int64 passed_time = Server->getTimeMS() - starttime;
int remaining_time = timeoutms == -1 ? -1 : ((timeoutms - passed_time) < 0 ? 0 : (timeoutms - passed_time));
size_t read = bpipe->Read(&encbuf[encbuf_pos], encbuf_size_incr, remaining_time);
if (read == 0)
return 0;
encbuf_pos += read;
}
SecBuffer inbuf[4] = {};
inbuf[0].BufferType = SECBUFFER_DATA;
inbuf[0].pvBuffer = encbuf.data();
inbuf[0].cbBuffer = encbuf_pos;
inbuf[1].BufferType = SECBUFFER_EMPTY;
inbuf[2].BufferType = SECBUFFER_EMPTY;
inbuf[3].BufferType = SECBUFFER_EMPTY;
SecBufferDesc inbuf_desc;
inbuf_desc.ulVersion = SECBUFFER_VERSION;
inbuf_desc.cBuffers = 4;
inbuf_desc.pBuffers = inbuf;
res = sec->DecryptMessage(&ctxt_handle, &inbuf_desc, 0, NULL);
if (res == SEC_E_OK
|| res== SEC_I_RENEGOTIATE)
{
if (inbuf[1].BufferType == SECBUFFER_DATA
&& inbuf[1].cbBuffer>0)
{
size_t toread = 0;
if (bsize > 0)
{
toread = (std::min)(bsize, (size_t)inbuf[1].cbBuffer);
memcpy(buffer, inbuf[1].pvBuffer, toread);
inbuf[1].cbBuffer -= toread;
bsize -= toread;
buffer += toread;
}
if (inbuf[1].cbBuffer > 0)
{
if (decbuf.size() - decbuf_pos < inbuf[1].cbBuffer)
{
decbuf.resize(decbuf.size() + inbuf[1].cbBuffer);
}
memcpy(&decbuf[decbuf_pos], reinterpret_cast<char*>(inbuf[1].pvBuffer) + toread, inbuf[1].cbBuffer);
decbuf_pos += inbuf[1].cbBuffer;
}
}
if (inbuf[3].BufferType == SECBUFFER_EXTRA
&& inbuf[3].cbBuffer>0)
{
encbuf.erase(encbuf.begin(), encbuf.begin() + (encbuf_pos - inbuf[3].cbBuffer));
encbuf_pos -= encbuf_pos - inbuf[3].cbBuffer;
}
else
{
encbuf.clear();
encbuf_pos = 0;
}
}
if (res == SEC_I_RENEGOTIATE)
{
if (!ssl_connect_negotiate(timeoutms, false))
{
return 0;
}
}
if (res == SEC_I_CONTEXT_EXPIRED)
{
return 0;
}
}
return orig_bsize-bsize;
}
bool SChannelPipe::Write(const char * buffer, size_t bsize, int timeoutms, bool flush)
{
if (has_error)
return false;
if (sendbuf_pos + bsize > sendbuf.size())
{
sendbuf.resize(sendbuf_pos + bsize);
}
memcpy(&sendbuf[sendbuf_pos], buffer, bsize);
sendbuf_pos += bsize;
if ((flush || sendbuf_pos>128 * 1024 || sendbuf_pos>= stream_sizes.cbMaximumMessage || (Server->getTimeMS() - last_flush_time)>200)
&& sendbuf_pos>0)
{
return Flush(timeoutms);
}
return true;
}
size_t SChannelPipe::Read(std::string * ret, int timeoutms)
{
if (has_error)
return 0;
char buf[1024];
size_t read = Read(buf, sizeof(buf), timeoutms);
if (read > 0)
ret->assign(buf, read);
return read;
}
bool SChannelPipe::Write(const std::string & str, int timeoutms, bool flush)
{
return Write(str.data(), str.size(), timeoutms, flush);
}
bool SChannelPipe::Flush(int timeoutms)
{
if (has_error)
return false;
size_t sendbuf_off = 0;
while (sendbuf_pos- sendbuf_off> 0)
{
size_t toflush = (std::min)((size_t)stream_sizes.cbMaximumMessage, sendbuf_pos- sendbuf_off);
SecBuffer outbuf[4] = {};
outbuf[0].BufferType = SECBUFFER_STREAM_HEADER;
outbuf[0].cbBuffer = header_buf.size();
outbuf[0].pvBuffer = header_buf.data();
outbuf[1].BufferType = SECBUFFER_DATA;
outbuf[1].cbBuffer = toflush;
outbuf[1].pvBuffer = &sendbuf[sendbuf_off];
outbuf[2].BufferType = SECBUFFER_STREAM_TRAILER;
outbuf[2].cbBuffer = trailer_buf.size();
outbuf[2].pvBuffer = trailer_buf.data();
outbuf[3].BufferType = SECBUFFER_EMPTY;
SecBufferDesc outbuf_desc;
outbuf_desc.ulVersion = SECBUFFER_VERSION;
outbuf_desc.cBuffers = 4;
outbuf_desc.pBuffers = outbuf;
HRESULT res = sec->EncryptMessage(&ctxt_handle, 0, &outbuf_desc, 0);
if (res != SEC_E_OK)
{
return false;
}
if (outbuf[0].cbBuffer > 0)
{
if (!bpipe->Write(reinterpret_cast<char*>(outbuf[0].pvBuffer), outbuf[0].cbBuffer, timeoutms, false))
{
has_error = true;
return false;
}
}
if (outbuf[1].cbBuffer > 0)
{
if (!bpipe->Write(reinterpret_cast<char*>(outbuf[1].pvBuffer), outbuf[1].cbBuffer, timeoutms, false))
{
has_error = true;
return false;
}
}
if (outbuf[2].cbBuffer > 0)
{
if (!bpipe->Write(reinterpret_cast<char*>(outbuf[2].pvBuffer), outbuf[2].cbBuffer, timeoutms, false))
{
has_error = true;
return false;
}
}
sendbuf_off += toflush;
}
sendbuf_pos -= sendbuf_off;
return bpipe->Flush(timeoutms);
}
bool SChannelPipe::isWritable(int timeoutms)
{
if (has_error)
return false;
return bpipe->isWritable(timeoutms);
}
bool SChannelPipe::isReadable(int timeoutms)
{
if (has_error)
return false;
return bpipe->isReadable(timeoutms);
}
bool SChannelPipe::hasError(void)
{
return has_error || bpipe->hasError();
}
void SChannelPipe::shutdown(void)
{
bpipe->shutdown();
}
size_t SChannelPipe::getNumElements(void)
{
return bpipe->getNumElements();
}
void SChannelPipe::addThrottler(IPipeThrottler * throttler)
{
bpipe->addThrottler(throttler);
}
void SChannelPipe::addOutgoingThrottler(IPipeThrottler * throttler)
{
bpipe->addOutgoingThrottler(throttler);
}
void SChannelPipe::addIncomingThrottler(IPipeThrottler * throttler)
{
bpipe->addIncomingThrottler(throttler);
}
_i64 SChannelPipe::getTransferedBytes(void)
{
return bpipe->getTransferedBytes();
}
void SChannelPipe::resetTransferedBytes(void)
{
bpipe->resetTransferedBytes();
}

76
SChannelPipe.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include "Interface/Pipe.h"
#include "StreamPipe.h"
#define SECURITY_WIN32
#include <Windows.h>
#include <security.h>
class SChannelPipe : public IPipe
{
public:
SChannelPipe(CStreamPipe* bpipe);
~SChannelPipe();
bool ssl_connect(const std::string& p_hostname, int timeoutms);
static void init();
// Inherited via IPipe
virtual size_t Read(char * buffer, size_t bsize, int timeoutms = -1);
virtual bool Write(const char * buffer, size_t bsize, int timeoutms = -1, bool flush = true);
virtual size_t Read(std::string * ret, int timeoutms = -1);
virtual bool Write(const std::string & str, int timeoutms = -1, bool flush = true);
virtual bool Flush(int timeoutms = -1);
virtual bool isWritable(int timeoutms = 0);
virtual bool isReadable(int timeoutms = 0);
virtual bool hasError(void);
virtual void shutdown(void);
virtual size_t getNumElements(void);
virtual void addThrottler(IPipeThrottler * throttler);
virtual void addOutgoingThrottler(IPipeThrottler * throttler);
virtual void addIncomingThrottler(IPipeThrottler * throttler);
virtual _i64 getTransferedBytes(void);
virtual void resetTransferedBytes(void);
private:
bool ssl_connect_negotiate(int timeoutms, bool do_read);
CStreamPipe* bpipe;
static PSecurityFunctionTableW sec;
bool has_cred_handle;
CredHandle cred_handle;
TimeStamp time_stamp;
bool has_ctxt_handle;
CtxtHandle ctxt_handle;
std::vector<char> encbuf;
size_t encbuf_pos;
std::vector<char> decbuf;
size_t decbuf_pos;
std::vector<char> sendbuf;
size_t sendbuf_pos;
std::string hostname;
int64 last_flush_time;
std::vector<char> header_buf;
std::vector<char> trailer_buf;
bool has_error;
SecPkgContext_StreamSizes stream_sizes;
};

View File

@ -57,8 +57,7 @@
#include "PipeThrottler.h"
#include "mt19937ar.h"
#include "Query.h"
#include "SChannelPipe.h"
#ifdef _WIN32
#include <condition_variable>
@ -91,6 +90,7 @@
#include <mach/mach.h>
#endif
const size_t SEND_BLOCKSIZE=8192;
const size_t MAX_THREAD_ID=std::string::npos;
@ -207,6 +207,8 @@ void CServer::setup(void)
#ifdef MODE_WIN
File::init_mutex();
#endif
SChannelPipe::init();
}
void CServer::destroyAllDatabases(void)
@ -1137,6 +1139,28 @@ IPipe* CServer::ConnectStream(std::string pServer, unsigned short pPort, unsigne
}
}
IPipe * CServer::ConnectSslStream(const std::string & pServer, unsigned short pPort, unsigned int pTimeoutms)
{
int64 starttime = Server->getTimeMS();
CStreamPipe* bpipe = static_cast<CStreamPipe*>(ConnectStream(pServer, pPort, pTimeoutms));
if (bpipe == NULL)
return NULL;
int64 remaining_time = pTimeoutms - (Server->getTimeMS() - starttime);
if (remaining_time < 0) remaining_time = 0;
SChannelPipe* ssl_pipe = new SChannelPipe(bpipe);
if (!ssl_pipe->ssl_connect(pServer, static_cast<int>(remaining_time)))
{
delete ssl_pipe;
return NULL;
}
return ssl_pipe;
}
IPipe *CServer::PipeFromSocket(SOCKET pSocket)
{
return new CStreamPipe(pSocket);

View File

@ -137,6 +137,7 @@ public:
virtual void StartCustomStreamService(IService *pService, std::string pServiceName, unsigned short pPort, int pMaxClientsPerThread=-1, IServer::BindTarget bindTarget=IServer::BindTarget_All);
virtual IPipe* ConnectStream(std::string pServer, unsigned short pPort, unsigned int pTimeoutms);
virtual IPipe* ConnectSslStream(const std::string& pServer, unsigned short pPort, unsigned int pTimeoutms);
virtual IPipe *PipeFromSocket(SOCKET pSocket);
virtual void DisconnectStream(IPipe *pipe);
virtual std::string LookupHostname(const std::string& pIp);

View File

@ -1,3 +1,5 @@
#pragma once
#include "Interface/Pipe.h"
#include "socket_header.h"
#include <vector>

View File

@ -38,6 +38,7 @@
#include <stdlib.h>
#include <memory.h>
#include <assert.h>
#include "../cryptoplugin/ICryptoFactory.h"
@ -221,7 +222,7 @@ void InternetClient::operator()(void)
{
if(n_connections<spare_connections)
{
Server->getThreadPool()->execute(new InternetClientThread(NULL, server_settings), "internet client");
Server->getThreadPool()->execute(new InternetClientThread(NULL, server_settings, NULL), "internet client");
newConnection();
}
else
@ -278,6 +279,7 @@ void InternetClient::doUpdateSettings(void)
std::string server_name;
std::string computername;
std::string server_port="55415";
std::string server_proxy;
std::string authkey;
if(!settings->getValue("internet_authkey", &authkey) && !settings->getValue("internet_authkey_def", &authkey))
{
@ -299,6 +301,8 @@ void InternetClient::doUpdateSettings(void)
{
computername=(IndexThread::getFileSrv()->getServerName());
}
if (!settings->getValue("internet_server_proxy", &server_proxy))
settings->getValue("internet_server_proxy_def", &server_proxy);
if( (settings->getValue("internet_server", &server_name) || settings->getValue("internet_server_def", &server_name))
&& !server_name.empty() )
{
@ -311,18 +315,27 @@ void InternetClient::doUpdateSettings(void)
std::vector<std::string> server_ports;
Tokenize(server_port, server_ports, ";");
std::vector<std::string> server_proxies;
Tokenize(server_proxy, server_proxies, ";");
for(size_t i=0;i<server_names.size();++i)
{
SServerConnectionSettings connection_settings;
connection_settings.hostname = server_names[i];
if(i<server_ports.size())
{
server_settings.servers.push_back(std::make_pair(server_names[i],
static_cast<unsigned short>(atoi(server_ports[i].c_str()))));
connection_settings.port = static_cast<unsigned short>(atoi(server_ports[i].c_str()));
}
else
else if(!server_ports.empty())
{
server_settings.servers.push_back(std::make_pair(server_names[i],
static_cast<unsigned short>(atoi(server_ports[server_ports.size()-1].c_str()))));
connection_settings.port = static_cast<unsigned short>(atoi(server_ports[server_ports.size() - 1].c_str()));
}
if (i < server_proxies.size())
connection_settings.proxy = server_proxies[i];
else if(!server_proxies.empty())
connection_settings.proxy = server_proxies[server_proxies.size()-1];
server_settings.servers.push_back(connection_settings);
}
server_settings.clientname=computername;
server_settings.authkey=authkey;
@ -340,6 +353,7 @@ void InternetClient::doUpdateSettings(void)
connected = false;
}
}
std::string tmp;
server_settings.internet_compress=true;
if(settings->getValue("internet_compress", &tmp) || settings->getValue("internet_compress_def", &tmp) )
@ -378,20 +392,20 @@ bool InternetClient::tryToConnect(IScopedLock *lock)
for(size_t i=0;i<server_settings.servers.size();++i)
{
std::string name=server_settings.servers[i].first;
unsigned short port=server_settings.servers[i].second;
SServerConnectionSettings selected_server_settings = server_settings.servers[i];
lock->relock(NULL);
Server->Log("Trying to connect to internet server \""+name+"\" at port "+convert(port), LL_DEBUG);
IPipe *cs=Server->ConnectStream(name, port, 10000);
Server->Log("Trying to connect to internet server \""+ selected_server_settings .hostname+"\" at port "+convert(selected_server_settings.port)
+ (selected_server_settings.proxy.empty() ? "" : (" via HTTP proxy "+ selected_server_settings.proxy)), LL_DEBUG);
std::auto_ptr<CTCPStack> tcpstack(new CTCPStack);
IPipe *cs = connect(selected_server_settings, *tcpstack);
lock->relock(mutex);
if(cs!=NULL)
{
server_settings.selected_server=i;
Server->Log("Successfully connected.", LL_DEBUG);
setStatusMsg("connected");
Server->getThreadPool()->execute(new InternetClientThread(cs, server_settings), "internet client");
Server->getThreadPool()->execute(new InternetClientThread(cs, server_settings, tcpstack.release()), "internet client");
newConnection();
return true;
}
@ -486,9 +500,16 @@ void InternetClient::setStatusMsg(const std::string& msg)
status_msg=msg;
}
InternetClientThread::InternetClientThread(IPipe *cs, const SServerSettings &server_settings)
: cs(cs), server_settings(server_settings)
InternetClientThread::InternetClientThread(IPipe *cs, const SServerSettings &server_settings, CTCPStack* tcpstack)
: cs(cs), server_settings(server_settings), tcpstack(tcpstack)
{
if (this->tcpstack == NULL)
this->tcpstack = new CTCPStack(true);
}
InternetClientThread::~InternetClientThread()
{
delete tcpstack;
}
char *InternetClientThread::getReply(CTCPStack *tcpstack, IPipe *pipe, size_t &replysize, unsigned int timeoutms)
@ -513,7 +534,6 @@ char *InternetClientThread::getReply(CTCPStack *tcpstack, IPipe *pipe, size_t &r
void InternetClientThread::operator()(void)
{
CTCPStack tcpstack(true);
bool finish_ok=false;
bool rm_connection=true;
@ -522,20 +542,20 @@ void InternetClientThread::operator()(void)
int tries=10;
while(tries>0 && cs==NULL)
{
cs=Server->ConnectStream(server_settings.servers[server_settings.selected_server].first,
server_settings.servers[server_settings.selected_server].second, 10000);
cs = InternetClient::connect(server_settings.servers[server_settings.selected_server], *tcpstack);
--tries;
InternetClient::setStatusMsg("connecting_failed");
if(cs==NULL && tries>0)
{
Server->Log("Connecting to server "+server_settings.servers[server_settings.selected_server].first
+ " failed. Retrying in 30s...", LL_INFO);
Server->Log("Connecting to server "+server_settings.servers[server_settings.selected_server].hostname
+(server_settings.servers[server_settings.selected_server].proxy.empty() ? "" : (" via HTTP proxy " + server_settings.servers[server_settings.selected_server].proxy)) + " failed. Retrying in 30s...", LL_INFO);
Server->wait(30000);
}
}
if(cs==NULL)
{
Server->Log("Connecting to server "+server_settings.servers[server_settings.selected_server].first
Server->Log("Connecting to server "+server_settings.servers[server_settings.selected_server].hostname
+(server_settings.servers[server_settings.selected_server].proxy.empty() ? "" : (" via HTTP proxy " + server_settings.servers[server_settings.selected_server].proxy))
+ " failed", LL_INFO);
InternetClient::rmConnection();
InternetClient::setHasConnection(false);
@ -572,7 +592,7 @@ void InternetClientThread::operator()(void)
{
char *buf;
size_t bufsize;
buf=getReply(&tcpstack, cs, bufsize, ic_auth_timeout);
buf=getReply(tcpstack, cs, bufsize, ic_auth_timeout);
if(buf==NULL)
{
Server->Log("Error receiving challenge packet");
@ -663,7 +683,7 @@ void InternetClientThread::operator()(void)
data.addString(client_challenge);
data.addUInt(pbkdf2_iterations);
tcpstack.Send(cs, data);
tcpstack->Send(cs, data);
challenge_response=crypto_fak->generateBinaryPasswordHash(hmac_key, client_challenge, 1);
}
@ -671,7 +691,7 @@ void InternetClientThread::operator()(void)
{
char *buf;
size_t bufsize;
buf=getReply(&tcpstack, cs, bufsize, ic_auth_timeout);
buf=getReply(tcpstack, cs, bufsize, ic_auth_timeout);
if(buf==NULL)
{
Server->Log("Error receiving authentication response");
@ -746,7 +766,7 @@ void InternetClientThread::operator()(void)
data.addUInt(capa);
tcpstack.Send(ics_pipe, data);
tcpstack->Send(ics_pipe, data);
}
comm_pipe=cs;
@ -784,7 +804,7 @@ void InternetClientThread::operator()(void)
ping_timeout=ic_ping_timeout;
}
buf=getReply(&tcpstack, comm_pipe, bufsize, ping_timeout);
buf=getReply(tcpstack, comm_pipe, bufsize, ping_timeout);
if(buf==NULL)
{
goto cleanup;
@ -801,7 +821,7 @@ void InternetClientThread::operator()(void)
{
CWData data;
data.addChar(ID_ISC_PONG);
tcpstack.Send(comm_pipe, data);
tcpstack->Send(comm_pipe, data);
}
else if(id==ID_ISC_CONNECT)
{
@ -812,7 +832,7 @@ void InternetClientThread::operator()(void)
{
CWData data;
data.addChar(ID_ISC_CONNECT_OK);
tcpstack.Send(comm_pipe, data);
tcpstack->Send(comm_pipe, data);
InternetClient::rmConnection();
rm_connection=false;
@ -868,7 +888,7 @@ cleanup:
void InternetClientThread::runServiceWrapper(IPipe *pipe, ICustomClient *client)
{
client->Init(Server->getThreadID(), pipe, server_settings.servers[server_settings.selected_server].first);
client->Init(Server->getThreadID(), pipe, server_settings.servers[server_settings.selected_server].hostname);
ClientConnector * cc=dynamic_cast<ClientConnector*>(client);
if(cc!=NULL)
{
@ -934,3 +954,167 @@ void InternetClientThread::printInfo( IPipe * pipe )
}
}
}
IPipe * InternetClient::connect(const SServerConnectionSettings & selected_server_settings, CTCPStack& tcpstack)
{
std::string proxy = selected_server_settings.proxy;
if (!proxy.empty())
{
bool ssl = false;
if (next(proxy, 0, "http://"))
{
ssl = false;
proxy = proxy.substr(7);
}
else if (next(proxy, 0, "https://"))
{
ssl = true;
proxy = proxy.substr(7);
}
std::string authorization;
if (proxy.find("@") != std::string::npos)
{
std::string udata = getuntil("@", proxy);
proxy = getafter("@", proxy);
std::string username = getuntil(":", proxy);
std::string password = getafter(":", proxy);
std::string adata = username + ":" + password;
authorization = "Authorization: Basic " + base64_encode(reinterpret_cast<const unsigned char*>(adata.c_str()), adata.size())+"\r\n";
}
unsigned short port = ssl ? 443 : 80;
if (proxy.find(":") != std::string::npos)
{
port = static_cast<unsigned short>(watoi(getafter(":", proxy)));
proxy = getuntil(":", proxy);
}
IPipe* cs;
if (ssl)
cs = Server->ConnectSslStream(proxy, port, 10000);
else
cs = Server->ConnectStream(proxy, port, 10000);
if (cs == NULL)
return cs;
std::string connect_data = "CONNECT " + selected_server_settings.hostname + ":" + convert(selected_server_settings.port) + " HTTP/1.1\r\n"
+"Host: "+ selected_server_settings.hostname+"\r\n"
+ authorization+"\r\n";
if (!cs->Write(connect_data))
{
Server->destroy(cs);
return NULL;
}
char buf[512];
int64 starttime = Server->getTimeMS();
int state = 0;
std::string state_data;
int http_code = 0;
do
{
size_t rc = cs->Read(buf, sizeof(buf), 10000);
if (rc == 0)
{
break;
}
for (size_t i = 0; i < rc; ++i)
{
char ch = buf[i];
switch (state)
{
case 0:
{
if (ch == ' ')
{
if (strlower(state_data) != "http/1.1"
&& strlower(state_data) != "http/1.0")
{
Server->Log("Unknown HTTP protocol: " + state_data, LL_ERROR);
Server->destroy(cs);
return NULL;
}
state_data.clear();
++state;
}
else
state_data += ch;
}break;
case 1:
{
if (ch == ' ')
{
http_code = watoi(state_data);
state_data.clear();
++state;
}
else
state_data += ch;
}break;
case 2:
{
if (ch == '\n')
{
if (http_code != 200)
{
Server->Log("HTTP proxy returned error code " + convert(http_code) + " message \"" + state_data + "\"", LL_ERROR);
Server->destroy(cs);
return NULL;
}
state_data.clear();
++state;
}
else if (ch != '\r')
{
state_data += ch;
}
} break;
case 3:
{
if (ch == '\n')
{
if (i + 1 < rc)
{
tcpstack.AddData(&buf[i + 1], rc - i - 1);
}
return cs;
}
else if (ch != '\r')
{
++state;
}
} break;
case 4:
{
if (ch == '\n')
{
state = 3;
}
}break;
default:
{
assert(false);
Server->destroy(cs);
return NULL;
}break;
}
}
} while (Server->getTimeMS() - starttime < 10000);
Server->Log("Timeout connecting via http proxy");
Server->destroy(cs);
return NULL;
}
return Server->ConnectStream(selected_server_settings.hostname,
selected_server_settings.port, 10000);
}

View File

@ -13,9 +13,16 @@ class ICustomClient;
class IScopedLock;
class ICondition;
struct SServerConnectionSettings
{
std::string hostname;
std::string proxy;
unsigned short port;
};
struct SServerSettings
{
std::vector<std::pair<std::string, unsigned short> > servers;
std::vector<SServerConnectionSettings> servers;
size_t selected_server;
std::string clientname;
std::string authkey;
@ -58,6 +65,8 @@ public:
static void setStatusMsg(const std::string& msg);
static IPipe* connect(const SServerConnectionSettings& selected_settings, CTCPStack& tcpstack);
private:
static IMutex *mutex;
@ -77,7 +86,8 @@ private:
class InternetClientThread : public IThread
{
public:
InternetClientThread(IPipe *cs, const SServerSettings &server_settings);
InternetClientThread(IPipe *cs, const SServerSettings &server_settings, CTCPStack* tcpstack);
~InternetClientThread();
void operator()(void);
char *getReply(CTCPStack *tcpstack, IPipe *pipe, size_t &replysize, unsigned int timeoutms);
@ -88,5 +98,6 @@ private:
std::string generateRandomBinaryAuthKey(void);
void printInfo( IPipe * pipe );
IPipe *cs;
CTCPStack* tcpstack;
SServerSettings server_settings;
};

View File

@ -56,6 +56,7 @@ std::vector<std::string> getSettingsList(void)
ret.push_back("image_letters");
ret.push_back("internet_server");
ret.push_back("internet_server_port");
ret.push_back("internet_server_proxy");
ret.push_back("internet_authkey");
ret.push_back("internet_speed");
ret.push_back("local_speed");
@ -146,6 +147,7 @@ std::vector<std::string> getGlobalizedSettingsList(void)
std::vector<std::string> ret;
ret.push_back("internet_server");
ret.push_back("internet_server_port");
ret.push_back("internet_server_proxy");
ret.push_back("server_url");
return ret;
}
@ -172,6 +174,7 @@ std::vector<std::string> getGlobalSettingsList(void)
ret.push_back("backup_database");
ret.push_back("internet_server");
ret.push_back("internet_server_port");
ret.push_back("internet_server_proxy");
ret.push_back("global_local_speed");
ret.push_back("global_internet_speed");
ret.push_back("use_tmpfiles");

View File

@ -369,6 +369,7 @@ void ServerSettings::readSettingsDefault(ISettingsReader* settings_default,
settings->image_letters=settings_default->getValue("image_letters", "C");
settings->backup_database=(settings_global->getValue("backup_database", "true")=="true");
settings->internet_server_port=(unsigned short)(atoi(settings_global->getValue("internet_server_port", "55415").c_str()));
settings->internet_server_proxy = settings_global->getValue("internet_server_proxy", "");
settings->client_set_settings=false;
settings->internet_server= settings_global->getValue("internet_server", "");
settings->internet_image_backups=(settings_default->getValue("internet_image_backups", "false")=="true");

View File

@ -79,6 +79,7 @@ struct SSettings
std::string internet_server;
bool client_set_settings;
unsigned short internet_server_port;
std::string internet_server_proxy;
std::string internet_authkey;
bool internet_full_file_backups;
bool internet_image_backups;

View File

@ -29,6 +29,7 @@ ACTION_IMPL(add_client)
ret.set("new_authkey", new_authkey);
ret.set("internet_server", s->internet_server);
ret.set("internet_server_port", s->internet_server_port);
ret.set("internet_server_proxy", s->internet_server_proxy);
ret.set("added_new_client", true);
}
else

View File

@ -55,6 +55,7 @@ namespace
ret+="internet_mode_enabled="+convert(settingsptr->internet_mode_enabled)+"\r\n";
ret+="internet_server="+settingsptr->internet_server+"\r\n";
ret+="internet_server_port="+convert(settingsptr->internet_server_port)+"\r\n";
ret += "internet_server_proxy=" + settingsptr->internet_server_proxy + "\r\n";
ret+="internet_authkey="+(authkey.empty() ? settingsptr->internet_authkey : authkey ) +"\r\n";
if(!clientname.empty())
{

View File

@ -234,6 +234,7 @@ void getGeneralSettings(JSON::Object& obj, IDatabase *db, ServerSettings &settin
SET_SETTING(backup_database);
SET_SETTING(internet_server);
SET_SETTING(internet_server_port);
SET_SETTING(internet_server_proxy);
SET_SETTING(global_local_speed);
SET_SETTING(global_soft_fs_quota);
SET_SETTING(global_internet_speed);

View File

@ -464,6 +464,12 @@
<input type="text" class="form-control" id="internet_server_port" value="{internet_server_port}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label" for="internet_server_proxy">{tConnect via HTTP(S) proxy (leave empty to connect without)}:</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="internet_server_proxy" value="{internet_server_proxy|s}"/>
</div>
</div>
{/global_settings}
{?main_client}
{^global_settings}