SSLHelper.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Version : 3.6.1
  5. * Author : Bruce Liang
  6. * Website : http://www.jessma.org
  7. * Project : https://github.com/ldcsaa
  8. * Blog : http://www.cnblogs.com/ldcsaa
  9. * Wiki : http://www.oschina.net/p/hp-socket
  10. * QQ Group : 75375912
  11. *
  12. * Licensed under the Apache License, Version 2.0 (the "License");
  13. * you may not use this file except in compliance with the License.
  14. * You may obtain a copy of the License at
  15. *
  16. * http://www.apache.org/licenses/LICENSE-2.0
  17. *
  18. * Unless required by applicable law or agreed to in writing, software
  19. * distributed under the License is distributed on an "AS IS" BASIS,
  20. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. * See the License for the specific language governing permissions and
  22. * limitations under the License.
  23. */
  24. #pragma once
  25. #include "openssl/ssl.h"
  26. #include "HPTypeDef.h"
  27. #include "../../Common/Src/bufferpool.h"
  28. /************************************************************************
  29. 名称:SSL 握手状态
  30. 描述:标识当前连接的 SSL 握手状态
  31. ************************************************************************/
  32. enum EnSSLHandShakeStatus
  33. {
  34. SSL_HSS_INIT = 0, // 初始状态
  35. SSL_HSS_PROC = 1, // 正在握手
  36. SSL_HSS_SUCC = 2, // 握手成功
  37. };
  38. /* SSL CRYPTO DYNLOCK 结构 */
  39. typedef struct CRYPTO_dynlock_value
  40. {
  41. CSimpleRWLock cs;
  42. } DynamicLock;
  43. /************************************************************************
  44. 名称:SSL Context
  45. 描述:初始化和清理 SSL 全局运行环境
  46. ************************************************************************/
  47. class CSSLContext
  48. {
  49. public:
  50. /* 获取 CSSLContext 单例对象 */
  51. static CSSLContext& getInstance() {return sm_Instance;}
  52. public:
  53. /*
  54. * 名称:初始化 SSL 全局环境参数
  55. * 描述:SSL 全局环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败
  56. *
  57. * 参数: enSessionMode -- SSL 工作模式(参考 EnSSLSessionMode)
  58. * iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
  59. * lpszPemCertFile -- 证书文件(客户端可选)
  60. * lpszPemKeyFile -- 私钥文件(客户端可选)
  61. * lpszKeyPasswod -- 私钥密码(没有密码则为空)
  62. * lpszCAPemCertFileOrPath -- CA 证书文件或目录(单向验证或客户端可选)
  63. *
  64. * 返回值: TRUE -- 成功
  65. * FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
  66. */
  67. BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPasswod = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr);
  68. /*
  69. * 名称:清理 SSL 全局运行环境
  70. * 描述:清理 SSL 全局运行环境,回收 SSL 相关内存
  71. * 1、CSSLContext 的析构函数会自动调用本方法
  72. * 2、当要重新设置 SSL 全局环境参数时,需要先调用本方法清理原先的环境参数
  73. *
  74. * 参数: 无
  75. *
  76. * 返回值:无
  77. */
  78. void Cleanup();
  79. /*
  80. * 名称:清理线程局部环境 SSL 资源
  81. * 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源
  82. * 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法
  83. * 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法
  84. *
  85. * 参数: dwThreadID -- 线程 ID(0:当前线程)
  86. *
  87. * 返回值:无
  88. */
  89. void RemoveThreadLocalState(DWORD dwThreadID = 0) {CleanupThreadState(dwThreadID);}
  90. /* 检查 SSL 全局运行环境是否初始化完成 */
  91. BOOL IsValid () const {return m_bValid;}
  92. /* 获取 SSL 全局运行环境 SSL_CTX 对象 */
  93. SSL_CTX* GetContext () const {return m_sslCtx;}
  94. /* 获取 SSL 全局运行环境的配置模式,配置模式参考:EnSSLSessionMode */
  95. EnSSLSessionMode GetSessionMode () const {return m_enSessionMode;}
  96. private:
  97. CSSLContext()
  98. : m_bValid (FALSE)
  99. , m_iLockNum (0)
  100. , m_pcsLocks (nullptr)
  101. , m_sslCtx (nullptr)
  102. , m_enSessionMode (SSL_SM_SERVER)
  103. {
  104. }
  105. ~CSSLContext()
  106. {
  107. Cleanup();
  108. }
  109. private:
  110. void CreateContext();
  111. BOOL LoadCertAndKey(int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPasswod, LPCTSTR lpszCAPemCertFileOrPath);
  112. private:
  113. static void CleanupGlobalState();
  114. static void CleanupThreadState(DWORD dwThreadID = 0);
  115. static void ssl_lock_callback(int mode, int n, const char *file, int line);
  116. static CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line);
  117. static void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line);
  118. static void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line);
  119. private:
  120. BOOL m_bValid;
  121. EnSSLSessionMode m_enSessionMode;
  122. int m_iLockNum;
  123. CSimpleRWLock* m_pcsLocks;
  124. SSL_CTX* m_sslCtx;
  125. private:
  126. static CSSLContext sm_Instance;
  127. };
  128. extern CSSLContext& g_SSL;
  129. class CSSLSession
  130. {
  131. public:
  132. BOOL WriteRecvChannel(const BYTE* pData, int iLength);
  133. BOOL ReadRecvChannel();
  134. BOOL WriteSendChannel(const BYTE* pData, int iLength);
  135. BOOL WriteSendChannel(const WSABUF pBuffers[], int iCount);
  136. BOOL ReadSendChannel();
  137. const WSABUF& GetRecvBuffer() const {return m_bufRecv;}
  138. const WSABUF& GetSendBuffer() const {return m_bufSend;}
  139. CSSLSession* Renew();
  140. BOOL Reset();
  141. BOOL IsValid() const {return GetStatus() != SSL_HSS_INIT;}
  142. BOOL IsHandShaking() const {return GetStatus() == SSL_HSS_PROC;}
  143. BOOL IsReady() const {return GetStatus() == SSL_HSS_SUCC;}
  144. EnSSLHandShakeStatus GetStatus() const {return m_enStatus;}
  145. DWORD GetFreeTime() const {return m_dwFreeTime;}
  146. CCriSec& GetSendLock() {return m_csSend;}
  147. private:
  148. BOOL IsFatalError(int iBytes);
  149. public:
  150. CSSLSession(CItemPool& itPool)
  151. : m_enStatus(SSL_HSS_INIT)
  152. , m_itPool (itPool)
  153. , m_ssl (nullptr)
  154. , m_bioSend (nullptr)
  155. , m_bioRecv (nullptr)
  156. , m_pitSend (nullptr)
  157. , m_pitRecv (nullptr)
  158. {
  159. }
  160. ~CSSLSession()
  161. {
  162. Reset();
  163. }
  164. private:
  165. CItemPool& m_itPool;
  166. CCriSec m_csSend;
  167. DWORD m_dwFreeTime;
  168. EnSSLHandShakeStatus m_enStatus;
  169. SSL* m_ssl;
  170. BIO* m_bioSend;
  171. BIO* m_bioRecv;
  172. TItem* m_pitSend;
  173. TItem* m_pitRecv;
  174. WSABUF m_bufSend;
  175. WSABUF m_bufRecv;
  176. };
  177. class CSSLSessionPool
  178. {
  179. typedef CRingPool<CSSLSession> TSSLSessionList;
  180. typedef CCASQueue<CSSLSession> TSSLSessionQueue;
  181. public:
  182. CSSLSession* PickFreeSession ();
  183. void PutFreeSession (CSSLSession* pSession);
  184. void Prepare ();
  185. void Clear ();
  186. private:
  187. void ReleaseGCSession (BOOL bForce = FALSE);
  188. public:
  189. void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
  190. void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
  191. void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
  192. void SetSessionLockTime (DWORD dwSessionLockTime) {m_dwSessionLockTime = dwSessionLockTime;}
  193. void SetSessionPoolSize (DWORD dwSessionPoolSize) {m_dwSessionPoolSize = dwSessionPoolSize;}
  194. void SetSessionPoolHold (DWORD dwSessionPoolHold) {m_dwSessionPoolHold = dwSessionPoolHold;}
  195. DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
  196. DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
  197. DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
  198. DWORD GetSessionLockTime() {return m_dwSessionLockTime;}
  199. DWORD GetSessionPoolSize() {return m_dwSessionPoolSize;}
  200. DWORD GetSessionPoolHold() {return m_dwSessionPoolHold;}
  201. public:
  202. CSSLSessionPool(DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
  203. DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
  204. DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
  205. : m_dwSessionPoolSize(dwPoolSize)
  206. , m_dwSessionPoolHold(dwPoolHold)
  207. , m_dwSessionLockTime(dwLockTime)
  208. {
  209. }
  210. ~CSSLSessionPool() {Clear();}
  211. DECLARE_NO_COPY_CLASS(CSSLSessionPool)
  212. public:
  213. static const DWORD DEFAULT_ITEM_CAPACITY;
  214. static const DWORD DEFAULT_ITEM_POOL_SIZE;
  215. static const DWORD DEFAULT_ITEM_POOL_HOLD;
  216. static const DWORD DEFAULT_SESSION_LOCK_TIME;
  217. static const DWORD DEFAULT_SESSION_POOL_SIZE;
  218. static const DWORD DEFAULT_SESSION_POOL_HOLD;
  219. private:
  220. CItemPool m_itPool;
  221. DWORD m_dwSessionLockTime;
  222. DWORD m_dwSessionPoolSize;
  223. DWORD m_dwSessionPoolHold;
  224. TSSLSessionList m_lsFreeSession;
  225. TSSLSessionQueue m_lsGCSession;
  226. };
  227. template<class T, class S> EnHandleResult ProcessHandShake(T* pThis, S* pSocketObj, CSSLSession* pSession)
  228. {
  229. EnHandleResult result = HR_OK;
  230. CCriSecLock locallock(pSession->GetSendLock());
  231. while(TRUE)
  232. {
  233. VERIFY(pSession->ReadSendChannel());
  234. const WSABUF& buffer = pSession->GetSendBuffer();
  235. if(buffer.len == 0)
  236. break;
  237. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  238. {
  239. result = HR_ERROR;
  240. break;
  241. }
  242. }
  243. return result;
  244. }
  245. template<class T, class S> EnHandleResult ProcessReceive(T* pThis, S* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength)
  246. {
  247. if(!pSession->WriteRecvChannel(pData, iLength))
  248. return HR_ERROR;
  249. EnHandleResult result = HR_OK;
  250. EnSSLHandShakeStatus enStatus = pSession->GetStatus();
  251. while(TRUE)
  252. {
  253. if(!pSession->ReadRecvChannel())
  254. return HR_ERROR;
  255. if(enStatus == SSL_HSS_PROC && pSession->IsReady())
  256. {
  257. result = ProcessHandShake(pThis, pSocketObj, pSession);
  258. if(result == HR_ERROR)
  259. break;
  260. enStatus = SSL_HSS_SUCC;
  261. result = pThis->DoFireHandShake(pSocketObj);
  262. if(result == HR_ERROR)
  263. break;
  264. }
  265. const WSABUF& buffer = pSession->GetRecvBuffer();
  266. if(buffer.len == 0)
  267. break;
  268. result = pThis->DoFireReceive(pSocketObj, (const BYTE*)buffer.buf, buffer.len);
  269. if(result == HR_ERROR)
  270. break;
  271. }
  272. if(result != HR_ERROR && pSession->IsHandShaking())
  273. result = ::ProcessHandShake(pThis, pSocketObj, pSession);
  274. return result;
  275. }
  276. template<class T, class S> BOOL ProcessSend(T* pThis, S* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount)
  277. {
  278. if(pSession == nullptr || !pSession->IsReady())
  279. {
  280. ::SetLastError(ERROR_INVALID_STATE);
  281. return FALSE;
  282. }
  283. CCriSecLock locallock(pSession->GetSendLock());
  284. if(!pSession->IsReady())
  285. {
  286. ::SetLastError(ERROR_INVALID_STATE);
  287. return FALSE;
  288. }
  289. VERIFY(pSession->WriteSendChannel(pBuffers, iCount));
  290. while(TRUE)
  291. {
  292. VERIFY(pSession->ReadSendChannel());
  293. const WSABUF& buffer = pSession->GetSendBuffer();
  294. if(buffer.len == 0)
  295. break;
  296. if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
  297. return FALSE;
  298. }
  299. return TRUE;
  300. }