/************************************************************************* * 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" #include "AcceptThread.h" #include "Server.h" #include "stringtools.h" #include "SelectThread.h" #include "Client.h" #include #ifndef _WIN32 #include #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(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(sentgetServerParameter("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;iLog("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;iFreeClients()>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; }