Sint32 Socket::write(SocketHandle socket, const void* ptr, Uint32 size) { #ifdef PEGASUS_OS_TYPE_WINDOWS return ::send(socket, (const char*)ptr, size, 0); #else int status; PEGASUS_RETRY_SYSTEM_CALL(::write(socket, (char*)ptr, size), status); return status; #endif }
void Socket::close(SocketHandle& socket) { if (socket != PEGASUS_INVALID_SOCKET) { #ifdef PEGASUS_OS_TYPE_WINDOWS if (!closesocket(socket)) { socket = PEGASUS_INVALID_SOCKET; } #else int status; PEGASUS_RETRY_SYSTEM_CALL(::close(socket), status); if (status == 0) { socket = PEGASUS_INVALID_SOCKET; } #endif } }
Boolean Socket::timedConnect( SocketHandle socket, sockaddr* address, int addressLength, Uint32 timeoutMilliseconds) { int connectResult; #ifdef PEGASUS_OS_TYPE_WINDOWS connectResult = ::connect(socket, address, addressLength); #else Boolean connectionAlreadyRefused = false; Uint32 maxConnectAttempts = 100; // Retry the connect() until it succeeds or it fails with an error other // than EINTR, EAGAIN (for Linux), or the first ECONNREFUSED (for HP-UX). while (((connectResult = ::connect(socket, address, addressLength)) == -1) && (maxConnectAttempts-- > 0) && ((errno == EINTR) || (errno == EAGAIN) || ((errno == ECONNREFUSED) && !connectionAlreadyRefused))) { if (errno == ECONNREFUSED) { connectionAlreadyRefused = true; } Threads::sleep(1); } #endif if (connectResult == 0) { return true; } if (getSocketError() == PEGASUS_NETWORK_EINPROGRESS) { PEG_TRACE((TRC_HTTP, Tracer::LEVEL4, "Connection to server in progress. Waiting up to %u milliseconds " "for the socket to become connected.", timeoutMilliseconds)); fd_set fdwrite; FD_ZERO(&fdwrite); FD_SET(socket, &fdwrite); struct timeval timeoutValue = { timeoutMilliseconds/1000, timeoutMilliseconds%1000*1000 }; int selectResult = -1; #ifdef PEGASUS_OS_TYPE_WINDOWS PEGASUS_RETRY_SYSTEM_CALL( select(FD_SETSIZE, NULL, &fdwrite, &fdwrite, &timeoutValue), selectResult); #else PEGASUS_RETRY_SYSTEM_CALL( select(FD_SETSIZE, NULL, &fdwrite, NULL, &timeoutValue), selectResult); #endif if (selectResult == 0) { PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1, "select() timed out waiting for the socket connection to be " "established."); return false; } else if (selectResult > 0) { int optval; SocketLength optlen = sizeof(int); getsockopt(socket, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen); if (optval == 0) { PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL4, "Connection with server established."); return true; } else { PEG_TRACE((TRC_HTTP, Tracer::LEVEL1, "Did not connect, getsockopt() returned optval = %d", optval)); return false; } } else { PEG_TRACE((TRC_HTTP, Tracer::LEVEL1, "select() returned error code %d", getSocketError())); return false; } } PEG_TRACE((TRC_HTTP, Tracer::LEVEL1, "connect() returned error code %d", getSocketError())); return false; }
Sint32 Socket::timedWrite( SocketHandle socket, const void* ptr, Uint32 size, Uint32 socketWriteTimeout) { Sint32 bytesWritten = 0; Sint32 totalBytesWritten = 0; Boolean socketTimedOut = false; int selreturn = 0; while (1) { #ifdef PEGASUS_OS_TYPE_WINDOWS PEGASUS_RETRY_SYSTEM_CALL( ::send(socket, (const char*)ptr, size, 0), bytesWritten); #else PEGASUS_RETRY_SYSTEM_CALL( ::write(socket, (char*)ptr, size), bytesWritten); #endif // Some data written this cycle ? // Add it to the total amount of written data. if (bytesWritten > 0) { totalBytesWritten += bytesWritten; socketTimedOut = false; } // All data written ? return amount of data written if ((Uint32)bytesWritten == size) { return totalBytesWritten; } // If data has been written partially, we resume writing data // this also accounts for the case of a signal interrupt // (i.e. errno = EINTR) if (bytesWritten > 0) { size -= bytesWritten; ptr = (void *)((char *)ptr + bytesWritten); continue; } // Something went wrong if (bytesWritten == PEGASUS_SOCKET_ERROR) { // if we already waited for the socket to get ready, bail out if (socketTimedOut) return bytesWritten; #ifdef PEGASUS_OS_TYPE_WINDOWS if (WSAGetLastError() == WSAEWOULDBLOCK) #else if (errno == EAGAIN || errno == EWOULDBLOCK) #endif { fd_set fdwrite; // max. timeout seconds waiting for the socket to get ready struct timeval tv = { socketWriteTimeout, 0 }; FD_ZERO(&fdwrite); FD_SET(socket, &fdwrite); selreturn = select(FD_SETSIZE, NULL, &fdwrite, NULL, &tv); if (selreturn == 0) socketTimedOut = true; // ran out of time continue; } return bytesWritten; } } }
Sint32 SSLSocket::connect(Uint32 timeoutMilliseconds) { PEG_METHOD_ENTER(TRC_SSL, "SSLSocket::connect()"); PEG_TRACE((TRC_SSL, Tracer::LEVEL4, "Connection timeout in milliseconds is : %d", timeoutMilliseconds)); SSL* sslConnection = static_cast<SSL*>(_SSLConnection); SSL_set_connect_state(sslConnection); while (1) { int ssl_rc = SSL_connect(sslConnection); if (ssl_rc > 0) { // Connected! break; } if (ssl_rc == 0) { PEG_TRACE(( TRC_SSL, Tracer::LEVEL1, "---> SSL: Shutdown SSL_connect() failed. Error string: %s", ERR_error_string(ssl_rc, NULL))); PEG_METHOD_EXIT(); return -1; } // Error case: ssl_rc < 0 int ssl_rsn = SSL_get_error(sslConnection, ssl_rc); if ((ssl_rsn == SSL_ERROR_SYSCALL) && ((errno == EAGAIN) || (errno == EINTR))) { // Temporary error; retry the SSL_connect() continue; } if ((ssl_rsn != SSL_ERROR_WANT_READ) && (ssl_rsn != SSL_ERROR_WANT_WRITE)) { // Error, connection failed if (Tracer::isTraceOn()) { unsigned long rc = ERR_get_error (); char buff[256]; // added in OpenSSL 0.9.6: ERR_error_string_n(rc, buff, sizeof(buff)); PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL1, "---> SSL: Not connected %d %s", ssl_rsn, buff)); } PEG_METHOD_EXIT(); return -1; } // Wait until the socket is ready for reading or writing (as // appropriate) and then retry the SSL_connect() fd_set fd; FD_ZERO(&fd); FD_SET(_socket, &fd); struct timeval timeoutValue = { timeoutMilliseconds/1000, timeoutMilliseconds%1000*1000 }; int selectResult = -1; if (ssl_rsn == SSL_ERROR_WANT_READ) { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Retry WANT_READ"); PEGASUS_RETRY_SYSTEM_CALL( select(FD_SETSIZE, &fd, NULL, NULL, &timeoutValue), selectResult); } else // (ssl_rsn == SSL_ERROR_WANT_WRITE) { PEGASUS_ASSERT(ssl_rsn == SSL_ERROR_WANT_WRITE); PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Retry WANT_WRITE"); PEGASUS_RETRY_SYSTEM_CALL( select(FD_SETSIZE, NULL, &fd, NULL, &timeoutValue), selectResult); } // Check the result of select. if (selectResult == 0) { PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1, "---> SSL: Failed to connect, connection timed out."); PEG_METHOD_EXIT(); return -1; } else if (selectResult == PEGASUS_SOCKET_ERROR) { PEG_TRACE((TRC_DISCARDED_DATA, Tracer::LEVEL1, "---> SSL: Failed to connect, select error, return code = %d", selectResult)); PEG_METHOD_EXIT(); return -1; } // else retry the SSL_connect() } PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, "---> SSL: Connected"); if (_SSLContext->isPeerVerificationEnabled()) { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "Attempting to verify server certificate."); X509* server_cert = SSL_get_peer_certificate(sslConnection); if (server_cert != NULL) { // // Do not check the verification result using // SSL_get_verify_result here to see whether or not to continue. // The prepareForCallback does not reset the verification result, // so it will still contain the original error. If the client // chose to override the default error in the callback and // return true, we got here and should proceed with the // transaction. Otherwise, the handshake was already terminated. // if (SSL_get_verify_result(sslConnection) == X509_V_OK) { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "--->SSL: Server Certificate verified."); } else { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "--->SSL: Server Certificate not verified, but the " "callback overrode the default error."); } X509_free (server_cert); } else { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL1, "-->SSL: Server not certified, no certificate received."); PEG_METHOD_EXIT(); return -1; } } else { PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, "---> SSL: Server certification disabled"); } PEG_METHOD_EXIT(); return 1; }