LibIRCClient 1.10 Used by Probotic
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
Bu depo arşivlendi. Dosyaları görüntüleyebilir ve klonlayabilirsiniz ama işlem gönderemez ve konu/değişiklik isteği açamazsınız.

395 satır
11KB

  1. /*
  2. * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com
  3. *
  4. * This library is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  12. * License for more details.
  13. */
  14. #if defined (ENABLE_SSL)
  15. // Nonzero if OpenSSL has been initialized
  16. static SSL_CTX * ssl_context = 0;
  17. #if defined (_WIN32)
  18. #include <windows.h>
  19. // This array will store all of the mutexes available to OpenSSL
  20. static CRITICAL_SECTION * mutex_buf = 0;
  21. // OpenSSL callback to utilize static locks
  22. static void cb_openssl_locking_function( int mode, int n, const char * file, int line )
  23. {
  24. if ( mode & CRYPTO_LOCK)
  25. EnterCriticalSection( &mutex_buf[n] );
  26. else
  27. LeaveCriticalSection( &mutex_buf[n] );
  28. }
  29. // OpenSSL callback to get the thread ID
  30. static unsigned long cb_openssl_id_function(void)
  31. {
  32. return ((unsigned long) GetCurrentThreadId() );
  33. }
  34. static int alloc_mutexes( unsigned int total )
  35. {
  36. unsigned int i;
  37. // Enable thread safety in OpenSSL
  38. mutex_buf = (CRITICAL_SECTION*) malloc( total * sizeof(CRITICAL_SECTION) );
  39. if ( !mutex_buf )
  40. return -1;
  41. for ( i = 0; i < total; i++)
  42. InitializeCriticalSection( &(mutex_buf[i]) );
  43. return 0;
  44. }
  45. #else
  46. // This array will store all of the mutexes available to OpenSSL
  47. static pthread_mutex_t * mutex_buf = 0;
  48. // OpenSSL callback to utilize static locks
  49. static void cb_openssl_locking_function( int mode, int n, const char * file, int line )
  50. {
  51. (void)file;
  52. (void)line;
  53. if ( mode & CRYPTO_LOCK)
  54. pthread_mutex_lock( &mutex_buf[n] );
  55. else
  56. pthread_mutex_unlock( &mutex_buf[n] );
  57. }
  58. // OpenSSL callback to get the thread ID
  59. static void cb_openssl_id_function( CRYPTO_THREADID * id )
  60. {
  61. CRYPTO_THREADID_set_pointer( id, pthread_self() );
  62. }
  63. static int alloc_mutexes( unsigned int total )
  64. {
  65. unsigned i;
  66. // Enable thread safety in OpenSSL
  67. mutex_buf = (pthread_mutex_t*) malloc( total * sizeof(pthread_mutex_t) );
  68. if ( !mutex_buf )
  69. return -1;
  70. for ( i = 0; i < total; i++)
  71. pthread_mutex_init( &(mutex_buf[i]), 0 );
  72. return 0;
  73. }
  74. #endif
  75. static int ssl_init_context( irc_session_t * session )
  76. {
  77. // Load the strings and init the library
  78. SSL_load_error_strings();
  79. // Enable thread safety in OpenSSL
  80. if ( alloc_mutexes( CRYPTO_num_locks() ) )
  81. return LIBIRC_ERR_NOMEM;
  82. // Register our callbacks
  83. CRYPTO_THREADID_set_callback( cb_openssl_id_function );
  84. CRYPTO_set_locking_callback( cb_openssl_locking_function );
  85. // Init it
  86. #if OPENSSL_VERSION_NUMBER < 0x10100000L
  87. SSL_library_init();
  88. #else
  89. OPENSSL_init_ssl(0, NULL);
  90. #endif
  91. if ( RAND_status() == 0 )
  92. return LIBIRC_ERR_SSL_INIT_FAILED;
  93. // Create an SSL context; currently a single context is used for all connections
  94. ssl_context = SSL_CTX_new( SSLv23_method() );
  95. if ( !ssl_context )
  96. return LIBIRC_ERR_SSL_INIT_FAILED;
  97. // Disable SSLv2 as it is unsecure
  98. if ( (SSL_CTX_set_options( ssl_context, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) == 0 )
  99. return LIBIRC_ERR_SSL_INIT_FAILED;
  100. // Enable only strong ciphers
  101. if ( SSL_CTX_set_cipher_list( ssl_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ) != 1 )
  102. return LIBIRC_ERR_SSL_INIT_FAILED;
  103. // Set the verification
  104. if ( session->options & LIBIRC_OPTION_SSL_NO_VERIFY )
  105. SSL_CTX_set_verify( ssl_context, SSL_VERIFY_NONE, 0 );
  106. else
  107. SSL_CTX_set_verify( ssl_context, SSL_VERIFY_PEER, 0 );
  108. // Disable session caching
  109. SSL_CTX_set_session_cache_mode( ssl_context, SSL_SESS_CACHE_OFF );
  110. // Enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER so we can move the buffer during sending
  111. SSL_CTX_set_mode( ssl_context, SSL_CTX_get_mode(ssl_context) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE );
  112. return 0;
  113. }
  114. #if defined (_WIN32)
  115. #define SSLINIT_LOCK_MUTEX(a) WaitForSingleObject( a, INFINITE )
  116. #define SSLINIT_UNLOCK_MUTEX(a) ReleaseMutex( a )
  117. #else
  118. #define SSLINIT_LOCK_MUTEX(a) pthread_mutex_lock( &a )
  119. #define SSLINIT_UNLOCK_MUTEX(a) pthread_mutex_unlock( &a )
  120. #endif
  121. // Initializes the SSL context. Must be called after the socket is created.
  122. static int ssl_init( irc_session_t * session )
  123. {
  124. static int ssl_context_initialized = 0;
  125. #if defined (_WIN32)
  126. static HANDLE initmutex = 0;
  127. // First time run? Create the mutex
  128. if ( initmutex == 0 )
  129. {
  130. HANDLE m = CreateMutex( 0, FALSE, 0 );
  131. // Now we check if the mutex has already been created by another thread performing the init concurrently.
  132. // If it was, we close our mutex and use the original one. This could be done synchronously by using the
  133. // InterlockedCompareExchangePointer function.
  134. if ( InterlockedCompareExchangePointer( &m, m, 0 ) != 0 )
  135. CloseHandle( m );
  136. }
  137. #else
  138. static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
  139. #endif
  140. // This initialization needs to be performed only once. The problem is that it is called from
  141. // irc_connect() and this function may be called simultaneously from different threads. So we have
  142. // to use mutex on Linux because it allows static mutex initialization. Windows doesn't, so here
  143. // we do the sabre dance around it.
  144. SSLINIT_LOCK_MUTEX( initmutex );
  145. if ( ssl_context_initialized == 0 )
  146. {
  147. int res = ssl_init_context( session );
  148. if ( res )
  149. {
  150. SSLINIT_UNLOCK_MUTEX( initmutex );
  151. return res;
  152. }
  153. ssl_context_initialized = 1;
  154. }
  155. SSLINIT_UNLOCK_MUTEX( initmutex );
  156. // Get the SSL context
  157. session->ssl = SSL_new( ssl_context );
  158. if ( !session->ssl )
  159. return LIBIRC_ERR_SSL_INIT_FAILED;
  160. // Let OpenSSL use our socket
  161. if ( SSL_set_fd( session->ssl, session->sock) != 1 )
  162. return LIBIRC_ERR_SSL_INIT_FAILED;
  163. // Since we're connecting on our own, tell openssl about it
  164. SSL_set_connect_state( session->ssl );
  165. return 0;
  166. }
  167. static void ssl_handle_error( irc_session_t * session, int ssl_error )
  168. {
  169. if ( ERR_GET_LIB(ssl_error) == ERR_LIB_SSL )
  170. {
  171. if ( ERR_GET_REASON(ssl_error) == SSL_R_CERTIFICATE_VERIFY_FAILED )
  172. {
  173. session->lasterror = LIBIRC_ERR_SSL_CERT_VERIFY_FAILED;
  174. return;
  175. }
  176. if ( ERR_GET_REASON(ssl_error) == SSL_R_UNKNOWN_PROTOCOL )
  177. {
  178. session->lasterror = LIBIRC_ERR_CONNECT_SSL_FAILED;
  179. return;
  180. }
  181. }
  182. #if defined (ENABLE_DEBUG)
  183. if ( IS_DEBUG_ENABLED(session) )
  184. fprintf (stderr, "[DEBUG] SSL error: %s\n\t(%d, %d)\n",
  185. ERR_error_string( ssl_error, NULL), ERR_GET_LIB( ssl_error), ERR_GET_REASON(ssl_error) );
  186. #endif
  187. }
  188. static int ssl_recv( irc_session_t * session )
  189. {
  190. int count;
  191. unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset;
  192. ERR_clear_error();
  193. // Read up to m_bufferLength bytes
  194. count = SSL_read( session->ssl, session->incoming_buf + session->incoming_offset, amount );
  195. if ( count > 0 )
  196. return count;
  197. else if ( count == 0 )
  198. return -1; // remote connection closed
  199. else
  200. {
  201. int ssl_error = SSL_get_error( session->ssl, count );
  202. // Handle SSL error since not all of them are actually errors
  203. switch ( ssl_error )
  204. {
  205. case SSL_ERROR_WANT_READ:
  206. // This is not really an error. We received something, but
  207. // OpenSSL gave nothing to us because all it read was
  208. // internal data. Repeat the same read.
  209. return 0;
  210. case SSL_ERROR_WANT_WRITE:
  211. // This is not really an error. We received something, but
  212. // now OpenSSL needs to send the data before returning any
  213. // data to us (like negotiations). This means we'd need
  214. // to wait for WRITE event, but call SSL_read() again.
  215. session->flags |= SESSIONFL_SSL_READ_WANTS_WRITE;
  216. return 0;
  217. }
  218. // This is an SSL error, handle it
  219. ssl_handle_error( session, ERR_get_error() );
  220. }
  221. return -1;
  222. }
  223. static int ssl_send( irc_session_t * session )
  224. {
  225. int count;
  226. ERR_clear_error();
  227. count = SSL_write( session->ssl, session->outgoing_buf, session->outgoing_offset );
  228. if ( count > 0 )
  229. return count;
  230. else if ( count == 0 )
  231. return -1;
  232. else
  233. {
  234. int ssl_error = SSL_get_error( session->ssl, count );
  235. switch ( ssl_error )
  236. {
  237. case SSL_ERROR_WANT_READ:
  238. // This is not really an error. We sent some internal OpenSSL data,
  239. // but now it needs to read more data before it can send anything.
  240. // Thus we wait for READ event, but will call SSL_write() again.
  241. session->flags |= SESSIONFL_SSL_WRITE_WANTS_READ;
  242. return 0;
  243. case SSL_ERROR_WANT_WRITE:
  244. // This is not really an error. We sent some data, but now OpenSSL
  245. // wants to send some internal data before sending ours.
  246. // Repeat the same write.
  247. return 0;
  248. }
  249. // This is an SSL error, handle it
  250. ssl_handle_error( session, ERR_get_error() );
  251. }
  252. return -1;
  253. }
  254. #endif
  255. // Handles both SSL and non-SSL reads.
  256. // Returns -1 in case there is an error and socket should be closed/connection terminated
  257. // Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case)
  258. // Returns a positive number if we actually read something
  259. static int session_socket_read( irc_session_t * session )
  260. {
  261. int length;
  262. #if defined (ENABLE_SSL)
  263. if ( session->ssl )
  264. {
  265. // Yes, I know this is tricky
  266. if ( session->flags & SESSIONFL_SSL_READ_WANTS_WRITE )
  267. {
  268. session->flags &= ~SESSIONFL_SSL_READ_WANTS_WRITE;
  269. ssl_send( session );
  270. return 0;
  271. }
  272. return ssl_recv( session );
  273. }
  274. #endif
  275. length = socket_recv( &session->sock,
  276. session->incoming_buf + session->incoming_offset,
  277. (sizeof (session->incoming_buf) - 1) - session->incoming_offset );
  278. // There is no "retry" errors for regular sockets
  279. if ( length <= 0 )
  280. return -1;
  281. return length;
  282. }
  283. // Handles both SSL and non-SSL writes.
  284. // Returns -1 in case there is an error and socket should be closed/connection terminated
  285. // Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case)
  286. // Returns a positive number if we actually sent something
  287. static int session_socket_write( irc_session_t * session )
  288. {
  289. int length;
  290. #if defined (ENABLE_SSL)
  291. if ( session->ssl )
  292. {
  293. // Yep
  294. if ( session->flags & SESSIONFL_SSL_WRITE_WANTS_READ )
  295. {
  296. session->flags &= ~SESSIONFL_SSL_WRITE_WANTS_READ;
  297. ssl_recv( session );
  298. return 0;
  299. }
  300. return ssl_send( session );
  301. }
  302. #endif
  303. length = socket_send (&session->sock, session->outgoing_buf, session->outgoing_offset);
  304. // There is no "retry" errors for regular sockets
  305. if ( length <= 0 )
  306. return -1;
  307. return length;
  308. }