1. OPENSSL接口封裝
MongoDB封裝了OPENSSL的SSL通信接口,代碼在mongo/util/net目錄。
主要包括以下幾個方面:
1) SSL配置參數,在ssl_options(.cpp/.h)
定義了數據結構SSLGlobalParams,SSLGlobalParams中保存了與SSL相關的所有的配置參數。
在ssl_options中定義了一個SSLGlobalParams類型的全局變量sslGlobalParams,在客戶端或者服務器進程啟動時會通過相關接口從全局的配置參數中將SSL相關的配置參數保存到sslGlobalParams之中。
主要接口:
addSSLServerOptions()
addSSLClientOptions()
2) SSL證書過期檢測,在ssl_expiration(.cpp/.h)
定義了類CertificateExpirationMonitor,該類繼承了PeriodicTask,會定期將證書的過期時間與當前時間做比較。如果證書過期,會報警告。
3)SSL連接管理,在ssl_manager(.cpp/.h)
定義了類SSLConnection,該類封裝了SSL通信時使用的SSL句柄,BIO以及Socket。
定義了接口類SSLManagerInterface,該類定義了建立SSL連接、驗證SSL證書、SSL讀寫數據等接口。
定義了類SSLThreadInfo,該類用於處理SSL多線程環境下使用的問題。
定義了結構Params,該類保存了所有的SSL配置參數。
定義了類SSLManager,該類繼承了接口類SSLManagerInterface,內部保存了SSL通信的Context,通過調用OPENSSL接口實現了SSLManagerInterface定義的接口。
定義了SSLManagerInterface指針類型的全局變量theSSLManager,提供接口getSSLManager(),實現了單例模式。
2. SSL通信與Socket通信的整合
除了封裝SSL的代碼之外,在MongoDB源代碼中使用了SSL接口的地方都使用了宏定義MONGO_SSL。在編譯時只有預定義了MONGO_SSL才會編譯支持SSL的MongoDB。
MongoDB與通信相關的接口主要是Socket類和MessagingPort類定義的接口。
Socket類封裝了socket通信相關的接口。SSL版本在Socket中增加了SSLMangerInterface指針和SSLConnection指針,增加了secure()和doSSLHandshake()接口用於創建SSL連接。
在Socket上創建了SSL連接之后,數據通信會調用SSLManagerInterface的SSL讀寫接口。這樣Socket數據收發接口保持不變,但增加了SSL數據收發的功能。
MessagingPort類中保存了Socket類的變量,增加了一個secure()接口用於創建SSL連接,該接口直接調用Socket的secure()接口。另外修改了recv()接口,增加了接收到SSL握手請求的處理。
在客戶端定義了類DBClientConnection用於建立連接,該類包含一個MessagingPort變量,通過調用MessagingPort接口實現通信。
類接口調用關系如下:
DBClientConnection::connect()
|
|---> DBClientConnection::_connect() // 建立socket連接
| |
| |---> MessagingPort::connect()
| |
| |---> Socket::connect()
|
|---> MessagingPort::secure() // 建立SSL連接
|
|---> Socket::secure()
| |
| |---> SSLManager::connect()
| |
| |---> SSL_connect()
|
|---> SSLManager::parseAndValidatePeerCertificate()
MessagingPort::recv()
|
|---> Socket::doSSLHandshake()
|
|---> SSLManager::accept() // 建立SSL連接
| |
| |---> SSL_accpet()
|
|---> SSLManager::parseAndValidatePeerCertificate()
Socket::send()
|
|---> SSLManager::SSL_write()
|
|---> SSL_write()
Socket::recv()
|
|---> Socket::_recv()
|
|---> SSLManager::SSL_read()
|
|---> SSL_read()