mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
372 lines
8.2 KiB
C++
372 lines
8.2 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 "vld.h"
|
|
|
|
#include "AcceptThread.h"
|
|
#include "Server.h"
|
|
#include "stringtools.h"
|
|
#include "SelectThread.h"
|
|
#include "Client.h"
|
|
#include <memory.h>
|
|
#ifndef _WIN32
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
extern bool run;
|
|
|
|
namespace
|
|
{
|
|
bool prepareSocket(SOCKET s)
|
|
{
|
|
#if !defined(_WIN32) && !defined(SOCK_CLOEXEC)
|
|
fcntl(s, F_SETFD, fcntl(s, F_GETFD, 0) | FD_CLOEXEC);
|
|
#endif
|
|
|
|
int optval = 1;
|
|
int rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(int));
|
|
if (rc == SOCKET_ERROR)
|
|
{
|
|
Server->Log("Failed setting SO_REUSEADDR", LL_ERROR);
|
|
return false;
|
|
}
|
|
#ifdef __APPLE__
|
|
optval = 1;
|
|
setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void*)&optval, sizeof(optval));
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
|
|
OutputCallback::OutputCallback(SOCKET fd_)
|
|
{
|
|
fd=fd_;
|
|
}
|
|
|
|
OutputCallback::~OutputCallback()
|
|
{
|
|
closesocket(fd);
|
|
}
|
|
|
|
void OutputCallback::operator() (const void* buf, size_t count)
|
|
{
|
|
size_t sent=0;
|
|
do
|
|
{
|
|
int rc = send(fd, (const char*)buf+sent, static_cast<int>(count-sent), MSG_NOSIGNAL);
|
|
if (rc < 0)
|
|
{
|
|
int ec;
|
|
#ifdef _WIN32
|
|
ec=GetLastError();
|
|
#else
|
|
ec=errno;
|
|
#endif
|
|
Server->Log("Send failed in OutputCallback ec="+convert(ec), LL_INFO);
|
|
throw std::runtime_error("Send failed in OutputCallback");
|
|
}
|
|
else
|
|
{
|
|
sent+=rc;
|
|
}
|
|
}
|
|
while(sent<count);
|
|
}
|
|
|
|
CAcceptThread::CAcceptThread( unsigned int nWorkerThreadsPerMaster, unsigned short int uPort )
|
|
: error(false), s_v6(SOCKET_ERROR), s(SOCKET_ERROR)
|
|
{
|
|
WorkerThreadsPerMaster=nWorkerThreadsPerMaster;
|
|
|
|
bool has_ipv4 = Server->getServerParameter("disable_ipv4").empty();
|
|
bool has_ipv6 = Server->getServerParameter("disable_ipv6").empty();
|
|
|
|
if (!has_ipv4 && !has_ipv6)
|
|
{
|
|
Server->Log("CAcceptThread: IPv4 and IPv6 disabled", LL_ERROR);
|
|
error = true;
|
|
return;
|
|
}
|
|
|
|
if (has_ipv4)
|
|
{
|
|
if (!init_socket_v4(uPort))
|
|
{
|
|
if (Server->getServerParameter("ipv4_optional").empty()
|
|
|| !has_ipv6)
|
|
{
|
|
error = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_ipv6)
|
|
{
|
|
if (!init_socket_v6(uPort))
|
|
{
|
|
if (!Server->getServerParameter("ipv6_required").empty()
|
|
|| !has_ipv4)
|
|
{
|
|
error = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Server->Log("Server started up successfully!",LL_INFO);
|
|
}
|
|
|
|
CAcceptThread::~CAcceptThread()
|
|
{
|
|
closesocket(s);
|
|
if (s_v6 != SOCKET_ERROR)
|
|
closesocket(s_v6);
|
|
Server->Log("Deleting SelectThreads..");
|
|
for(size_t i=0;i<SelectThreads.size();++i)
|
|
{
|
|
delete SelectThreads[i];
|
|
}
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
void printLinError(void)
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EWOULDBLOCK: Server->Log("Reason: EWOULDBLOCK", LL_ERROR); break;
|
|
case EBADF: Server->Log("Reason: EBADF", LL_ERROR); break;
|
|
case ECONNABORTED: Server->Log("Reason: ECONNABORTED", LL_ERROR); break;
|
|
case EINTR: Server->Log("Reason: EINTR", LL_ERROR); break;
|
|
case EINVAL: Server->Log("Reason: EINVAL", LL_ERROR); break;
|
|
case EMFILE: Server->Log("Reason: EMFILE", LL_ERROR); break;
|
|
case ENFILE: Server->Log("Reason: ENFILE", LL_ERROR); break;
|
|
case ENOTSOCK: Server->Log("Reason: ENOTSOCK", LL_ERROR); break;
|
|
case EFAULT: Server->Log("Reason: EFAULT", LL_ERROR); break;
|
|
case ENOBUFS: Server->Log("Reason: ENOBUFS", LL_ERROR); break;
|
|
case ENOMEM: Server->Log("Reason: ENOMEM", LL_ERROR); break;
|
|
case EPROTO: Server->Log("Reason: EPROTO", LL_ERROR); break;
|
|
case EPERM: Server->Log("Reason: EPERM", LL_ERROR); break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CAcceptThread::operator()(bool single)
|
|
{
|
|
do
|
|
{
|
|
socklen_t addrsize=sizeof(sockaddr_in);
|
|
|
|
#ifdef _WIN32
|
|
fd_set fdset;
|
|
FD_ZERO(&fdset);
|
|
if(s!=SOCKET_ERROR)
|
|
FD_SET(s, &fdset);
|
|
if(s_v6!=SOCKET_ERROR)
|
|
FD_SET(s_v6, &fdset);
|
|
|
|
timeval lon;
|
|
lon.tv_sec=1;
|
|
lon.tv_usec=0;
|
|
|
|
_i32 rc=select((int)s+1, &fdset, 0, 0, &lon);
|
|
#else
|
|
pollfd conn[2];
|
|
conn[0].fd = s;
|
|
conn[0].events = POLLIN;
|
|
conn[0].revents = 0;
|
|
conn[1].revents = 0;
|
|
nfds_t num_fds = 1;
|
|
|
|
if (s_v6 != SOCKET_ERROR)
|
|
{
|
|
conn[1].fd = s_v6;
|
|
conn[1].events = POLLIN;
|
|
num_fds = 2;
|
|
}
|
|
|
|
int rc = poll(conn, num_fds, 1000);
|
|
#endif
|
|
if (rc<0)
|
|
return;
|
|
|
|
if (rc > 0)
|
|
{
|
|
for (size_t n = 0; n < 2; ++n)
|
|
{
|
|
#ifdef _WIN32
|
|
SOCKET accept_socket = n == 0 ? s : s_v6;
|
|
if (accept_socket == SOCKET_ERROR)
|
|
continue;
|
|
if (!FD_ISSET(accept_socket, &fdset))
|
|
continue;
|
|
#else
|
|
if (conn[n].revents == 0)
|
|
continue;
|
|
SOCKET accept_socket = conn[n].fd;
|
|
#endif
|
|
|
|
sockaddr_in naddr;
|
|
SOCKET ns = ACCEPT_CLOEXEC(accept_socket, (sockaddr*)&naddr, &addrsize);
|
|
if (ns != SOCKET_ERROR)
|
|
{
|
|
|
|
#ifdef __APPLE__
|
|
int optval = 1;
|
|
setsockopt(ns, SOL_SOCKET, SO_NOSIGPIPE, (void*)&optval, sizeof(optval));
|
|
#endif
|
|
//Server->Log("New Connection incomming", LL_INFO);
|
|
|
|
OutputCallback *output = new OutputCallback(ns);
|
|
FCGIProtocolDriver *driver = new FCGIProtocolDriver(*output);
|
|
|
|
CClient *client = new CClient();
|
|
client->set(ns, output, driver);
|
|
|
|
AddToSelectThread(client);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Accepting client failed", LL_ERROR);
|
|
#ifndef _WIN32
|
|
printLinError();
|
|
#endif
|
|
Server->wait(1000);
|
|
}
|
|
}
|
|
}
|
|
}while(!single);
|
|
}
|
|
|
|
void CAcceptThread::AddToSelectThread(CClient *client)
|
|
{
|
|
for(size_t i=0;i<SelectThreads.size();++i)
|
|
{
|
|
if( SelectThreads[i]->FreeClients()>0 )
|
|
{
|
|
SelectThreads[i]->AddClient( client );
|
|
return;
|
|
}
|
|
}
|
|
|
|
CSelectThread *nt=new CSelectThread(WorkerThreadsPerMaster);
|
|
nt->AddClient( client );
|
|
|
|
SelectThreads.push_back( nt );
|
|
|
|
Server->createThread(nt, "fastcgi: accept");
|
|
}
|
|
|
|
bool CAcceptThread::init_socket_v4(unsigned short port)
|
|
{
|
|
int type = SOCK_STREAM;
|
|
#if !defined(_WIN32) && defined(SOCK_CLOEXEC)
|
|
type |= SOCK_CLOEXEC;
|
|
#endif
|
|
s = socket(AF_INET, type, 0);
|
|
if (s < 1)
|
|
{
|
|
Server->Log("Creating SOCKET failed. Port " + convert((int)port) + " may already be in use", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!prepareSocket(s))
|
|
{
|
|
closesocket(s);
|
|
s = SOCKET_ERROR;
|
|
return false;
|
|
}
|
|
|
|
sockaddr_in addr;
|
|
|
|
memset(&addr, 0, sizeof(sockaddr_in));
|
|
addr.sin_family = AF_INET;
|
|
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)
|
|
{
|
|
Server->Log("Failed binding SOCKET to Port " + convert(port), LL_ERROR);
|
|
closesocket(s);
|
|
s = SOCKET_ERROR;
|
|
return false;
|
|
}
|
|
|
|
listen(s, 10000);
|
|
return true;
|
|
}
|
|
|
|
bool CAcceptThread::init_socket_v6(unsigned short port)
|
|
{
|
|
int type = SOCK_STREAM;
|
|
#if !defined(_WIN32) && defined(SOCK_CLOEXEC)
|
|
type |= SOCK_CLOEXEC;
|
|
#endif
|
|
s_v6 = socket(AF_INET6, type, 0);
|
|
|
|
if (s_v6<1)
|
|
{
|
|
Server->Log("Creating ipv6 SOCKET failed. Port " + convert((int)port) + " may already be in use", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!prepareSocket(s_v6))
|
|
{
|
|
closesocket(s_v6);
|
|
s_v6 = SOCKET_ERROR;
|
|
return false;
|
|
}
|
|
|
|
int optval = 1;
|
|
setsockopt(s_v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&optval, sizeof(optval));
|
|
|
|
sockaddr_in6 addr = {};
|
|
addr.sin6_family = AF_INET6;
|
|
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)
|
|
{
|
|
Server->Log("Failed binding ipv6 SOCKET to Port " + convert(port), LL_ERROR);
|
|
closesocket(s_v6);
|
|
s_v6 = SOCKET_ERROR;
|
|
return false;
|
|
}
|
|
|
|
listen(s_v6, 10000);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CAcceptThread::has_error(void)
|
|
{
|
|
return error;
|
|
}
|