mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
Implement connection establishment via web sockets
This commit is contained in:
parent
bde001411b
commit
4fb806e7b0
@ -299,6 +299,11 @@ bool CAcceptThread::init_socket_v4(unsigned short port)
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if (Server->getServerParameter("fastcgi_localhost_only") == "1")
|
||||
{
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
|
||||
int rc = bind(s, (sockaddr*)&addr, sizeof(addr));
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
@ -341,6 +346,11 @@ bool CAcceptThread::init_socket_v6(unsigned short port)
|
||||
addr.sin6_port = htons(port);
|
||||
addr.sin6_addr = in6addr_any;
|
||||
|
||||
if (Server->getServerParameter("fastcgi_localhost_only") == "1")
|
||||
{
|
||||
addr.sin6_addr = in6addr_loopback;
|
||||
}
|
||||
|
||||
int rc = bind(s_v6, (sockaddr*)&addr, sizeof(addr));
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
|
||||
@ -343,6 +343,7 @@
|
||||
<ClInclude Include="Interface\DatabaseInt.h" />
|
||||
<ClInclude Include="Interface\PipeThrottler.h" />
|
||||
<ClInclude Include="Interface\SharedMutex.h" />
|
||||
<ClInclude Include="Interface\WebSocket.h" />
|
||||
<ClInclude Include="libs.h" />
|
||||
<ClInclude Include="LoadbalancerClient.h" />
|
||||
<ClInclude Include="LookupService.h" />
|
||||
|
||||
@ -380,5 +380,8 @@
|
||||
<ClInclude Include="SChannelPipe.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Interface\WebSocket.h">
|
||||
<Filter>Interface</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -31,6 +31,7 @@ class IScopedLock;
|
||||
class IDatabaseFactory;
|
||||
class IPipeThrottler;
|
||||
class IPipeThrottlerUpdater;
|
||||
class IWebSocket;
|
||||
|
||||
struct SPostfile
|
||||
{
|
||||
@ -80,6 +81,9 @@ public:
|
||||
virtual void setActionContext(std::string context)=0;
|
||||
virtual void resetActionContext(void)=0;
|
||||
|
||||
virtual void addWebSocket(IWebSocket* websocket) = 0;
|
||||
virtual THREAD_ID ExecuteWebSocket(const std::string& name, str_map& GET, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name) = 0;
|
||||
|
||||
virtual int64 getTimeSeconds(void)=0;
|
||||
virtual int64 getTimeMS(void)=0;
|
||||
|
||||
|
||||
12
Interface/WebSocket.h
Normal file
12
Interface/WebSocket.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#include "Object.h"
|
||||
#include "Pipe.h"
|
||||
|
||||
class IWebSocket : public IObject
|
||||
{
|
||||
public:
|
||||
virtual void Execute(str_map& GET, THREAD_ID tid, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name) = 0;
|
||||
virtual std::string getName() = 0;
|
||||
};
|
||||
@ -11,7 +11,7 @@ if WITH_EMBEDDED_SQLITE3
|
||||
urbackupclientbackend_SOURCES += sqlite/sqlite3.c
|
||||
endif
|
||||
|
||||
urbackupclientbackend_SOURCES += urbackupcommon/os_functions_lin.cpp urbackupcommon/sha2/sha2.cpp urbackupcommon/fileclient/FileClient.cpp urbackupcommon/fileclient/tcpstack.cpp urbackupcommon/escape.cpp urbackupcommon/bufmgr.cpp urbackupcommon/json.cpp urbackupcommon/CompressedPipe.cpp urbackupcommon/InternetServicePipe2.cpp urbackupcommon/settingslist.cpp urbackupcommon/fileclient/FileClientChunked.cpp urbackupcommon/InternetServicePipe.cpp urbackupcommon/filelist_utils.cpp urbackupcommon/file_metadata.cpp urbackupcommon/glob.cpp urbackupcommon/chunk_hasher.cpp urbackupcommon/CompressedPipe2.cpp urbackupcommon/SparseFile.cpp urbackupcommon/ExtentIterator.cpp urbackupcommon/TreeHash.cpp urbackupcommon/WalCheckpointThread.cpp
|
||||
urbackupclientbackend_SOURCES += urbackupcommon/os_functions_lin.cpp urbackupcommon/sha2/sha2.cpp urbackupcommon/fileclient/FileClient.cpp urbackupcommon/fileclient/tcpstack.cpp urbackupcommon/escape.cpp urbackupcommon/bufmgr.cpp urbackupcommon/json.cpp urbackupcommon/CompressedPipe.cpp urbackupcommon/InternetServicePipe2.cpp urbackupcommon/settingslist.cpp urbackupcommon/fileclient/FileClientChunked.cpp urbackupcommon/InternetServicePipe.cpp urbackupcommon/filelist_utils.cpp urbackupcommon/file_metadata.cpp urbackupcommon/glob.cpp urbackupcommon/chunk_hasher.cpp urbackupcommon/CompressedPipe2.cpp urbackupcommon/SparseFile.cpp urbackupcommon/ExtentIterator.cpp urbackupcommon/TreeHash.cpp urbackupcommon/WalCheckpointThread.cpp urbackupcommon/WebSocketPipe.cpp
|
||||
|
||||
if WITH_ZSTD
|
||||
urbackupclientbackend_SOURCES += urbackupcommon/CompressedPipeZstd.cpp
|
||||
@ -277,7 +277,7 @@ else
|
||||
client_headers =
|
||||
endif
|
||||
|
||||
urbackupclient_headers = urbackupclient/DirectoryWatcherThread.h urbackupcommon/os_functions.h urbackupclient/ChangeJournalWatcher.h urbackupcommon/sha2/sha2.h urbackupclient/database.h urbackupcommon/escape.h urbackupclient/ClientSend.h urbackupclient/clientdao.h urbackupclient/client.h urbackupclient/ClientService.h fileservplugin/IFileServFactory.h fileservplugin/IFileServ.h common/data.h urbackupcommon/fileclient/tcpstack.h urbackupcommon/capa_bits.h urbackupclient/ServerIdentityMgr.h urbackupcommon/bufmgr.h urbackupcommon/CompressedPipe.h urbackupclient/ImageThread.h urbackupclient/InternetClient.h urbackupcommon/InternetServicePipe2.h urbackupcommon/settingslist.h cryptoplugin/IZlibCompression.h cryptoplugin/IZlibDecompression.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESDecryption.h cryptoplugin/IAESEncryption.h urbackupcommon/internet_pipe_capabilities.h urbackupcommon/settings.h urbackupcommon/fileclient/socket_header.h urbackupcommon/mbrdata.h urbackupcommon/InternetServiceIDs.h urbackupcommon/json.h urbackupclient/file_permissions.h urbackupclient/lin_ver.h urbackupcommon/glob.h urbackupclient/tokens.h urbackupclient/FileMetadataDownloadThread.h urbackupclient/RestoreFiles.h urbackupcommon/chunk_hasher.h common/adler32.h urbackupcommon/fileclient/FileClient.h urbackupcommon/fileclient/FileClientChunked.h urbackupcommon/file_metadata.h urbackupcommon/filelist_utils.h urbackupclient/RestoreDownloadThread.h urbackupclient/TokenCallback.h urbackupcommon/CompressedPipe2.h urbackupcommon/server_compat.h urbackupcommon/fileclient/packet_ids.h urbackupcommon/InternetServicePipe.h urbackupclient/backup_client_db.h urbackupcommon/SparseFile.h urbackupcommon/ExtentIterator.h urbackupcommon/TreeHash.h urbackupcommon/WalCheckpointThread.h common/miniz.h urbackupclient/ParallelHash.h urbackupclient/ClientHash.h urbackupcommon/CompressedPipeZstd.h urbackupclient/lin_sysvol.h
|
||||
urbackupclient_headers = urbackupclient/DirectoryWatcherThread.h urbackupcommon/os_functions.h urbackupclient/ChangeJournalWatcher.h urbackupcommon/sha2/sha2.h urbackupclient/database.h urbackupcommon/escape.h urbackupclient/ClientSend.h urbackupclient/clientdao.h urbackupclient/client.h urbackupclient/ClientService.h fileservplugin/IFileServFactory.h fileservplugin/IFileServ.h common/data.h urbackupcommon/fileclient/tcpstack.h urbackupcommon/capa_bits.h urbackupclient/ServerIdentityMgr.h urbackupcommon/bufmgr.h urbackupcommon/CompressedPipe.h urbackupclient/ImageThread.h urbackupclient/InternetClient.h urbackupcommon/InternetServicePipe2.h urbackupcommon/settingslist.h cryptoplugin/IZlibCompression.h cryptoplugin/IZlibDecompression.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESDecryption.h cryptoplugin/IAESEncryption.h urbackupcommon/internet_pipe_capabilities.h urbackupcommon/settings.h urbackupcommon/fileclient/socket_header.h urbackupcommon/mbrdata.h urbackupcommon/InternetServiceIDs.h urbackupcommon/json.h urbackupclient/file_permissions.h urbackupclient/lin_ver.h urbackupcommon/glob.h urbackupclient/tokens.h urbackupclient/FileMetadataDownloadThread.h urbackupclient/RestoreFiles.h urbackupcommon/chunk_hasher.h common/adler32.h urbackupcommon/fileclient/FileClient.h urbackupcommon/fileclient/FileClientChunked.h urbackupcommon/file_metadata.h urbackupcommon/filelist_utils.h urbackupclient/RestoreDownloadThread.h urbackupclient/TokenCallback.h urbackupcommon/CompressedPipe2.h urbackupcommon/server_compat.h urbackupcommon/fileclient/packet_ids.h urbackupcommon/InternetServicePipe.h urbackupclient/backup_client_db.h urbackupcommon/SparseFile.h urbackupcommon/ExtentIterator.h urbackupcommon/TreeHash.h urbackupcommon/WalCheckpointThread.h common/miniz.h urbackupclient/ParallelHash.h urbackupclient/ClientHash.h urbackupcommon/CompressedPipeZstd.h urbackupclient/lin_sysvol.h urbackupcommon/WebSocketPipe.h
|
||||
|
||||
|
||||
tclap_headers = \
|
||||
@ -340,7 +340,7 @@ zstd_headers = \
|
||||
external/zstd/dictBuilder/zdict.h \
|
||||
external/zstd/zstd.h
|
||||
|
||||
noinst_HEADERS=SessionMgr.h WorkerThread.h Helper_win32.h Database.h defaults.h ServiceAcceptor.h Query.h SettingsReader.h file.h file_memory.h MemorySettingsReader.h Condition_lin.h LookupService.h Template.h types.h DBSettingsReader.h stringtools.h ThreadPool.h libs.h vld_.h ServiceWorker.h StreamPipe.h LoadbalancerClient.h socket_header.h FileSettingsReader.h SelectThread.h md5.h vld.h Table.h Client.h MemoryPipe.h Mutex_lin.h AcceptThread.h OutputStream.h Server.h Interface/SessionMgr.h Interface/Service.h Interface/PluginMgr.h Interface/Database.h Interface/Pipe.h Interface/CustomClient.h Interface/User.h Interface/Query.h Interface/SettingsReader.h Interface/Types.h Interface/Template.h Interface/ThreadPool.h Interface/Mutex.h Interface/File.h Interface/Condition.h Interface/Table.h Interface/Plugin.h Interface/Thread.h Interface/Action.h Interface/Object.h Interface/OutputStream.h Interface/Server.h libfastcgi/fastcgi.hpp sqlite/sqlite3.h sqlite/sqlite3ext.h utf8/utf8.h utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESEncryption.h cryptoplugin/IAESDecryption.h Interface/DatabaseFactory.h Interface/DatabaseInt.h sqlite/shell.h SQLiteFactory.h PipeThrottler.h Interface/PipeThrottler.h mt19937ar.h DatabaseCursor.h Interface/DatabaseCursor.h client_version.h Interface/SharedMutex.h SharedMutex_lin.h StaticPluginRegistration.h common/bitmap.h OpenSSLPipe.h $(cryptoplugin_headers) $(fileservplugin_headers) $(fsimageplugin_headers) $(urbackupclientctl_headers) $(client_headers) $(tclap_headers) $(urbackupclient_headers) $(cryptopp_headers) $(blockalign_headers) $(zstd_headers)
|
||||
noinst_HEADERS=SessionMgr.h WorkerThread.h Helper_win32.h Database.h defaults.h ServiceAcceptor.h Query.h SettingsReader.h file.h file_memory.h MemorySettingsReader.h Condition_lin.h LookupService.h Template.h types.h DBSettingsReader.h stringtools.h ThreadPool.h libs.h vld_.h ServiceWorker.h StreamPipe.h LoadbalancerClient.h socket_header.h FileSettingsReader.h SelectThread.h md5.h vld.h Table.h Client.h MemoryPipe.h Mutex_lin.h AcceptThread.h OutputStream.h Server.h Interface/SessionMgr.h Interface/Service.h Interface/PluginMgr.h Interface/Database.h Interface/Pipe.h Interface/CustomClient.h Interface/User.h Interface/Query.h Interface/SettingsReader.h Interface/Types.h Interface/Template.h Interface/ThreadPool.h Interface/Mutex.h Interface/File.h Interface/Condition.h Interface/Table.h Interface/Plugin.h Interface/Thread.h Interface/Action.h Interface/Object.h Interface/OutputStream.h Interface/Server.h libfastcgi/fastcgi.hpp sqlite/sqlite3.h sqlite/sqlite3ext.h utf8/utf8.h utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESEncryption.h cryptoplugin/IAESDecryption.h Interface/DatabaseFactory.h Interface/DatabaseInt.h sqlite/shell.h SQLiteFactory.h PipeThrottler.h Interface/PipeThrottler.h mt19937ar.h DatabaseCursor.h Interface/DatabaseCursor.h Interface/WebSocket.h client_version.h Interface/SharedMutex.h SharedMutex_lin.h StaticPluginRegistration.h common/bitmap.h OpenSSLPipe.h $(cryptoplugin_headers) $(fileservplugin_headers) $(fsimageplugin_headers) $(urbackupclientctl_headers) $(client_headers) $(tclap_headers) $(urbackupclient_headers) $(cryptopp_headers) $(blockalign_headers) $(zstd_headers)
|
||||
|
||||
|
||||
EXTRA_DIST_GUI = client/info.txt client/data/backup-bad.xpm client/data/backup-ok.xpm client/data/backup-progress.xpm client/data/backup-progress-pause.xpm client/data/backup-no-server.xpm client/data/backup-no-recent.xpm client/data/backup-indexing.xpm client/data/logo1.png client/data/lang/it/urbackup.mo client/data/lang/pl/urbackup.mo client/data/lang/pt_BR/urbackup.mo client/data/lang/sk/urbackup.mo client/data/lang/zh_TW/urbackup.mo client/data/lang/zh_CN/urbackup.mo client/data/lang/de/urbackup.mo client/data/lang/es/urbackup.mo client/data/lang/fr/urbackup.mo client/data/lang/ru/urbackup.mo client/data/lang/uk/urbackup.mo client/data/lang/da/urbackup.mo client/data/lang/nl/urbackup.mo client/data/lang/fa/urbackup.mo client/data/lang/cs/urbackup.mo client/gui/GUISetupWizard.h client/SetupWizard.h
|
||||
|
||||
File diff suppressed because one or more lines are too long
29
Server.cpp
29
Server.cpp
@ -35,6 +35,7 @@
|
||||
#include "Interface/PluginMgr.h"
|
||||
#include "Interface/Thread.h"
|
||||
#include "Interface/DatabaseFactory.h"
|
||||
#include "Interface/WebSocket.h"
|
||||
|
||||
#include "Server.h"
|
||||
#include "Template.h"
|
||||
@ -175,6 +176,7 @@ CServer::CServer()
|
||||
|
||||
log_mutex=createMutex();
|
||||
action_mutex=createMutex();
|
||||
web_socket_mutex = createMutex();
|
||||
requests_mutex=createMutex();
|
||||
outputs_mutex=createMutex();
|
||||
db_mutex=createMutex();
|
||||
@ -353,6 +355,7 @@ CServer::~CServer()
|
||||
|
||||
destroy(log_mutex);
|
||||
destroy(action_mutex);
|
||||
destroy(web_socket_mutex);
|
||||
destroy(requests_mutex);
|
||||
destroy(outputs_mutex);
|
||||
destroy(db_mutex);
|
||||
@ -2267,3 +2270,29 @@ int CServer::getRecvWindowSize()
|
||||
return recv_window_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CServer::addWebSocket(IWebSocket* websocket)
|
||||
{
|
||||
IScopedLock lock(web_socket_mutex);
|
||||
|
||||
web_sockets[websocket->getName()] = websocket;
|
||||
}
|
||||
|
||||
THREAD_ID CServer::ExecuteWebSocket(const std::string& name, str_map& GET, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name)
|
||||
{
|
||||
IWebSocket* ws;
|
||||
{
|
||||
IScopedLock lock(web_socket_mutex);
|
||||
std::map<std::string, IWebSocket*>::iterator it = web_sockets.find(name);
|
||||
if (it == web_sockets.end())
|
||||
return ILLEGAL_THREAD_ID;
|
||||
|
||||
ws = it->second;
|
||||
}
|
||||
|
||||
THREAD_ID tid = getThreadID();
|
||||
|
||||
ws->Execute(GET, tid, PARAMS, pipe, endpoint_name);
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
6
Server.h
6
Server.h
@ -87,6 +87,9 @@ public:
|
||||
virtual void setActionContext(std::string context);
|
||||
virtual void resetActionContext(void);
|
||||
|
||||
virtual void addWebSocket(IWebSocket* websocket);
|
||||
virtual THREAD_ID ExecuteWebSocket(const std::string& name, str_map& GET, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name);
|
||||
|
||||
virtual int64 getTimeSeconds(void);
|
||||
virtual int64 getTimeMS(void);
|
||||
|
||||
@ -233,6 +236,7 @@ private:
|
||||
|
||||
IMutex* log_mutex;
|
||||
IMutex* action_mutex;
|
||||
IMutex* web_socket_mutex;
|
||||
IMutex* requests_mutex;
|
||||
IMutex* outputs_mutex;
|
||||
IMutex* db_mutex;
|
||||
@ -249,6 +253,8 @@ private:
|
||||
|
||||
std::map< std::string, std::map<std::string, IAction*> > actions;
|
||||
|
||||
std::map<std::string, IWebSocket*> web_sockets;
|
||||
|
||||
std::map<std::string, UNLOADACTIONS> unload_functs;
|
||||
std::vector<HMODULE> unload_handles;
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fileservplugin", "fileservplugin\fileservplugin.vcxproj", "{B1F1AF2E-E544-45F7-864A-883461A4B574}"
|
||||
EndProject
|
||||
@ -177,4 +177,7 @@ Global
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {790C4817-D528-41A0-94B8-198C09841390}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@ -397,3 +397,10 @@ IECDHKeyExchange* CryptoFactory::createECDHKeyExchange()
|
||||
{
|
||||
return new ECDHKeyExchange();
|
||||
}
|
||||
|
||||
std::string CryptoFactory::sha1Binary(const std::string& data)
|
||||
{
|
||||
byte sha1_digest[CryptoPP::SHA1::DIGESTSIZE];
|
||||
CryptoPP::SHA1().CalculateDigest(sha1_digest, reinterpret_cast<const byte*>(data.data()), data.size());
|
||||
return std::string(reinterpret_cast<char*>(sha1_digest), sizeof(sha1_digest));
|
||||
}
|
||||
|
||||
@ -27,5 +27,6 @@ public:
|
||||
virtual std::string encryptAuthenticatedAES(const std::string& data, const std::string &password, size_t iterations=20000);
|
||||
virtual std::string decryptAuthenticatedAES(const std::string& data, const std::string &password, size_t iterations=20000);
|
||||
virtual IECDHKeyExchange* createECDHKeyExchange();
|
||||
virtual std::string sha1Binary(const std::string& data);
|
||||
|
||||
};
|
||||
@ -39,4 +39,6 @@ public:
|
||||
virtual std::string encryptAuthenticatedAES(const std::string& data, const std::string &password, size_t iterations=20000)=0;
|
||||
virtual std::string decryptAuthenticatedAES(const std::string& data, const std::string &password, size_t iterations=20000)=0;
|
||||
virtual IECDHKeyExchange* createECDHKeyExchange()=0;
|
||||
|
||||
virtual std::string sha1Binary(const std::string& data) = 0;
|
||||
};
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "HTTPFile.h"
|
||||
#include "HTTPAction.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "HTTPSocket.h"
|
||||
|
||||
extern CHTTPService* http_service;
|
||||
|
||||
@ -118,7 +119,7 @@ bool CHTTPClient::Run(IRunOtherCallback* run_other)
|
||||
{
|
||||
if( http_g_state==HTTP_STATE_WAIT_FOR_THREAD )
|
||||
{
|
||||
if( Server->getThreadPool()->isRunning(request_ticket)==false )
|
||||
if( !Server->getThreadPool()->isRunning(request_ticket) )
|
||||
{
|
||||
//Server->Log("Connection: "+http_params["CONNECTION"], LL_DEBUG);
|
||||
/*if( strlower(http_params["CONNECTION"])=="close" )
|
||||
@ -507,7 +508,18 @@ bool CHTTPClient::processRequest(void)
|
||||
std::string name;
|
||||
std::string context;
|
||||
size_t pstart;
|
||||
if( pl->size()>1 && (*pl)[0]=='x' && (*pl)[1]=='?' )
|
||||
str_map::iterator upgrade_param = http_params.find("UPGRADE");
|
||||
if (upgrade_param != http_params.end()
|
||||
&& upgrade_param->second == "websocket")
|
||||
{
|
||||
std::string name = getuntil("?", *pl);
|
||||
std::string gparams = getafter("?", *pl);
|
||||
CHTTPSocket* socket_handler = new CHTTPSocket(name, gparams, http_params, pipe, endpoint);
|
||||
request_ticket = Server->getThreadPool()->execute(socket_handler, "http websocket");
|
||||
request_handler = socket_handler;
|
||||
return true;
|
||||
}
|
||||
else if( pl->size()>1 && (*pl)[0]=='x' && (*pl)[1]=='?' )
|
||||
{
|
||||
parseAction(*pl, name, context);
|
||||
}
|
||||
@ -538,7 +550,7 @@ bool CHTTPClient::processRequest(void)
|
||||
rp = greplace("\\", "_", rp);
|
||||
#endif
|
||||
CHTTPFile *file_handler=new CHTTPFile(http_service->getRoot()+rp, pipe);
|
||||
request_ticket=Server->getThreadPool()->execute(file_handler);
|
||||
request_ticket=Server->getThreadPool()->execute(file_handler, "http file request");
|
||||
request_handler=file_handler;
|
||||
return true;
|
||||
}
|
||||
@ -573,9 +585,7 @@ void CHTTPClient::WaitForRemove(void)
|
||||
{
|
||||
if(request_ticket!=ILLEGAL_THREADPOOL_TICKET)
|
||||
{
|
||||
std::vector<THREADPOOL_TICKET> tmp;
|
||||
tmp.push_back(request_ticket);
|
||||
Server->getThreadPool()->waitFor(tmp);
|
||||
Server->getThreadPool()->waitFor(request_ticket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
httpserver/HTTPSocket.cpp
Normal file
32
httpserver/HTTPSocket.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "HTTPSocket.h"
|
||||
#include "../stringtools.h"
|
||||
#include "../Interface/Server.h"
|
||||
#include "../Interface/Pipe.h"
|
||||
|
||||
CHTTPSocket::CHTTPSocket(const std::string& name, const std::string& gparams, const str_map& pRawPARAMS, IPipe* pOutput, const std::string& endpoint_name)
|
||||
: RawPARAMS(pRawPARAMS), output(pOutput), name(name), gparams(gparams), endpoint_name(endpoint_name)
|
||||
{
|
||||
}
|
||||
|
||||
void CHTTPSocket::operator()()
|
||||
{
|
||||
std::map<std::string, std::string> GET;
|
||||
ParseParamStrHttp(gparams, &GET, true);
|
||||
|
||||
THREAD_ID tid = 0;
|
||||
try
|
||||
{
|
||||
tid = Server->ExecuteWebSocket(name, GET, RawPARAMS, output, endpoint_name);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (tid == ILLEGAL_THREAD_ID)
|
||||
{
|
||||
std::string error = "Error: Unknown web socket [" + EscapeHTML(name) + "]";
|
||||
Server->Log(error, LL_WARNING);
|
||||
output->Write("Content-type: text/html; charset=UTF-8\r\n\r\n" + error);
|
||||
}
|
||||
}
|
||||
23
httpserver/HTTPSocket.h
Normal file
23
httpserver/HTTPSocket.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Interface/Types.h"
|
||||
#include "../Interface/Thread.h"
|
||||
#include "../Interface/Object.h"
|
||||
|
||||
class IPipe;
|
||||
|
||||
class CHTTPSocket : public IThread, public IObject
|
||||
{
|
||||
public:
|
||||
CHTTPSocket(const std::string& name, const std::string& gparams, const str_map& pRawPARAMS, IPipe* pOutput, const std::string& endpoint_name);
|
||||
|
||||
void operator()();
|
||||
private:
|
||||
std::string name;
|
||||
std::string gparams;
|
||||
str_map RawPARAMS;
|
||||
std::string endpoint_name;
|
||||
|
||||
IPipe* output;
|
||||
|
||||
};
|
||||
@ -105,7 +105,14 @@ DLLEXPORT void LoadActions(IServer* pServer)
|
||||
|
||||
Server->Log("Starting HTTP-Server on port "+convert(port), LL_INFO);
|
||||
|
||||
Server->StartCustomStreamService( http_service, "HTTP", (unsigned short)port, 1);
|
||||
IServer::BindTarget bind_target = IServer::BindTarget_All;
|
||||
|
||||
if (Server->getServerParameter("http_localhost_only") == "1")
|
||||
{
|
||||
bind_target = IServer::BindTarget_Localhost;
|
||||
}
|
||||
|
||||
Server->StartCustomStreamService( http_service, "HTTP", (unsigned short)port, 1, bind_target);
|
||||
}
|
||||
|
||||
DLLEXPORT void UnloadActions(void)
|
||||
|
||||
@ -170,6 +170,7 @@
|
||||
<ClCompile Include="HTTPFile.cpp" />
|
||||
<ClCompile Include="HTTPProxy.cpp" />
|
||||
<ClCompile Include="HTTPService.cpp" />
|
||||
<ClCompile Include="HTTPSocket.cpp" />
|
||||
<ClCompile Include="IndexFiles.cpp" />
|
||||
<ClCompile Include="MIMEType.cpp" />
|
||||
<ClCompile Include="..\stringtools.cpp" />
|
||||
@ -180,6 +181,7 @@
|
||||
<ClInclude Include="HTTPFile.h" />
|
||||
<ClInclude Include="HTTPProxy.h" />
|
||||
<ClInclude Include="HTTPService.h" />
|
||||
<ClInclude Include="HTTPSocket.h" />
|
||||
<ClInclude Include="IndexFiles.h" />
|
||||
<ClInclude Include="MIMEType.h" />
|
||||
<ClInclude Include="..\stringtools.h" />
|
||||
|
||||
@ -42,6 +42,9 @@
|
||||
<ClCompile Include="..\stringtools.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HTTPSocket.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="HTTPAction.h">
|
||||
@ -68,5 +71,8 @@
|
||||
<ClInclude Include="..\stringtools.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HTTPSocket.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -34,6 +34,7 @@
|
||||
#include "../urbackupcommon/internet_pipe_capabilities.h"
|
||||
#include "../urbackupcommon/CompressedPipe2.h"
|
||||
#include "../urbackupcommon/CompressedPipeZstd.h"
|
||||
#include "../urbackupcommon/WebSocketPipe.h"
|
||||
|
||||
#include "../stringtools.h"
|
||||
|
||||
@ -1006,7 +1007,7 @@ IPipe * InternetClient::connect(const SServerConnectionSettings & selected_serve
|
||||
std::string password = getafter(":", udata);
|
||||
std::string adata = username + ":" + password;
|
||||
|
||||
authorization = "Proxy-Authorization: Basic " + base64_encode(reinterpret_cast<const unsigned char*>(adata.c_str()), adata.size())+"\r\n";
|
||||
authorization = "Proxy-Authorization: Basic " + base64_encode(reinterpret_cast<const unsigned char*>(adata.c_str()), static_cast<unsigned int>(adata.size()))+"\r\n";
|
||||
}
|
||||
|
||||
unsigned short port = ssl ? 443 : 80;
|
||||
@ -1145,6 +1146,204 @@ IPipe * InternetClient::connect(const SServerConnectionSettings & selected_serve
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
else if(next(selected_server_settings.hostname, 0, "ws://") ||
|
||||
next(selected_server_settings.hostname, 0, "wss://") )
|
||||
{
|
||||
std::string hostname;
|
||||
bool ssl;
|
||||
if (next(selected_server_settings.hostname, 0, "ws://"))
|
||||
{
|
||||
ssl = false;
|
||||
hostname = selected_server_settings.hostname.substr(5);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssl = true;
|
||||
hostname = selected_server_settings.hostname.substr(6);
|
||||
}
|
||||
|
||||
std::string authorization;
|
||||
if (hostname.find("@") != std::string::npos)
|
||||
{
|
||||
std::string udata = getuntil("@", hostname);
|
||||
hostname = getafter("@", hostname);
|
||||
|
||||
std::string username = getuntil(":", udata);
|
||||
std::string password = getafter(":", udata);
|
||||
std::string adata = username + ":" + password;
|
||||
|
||||
authorization = "Authorization: Basic " + base64_encode(reinterpret_cast<const unsigned char*>(adata.c_str()), static_cast<unsigned int>(adata.size())) + "\r\n";
|
||||
}
|
||||
|
||||
unsigned short port = ssl ? 443 : 80;
|
||||
std::string loc;
|
||||
if (hostname.find(":") != std::string::npos)
|
||||
{
|
||||
std::string port_str = getafter(":", hostname);
|
||||
if(port_str.find("/")!=std::string::npos)
|
||||
{
|
||||
port = static_cast<unsigned short>(watoi(getuntil("/", port_str)));
|
||||
loc = getafter("/", port_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
port = static_cast<unsigned short>(watoi(port_str));
|
||||
}
|
||||
|
||||
hostname = getuntil(":", hostname);
|
||||
}
|
||||
|
||||
if (loc.empty() ||
|
||||
loc[0] != '/')
|
||||
loc = "/" + loc;
|
||||
|
||||
IPipe* cs;
|
||||
if (ssl)
|
||||
cs = Server->ConnectSslStream(hostname, port, 10000);
|
||||
else
|
||||
cs = Server->ConnectStream(hostname, port, 10000);
|
||||
|
||||
if (cs == NULL)
|
||||
return cs;
|
||||
|
||||
unsigned char websocket_key[16];
|
||||
Server->secureRandomFill(reinterpret_cast<char*>(websocket_key), sizeof(websocket_key));
|
||||
|
||||
std::string websocket_key_str = base64_encode(websocket_key, sizeof(websocket_key));
|
||||
|
||||
cs->Write(std::string("GET ")+loc+" HTTP/1.1\r\n"
|
||||
"Host: "+hostname+"\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: "+ websocket_key_str+"\r\n"
|
||||
"Sec-WebSocket-Protocol: urbackup\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n");
|
||||
|
||||
char buf[512];
|
||||
|
||||
int64 resp_timeout = 30000;
|
||||
|
||||
int64 starttime = Server->getTimeMS();
|
||||
int state = 0;
|
||||
std::string header;
|
||||
std::string pipe_add;
|
||||
bool has_header = false;
|
||||
do
|
||||
{
|
||||
size_t read = cs->Read(buf, sizeof(buf), 10000);
|
||||
|
||||
if (read > 0)
|
||||
{
|
||||
for (size_t i = 0; i < read; ++i)
|
||||
{
|
||||
char ch = buf[i];
|
||||
if (state == 0)
|
||||
{
|
||||
if (ch == '\r')
|
||||
state = 1;
|
||||
else if (ch == '\n')
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 1)
|
||||
{
|
||||
if (ch == '\n')
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 2)
|
||||
{
|
||||
if (ch == '\r')
|
||||
state = 3;
|
||||
else if (ch == '\n')
|
||||
state = 4;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 3)
|
||||
{
|
||||
if (ch == '\n')
|
||||
state = 4;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 4)
|
||||
{
|
||||
pipe_add.assign(buf + i, read - i);
|
||||
has_header = true;
|
||||
break;
|
||||
}
|
||||
|
||||
header += ch;
|
||||
}
|
||||
}
|
||||
} while (!has_header &&
|
||||
Server->getTimeMS() - starttime < resp_timeout);
|
||||
|
||||
|
||||
if (!has_header)
|
||||
{
|
||||
Server->Log("Timeout connecting via web socket");
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!next(header, 0, "HTTP/1.1 101"))
|
||||
{
|
||||
Server->Log("Error connecting via web socket. Response: " + header, LL_ERROR);
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::vector<std::string> headers;
|
||||
Tokenize(getafter("\n", header), headers, "\n");
|
||||
str_map header_map;
|
||||
for (size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
std::string key = strlower(getuntil(":", headers[i]));
|
||||
header_map[key] = trim(getafter(":", headers[i]));
|
||||
}
|
||||
|
||||
if (header_map["sec-websocket-protocol"] != "urbackup")
|
||||
{
|
||||
Server->Log("Unknown web socket protocol \"" + header_map["sec-websocket-protocol"] + "\"", LL_ERROR);
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (header_map["upgrade"] != "websocket")
|
||||
{
|
||||
Server->Log("Unknown web socket upgrade value \"" + header_map["upgrade"] + "\"", LL_ERROR);
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (header_map["connection"] != "upgrade")
|
||||
{
|
||||
Server->Log("Unknown web socket connection value \"" + header_map["connection"] + "\"", LL_ERROR);
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string accept_key = header_map["sec-websocket-accept"];
|
||||
|
||||
std::string expected_accept_key = websocket_key_str + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
std::string sha_bin = crypto_fak->sha1Binary(expected_accept_key);
|
||||
|
||||
expected_accept_key = base64_encode(reinterpret_cast<const unsigned char*>(sha_bin.data()), static_cast<unsigned int>(sha_bin.size()));
|
||||
|
||||
if (accept_key != expected_accept_key)
|
||||
{
|
||||
Server->Log("Web socket accept key wrong. Expected " + expected_accept_key + " got "+accept_key, LL_ERROR);
|
||||
Server->destroy(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new WebSocketPipe(cs, true, false, pipe_add, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Server->ConnectStream(selected_server_settings.hostname,
|
||||
|
||||
438
urbackupcommon/WebSocketPipe.cpp
Normal file
438
urbackupcommon/WebSocketPipe.cpp
Normal file
@ -0,0 +1,438 @@
|
||||
#include "WebSocketPipe.h"
|
||||
#include "WebSocketPipe.h"
|
||||
#include "../Interface/Server.h"
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
inline WebSocketPipe::WebSocketPipe(IPipe* pipe, bool mask_writes, bool expect_read_mask, std::string pipe_add, bool destroy_pipe)
|
||||
: pipe(pipe), mask_writes(mask_writes), expect_read_mask(expect_read_mask), has_error(false),
|
||||
pipe_add(pipe_add), read_state(EReadState_Header1), masking_key(0), destroy_pipe(destroy_pipe),
|
||||
read_mutex(Server->createMutex()), write_mutex(Server->createMutex())
|
||||
{
|
||||
if (mask_writes)
|
||||
{
|
||||
/*
|
||||
* Just use a fixed non-random masking key. We are not a browser, so the security implications
|
||||
* are a bit different
|
||||
*/
|
||||
while (masking_key == 0)
|
||||
{
|
||||
masking_key = Server->getRandomNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketPipe::~WebSocketPipe()
|
||||
{
|
||||
if (destroy_pipe)
|
||||
delete pipe;
|
||||
}
|
||||
|
||||
size_t WebSocketPipe::Read(char* buffer, size_t bsize, int timeoutms)
|
||||
{
|
||||
IScopedLock lock(read_mutex.get());
|
||||
|
||||
if (!pipe_add.empty())
|
||||
{
|
||||
size_t consumed_out = 0;
|
||||
size_t data_size = consume(&pipe_add[0], (std::min)(bsize, pipe_add.size()), timeoutms, &consumed_out);
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
memcpy(buffer, pipe_add.data(), data_size);
|
||||
}
|
||||
|
||||
if (consumed_out > 0)
|
||||
{
|
||||
pipe_add.erase(0, consumed_out);
|
||||
}
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
return data_size;
|
||||
}
|
||||
}
|
||||
|
||||
int64 starttime = 0;
|
||||
|
||||
if (timeoutms > 0)
|
||||
{
|
||||
starttime = Server->getTimeMS();
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
int curr_timeoutms = static_cast<int>(timeoutms > 0 ? (timeoutms - (Server->getTimeMS() - starttime)) : timeoutms);
|
||||
size_t read = pipe->Read(buffer, bsize, curr_timeoutms);
|
||||
|
||||
if (read > 0)
|
||||
{
|
||||
size_t data_size = consume(buffer, read, curr_timeoutms, NULL);
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
return data_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} while (timeoutms == -1 || (timeoutms > 0 && Server->getTimeMS() - starttime < timeoutms));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WebSocketPipe::Write(const char* buffer, size_t bsize, int timeoutms, bool flush)
|
||||
{
|
||||
IScopedLock lock(write_mutex.get());
|
||||
|
||||
size_t payload_len_size = 1;
|
||||
|
||||
if (bsize > 65535)
|
||||
{
|
||||
payload_len_size = 8;
|
||||
}
|
||||
else if (bsize > 125)
|
||||
{
|
||||
payload_len_size = 2;
|
||||
}
|
||||
|
||||
char header[2 + 8 + 4];
|
||||
|
||||
unsigned char bits = 0;
|
||||
|
||||
bits |= 1 << 7; //FIN bit
|
||||
|
||||
unsigned char opcode = 2; //binary frame;
|
||||
|
||||
bits |= opcode;
|
||||
|
||||
header[0] = static_cast<char>(bits);
|
||||
|
||||
size_t header_pos = 1;
|
||||
|
||||
if (bsize > 65535)
|
||||
{
|
||||
header[header_pos] = 127;
|
||||
++header_pos;
|
||||
|
||||
uint64 payload_size = bsize;
|
||||
memcpy(&header[header_pos], &payload_size, sizeof(payload_size));
|
||||
header_pos += sizeof(payload_size);
|
||||
}
|
||||
else if (bsize > 125)
|
||||
{
|
||||
header[header_pos] = 126;
|
||||
++header_pos;
|
||||
|
||||
unsigned short payload_size = static_cast<unsigned short>(bsize);
|
||||
memcpy(&header[header_pos], &payload_size, sizeof(payload_size));
|
||||
header_pos += sizeof(payload_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
header[header_pos] = static_cast<char>(bsize);
|
||||
++header_pos;
|
||||
}
|
||||
|
||||
if (mask_writes)
|
||||
{
|
||||
header[1] |= 1 << 7;
|
||||
|
||||
memcpy(&header[header_pos], &masking_key, sizeof(masking_key));
|
||||
header_pos += sizeof(masking_key);
|
||||
|
||||
std::vector<char> new_buf;
|
||||
new_buf.resize(header_pos + bsize);
|
||||
|
||||
memcpy(new_buf.data(), header, header_pos);
|
||||
|
||||
char* mask_ptr = reinterpret_cast<char*>(&masking_key);
|
||||
for (size_t i = 0; i < bsize; ++i)
|
||||
{
|
||||
size_t j = i % 4;
|
||||
new_buf[header_pos + i] = buffer[i] ^ mask_ptr[j];
|
||||
}
|
||||
|
||||
return pipe->Write(new_buf.data(), header_pos + bsize, timeoutms, flush);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!pipe->Write(header, header_pos, timeoutms, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return pipe->Write(buffer, bsize, timeoutms, flush);
|
||||
}
|
||||
}
|
||||
|
||||
size_t WebSocketPipe::Read(std::string* ret, int timeoutms)
|
||||
{
|
||||
IScopedLock lock(read_mutex.get());
|
||||
|
||||
if (!pipe_add.empty())
|
||||
{
|
||||
size_t consumed_out = 0;
|
||||
size_t data_size = consume(&pipe_add[0], pipe_add.size(), timeoutms, &consumed_out);
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
ret->assign(pipe_add.data(), data_size);
|
||||
}
|
||||
|
||||
if (consumed_out > 0)
|
||||
{
|
||||
pipe_add.erase(0, consumed_out);
|
||||
}
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
return data_size;
|
||||
}
|
||||
}
|
||||
|
||||
int64 starttime = 0;
|
||||
|
||||
if (timeoutms > 0)
|
||||
{
|
||||
starttime = Server->getTimeMS();
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
int curr_timeoutms = static_cast<int>(timeoutms > 0 ? (timeoutms - (Server->getTimeMS() - starttime)) : timeoutms);
|
||||
size_t read = pipe->Read(ret, curr_timeoutms);
|
||||
|
||||
if (read > 0)
|
||||
{
|
||||
size_t data_size = consume(&(*ret)[0], ret->size(), curr_timeoutms, NULL);
|
||||
|
||||
if (data_size > 0)
|
||||
{
|
||||
ret->resize(data_size);
|
||||
return ret->size();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} while (timeoutms < 0
|
||||
|| (timeoutms > 0 && Server->getTimeMS() - starttime < timeoutms));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t WebSocketPipe::consume(char* buffer, size_t bsize, int write_timeoutms, size_t* consumed_out)
|
||||
{
|
||||
size_t consumed = 0;
|
||||
size_t out_off = 0;
|
||||
while (bsize > consumed)
|
||||
{
|
||||
switch (read_state)
|
||||
{
|
||||
case EReadState_Header1:
|
||||
{
|
||||
header_bits1 = buffer[consumed];
|
||||
++consumed;
|
||||
read_state = EReadState_HeaderSize1;
|
||||
|
||||
unsigned char opcode = get_opcode();
|
||||
|
||||
if (opcode != 0
|
||||
&& opcode != 1
|
||||
&& opcode != 2
|
||||
&& opcode != 8
|
||||
&& opcode != 9
|
||||
&& opcode != 10)
|
||||
{
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
if (!(header_bits1 & 1)
|
||||
&& opcode != 0
|
||||
&& opcode != 1
|
||||
&& opcode != 2)
|
||||
{
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
}break;
|
||||
case EReadState_HeaderSize1:
|
||||
{
|
||||
header_bits2 = buffer[consumed];
|
||||
|
||||
if (expect_read_mask
|
||||
&& !has_read_mask())
|
||||
{
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
unsigned char tmp_payload_size = header_bits2 & 0x7F;
|
||||
++consumed;
|
||||
|
||||
if (tmp_payload_size < 126)
|
||||
{
|
||||
payload_size = tmp_payload_size;
|
||||
|
||||
if (has_read_mask())
|
||||
{
|
||||
read_state = EReadState_HeaderMask;
|
||||
remaining_size_bytes = 4;
|
||||
consumed_size_bytes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
read_mask = 0;
|
||||
read_state = EReadState_Body;
|
||||
}
|
||||
}
|
||||
else if (tmp_payload_size == 126)
|
||||
{
|
||||
remaining_size_bytes = 2;
|
||||
consumed_size_bytes = 0;
|
||||
read_state = EReadState_HeaderSize2;
|
||||
}
|
||||
else if (tmp_payload_size == 127)
|
||||
{
|
||||
remaining_size_bytes = 8;
|
||||
consumed_size_bytes = 0;
|
||||
read_state = EReadState_HeaderSize2;
|
||||
}
|
||||
else
|
||||
{
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
}break;
|
||||
case EReadState_HeaderSize2:
|
||||
{
|
||||
//PERF: In EReadState_HeaderSize2 and EReadState_HeaderMask read multiple via memcpy if available
|
||||
unsigned char size_byte = buffer[consumed];
|
||||
++consumed;
|
||||
--remaining_size_bytes;
|
||||
|
||||
payload_size |= size_byte << (consumed_size_bytes * 8);
|
||||
++consumed_size_bytes;
|
||||
|
||||
if (remaining_size_bytes == 0)
|
||||
{
|
||||
if (has_read_mask())
|
||||
{
|
||||
read_state = EReadState_HeaderMask;
|
||||
remaining_size_bytes = 4;
|
||||
consumed_size_bytes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
read_mask = 0;
|
||||
read_state = EReadState_Body;
|
||||
}
|
||||
}
|
||||
}break;
|
||||
case EReadState_HeaderMask:
|
||||
{
|
||||
unsigned char mask_byte = buffer[consumed];
|
||||
++consumed;
|
||||
--remaining_size_bytes;
|
||||
|
||||
read_mask |= mask_byte << (consumed_size_bytes * 8);
|
||||
++consumed_size_bytes;
|
||||
|
||||
if (remaining_size_bytes == 0)
|
||||
{
|
||||
read_state = EReadState_Body;
|
||||
read_mask_idx = 0;
|
||||
}
|
||||
} break;
|
||||
case EReadState_Body:
|
||||
{
|
||||
size_t toread = static_cast<size_t>((std::min)(static_cast<uint64>(bsize - consumed), payload_size));
|
||||
payload_size -= toread;
|
||||
|
||||
unsigned char opcode = get_opcode();
|
||||
|
||||
if (opcode != 0 && opcode!=1 && opcode != 2)
|
||||
{
|
||||
//Ignore payload
|
||||
consumed += toread;
|
||||
}
|
||||
else if (out_off == consumed)
|
||||
{
|
||||
if (read_mask != 0)
|
||||
{
|
||||
for (size_t i = 0; i < toread; ++i)
|
||||
{
|
||||
buffer[out_off] = buffer[out_off] ^ reinterpret_cast<char*>(&read_mask)[read_mask_idx % 4];
|
||||
++read_mask_idx;
|
||||
++out_off;
|
||||
}
|
||||
consumed = out_off;
|
||||
}
|
||||
else
|
||||
{
|
||||
consumed += toread;
|
||||
out_off += toread;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(out_off < consumed);
|
||||
if (read_mask != 0)
|
||||
{
|
||||
for (size_t i = 0; i < toread; ++i)
|
||||
{
|
||||
buffer[out_off] = buffer[consumed] ^ reinterpret_cast<char*>(&read_mask)[read_mask_idx % 4];
|
||||
++read_mask_idx;
|
||||
++out_off;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(&buffer[out_off], &buffer[consumed], toread);
|
||||
consumed += toread;
|
||||
out_off += toread;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload_size == 0)
|
||||
{
|
||||
read_state = EReadState_Header1;
|
||||
|
||||
unsigned char opcode = get_opcode();
|
||||
|
||||
if (opcode == 8)
|
||||
{
|
||||
//Close
|
||||
char msg[2];
|
||||
msg[0] = header_bits1;
|
||||
msg[1] = header_bits2 | (1<<7);
|
||||
pipe->Write(msg, 2, write_timeoutms, true);
|
||||
has_error = true;
|
||||
}
|
||||
else if (opcode == 9)
|
||||
{
|
||||
//Close
|
||||
char msg[2];
|
||||
unsigned char opcode = 10; //pong
|
||||
msg[0] = opcode | (1 << 7);
|
||||
msg[1] = (1<<7);
|
||||
if (!pipe->Write(msg, 2, write_timeoutms, true))
|
||||
{
|
||||
has_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
if (consumed_out != NULL)
|
||||
*consumed_out = consumed;
|
||||
|
||||
return out_off;
|
||||
}
|
||||
111
urbackupcommon/WebSocketPipe.h
Normal file
111
urbackupcommon/WebSocketPipe.h
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Interface/Pipe.h"
|
||||
#include "../Interface/Mutex.h"
|
||||
#include <memory>
|
||||
|
||||
class WebSocketPipe : public IPipe
|
||||
{
|
||||
enum EReadState
|
||||
{
|
||||
EReadState_Header1,
|
||||
EReadState_HeaderSize1,
|
||||
EReadState_HeaderSize2,
|
||||
EReadState_HeaderMask,
|
||||
EReadState_Body
|
||||
};
|
||||
|
||||
public:
|
||||
WebSocketPipe(IPipe* pipe, bool mask_writes, bool expect_read_mask, std::string pipe_add, bool destroy_pipe);
|
||||
~WebSocketPipe();
|
||||
|
||||
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)
|
||||
{
|
||||
return Write(str.data(), str.size(), timeoutms, flush);
|
||||
}
|
||||
virtual bool Flush(int timeoutms = -1)
|
||||
{
|
||||
return pipe->Flush(timeoutms);
|
||||
}
|
||||
virtual bool isWritable(int timeoutms = 0)
|
||||
{
|
||||
return pipe->isWritable();
|
||||
}
|
||||
virtual bool isReadable(int timeoutms = 0)
|
||||
{
|
||||
return pipe->isReadable();
|
||||
}
|
||||
virtual bool hasError(void)
|
||||
{
|
||||
return has_error || pipe->hasError();
|
||||
}
|
||||
virtual void shutdown(void)
|
||||
{
|
||||
pipe->shutdown();
|
||||
}
|
||||
virtual size_t getNumElements(void)
|
||||
{
|
||||
return pipe->getNumElements();
|
||||
}
|
||||
virtual void addThrottler(IPipeThrottler* throttler)
|
||||
{
|
||||
pipe->addThrottler(throttler);
|
||||
}
|
||||
virtual void addOutgoingThrottler(IPipeThrottler* throttler)
|
||||
{
|
||||
pipe->addOutgoingThrottler(throttler);
|
||||
}
|
||||
virtual void addIncomingThrottler(IPipeThrottler* throttler)
|
||||
{
|
||||
pipe->addIncomingThrottler(throttler);
|
||||
}
|
||||
virtual _i64 getTransferedBytes(void)
|
||||
{
|
||||
return pipe->getTransferedBytes();
|
||||
}
|
||||
virtual void resetTransferedBytes(void)
|
||||
{
|
||||
pipe->resetTransferedBytes();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool has_read_mask()
|
||||
{
|
||||
return header_bits2 & (1 << 7);
|
||||
}
|
||||
|
||||
unsigned char get_opcode()
|
||||
{
|
||||
return (header_bits1 & (1 << 3 | 1 << 2 | 1 << 1 | 1 << 0));
|
||||
}
|
||||
|
||||
size_t consume(char* buffer, size_t bsize, int write_timeoutms, size_t* consumed_out);
|
||||
|
||||
bool mask_writes;
|
||||
bool expect_read_mask;
|
||||
IPipe* pipe;
|
||||
|
||||
EReadState read_state;
|
||||
std::vector<char> read_buffer;
|
||||
unsigned char header_bits1;
|
||||
unsigned char header_bits2;
|
||||
uint64 payload_size;
|
||||
size_t remaining_size_bytes;
|
||||
size_t consumed_size_bytes;
|
||||
unsigned int read_mask;
|
||||
unsigned int read_mask_idx;
|
||||
bool has_error;
|
||||
std::string pipe_add;
|
||||
unsigned int masking_key;
|
||||
bool destroy_pipe;
|
||||
|
||||
std::auto_ptr<IMutex> read_mutex;
|
||||
std::auto_ptr<IMutex> write_mutex;
|
||||
};
|
||||
90
urbackupserver/WebSocketConnector.cpp
Normal file
90
urbackupserver/WebSocketConnector.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "WebSocketConnector.h"
|
||||
#include "../stringtools.h"
|
||||
#include "../Interface/Server.h"
|
||||
#include "../cryptoplugin/ICryptoFactory.h"
|
||||
#include "../urbackupcommon/WebSocketPipe.h"
|
||||
#include <assert.h>
|
||||
|
||||
extern ICryptoFactory* crypto_fak;
|
||||
|
||||
void WebSocketConnector::Execute(str_map& GET, THREAD_ID tid, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name)
|
||||
{
|
||||
if (PARAMS["CONNECTION"] != "Upgrade")
|
||||
{
|
||||
pipe->Write("HTTP/1.1 500 Expecting Connection: Upgrade\r\nConnection: Close\r\n\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string protocol_list = PARAMS["SEC-WEBSOCKET-PROTOCOL"];
|
||||
std::vector<std::string> protocols;
|
||||
Tokenize(protocol_list, protocols, ",");
|
||||
|
||||
for (size_t i = 0; i < protocols.size(); ++i)
|
||||
protocols[i] = trim(protocols[i]);
|
||||
|
||||
if (std::find(protocols.begin(), protocols.end(), "urbackup") == protocols.end())
|
||||
{
|
||||
pipe->Write("HTTP/1.1 500 urbackup protocol not supported\r\nConnection: Close\r\n\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (PARAMS["SEC-WEBSOCKET-VERSION"] != "13")
|
||||
{
|
||||
pipe->Write("HTTP/1.1 500 websocket protocol version not supported\r\nConnection: Close\r\n\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string websocket_key = trim(PARAMS["SEC-WEBSOCKET-KEY"]);
|
||||
websocket_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
std::string key_response = crypto_fak->sha1Binary(websocket_key);
|
||||
pipe->Write(std::string("HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: ") + base64_encode(reinterpret_cast<const unsigned char*>(key_response.data()),
|
||||
static_cast<unsigned int>(key_response.size())) + "\r\n"
|
||||
"Sec-WebSocket-Protocol: urbackup\r\n\r\n");
|
||||
|
||||
ICustomClient* client = wrapped_service->createClient();
|
||||
|
||||
str_map::iterator it_forwarded_for = PARAMS.find("X-FORWARDED-FOR");
|
||||
|
||||
WebSocketPipe ws_pipe(pipe, false, true, std::string(), false);
|
||||
|
||||
client->Init(tid, &ws_pipe, it_forwarded_for != PARAMS.end() ? it_forwarded_for->second : endpoint_name);
|
||||
|
||||
while (true)
|
||||
{
|
||||
bool b = client->Run(NULL);
|
||||
if (!b)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (client->wantReceive())
|
||||
{
|
||||
if (ws_pipe.isReadable(10))
|
||||
{
|
||||
client->ReceivePackets(NULL);
|
||||
}
|
||||
else if (ws_pipe.hasError())
|
||||
{
|
||||
client->ReceivePackets(NULL);
|
||||
Server->wait(20);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Server->wait(20);
|
||||
}
|
||||
}
|
||||
|
||||
wrapped_service->destroyClient(client);
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::string WebSocketConnector::getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
19
urbackupserver/WebSocketConnector.h
Normal file
19
urbackupserver/WebSocketConnector.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Interface/WebSocket.h"
|
||||
#include "../Interface/Service.h"
|
||||
|
||||
class WebSocketConnector : public IWebSocket
|
||||
{
|
||||
public:
|
||||
WebSocketConnector(IService* wrapped_service, const std::string& name)
|
||||
: wrapped_service(wrapped_service),
|
||||
name(name) {}
|
||||
|
||||
virtual void Execute(str_map& GET, THREAD_ID tid, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name);
|
||||
virtual std::string getName();
|
||||
|
||||
private:
|
||||
IService* wrapped_service;
|
||||
std::string name;
|
||||
};
|
||||
@ -230,6 +230,32 @@ void read_config_file(std::string fn, std::vector<std::string>& real_args)
|
||||
real_args.push_back(strlower(val));
|
||||
}
|
||||
}
|
||||
if (settings->getValue("HTTP_LOCALHOST_ONLY", &val))
|
||||
{
|
||||
val = unquote_value(val);
|
||||
|
||||
if (!val.empty()
|
||||
&& ( val=="1" ||
|
||||
strlower(val)=="true" ||
|
||||
strlower(val)=="yes") )
|
||||
{
|
||||
real_args.push_back("--http_localhost_only");
|
||||
real_args.push_back("1");
|
||||
}
|
||||
}
|
||||
if (settings->getValue("FASTCGI_LOCALHOST_ONLY", &val))
|
||||
{
|
||||
val = unquote_value(val);
|
||||
|
||||
if (!val.empty()
|
||||
&& (val == "1" ||
|
||||
strlower(val) == "true" ||
|
||||
strlower(val) == "yes"))
|
||||
{
|
||||
real_args.push_back("--fastcgi_localhost_only");
|
||||
real_args.push_back("1");
|
||||
}
|
||||
}
|
||||
if (settings->getValue("LOG_ROTATE_FILESIZE", &val))
|
||||
{
|
||||
val = trim(unquote_value(val));
|
||||
|
||||
@ -99,6 +99,7 @@ SStartupStatus startup_status;
|
||||
#include "FileMetadataDownloadThread.h"
|
||||
#include "../urbackupcommon/chunk_hasher.h"
|
||||
#include "LogReport.h"
|
||||
#include "WebSocketConnector.h"
|
||||
|
||||
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||
#include "../common/miniz.h"
|
||||
@ -382,6 +383,8 @@ void detach_other_dbs(IDatabase* db)
|
||||
db->Write("DETACH DATABASE links_db");
|
||||
}
|
||||
|
||||
#include "../urbackupcommon/WebSocketPipe.h"
|
||||
|
||||
DLLEXPORT void LoadActions(IServer* pServer)
|
||||
{
|
||||
Server=pServer;
|
||||
@ -414,6 +417,98 @@ DLLEXPORT void LoadActions(IServer* pServer)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
IPipe* pipe = Server->ConnectStream("192.168.239.142", 8080, 10000);
|
||||
|
||||
pipe->Write("GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
|
||||
"Origin: http://localhost\r\n"
|
||||
"Sec-WebSocket-Protocol: urbackup\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n");
|
||||
|
||||
char buf[512];
|
||||
|
||||
int64 resp_timeout = 30000;
|
||||
|
||||
int64 starttime = Server->getTimeMS();
|
||||
int state = 0;
|
||||
std::string header;
|
||||
std::string pipe_add;
|
||||
bool has_header = false;
|
||||
do
|
||||
{
|
||||
_u32 read = pipe->Read(buf, sizeof(buf), 10000);
|
||||
|
||||
if (read > 0)
|
||||
{
|
||||
for (_u32 i = 0; i < read; ++i)
|
||||
{
|
||||
char ch = buf[i];
|
||||
if (state == 0)
|
||||
{
|
||||
if (ch == '\r')
|
||||
state = 1;
|
||||
else if (ch == '\n')
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 1)
|
||||
{
|
||||
if (ch == '\n')
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 2)
|
||||
{
|
||||
if (ch == '\r')
|
||||
state = 3;
|
||||
else if (ch == '\n')
|
||||
state = 4;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 3)
|
||||
{
|
||||
if (ch == '\n')
|
||||
state = 4;
|
||||
else
|
||||
state = 0;
|
||||
}
|
||||
else if (state == 4)
|
||||
{
|
||||
pipe_add.assign(buf + i, read - i);
|
||||
has_header = true;
|
||||
break;
|
||||
}
|
||||
|
||||
header += ch;
|
||||
}
|
||||
}
|
||||
} while (!has_header &&
|
||||
Server->getTimeMS() - starttime < resp_timeout);
|
||||
|
||||
|
||||
WebSocketPipe ws_pipe(pipe, true, false, pipe_add);
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string ret;
|
||||
ws_pipe.Read(&ret);
|
||||
|
||||
Server->Log("Read " + ret);
|
||||
|
||||
if (ret == "something")
|
||||
{
|
||||
ws_pipe.Write("other");
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
std::string download_file=Server->getServerParameter("download_file");
|
||||
if(!download_file.empty())
|
||||
{
|
||||
@ -871,7 +966,11 @@ DLLEXPORT void LoadActions(IServer* pServer)
|
||||
{
|
||||
port=settings.getSettings()->internet_server_port;
|
||||
}
|
||||
Server->StartCustomStreamService(new InternetService(backup_server), "InternetService", port);
|
||||
InternetService* internet_service = new InternetService(backup_server);
|
||||
Server->StartCustomStreamService(internet_service, "InternetService", port);
|
||||
|
||||
Server->addWebSocket(new WebSocketConnector(internet_service, "socket"));
|
||||
Server->addWebSocket(new WebSocketConnector(internet_service, ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -213,6 +213,7 @@
|
||||
<ClCompile Include="..\urbackupcommon\SparseFile.cpp" />
|
||||
<ClCompile Include="..\urbackupcommon\TreeHash.cpp" />
|
||||
<ClCompile Include="..\urbackupcommon\WalCheckpointThread.cpp" />
|
||||
<ClCompile Include="..\urbackupcommon\WebSocketPipe.cpp" />
|
||||
<ClCompile Include="Alerts.cpp" />
|
||||
<ClCompile Include="apps\blockalign.cpp" />
|
||||
<ClCompile Include="apps\check_files_index.cpp" />
|
||||
@ -300,6 +301,7 @@
|
||||
<ClCompile Include="treediff\TreeNode.cpp" />
|
||||
<ClCompile Include="treediff\TreeReader.cpp" />
|
||||
<ClCompile Include="verify_hashes.cpp" />
|
||||
<ClCompile Include="WebSocketConnector.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\common\adler32.h" />
|
||||
@ -333,6 +335,7 @@
|
||||
<ClInclude Include="..\urbackupcommon\SparseFile.h" />
|
||||
<ClInclude Include="..\urbackupcommon\TreeHash.h" />
|
||||
<ClInclude Include="..\urbackupcommon\WalCheckpointThread.h" />
|
||||
<ClInclude Include="..\urbackupcommon\WebSocketPipe.h" />
|
||||
<ClInclude Include="action_header.h" />
|
||||
<ClInclude Include="actions.h" />
|
||||
<ClInclude Include="Alerts.h" />
|
||||
@ -402,6 +405,7 @@
|
||||
<ClInclude Include="treediff\TreeNode.h" />
|
||||
<ClInclude Include="treediff\TreeReader.h" />
|
||||
<ClInclude Include="server_status.h" />
|
||||
<ClInclude Include="WebSocketConnector.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@ -390,6 +390,12 @@
|
||||
<ClCompile Include="serverinterface\restore_image.cpp">
|
||||
<Filter>serverinterface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WebSocketConnector.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\urbackupcommon\WebSocketPipe.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="action_header.h">
|
||||
@ -692,5 +698,11 @@
|
||||
<ClInclude Include="..\urbackupcommon\sha2\sha2.h">
|
||||
<Filter>sha2</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WebSocketConnector.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\urbackupcommon\WebSocketPipe.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Loading…
Reference in New Issue
Block a user