/** * Throws NSPRException upon NSPR error */ PRFileDesc *Connection::createSocket(const PRNetAddr& address, bool useSSL, const std::string &certDBPasswd, const std::string &certNickName, bool alwaysTrustServerCert) { PRFileDesc *rawSocket = PR_NewTCPSocket(); if (tcp_nodelay_is_enabled) { // set the TCP_NODELAY option to disable Nagle algorithm PRSocketOptionData socket_opt; socket_opt.option = PR_SockOpt_NoDelay; socket_opt.value.no_delay = PR_TRUE; PRStatus prStatus = PR_SetSocketOption(rawSocket, &socket_opt); if (PR_SUCCESS != prStatus) { throw NSPRException("Connection::Connection", "PR_SetSocketOption"); } } PRFileDesc *sslSocket; if (certDBPasswd.size() > 0) { certdbpasswd = strdup(certDBPasswd.c_str()); } if (static_cast<PRFileDesc *>(NULL) != rawSocket) { if (useSSL) { sslSocket = secureSocket(certDBPasswd, certNickName, alwaysTrustServerCert, rawSocket); } else { sslSocket = rawSocket; } } else { throw NSPRException("Connection::createSocket", "PR_NewTCPSocket"); } return sslSocket; }
/** * Throws NSPRException upon NSPR error */ Connection::Connection(const ServerInfo& server, const std::string &certDBPasswd, const std::string &certNickName, bool alwaysTrustServerCert) : socket(NULL), certdbpasswd(NULL), certnickname(NULL) { char buffer[PR_NETDB_BUF_SIZE]; PRNetAddr address; PRHostEnt hostEntry; PRIntn hostIndex; PRStatus prStatus; SECStatus secStatus; prStatus = PR_GetHostByName(server.getHost().c_str(), buffer, sizeof(buffer), &hostEntry); if (PR_SUCCESS != prStatus) { throw NSPRException("Connection::Connection", "PR_GetHostByName"); } hostIndex = PR_EnumerateHostEnt(0, &hostEntry, server.getPort(), &address); if (hostIndex < 0) { throw NSPRException("Connection::Connection", "PR_EnumerateHostEnt"); } socket = createSocket(address, server.useSSL(), certDBPasswd, certNickName, alwaysTrustServerCert); if (server.useSSL()) { secStatus = SSL_SetURL(socket, server.getHost().c_str()); if (SECSuccess != secStatus) { PRErrorCode error = PR_GetError(); PR_Shutdown(socket, PR_SHUTDOWN_BOTH); PR_Close(socket); Log::log(Log::ALL_MODULES, Log::LOG_ERROR, "SSL_SetURL() returned error: %s", PR_ErrorToString(error, PR_LANGUAGE_I_DEFAULT)); throw NSPRException("Connection::Connection", "SSL_SetURL", error); } } prStatus = PR_Connect(socket, &address, connect_timeout); if (prStatus != PR_SUCCESS) { PRErrorCode error = PR_GetError(); PR_Shutdown(socket, PR_SHUTDOWN_BOTH); PR_Close(socket); throw NSPRException("Connection::Connection PR_Connect", "PR_Connect", error); } }
am_status_t Connection::read(char *buffer, std::size_t& bufferLen) { am_status_t status = AM_SUCCESS; PRInt32 bytesRead; PRInt32 bytesToRead; if (bufferLen > static_cast<std::size_t>(MAX_READ_WRITE_LEN)) { bytesToRead = MAX_READ_WRITE_LEN; } else { bytesToRead = static_cast<PRInt32>(bufferLen); } bytesRead = PR_Recv(socket, buffer, bytesToRead, 0, receive_timeout); if (bytesRead < 0) { status = AM_NSPR_ERROR; PRErrorCode error = PR_GetError(); Log::log(Log::ALL_MODULES, Log::LOG_ERROR, "Connection::read(): NSPR Error while reading data:%d", PR_GetError()); if (error == PR_IO_TIMEOUT_ERROR || error == PR_CONNECT_RESET_ERROR || error == PR_CONNECT_REFUSED_ERROR || error == PR_NETWORK_DOWN_ERROR || error == PR_NETWORK_UNREACHABLE_ERROR || error == PR_HOST_UNREACHABLE_ERROR) { throw NSPRException("Connection::read()", "PR_Recv"); } } else { bufferLen = static_cast<std::size_t>(bytesRead); } return status; }
/** * Throws: * std::bad_alloc if no memory. * NSPRException if NSPR error. * ParseException upon parse error. */ Http::Response::Response(Log::ModuleId logModule, Connection& conn) : httpStatus(INVALID), cookieList(), extraHdrs(), bodyPtr(NULL), bodyLen(0) { am_status_t status; status = readAndParse(logModule, conn); if (AM_SUCCESS != status) { if (AM_NO_MEMORY == status) { throw std::bad_alloc(); } else if (AM_NSPR_ERROR == status) { throw NSPRException("Http::Response()", "Connection::receiveData"); } else { throw ParseException(status); } } }
/** * Performs SSL handshake on a TCP socket. */ PRFileDesc *Connection::secureSocket(const std::string &certDBPasswd, const std::string &certNickName, bool alwaysTrustServerCert, PRFileDesc *rawSocket) { bool upgradeExisting = false; // Use object's socket if none passed if (rawSocket == static_cast<PRFileDesc *>(NULL)) { rawSocket = socket; upgradeExisting = true; } PRFileDesc *sslSocket = SSL_ImportFD(NULL, rawSocket); if (static_cast<PRFileDesc *>(NULL) != sslSocket) { SECStatus secStatus = SECSuccess; const char *sslMethodName; // In case there was any communication on the socket // before the upgrade we should call a reset if (upgradeExisting) { sslMethodName = "SSL_ResetHandshake"; secStatus = SSL_ResetHandshake(sslSocket, false); } if (SECSuccess == secStatus) { sslMethodName = "SSL_OptionSet"; secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE); } if (SECSuccess == secStatus) { secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); if (SECSuccess == secStatus && alwaysTrustServerCert) { sslMethodName = "SSL_AuthCertificateHook"; secStatus = SSL_AuthCertificateHook(sslSocket, acceptAnyCert, NULL); } if (SECSuccess == secStatus && certDBPasswd.size() > 0) { sslMethodName = "SSL_SetPKCS11PinArg"; secStatus = SSL_SetPKCS11PinArg(sslSocket, certdbpasswd); } if (SECSuccess == secStatus) { if (certNickName.size() > 0) { certnickname = strdup(certNickName.c_str()); } sslMethodName = "SSL_GetClientAuthDataHook"; secStatus = SSL_GetClientAuthDataHook(sslSocket, NSS_GetClientAuthData, static_cast<void *>(certnickname)); } if (SECSuccess == secStatus) { sslMethodName = "SSL_HandshakeCallback"; secStatus = SSL_HandshakeCallback(sslSocket, (SSLHandshakeCallback)finishedHandshakeHandler, NULL); } } if (SECSuccess != secStatus) { PRErrorCode error = PR_GetError(); PR_Close(sslSocket); throw NSPRException("Connection::secureSocket", sslMethodName, error); } } else { PRErrorCode error = PR_GetError(); PR_Close(rawSocket); throw NSPRException("Connection::secureSocket", "SSL_ImportFD", error); } socket = sslSocket; return sslSocket; }