@@ -83,9 +83,6 @@ bool isError(const char* call, long result, long expected) {
83
83
84
84
SSLSocket::~SSLSocket () {
85
85
BIO_free_all (m_bio);
86
- if (m_ctx) {
87
- SSL_CTX_free (m_ctx);
88
- }
89
86
}
90
87
91
88
ssize_t SSLSocket::send (const char * data, size_t len) {
@@ -142,26 +139,56 @@ bool SSLSocket::isValid() {
142
139
return time (nullptr ) < m_until && !BIO_eof (m_bio);
143
140
}
144
141
142
+ void sslInfoCallback (const SSL *ssl, int type, int val) {
143
+ if (!needsLog (lf_network, (val == 0 ) ? ll_error : ll_debug)) {
144
+ return ;
145
+ }
146
+ logWrite (lf_network,
147
+ (val == 0 ) ? ll_error : ll_debug,
148
+ " SSL state %s: type 0x%x=%s%s%s%s%s%s%s%s%s val %d=%s" ,
149
+ SSL_state_string_long (ssl),
150
+ type,
151
+ (type & SSL_CB_LOOP) ? " loop," : " " ,
152
+ (type & SSL_CB_EXIT) ? " exit," : " " ,
153
+ (type & SSL_CB_READ) ? " read," : " " ,
154
+ (type & SSL_CB_WRITE) ? " write," : " " ,
155
+ (type & SSL_CB_ALERT) ? " alert," : " " ,
156
+ (type & SSL_ST_ACCEPT) ? " accept," : " " ,
157
+ (type & SSL_ST_CONNECT) ? " connect," : " " ,
158
+ (type & SSL_CB_HANDSHAKE_START) ? " start," : " " ,
159
+ (type & SSL_CB_HANDSHAKE_DONE) ? " done," : " " ,
160
+ val,
161
+ (type & SSL_CB_ALERT) ? SSL_alert_desc_string_long (val) : " ?" );
162
+ }
163
+
145
164
SSLSocket* SSLSocket::connect (const string& host, const uint16_t & port, bool https, int timeout, const char * caFile,
146
165
const char * caPath) {
147
- BIO *bio = nullptr ;
148
- SSL_CTX *ctx = nullptr ;
149
166
ostringstream ostr;
150
167
ostr << host << ' :' << static_cast <unsigned >(port);
151
168
const string hostPort = ostr.str ();
152
169
time_t until = time (nullptr ) + 1 + (timeout <= 5 ? 5 : timeout); // at least 5 seconds, 1 extra for rounding
153
170
if (!https) {
154
171
do {
155
- bio = BIO_new_connect (static_cast <const char *>(hostPort.c_str ()));
172
+ BIO * bio = BIO_new_connect (static_cast <const char *>(hostPort.c_str ()));
156
173
if (isError (" connect" , bio != nullptr )) {
157
174
break ;
158
175
}
159
176
BIO_set_nbio (bio, 1 ); // set non-blocking
160
- return new SSLSocket (nullptr , bio, until);
177
+ return new SSLSocket (bio, until);
161
178
} while (false );
162
- } else {
163
- SSL *ssl = nullptr ;
164
- do {
179
+ return nullptr ;
180
+ }
181
+ BIO *bio = nullptr ;
182
+ static SSL_CTX *ctx = nullptr ;
183
+ static int sslContextInitTries = 0 ;
184
+ do {
185
+ // const SSL_METHOD *method = TLS_client_method();
186
+ static bool verifyPeer = true ;
187
+ if (ctx == nullptr ) { // according to openssl manpage, ctx is global and should be created once only
188
+ if (sslContextInitTries > 2 ) { // give it up to 3 tries to initialize the context
189
+ break ;
190
+ }
191
+ sslContextInitTries++;
165
192
const SSL_METHOD *method = SSLv23_method ();
166
193
if (isError (" method" , method != nullptr )) {
167
194
break ;
@@ -170,7 +197,8 @@ SSLSocket* SSLSocket::connect(const string& host, const uint16_t& port, bool htt
170
197
if (isError (" ctx_new" , ctx != nullptr )) {
171
198
break ;
172
199
}
173
- bool verifyPeer = !caFile || strcmp (caFile, " #" ) != 0 ;
200
+ SSL_CTX_set_info_callback (ctx, sslInfoCallback);
201
+ verifyPeer = !caFile || strcmp (caFile, " #" ) != 0 ;
174
202
SSL_CTX_set_verify (ctx, verifyPeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, nullptr );
175
203
if (verifyPeer) {
176
204
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
@@ -185,79 +213,78 @@ SSLSocket* SSLSocket::connect(const string& host, const uint16_t& port, bool htt
185
213
}
186
214
#endif
187
215
if ((caFile || caPath) && isError (" verify_loc" , SSL_CTX_load_verify_locations (ctx, caFile, caPath), 1 )) {
216
+ SSL_CTX_free (ctx);
217
+ ctx = nullptr ;
188
218
break ;
189
219
}
190
220
}
191
- const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
192
- SSL_CTX_set_options (ctx, flags);
193
- bio = BIO_new_ssl_connect (ctx);
194
- if (isError (" new_ssl_conn" , bio != nullptr )) {
195
- break ;
196
- }
197
- if (isError (" conn_hostname" , BIO_set_conn_hostname (bio, hostPort.c_str ()), 1 )) {
198
- break ;
199
- }
200
- BIO_set_nbio (bio, 1 ); // set non-blocking
201
- BIO_get_ssl (bio, &ssl);
202
- if (isError (" get_ssl" , ssl != nullptr )) {
203
- break ;
204
- }
205
- SSL_set_mode (ssl, SSL_MODE_AUTO_RETRY);
206
- const char *hostname = host.c_str ();
207
- if (isError (" tls_host" , SSL_set_tlsext_host_name (ssl, hostname), 1 )) {
208
- break ;
209
- }
210
- long res = BIO_do_connect (bio);
211
- time_t now = 0 ;
212
- while (res <= 0 && (BIO_should_retry (bio) || now == 0 )) { // always repeat on first failure
213
- if ((now=time (nullptr )) > until) {
214
- break ;
215
- }
216
- usleep (SLEEP_NANOS);
217
- res = BIO_do_connect (bio);
218
- }
219
- if (res <= 0 && now > until) {
220
- logError (lf_network, " HTTP connect: timed out after %d sec" , now-until);
221
- break ;
222
- }
223
- if (isError (" connect" , res, 1 )) {
224
- break ;
225
- }
226
- X509 *cert = SSL_get_peer_certificate (ssl);
227
- if (isError (" peer_cert" , cert != nullptr )) {
228
- break ;
229
- }
230
- X509_free (cert); // decrement reference count incremented by above call
231
- if (verifyPeer && isError (" verify" , SSL_get_verify_result (ssl), X509_V_OK)) {
232
- break ;
233
- }
234
- // check hostname
235
- X509_NAME *sname = X509_get_subject_name (cert);
236
- if (isError (" get_subject" , sname != nullptr )) {
221
+ SSL_CTX_set_options (ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
222
+ }
223
+ bio = BIO_new_ssl_connect (ctx);
224
+ if (isError (" new_ssl_conn" , bio != nullptr )) {
225
+ break ;
226
+ }
227
+ if (isError (" conn_hostname" , BIO_set_conn_hostname (bio, hostPort.c_str ()), 1 )) {
228
+ break ;
229
+ }
230
+ BIO_set_nbio (bio, 1 ); // set non-blocking
231
+ SSL *ssl = nullptr ;
232
+ BIO_get_ssl (bio, &ssl);
233
+ if (isError (" get_ssl" , ssl != nullptr )) {
234
+ break ;
235
+ }
236
+ SSL_set_mode (ssl, SSL_MODE_AUTO_RETRY);
237
+ const char *hostname = host.c_str ();
238
+ if (isError (" tls_host" , SSL_set_tlsext_host_name (ssl, hostname), 1 )) {
239
+ break ;
240
+ }
241
+ long res = BIO_do_connect (bio);
242
+ time_t now = 0 ;
243
+ while (res <= 0 && (BIO_should_retry (bio) || now == 0 )) { // always repeat on first failure
244
+ if ((now=time (nullptr )) > until) {
237
245
break ;
238
246
}
239
- char peerName[64 ];
240
- if (isError (" subject name" , X509_NAME_get_text_by_NID (sname, NID_commonName, peerName, sizeof (peerName)) > 0 )) {
247
+ usleep (SLEEP_NANOS);
248
+ res = BIO_do_connect (bio);
249
+ }
250
+ if (res <= 0 && now > until) {
251
+ logError (lf_network, " HTTP connect: timed out after %d sec" , now-until);
252
+ break ;
253
+ }
254
+ if (isError (" connect" , res, 1 )) {
255
+ break ;
256
+ }
257
+ X509 *cert = SSL_get_peer_certificate (ssl);
258
+ if (isError (" peer_cert" , cert != nullptr )) {
259
+ break ;
260
+ }
261
+ X509_free (cert); // decrement reference count incremented by above call
262
+ if (verifyPeer && isError (" verify" , SSL_get_verify_result (ssl), X509_V_OK)) {
263
+ break ;
264
+ }
265
+ // check hostname
266
+ X509_NAME *sname = X509_get_subject_name (cert);
267
+ if (isError (" get_subject" , sname != nullptr )) {
268
+ break ;
269
+ }
270
+ char peerName[64 ];
271
+ if (isError (" subject name" , X509_NAME_get_text_by_NID (sname, NID_commonName, peerName, sizeof (peerName)) > 0 )) {
272
+ break ;
273
+ }
274
+ if (strcmp (peerName, hostname) != 0 ) {
275
+ char * dotpos = NULL ;
276
+ if (peerName[0 ] == ' *' && peerName[1 ] == ' .' && (dotpos=strchr ((char *)hostname, ' .' ))
277
+ && strcmp (peerName+2 , dotpos+1 ) == 0 ) {
278
+ // wildcard matches
279
+ } else if (isError (" subject" , 1 , 0 )) {
241
280
break ;
242
281
}
243
- if (strcmp (peerName, hostname) != 0 ) {
244
- char * dotpos = NULL ;
245
- if (peerName[0 ] == ' *' && peerName[1 ] == ' .' && (dotpos=strchr ((char *)hostname, ' .' ))
246
- && strcmp (peerName+2 , dotpos+1 ) == 0 ) {
247
- // wildcard matches
248
- } else if (isError (" subject" , 1 , 0 )) {
249
- break ;
250
- }
251
- }
252
- return new SSLSocket (ctx, bio, until);
253
- } while (false );
254
- }
282
+ }
283
+ return new SSLSocket (bio, until);
284
+ } while (false );
255
285
if (bio) {
256
286
BIO_free_all (bio);
257
287
}
258
- if (ctx) {
259
- SSL_CTX_free (ctx);
260
- }
261
288
return nullptr ;
262
289
}
263
290
0 commit comments