#include "stringex.h" #include "Defines.h" #include "Common.h" #include "Object.h" #include "String.h" #include "Logger.h" #include "Socket.h" #ifndef SINGLE_THREADED_MODEL #include "Thread.h" #endif #include "Timer.h" #include "NetUtil.h" #include "Global.h" #include "StreamBuffer.h" #include "MultiplexSocket.h" #include "MultiplexSslSocket.h" //#include /* common define */ typedef int bool_t; //#define errlog printf #if defined(_WIN32) /* Windows specific #includes and #defines */ #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 /* To make it link in VS2005 */ #endif //#include #define SSL_LIB "ssleay32.dll" #define CRYPTO_LIB "libeay32.dll" static HANDLE dlopen(const char *dll_name, int flags) { return (LoadLibraryA(dll_name)); } #define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y)) #define RTLD_LAZY 0 #if _MSC_VER <= 1200 #define __func__ "" #else #define __func__ __FUNCTION__ #endif #else #include #include #include #include #include #include #include #include #include #include #include #define SSL_LIB "libssl.so" #define CRYPTO_LIB "libcrypto.so" typedef int SOCKET; //#define false 0 //#define true 1 #define closesocket close #endif #include "openssl/ssl.h" #include "openssl/tls1.h" #include "openssl/err.h" #ifdef _WIN32 #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") #endif HSyncObject * g_arrSyncObject; void ssl_locking_callback(int mode, int n, const char *file, int line) { if (mode & CRYPTO_LOCK) g_arrSyncObject[n].Lock(); else g_arrSyncObject[n].Unlock(); } /* This is a context that we pass to callbacks */ typedef struct tlsextctx_st { BIO *biodebug; int ack; } tlsextctx; static int ssl_servername_cb(SSL *s, int *ad, void *arg) { tlsextctx *p = (tlsextctx *) arg; const char *hn = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (SSL_get_servername_type(s) != -1) p->ack = !SSL_session_reused(s) && hn != NULL; //else // BIO_printf(bio_err, "Can't use SSL_get_servername\n"); return SSL_TLSEXT_ERR_OK; } void ssl_init() { int nLockCount = CRYPTO_num_locks(); g_arrSyncObject = new HSyncObject[nLockCount]; CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))ssl_locking_callback); /* Initialize SSL crap */ SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); } SSL_CTX * ssl_client_init(const char * pszHostName) //... { if (pszHostName[0]) { //if (strcmp(GetDomainNamePtr(pszHostName), "igi-global.com") == 0) // www.igi-global.com // return SSL_CTX_new(TLSv1_1_client_method()); if (strcmp(GetDomainNamePtr(pszHostName), "wipson.com") == 0 || // www.wipson.com strcmp(GetDomainNamePtr(pszHostName), "ibfd.org") == 0 || // www.ibfd.org strcmp(GetDomainNamePtr(pszHostName), "igi-global.com") == 0 || // www.igi-global.com strcmp(GetDomainNamePtr(pszHostName), "wisdomain.com") == 0 || // www.igi-global.com strcmp(GetDomainNamePtr(pszHostName), "tobaccojournal.com") == 0 || // www.tobaccojournal.com strcmp(GetDomainNamePtr(pszHostName), "cabdirect.org") == 0 || // www.tobaccojournal.com strcmp(GetDomainNamePtr(pszHostName), "ebsco.com") == 0)// || // atoz.ebsco.com //strcmp(GetDomainNamePtr(pszHostName), "refworks.com") == 0) // www.refworks.com return SSL_CTX_new(TLSv1_client_method()); else if (strcmp(GetDomainNamePtr(pszHostName), "dbpia.co.kr") == 0) return SSL_CTX_new(SSLv23_client_method()); //else if (strcmp(pszHostName, "saemobilus.sae.org") == 0 || // strcmp(pszHostName, "www.samsungdesign.net") == 0) // return SSL_CTX_new(TLSv1_2_client_method()); //return SSL_CTX_new(SSLv23_client_method()); // www.samsungdesign.net, hb8xw4yu6z.search.serialssolutions.com fail //return SSL_CTX_new(TLSv1_client_method()); // saemobilus.sae.org fail } return SSL_CTX_new(TLSv1_2_client_method()); } int verify_callback(int ok, X509_STORE_CTX *store) { char data[256]; /* if (ok) to debug */ if (!ok) { X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); fprintf(stderr, "-Error with certificate at depth: %i\n", depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); fprintf(stderr, " issuer = %s\n", data); X509_NAME_oneline(X509_get_subject_name(cert), data, 256); fprintf(stderr, " subject = %s\n", data); fprintf(stderr, " err %i:%s\n", err, X509_verify_cert_error_string(err) ); } return ok; } SSL_CTX * ssl_server_init(const char * pszCertPath, const char * pszKeyPath, const char * pszCaPath) { SSL_CTX * ssl_ctx = SSL_CTX_new(SSLv23_server_method()); if (ssl_ctx == NULL) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Failed to SSL_CTX_new in ssl_server_init\n"); return NULL; } //if (ctx->ssl_password_callback != NULL) // SSL_CTX_set_default_passwd_cb(CTX, ctx->ssl_password_callback); if (SSL_CTX_use_certificate_file(ssl_ctx, pszCertPath, SSL_FILETYPE_PEM) == 0) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Failed to SSL_CTX_use_certificate_file, %s\n", pszCertPath); return NULL; } if (SSL_CTX_use_PrivateKey_file(ssl_ctx, pszKeyPath, SSL_FILETYPE_PEM) == 0) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Failed to SSL_CTX_use_certificate_file, %s\n", pszKeyPath); return NULL; } #if 0 sprintf(szPemFile, "%slibproxy_chain.pem", pszAppDir); if (SSL_CTX_use_certificate_chain_file(ssl_ctx, szPemFile) == 0) { HLOGF(2, "openssl\t[ERROR] Failed to SSL_CTX_use_certificate_chain_file, %s\n", szPemFile); return NULL; } #endif /* Load the CAs we trust*/ if (SSL_CTX_load_verify_locations(ssl_ctx, pszCaPath, 0) == 0) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Failed to SSL_CTX_load_verify_locations, %s\n", pszCaPath); return NULL; } //SSL_CTX_set_verify_depth(ssl_ctx, 1); //SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, verify_callback); return ssl_ctx; } void ssl_ctx_destroy(SSL_CTX * ssl_ctx) { if (!ssl_ctx) return; SSL_CTX_free(ssl_ctx); } int ssl_send(SSL * ssl, char * buf, int len, int flags) { if (!ssl) return -1; return SSL_write(ssl, buf, len); } int ssl_recv(SSL * ssl, char * buf, int len, int flags) { if (!ssl) return -1; return SSL_read(ssl, buf, len); } ////////////////////////////////////////////////////////////////////////////// SSL_CTX * HMultiplexSslSocket::m_sslCtxServer = NULL; void HMultiplexSslSocket::SslInit(const char * pszCertPath, const char * pszKeyPath, const char * pszCaPath) { static bool bIsSslInit = false; if (bIsSslInit == false) { bIsSslInit = true; ssl_init(); HMultiplexSslSocket::m_sslCtxServer = ssl_server_init(pszCertPath, pszKeyPath, pszCaPath); } } void HMultiplexSslSocket::SslDestroy() { ssl_ctx_destroy(m_sslCtxServer); m_sslCtxServer = NULL; ERR_remove_state(0); //ENGINE_cleanup(); //CONF_modules_unload(1); ERR_free_strings(); EVP_cleanup(); sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); CRYPTO_cleanup_all_ex_data(); delete [] g_arrSyncObject; } /////////////////////////////////////// HMultiplexSslSocket::HMultiplexSslSocket() { m_ssl_ctx = NULL; m_ssl = NULL; m_rbio = NULL; m_pStreamBufferForDecrypt = new HStreamBuffer(65536); m_wbio = NULL; m_pStreamBufferForSend = new HStreamBuffer(SSL3_RT_MAX_PACKET_SIZE); m_pBufferForRecv = (char *)malloc(g_nSocketReceiveBlockSize); m_szDstServerName[0] = '\0'; m_bIsSslConnecting = false; m_bEncryptRetry = false; } HMultiplexSslSocket::HMultiplexSslSocket(SOCKET sock, struct sockaddr_in * psaiRemote, HMpsDispatcher * pDispatcher) : HMultiplexTcpSocket(sock, psaiRemote, pDispatcher) { m_ssl_ctx = NULL; m_ssl = NULL; m_rbio = NULL; m_pStreamBufferForDecrypt = new HStreamBuffer(65536); m_wbio = NULL; m_pStreamBufferForSend = new HStreamBuffer(SSL3_RT_MAX_PACKET_SIZE); m_pBufferForRecv = (char *)malloc(g_nSocketReceiveBlockSize); m_szDstServerName[0] = '\0'; m_bIsSslConnecting = false; m_bEncryptRetry = false; } HMultiplexSslSocket::~HMultiplexSslSocket() { if (m_ssl) SSL_free(m_ssl); if (m_ssl_ctx != m_sslCtxServer) ssl_ctx_destroy(m_ssl_ctx); delete m_pStreamBufferForDecrypt; delete m_pStreamBufferForSend; free(m_pBufferForRecv); } bool HMultiplexSslSocket::Close() { if (m_nState == MPS_DISABLE || (m_nState == MPS_DELETE && m_fdSocket == INVALID_SOCKET)) //if (m_fdSocket == INVALID_SOCKET) return false; OnClose(); if (m_nState != MPS_LISTENING) shutdown(m_fdSocket, SD_BOTH); if (m_nState != MPS_DELETE) m_nState = MPS_DISABLE; SSL_free(m_ssl); // free the SSL object and its BIO's m_ssl = NULL; closesocket(m_fdSocket); HLOGF(HLOG_INFO5, "INFO, SslSock, socket closed, socket = %d\n", m_fdSocket); m_fdSocket = INVALID_SOCKET; return true; } int HMultiplexSslSocket::Send(const char * pData, int nSize) { if ((size_t)nSize > m_pStreamBufferForSend->RemainSize()) { SendBufferedData(); m_bEncryptRetry = true; m_bSendRetry = true; return 0; } if (!SSL_is_init_finished(m_ssl)) { m_bEncryptRetry = true; m_bSendRetry = true; return 0; } const char * pDataOffset = pData; int nRemainSize = nSize; int nResult; int nSslError; while (nRemainSize > 0) { nResult = SSL_write(m_ssl, pDataOffset, nRemainSize); nSslError = SSL_get_error(m_ssl, nResult); if (nResult > 0) { pDataOffset += nResult; nRemainSize -= nResult; do { nResult = BIO_read(m_wbio, m_pStreamBufferForSend->Tail(), m_pStreamBufferForSend->RemainSize()); if (nResult > 0) m_pStreamBufferForSend->MoveTail(nResult); else if (!BIO_should_retry(m_wbio)) { HLOGF(HLOG_ERROR, "SSL_TRACE, 1, socket = %d\n", m_fdSocket); m_nState = MPS_CLOSING; return SOCKET_ERROR; } } while (nResult > 0); } if (nSslError != SSL_ERROR_NONE && nSslError != SSL_ERROR_WANT_WRITE && nSslError != SSL_ERROR_WANT_READ) { HLOGF(HLOG_WARNING, "[WARN], SslSock, socket = %d, error = %d, %d\n", m_fdSocket, nSslError, nResult); m_nState = MPS_CLOSING; return SOCKET_ERROR; } if (nResult == 0) break; } m_bEncryptRetry = false; if (m_pStreamBufferForSend->StoredSize() > 0) SendBufferedData(); if (nRemainSize > 0) { m_bEncryptRetry = true; m_bSendRetry = true; return nSize - nRemainSize; } return nSize; } int HMultiplexSslSocket::SendBufferedData() { if (m_pStreamBufferForSend->StoredSize() == 0) return SOCKET_ERROR; int nSentSize = HMultiplexTcpSocket::Send(m_pStreamBufferForSend->Head(), m_pStreamBufferForSend->StoredSize()); if (nSentSize <= 0) return nSentSize; m_pStreamBufferForSend->Pop(nSentSize); return nSentSize; } int HMultiplexSslSocket::Receive() { int nReceivedSize = HMultiplexTcpSocket::Receive(m_pBufferForRecv, g_nSocketReceiveBlockSize); if (nReceivedSize <= 0) return nReceivedSize; char * pBufferOffset = m_pBufferForRecv; int nResult; while (nReceivedSize > 0) { nResult = BIO_write(m_rbio, pBufferOffset, nReceivedSize); if (nResult <= 0) { HLOGF(HLOG_INFO4, "INFO, SslSock, assume bio write failure is unrecoverable, socket = %d\n", m_fdSocket); m_nState = MPS_CLOSING; return SOCKET_ERROR; } pBufferOffset += nResult; nReceivedSize -= nResult; if (!SSL_is_init_finished(m_ssl)) { if (ProcHandShake() == false) { if (m_bIsSslConnecting) OnConnectChild(false); return -1; } if (!SSL_is_init_finished(m_ssl)) return 0; } do { nResult = SSL_read(m_ssl, m_pStreamBufferForDecrypt->Tail(), m_pStreamBufferForDecrypt->RemainSize()); if (nResult > 0) m_pStreamBufferForDecrypt->MoveTail(nResult); } while (nResult > 0); int nSslError = SSL_get_error(m_ssl, nResult); if (nSslError == SSL_ERROR_WANT_WRITE || nSslError == SSL_ERROR_WANT_READ) { do { nResult = BIO_read(m_wbio, m_pStreamBufferForSend->Tail(), m_pStreamBufferForSend->RemainSize()); if (nResult > 0) m_pStreamBufferForSend->MoveTail(nResult); else if (!BIO_should_retry(m_wbio)) { HLOGF(HLOG_WARNING, "TRACE, SslSock, 1. Did SSL request to write bytes? This can happen if peer has requested SSL * renegotiation, socket = %d, %d, %d\n", m_fdSocket, nSslError, nResult); m_nState = MPS_CLOSING; return SOCKET_ERROR; } } while (nResult > 0); } else if (nSslError == SSL_ERROR_ZERO_RETURN || nSslError == SSL_ERROR_SYSCALL) { HLOGF(HLOG_WARNING, "TRACE, SslSock, 2. Did SSL request to write bytes? This can happen if peer has requested SSL * renegotiation, socket = %d, %d, %d\n", m_fdSocket, nSslError, nResult); //...??? } } if (m_pStreamBufferForSend->StoredSize() > 0) SendBufferedData(); if (m_bIsSslConnecting) { m_bIsSslConnecting = false; OnConnectChild(true); return 0; } return m_pStreamBufferForDecrypt->StoredSize(); } bool HMultiplexSslSocket::SetRemoteAddressHostByName(const char * pszHostName, u_short nPort) { strcpy(m_szDstServerName, pszHostName); return HMultiplexTcpSocket::SetRemoteAddressHostByName(pszHostName, nPort); } bool HMultiplexSslSocket::IsRemain() { if (m_ssl == NULL) return false; return SSL_pending(m_ssl) > 0; } void HMultiplexSslSocket::OnConnect(bool bIsSuccess) { if (bIsSuccess) { m_ssl_ctx = ssl_client_init(m_szDstServerName); //tlsextcbp.biodebug = bio_err; //SSL_CTX_set_tlsext_servername_callback(m_ssl_ctx, ssl_servername_cb); //SSL_CTX_set_tlsext_servername_arg(ctx, &tlsextcbp); if ((m_ssl = SSL_new(m_ssl_ctx)) == NULL) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Faield to SSL_new 1, socket = %d\n", m_fdSocket); OnConnectChild(false); return; } if (m_szDstServerName[0] && !SSL_set_tlsext_host_name(m_ssl, m_szDstServerName)) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Unable to set TLS servername extension, socket = %d\n", m_fdSocket); OnConnectChild(false); return; } SSL_set_connect_state(m_ssl); m_rbio = BIO_new(BIO_s_mem()); m_wbio = BIO_new(BIO_s_mem()); SSL_set_bio(m_ssl, m_rbio, m_wbio); m_bIsSslConnecting = true; ProcHandShake(); } else { OnConnectChild(false); } } void HMultiplexSslSocket::OnAccepted() { if ((m_ssl = SSL_new(m_sslCtxServer)) == NULL) { HLOGF(HLOG_ERROR, "[ERROR], SslSock, Faield to SSL_new 2, socket = %d\n", m_fdSocket); return; } m_ssl_ctx = m_sslCtxServer; SSL_set_accept_state(m_ssl); m_rbio = BIO_new(BIO_s_mem()); m_wbio = BIO_new(BIO_s_mem()); SSL_set_bio(m_ssl, m_rbio, m_wbio); //m_bIsSslConnecting = true; } bool HMultiplexSslSocket::ProcHandShake() { int nSslResult = SSL_do_handshake(m_ssl); int nSslError = SSL_get_error(m_ssl, nSslResult); if (nSslError == SSL_ERROR_WANT_WRITE || nSslError == SSL_ERROR_WANT_READ) { do { nSslResult = BIO_read(m_wbio, m_pStreamBufferForSend->Tail(), m_pStreamBufferForSend->RemainSize()); if (nSslResult > 0) m_pStreamBufferForSend->MoveTail(nSslResult); else if (!BIO_should_retry(m_wbio)) { m_pStreamBufferForSend->Reset(); HLOGF(HLOG_WARNING, "[WARN], SslSock, Failed to HandShake 1, socket = %d, error = %d\n", m_fdSocket, nSslError); return false; } } while (nSslResult > 0); if (m_pStreamBufferForSend->StoredSize() > 0) SendBufferedData(); } else if (nSslError == SSL_ERROR_ZERO_RETURN || nSslError == SSL_ERROR_SYSCALL) { HLOGF(HLOG_WARNING, "[WARN], SslSock, Failed to HandShake 2, socket = %d, error = %d\n", m_fdSocket, nSslError); return false; } return true; }