/************************************************************************* * 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 . **************************************************************************/ #include "vld.h" #ifdef _WIN32 #include #endif #include "ServiceAcceptor.h" #include "stringtools.h" #include "ServiceWorker.h" #include #include "Interface/Mutex.h" #include "Interface/Condition.h" #include "Interface/Service.h" 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; } } CServiceAcceptor::CServiceAcceptor(IService * pService, std::string pName, unsigned short port, int pMaxClientsPerThread, IServer::BindTarget bindTarget) : maxClientsPerThread(pMaxClientsPerThread), s_v6(SOCKET_ERROR) { name=pName; service=pService; exitpipe=Server->createMemoryPipe(); do_exit=false; has_error=false; #ifndef _WIN32 pipe(xpipe); #endif int rc; #ifdef _WIN32 WSADATA wsadata; rc = WSAStartup(MAKEWORD(2,2), &wsadata); if(rc == SOCKET_ERROR) return; #endif 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(name+": Creating SOCKET failed",LL_ERROR); has_error=true; return; } if (!prepareSocket(s)) { has_error = true; return; } sockaddr_in addr; memset(&addr, 0, sizeof(sockaddr_in)); addr.sin_family=AF_INET; addr.sin_port=htons(port); switch(bindTarget) { case IServer::BindTarget_All: addr.sin_addr.s_addr= htonl(INADDR_ANY); break; case IServer::BindTarget_Localhost: addr.sin_addr.s_addr=htonl(INADDR_LOOPBACK); break; } rc=bind(s,(sockaddr*)&addr,sizeof(addr)); if(rc==SOCKET_ERROR) { Server->Log(name+": Failed binding socket to port "+convert(port)+". Another instance of this application may already be active and bound to this port.",LL_ERROR); has_error=true; return; } listen(s, 10000); if (Server->getServerParameter("disable_ipv6").empty()) { s_v6 = socket(AF_INET6, type, 0); if (s_v6 < 1) { Server->Log(name + ": Creating v6 SOCKET failed", LL_ERROR); has_error = true; return; } if (!prepareSocket(s_v6)) { has_error = true; return; } int optval = 1; setsockopt(s_v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&optval, sizeof(optval)); sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(port); switch (bindTarget) { case IServer::BindTarget_All: addr.sin6_addr = in6addr_any; break; case IServer::BindTarget_Localhost: addr.sin6_addr= in6addr_loopback; break; } rc = bind(s_v6, (sockaddr*)&addr, sizeof(addr)); if (rc == SOCKET_ERROR) { Server->Log(name + ": Failed binding ipv6 socket to port " + convert(port) + ". Another instance of this application may already be active and bound to this port.", LL_ERROR); has_error = true; return; } listen(s_v6, 10000); } Server->Log(name+": Server started up successfully!",LL_INFO); } CServiceAcceptor::~CServiceAcceptor() { if(has_error) return; do_exit=true; #ifndef _WIN32 char ch=0; write(xpipe[1], &ch, 1); #endif closesocket(s); if (s_v6 != SOCKET_ERROR) closesocket(s_v6); for(size_t i=0;istop(); } size_t c=0; Server->Log("c=0/"+convert(workers.size()+1)); while(cRead(&r); if(r=="ok") { ++c; Server->Log("c="+convert(c)+"/"+convert(workers.size()+1)); } } Server->destroy(exitpipe); for(size_t i=0;i=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 if (do_exit) break; union { sockaddr_in naddr_v4; sockaddr_in6 naddr_v6; } naddr; socklen_t addrsize; if (accept_socket == s_v6) addrsize = sizeof(naddr.naddr_v6); else addrsize = sizeof(naddr.naddr_v4); sockaddr* paddr = (sockaddr*)&naddr; SOCKET ns= ACCEPT_CLOEXEC(accept_socket, paddr, &addrsize); if(ns!=SOCKET_ERROR) { #ifdef __APPLE__ int optval = 1; setsockopt(ns, SOL_SOCKET, SO_NOSIGPIPE, (void*)&optval, sizeof(optval)); #endif //Server->Log(name+": New Connection incomming "+convert(Server->getTimeMS())+" s: "+convert((int)ns), LL_DEBUG); #ifdef _WIN32 int window_size=Server->getSendWindowSize(); if(window_size>0) setsockopt(ns, SOL_SOCKET, SO_SNDBUF, (char *) &window_size, sizeof(window_size)); window_size = Server->getRecvWindowSize(); if(window_size>0) setsockopt(ns, SOL_SOCKET, SO_RCVBUF, (char *) &window_size, sizeof(window_size)); #endif char buf[100]; const char* endpoint = inet_ntop(paddr->sa_family, accept_socket == s_v6 ? (void*)&naddr.naddr_v6.sin6_addr : (void*)&naddr.naddr_v4.sin_addr, buf, sizeof(buf)); AddToWorker(ns, endpoint!=NULL ? endpoint : ""); } } } else { Server->Log("Select error in CServiceAcceptor Thread.", LL_ERROR); break; } } Server->Log("ServiceAcceptor finished", LL_DEBUG); exitpipe->Write("ok"); } void CServiceAcceptor::AddToWorker(SOCKET pSocket, const std::string& endpoint) { for(size_t i=0;igetAvailableSlots()>0 ) { workers[i]->AddClient(pSocket, endpoint); return; } } Server->Log(name+": No available slots... starting new Worker", LL_DEBUG); CServiceWorker *nw=new CServiceWorker(service, name, exitpipe, maxClientsPerThread); workers.push_back(nw); Server->createThread(nw, name+": worker"); nw->AddClient( pSocket, endpoint ); }