void HTTPAcceptor::_acceptConnection() { // This function cannot be called on an invalid socket! PEGASUS_ASSERT(_rep != 0); // Accept the connection (populate the address): struct sockaddr* accept_address; SocketLength address_size; if (_connectionType == LOCAL_CONNECTION) { #ifndef PEGASUS_DISABLE_LOCAL_DOMAIN_SOCKET accept_address = reinterpret_cast<struct sockaddr*>(new struct sockaddr_un); address_size = sizeof(struct sockaddr_un); #else PEGASUS_ASSERT(false); #endif } else { #ifdef PEGASUS_ENABLE_IPV6 accept_address = reinterpret_cast<struct sockaddr*> (new struct sockaddr_storage); address_size = sizeof(struct sockaddr_storage); #else accept_address = reinterpret_cast<struct sockaddr*>(new struct sockaddr_in); address_size = sizeof(struct sockaddr_in); #endif } // It is not necessary to handle EINTR errors from this accept() call. // An EINTR error should not occur on a non-blocking socket. If the // listen socket is blocking and EINTR occurs, the new socket connection // is not accepted here. // EAGAIN errors are also not handled here. An EAGAIN error should not // occur after select() indicates that the listen socket is available for // reading. If the accept() fails with an EAGAIN error code, a new // connection is not accepted here. SocketHandle socket = accept(_rep->socket, accept_address, &address_size); if (socket == PEGASUS_SOCKET_ERROR) { // the remote connection is invalid, destroy client address. delete accept_address; // TCPIP is down reconnect this acceptor if (getSocketError() == PEGASUS_NETWORK_TCPIP_STOPPED) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "Socket has an IO error. TCP/IP down. Try to reconnect."); reconnectConnectionSocket(); return; } PEG_TRACE(( TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor: accept() failed. errno: %u", errno)); return; } // Use an AutoPtr to ensure the socket handle is closed on exception AutoPtr<SocketHandle, CloseSocketHandle> socketPtr(&socket); #ifndef PEGASUS_OS_TYPE_WINDOWS // We need to ensure that the socket number is not higher than // what fits into FD_SETSIZE, because we else won't be able to select on it // and won't ever communicate correct on that socket. if (socket >= FD_SETSIZE) { // the remote connection is invalid, destroy client address. delete accept_address; PEG_TRACE( (TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor out of available sockets." "accept() returned too large socket number %u." "Closing connection to the new client.", socket)); return; } #endif String ipAddress; if (_connectionType == LOCAL_CONNECTION) { ipAddress = "localhost"; } else { #ifdef PEGASUS_ENABLE_IPV6 char ipBuffer[PEGASUS_INET6_ADDRSTR_LEN]; int rc; if ((rc = System::getNameInfo(accept_address, address_size, ipBuffer, PEGASUS_INET6_ADDRSTR_LEN, 0, 0, NI_NUMERICHOST))) { PEG_TRACE(( TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor: getnameinfo() failed. rc: %d", rc)); delete accept_address; return; } ipAddress = ipBuffer; #else unsigned char* sa = reinterpret_cast<unsigned char*>( &reinterpret_cast<struct sockaddr_in*>( accept_address)->sin_addr.s_addr); char ipBuffer[32]; sprintf(ipBuffer, "%u.%u.%u.%u", sa[0], sa[1], sa[2], sa[3]); ipAddress = ipBuffer; #endif } delete accept_address; // set the close on exec flag #if !defined(PEGASUS_OS_TYPE_WINDOWS) && !defined(PEGASUS_OS_VMS) int sock_flags; if ((sock_flags = fcntl(socket, F_GETFD, 0)) < 0) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor: fcntl(F_GETFD) failed"); } else { sock_flags |= FD_CLOEXEC; if (fcntl(socket, F_SETFD, sock_flags) < 0) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor: fcntl(F_SETFD) failed"); } } #endif PEG_TRACE(( TRC_HTTP, Tracer::LEVEL3, "HTTPAcceptor - accept() success. Socket: %u", socket)); SharedPtr<MP_Socket> mp_socket(new MP_Socket( socket, _sslcontext, _sslContextObjectLock, ipAddress)); // mp_socket now has responsibility for closing the socket handle socketPtr.release(); mp_socket->disableBlocking(); { #ifndef PEGASUS_INTEGERS_BOUNDARY_ALIGNED AutoMutex lock(_socketWriteTimeoutMutex); #endif mp_socket->setSocketWriteTimeout(_socketWriteTimeout); } // Perform the SSL handshake, if applicable. Sint32 socketAcceptStatus = mp_socket->accept(); if (socketAcceptStatus < 0) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor: SSL_accept() failed"); return; } // Create a new connection and add it to the connection list: AutoPtr<HTTPConnection> connection(new HTTPConnection( _monitor, mp_socket, ipAddress, this, _outputMessageQueue)); if (HTTPConnection::getIdleConnectionTimeout()) { Time::gettimeofday(&connection->_idleStartTime); } if (socketAcceptStatus == 0) { PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1, "HTTPAcceptor: SSL_accept() pending"); connection->_acceptPending = true; Time::gettimeofday(&connection->_acceptPendingStartTime); } // Solicit events on this new connection's socket: int index; if (-1 == (index = _monitor->solicitSocketMessages( connection->getSocket(), SocketMessage::READ | SocketMessage::EXCEPTION, connection->getQueueId(), MonitorEntry::TYPE_CONNECTION)) ) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPAcceptor::_acceptConnection: Attempt to allocate entry in " "_entries table failed."); return; } connection->_entry_index = index; AutoMutex autoMut(_rep->_connection_mut); _rep->connections.append(connection.get()); connection.release(); }
HTTPConnection* HTTPConnector::connect( const String& host, const Uint32 portNumber, SSLContext * sslContext, Uint32 timeoutMilliseconds, MessageQueue* outputMessageQueue) { PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnector::connect()"); #ifdef PEGASUS_OS_PASE AutoPtr<PaseCcsid> ccsid; #endif SocketHandle socket = PEGASUS_INVALID_SOCKET; // Use an AutoPtr to ensure the socket handle is closed on exception AutoPtr<SocketHandle, CloseSocketHandle> socketPtr(&socket); #ifndef PEGASUS_DISABLE_LOCAL_DOMAIN_SOCKET if (0 == host.size()) { // Set up the domain socket for a local connection sockaddr_un address; #ifdef PEGASUS_OS_PASE // PASE needs ccsid 819 to perform domain socket operation int orig_ccsid; orig_ccsid = _SETCCSID(-1); if (orig_ccsid == -1) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, "HTTPConnector::connect() Can not get current PASE CCSID."); orig_ccsid = 1208; } ccsid.reset(new PaseCcsid(819, orig_ccsid)); #endif memset(&address, 0, sizeof(address)); address.sun_family = AF_UNIX; strcpy(address.sun_path, PEGASUS_LOCAL_DOMAIN_SOCKET_PATH); socket = Socket::createSocket(AF_UNIX, SOCK_STREAM, 0); if (socket == PEGASUS_INVALID_SOCKET) { PEG_METHOD_EXIT(); throw CannotCreateSocketException(); } Socket::disableBlocking(socket); // Connect the socket to the address: if (!Socket::timedConnect( socket, reinterpret_cast<sockaddr*>(&address), sizeof(address), timeoutMilliseconds)) { MessageLoaderParms parms( "Common.HTTPConnector.CONNECTION_FAILED_LOCAL_CIM_SERVER", "Cannot connect to local CIM server. Connection failed."); PEG_METHOD_EXIT(); throw CannotConnectException(parms); } } else #endif { // Set up the IP socket connection // Make the internet address: #ifdef PEGASUS_ENABLE_IPV6 struct addrinfo *addrInfo, *addrInfoRoot = NULL; #else sockaddr_in address; #endif #ifdef PEGASUS_ENABLE_IPV6 if (!_MakeAddress( (const char*)host.getCString(), portNumber, (void**)(void*)&addrInfoRoot)) #else if (!_MakeAddress((const char*)host.getCString(), portNumber, address)) #endif { char scratch[22]; Uint32 n; const char * portStr = Uint32ToString(scratch, portNumber, n); PEG_METHOD_EXIT(); throw InvalidLocatorException(host+":"+String(portStr,n)); } #ifdef PEGASUS_ENABLE_IPV6 addrInfo = addrInfoRoot; while (addrInfo) { // Create the socket: socket = Socket::createSocket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol); #else socket = Socket::createSocket(PF_INET, SOCK_STREAM, IPPROTO_TCP); #endif if (socket == PEGASUS_INVALID_SOCKET) { #ifdef PEGASUS_ENABLE_IPV6 freeaddrinfo(addrInfoRoot); #endif PEG_METHOD_EXIT(); throw CannotCreateSocketException(); } #ifndef PEGASUS_OS_TYPE_WINDOWS // We need to ensure that the socket number is not higher than // what fits into FD_SETSIZE,because we else won't be able to select // on it and won't ever communicate correct on that socket. if (socket >= FD_SETSIZE) { # ifdef PEGASUS_ENABLE_IPV6 freeaddrinfo(addrInfoRoot); # endif // the socket is useless to us, close it Socket::close(socket); PEG_TRACE( (TRC_DISCARDED_DATA, Tracer::LEVEL1, "createSocket() returned too large socket number %d." "Cannot connect to %s:%d. Connection failed.", socket, (const char*) host.getCString(), portNumber)); PEG_METHOD_EXIT(); throw CannotCreateSocketException(); } #endif Socket::disableBlocking(socket); // Connect the socket to the address: if (!Socket::timedConnect( socket, #ifdef PEGASUS_ENABLE_IPV6 reinterpret_cast<sockaddr*>(addrInfo->ai_addr), addrInfo->ai_addrlen, #else reinterpret_cast<sockaddr*>(&address), sizeof(address), #endif timeoutMilliseconds)) { #ifdef PEGASUS_ENABLE_IPV6 addrInfo = addrInfo->ai_next; if (addrInfo) { Socket::close(socket); continue; } #endif char scratch[22]; Uint32 n; const char * portStr = Uint32ToString(scratch, portNumber, n); MessageLoaderParms parms( "Common.HTTPConnector.CONNECTION_FAILED_TO", "Cannot connect to $0:$1. Connection failed.", host, portStr); #ifdef PEGASUS_ENABLE_IPV6 freeaddrinfo(addrInfoRoot); #endif PEG_METHOD_EXIT(); throw CannotConnectException(parms); } #ifdef PEGASUS_ENABLE_IPV6 break; } freeaddrinfo(addrInfoRoot); #endif } // Create HTTPConnection object: SharedPtr<MP_Socket> mp_socket(new MP_Socket(socket, sslContext, 0)); // mp_socket now has responsibility for closing the socket handle socketPtr.release(); if (mp_socket->connect(timeoutMilliseconds) < 0) { char scratch[22]; Uint32 n; const char * portStr = Uint32ToString(scratch, portNumber, n); MessageLoaderParms parms( "Common.HTTPConnector.CONNECTION_FAILED_TO", "Cannot connect to $0:$1. Connection failed.", host, portStr); PEG_METHOD_EXIT(); throw CannotConnectException(parms); } AutoPtr<HTTPConnection> connection(new HTTPConnection( _monitor, mp_socket, String::EMPTY, 0, outputMessageQueue)); // Solicit events on this new connection's socket: int index; if (-1 == (index = _monitor->solicitSocketMessages( connection->getSocket(), connection->getQueueId(), MonitorEntry::TYPE_CONNECTION))) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "HTTPConnector::connect: Attempt to allocate entry in " "_entries table failed."); (connection->getMPSocket()).close(); } connection->_entry_index = index; _rep->connections.append(connection.get()); PEG_METHOD_EXIT(); return connection.release(); }