//////////////////////////////////////////////////////////////////////////////// /// @brief tasks used to establish connections /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Achim Brandt /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2008-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "ListenTask.h" #include #ifdef TRI_HAVE_LINUX_SOCKETS #include #include #include #include #include #endif #ifdef TRI_HAVE_WINSOCK2_H #include "Basics/win-utils.h" #include #include #endif #include #include "Basics/MutexLocker.h" #include "Basics/StringUtils.h" #include "Basics/logging.h" #include "Basics/socket-utils.h" #include "Scheduler/Scheduler.h" using namespace triagens::basics; using namespace triagens::rest; // ----------------------------------------------------------------------------- // constructors and destructors // ----------------------------------------------------------------------------- ListenTask::ListenTask (Endpoint* endpoint) : Task("ListenTask"), readWatcher(0), _endpoint(endpoint), acceptFailures(0) { TRI_invalidatesocket(&_listenSocket); bindSocket(); } ListenTask::~ListenTask () { if (readWatcher != 0) { _scheduler->uninstallEvent(readWatcher); } } // ----------------------------------------------------------------------------- // public methods // ----------------------------------------------------------------------------- bool ListenTask::isBound () const { MUTEX_LOCKER(changeLock); return _endpoint != 0 && _endpoint->isConnected(); } // ----------------------------------------------------------------------------- // Task methods // ----------------------------------------------------------------------------- bool ListenTask::setup (Scheduler* scheduler, EventLoop loop) { if (! isBound()) { return true; } #ifdef _WIN32 // .......................................................................... // The problem we have here is that this opening of the fs handle may fail. // There is no mechanism to the calling function to report failure. // .......................................................................... LOG_TRACE("attempting to convert socket handle to socket descriptor"); if (! TRI_isvalidsocket(_listenSocket)) { LOG_ERROR("In ListenTask::setup could not convert socket handle to socket descriptor -- invalid socket handle"); return false; } // For the official version of libev we would do this: // int res = _open_osfhandle (_listenSocket.fileHandle, 0); // However, this opens a whole lot of problems and in general one should // never use _open_osfhandle for sockets. // Therefore, we do the following, although it has the potential to // lose the higher bits of the socket handle: int res = (int)_listenSocket.fileHandle; if (res == - 1) { LOG_ERROR("In ListenTask::setup could not convert socket handle to socket descriptor -- _open_osfhandle(...) failed"); res = TRI_CLOSE_SOCKET(_listenSocket); if (res != 0) { res = WSAGetLastError(); LOG_ERROR("In ListenTask::setup closesocket(...) failed with error code: %d", (int) res); } TRI_invalidatesocket(&_listenSocket); return false; } _listenSocket.fileDescriptor = res; #endif this->_scheduler = scheduler; this->_loop = loop; readWatcher = scheduler->installSocketEvent(loop, EVENT_SOCKET_READ, this, _listenSocket); if (readWatcher == -1) { return false; } return true; } void ListenTask::cleanup () { if (_scheduler == 0) { LOG_WARNING("In ListenTask::cleanup the scheduler has disappeared -- invalid pointer"); readWatcher = 0; return; } _scheduler->uninstallEvent(readWatcher); readWatcher = 0; } bool ListenTask::handleEvent (EventToken token, EventType revents) { if (token == readWatcher) { if ((revents & EVENT_SOCKET_READ) == 0) { return true; } sockaddr_in addr; socklen_t len = sizeof(addr); memset(&addr, 0, sizeof(addr)); // accept connection TRI_socket_t connectionSocket; connectionSocket = TRI_accept(_listenSocket, (sockaddr*) &addr, &len); if (! TRI_isvalidsocket(connectionSocket)) { ++acceptFailures; if (acceptFailures < MAX_ACCEPT_ERRORS) { LOG_WARNING("accept failed with %d (%s)", (int) errno, strerror(errno)); } else if (acceptFailures == MAX_ACCEPT_ERRORS) { LOG_ERROR("too many accept failures, stopping logging"); } // TODO GeneralFigures::incCounter(); return true; } acceptFailures = 0; struct sockaddr_in addr_out; socklen_t len_out = sizeof(addr_out); int res = TRI_getsockname(connectionSocket, (sockaddr*) &addr_out, &len_out); if (res != TRI_ERROR_NO_ERROR) { TRI_CLOSE_SOCKET(connectionSocket); LOG_WARNING("getsockname failed with %d (%s)", errno, strerror(errno)); // TODO GeneralFigures::incCounter(); return true; } // disable nagle's algorithm, set to non-blocking and close-on-exec bool result = _endpoint->initIncoming(connectionSocket); if (! result) { TRI_CLOSE_SOCKET(connectionSocket); // TODO GeneralFigures::incCounter(); return true; } // set client address and port ConnectionInfo info; char host[NI_MAXHOST], serv[NI_MAXSERV]; if (getnameinfo((sockaddr*) &addr, len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { info.clientAddress = std::string(host); info.clientPort = addr.sin_port; } else { info.clientAddress = inet_ntoa(addr.sin_addr); info.clientPort = addr.sin_port; } info.serverAddress = _endpoint->getHost(); info.serverPort = _endpoint->getPort(); info.endpoint = _endpoint->getSpecification(); info.endpointType = _endpoint->getDomainType(); return handleConnected(connectionSocket, info); } return true; } // ----------------------------------------------------------------------------- // private methods // ----------------------------------------------------------------------------- bool ListenTask::bindSocket () { _listenSocket = _endpoint->connect(30, 300); // connect timeout in seconds if (! TRI_isvalidsocket(_listenSocket)) { return false; } return true; } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: