Skip to content

Commit 6515183

Browse files
committed
Merge pull request #229 from ajdavis/CDRIVER-630-block-in-tls-handshake
fix hang in TLS handshake
2 parents 22eaf49 + 30162bb commit 6515183

File tree

8 files changed

+183
-41
lines changed

8 files changed

+183
-41
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ if (OPENSSL_FOUND)
234234
${SOURCE_DIR}/tests/test-x509.c
235235
${SOURCE_DIR}/tests/ssl-test.c
236236
${SOURCE_DIR}/tests/test-mongoc-stream-tls.c
237-
${SOURCE_DIR}/tests/test-mongoc-stream-tls-hangup.c)
237+
${SOURCE_DIR}/tests/test-mongoc-stream-tls-error.c)
238238
mongoc_add_test(test-replica-set-ssl FALSE
239239
${SOURCE_DIR}/tests/test-replica-set-ssl.c
240240
${SOURCE_DIR}/tests/ha-test.c

src/mongoc/mongoc-client.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ mongoc_client_default_stream_initiator (const mongoc_uri_t *uri,
283283
const bson_t *options;
284284
bson_iter_t iter;
285285
const char *mechanism;
286+
int32_t connecttimeoutms = MONGOC_DEFAULT_CONNECTTIMEOUTMS;
286287
#endif
287288

288289
bson_return_val_if_fail (uri, NULL);
@@ -336,14 +337,20 @@ mongoc_client_default_stream_initiator (const mongoc_uri_t *uri,
336337
return NULL;
337338
}
338339

339-
if (!mongoc_stream_tls_do_handshake (base_stream, -1) ||
340+
if (bson_iter_init_find_case (&iter, options, "connecttimeoutms") &&
341+
BSON_ITER_HOLDS_INT32 (&iter)) {
342+
if (!(connecttimeoutms = bson_iter_int32(&iter))) {
343+
connecttimeoutms = MONGOC_DEFAULT_CONNECTTIMEOUTMS;
344+
}
345+
}
346+
347+
if (!mongoc_stream_tls_do_handshake (base_stream, connecttimeoutms) ||
340348
!mongoc_stream_tls_check_cert (base_stream, host->host)) {
341349
bson_set_error (error,
342350
MONGOC_ERROR_STREAM,
343351
MONGOC_ERROR_STREAM_SOCKET,
344352
"Failed to handshake and validate TLS certificate.");
345353
mongoc_stream_destroy (base_stream);
346-
base_stream = NULL;
347354
return NULL;
348355
}
349356
}

tests/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ if ENABLE_SSL
101101
test_libmongoc_SOURCES += \
102102
tests/test-x509.c \
103103
tests/test-mongoc-stream-tls.c \
104-
tests/test-mongoc-stream-tls-hangup.c \
104+
tests/test-mongoc-stream-tls-error.c \
105105
tests/ssl-test.c \
106106
tests/ssl-test.h
107107
endif

tests/ssl-test.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
#include <mongoc-thread-private.h>
44

5+
typedef enum ssl_test_behavior {
6+
SSL_TEST_BEHAVIOR_NORMAL,
7+
SSL_TEST_BEHAVIOR_HANGUP_AFTER_HANDSHAKE,
8+
SSL_TEST_BEHAVIOR_STALL_BEFORE_HANDSHAKE,
9+
} ssl_test_behavior_t;
10+
511
typedef enum ssl_test_state {
612
SSL_TEST_CRASH,
713
SSL_TEST_SUCCESS,
@@ -19,14 +25,16 @@ typedef struct ssl_test_result {
1925

2026
typedef struct ssl_test_data
2127
{
22-
mongoc_ssl_opt_t *client;
23-
mongoc_ssl_opt_t *server;
24-
const char *host;
25-
unsigned short server_port;
26-
mongoc_cond_t cond;
27-
mongoc_mutex_t cond_mutex;
28-
ssl_test_result_t *client_result;
29-
ssl_test_result_t *server_result;
28+
mongoc_ssl_opt_t *client;
29+
mongoc_ssl_opt_t *server;
30+
ssl_test_behavior_t behavior;
31+
int64_t handshake_stall_ms;
32+
const char *host;
33+
unsigned short server_port;
34+
mongoc_cond_t cond;
35+
mongoc_mutex_t cond_mutex;
36+
ssl_test_result_t *client_result;
37+
ssl_test_result_t *server_result;
3038
} ssl_test_data_t;
3139

3240
void

tests/test-libmongoc.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,24 @@ extern void test_write_concern_install (TestSuite *suite);
4848
#ifdef MONGOC_ENABLE_SSL
4949
extern void test_x509_install (TestSuite *suite);
5050
extern void test_stream_tls_install (TestSuite *suite);
51-
extern void test_stream_tls_hangup_install (TestSuite *suite);
51+
extern void test_stream_tls_error_install (TestSuite *suite);
52+
#endif
53+
54+
55+
#ifdef _WIN32
56+
void
57+
usleep (int64_t usec)
58+
{
59+
HANDLE timer;
60+
LARGE_INTEGER ft;
61+
62+
ft.QuadPart = -(10 * usec);
63+
64+
timer = CreateWaitableTimer(NULL, true, NULL);
65+
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
66+
WaitForSingleObject(timer, INFINITE);
67+
CloseHandle(timer);
68+
}
5269
#endif
5370

5471

@@ -427,7 +444,7 @@ main (int argc,
427444
#ifdef MONGOC_ENABLE_SSL
428445
test_x509_install (&suite);
429446
test_stream_tls_install (&suite);
430-
test_stream_tls_hangup_install (&suite);
447+
test_stream_tls_error_install (&suite);
431448
#endif
432449

433450
ret = TestSuite_Run (&suite);

tests/test-libmongoc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
#ifndef TEST_LIBMONGOC_H
1919
#define TEST_LIBMONGOC_H
2020

21+
22+
#ifdef _WIN32
23+
void usleep (int64_t usec);
24+
#endif
25+
26+
2127
char *gen_collection_name (const char *prefix);
2228
void suppress_one_message (void);
2329
char *test_framework_get_host (void);

tests/test-mongoc-client.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,6 @@
1515
#undef MONGOC_LOG_DOMAIN
1616
#define MONGOC_LOG_DOMAIN "client-test"
1717

18-
19-
#ifdef _WIN32
20-
static void
21-
usleep (int64_t usec)
22-
{
23-
HANDLE timer;
24-
LARGE_INTEGER ft;
25-
26-
ft.QuadPart = -(10 * usec);
27-
28-
timer = CreateWaitableTimer(NULL, true, NULL);
29-
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
30-
WaitForSingleObject(timer, INFINITE);
31-
CloseHandle(timer);
32-
}
33-
#endif
34-
35-
3618
static mongoc_collection_t *
3719
get_test_collection (mongoc_client_t *client,
3820
const char *name)

tests/test-mongoc-stream-tls-hangup.c renamed to tests/test-mongoc-stream-tls-error.c

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* 7. hangs up
1919
*/
2020
static void *
21-
ssl_hangup_server (void *ptr)
21+
ssl_error_server (void *ptr)
2222
{
2323
ssl_test_data_t *data = (ssl_test_data_t *)ptr;
2424

@@ -69,18 +69,29 @@ ssl_hangup_server (void *ptr)
6969
ssl_stream = mongoc_stream_tls_new (sock_stream, data->server, 0);
7070
assert (ssl_stream);
7171

72-
r = mongoc_stream_tls_do_handshake (ssl_stream, TIMEOUT);
73-
assert (r);
72+
switch (data->behavior) {
73+
case SSL_TEST_BEHAVIOR_STALL_BEFORE_HANDSHAKE:
74+
usleep (data->handshake_stall_ms * 1000);
75+
break;
76+
case SSL_TEST_BEHAVIOR_HANGUP_AFTER_HANDSHAKE:
77+
r = mongoc_stream_tls_do_handshake (ssl_stream, TIMEOUT);
78+
assert (r);
79+
80+
r = mongoc_stream_readv (ssl_stream, &iov, 1, 1, TIMEOUT);
81+
assert (r == 1);
82+
break;
83+
case SSL_TEST_BEHAVIOR_NORMAL:
84+
default:
85+
fprintf (stderr, "unimplemented ssl_test_behavior_t\n");
86+
abort ();
87+
}
7488

75-
r = mongoc_stream_readv (ssl_stream, &iov, 1, 1, TIMEOUT);
76-
assert (r == 1);
89+
data->server_result->result = SSL_TEST_SUCCESS;
7790

7891
mongoc_stream_close (ssl_stream);
7992
mongoc_stream_destroy (ssl_stream);
8093
mongoc_socket_destroy (listen_sock);
8194

82-
data->server_result->result = SSL_TEST_SUCCESS;
83-
8495
return NULL;
8596
}
8697

@@ -173,14 +184,15 @@ test_mongoc_tls_hangup (void)
173184

174185
data.server = &sopt;
175186
data.client = &copt;
187+
data.behavior = SSL_TEST_BEHAVIOR_HANGUP_AFTER_HANDSHAKE;
176188
data.server_result = &sr;
177189
data.client_result = &cr;
178190
data.host = "localhost";
179191

180192
mongoc_mutex_init (&data.cond_mutex);
181193
mongoc_cond_init (&data.cond);
182194

183-
r = mongoc_thread_create (threads, &ssl_hangup_server, &data);
195+
r = mongoc_thread_create (threads, &ssl_error_server, &data);
184196
assert (r == 0);
185197

186198
r = mongoc_thread_create (threads + 1, &ssl_hangup_client, &data);
@@ -198,8 +210,118 @@ test_mongoc_tls_hangup (void)
198210
ASSERT (sr.result == SSL_TEST_SUCCESS);
199211
}
200212

213+
214+
/** run as a child thread by test_mongoc_tls_handshake_stall
215+
*
216+
* It:
217+
* 1. spins up
218+
* 2. waits on a condvar until the server is up
219+
* 3. connects to the server's port
220+
* 4. attempts handshake
221+
* 5. confirms that it times out
222+
* 6. shuts down
223+
*/
224+
static void *
225+
handshake_stall_client (void *ptr)
226+
{
227+
ssl_test_data_t *data = (ssl_test_data_t *)ptr;
228+
char *uri_str;
229+
mongoc_client_t *client;
230+
bson_t reply;
231+
bson_error_t error;
232+
int64_t connect_timeout_ms = data->handshake_stall_ms - 100;
233+
int64_t duration_ms;
234+
235+
int64_t start_time;
236+
237+
mongoc_mutex_lock (&data->cond_mutex);
238+
while (!data->server_port) {
239+
mongoc_cond_wait (&data->cond, &data->cond_mutex);
240+
}
241+
mongoc_mutex_unlock (&data->cond_mutex);
242+
243+
uri_str = bson_strdup_printf (
244+
"mongodb://localhost:%u/?ssl=true&connecttimeoutms=%" PRId64,
245+
data->server_port, connect_timeout_ms);
246+
247+
client = mongoc_client_new (uri_str);
248+
249+
/* we should time out after about 200ms */
250+
start_time = bson_get_monotonic_time ();
251+
mongoc_client_get_server_status (client,
252+
NULL,
253+
&reply,
254+
&error);
255+
256+
/* time is in microseconds */
257+
duration_ms = (bson_get_monotonic_time () - start_time) / 1000;
258+
259+
if (llabs(duration_ms - connect_timeout_ms) > 100) {
260+
fprintf (stderr,
261+
"expected timeout after about 200ms, not %" PRId64 "\n",
262+
duration_ms);
263+
abort ();
264+
}
265+
266+
data->client_result->result = SSL_TEST_SUCCESS;
267+
268+
bson_destroy (&reply);
269+
mongoc_client_destroy (client);
270+
bson_free (uri_str);
271+
272+
return NULL;
273+
}
274+
275+
276+
static void
277+
test_mongoc_tls_handshake_stall (void)
278+
{
279+
mongoc_ssl_opt_t sopt = { 0 };
280+
mongoc_ssl_opt_t copt = { 0 };
281+
ssl_test_result_t sr;
282+
ssl_test_result_t cr;
283+
ssl_test_data_t data = { 0 };
284+
mongoc_thread_t threads[2];
285+
int i, r;
286+
287+
sopt.pem_file = PEMFILE_NOPASS;
288+
sopt.weak_cert_validation = 1;
289+
copt.weak_cert_validation = 1;
290+
291+
data.server = &sopt;
292+
data.client = &copt;
293+
data.behavior = SSL_TEST_BEHAVIOR_STALL_BEFORE_HANDSHAKE;
294+
data.handshake_stall_ms = 300;
295+
data.server_result = &sr;
296+
data.client_result = &cr;
297+
data.host = "localhost";
298+
299+
mongoc_mutex_init (&data.cond_mutex);
300+
mongoc_cond_init (&data.cond);
301+
302+
r = mongoc_thread_create (threads, &ssl_error_server, &data);
303+
assert (r == 0);
304+
305+
r =
306+
mongoc_thread_create (threads + 1, &handshake_stall_client, &data);
307+
assert (r == 0);
308+
309+
for (i = 0; i < 2; i++) {
310+
r = mongoc_thread_join (threads[i]);
311+
assert (r == 0);
312+
}
313+
314+
mongoc_mutex_destroy (&data.cond_mutex);
315+
mongoc_cond_destroy (&data.cond);
316+
317+
ASSERT (cr.result == SSL_TEST_SUCCESS);
318+
ASSERT (sr.result == SSL_TEST_SUCCESS);
319+
}
320+
201321
void
202-
test_stream_tls_hangup_install (TestSuite *suite)
322+
test_stream_tls_error_install (TestSuite *suite)
203323
{
204324
TestSuite_Add (suite, "/TLS/hangup", test_mongoc_tls_hangup);
325+
TestSuite_Add (suite, "/TLS/handshake_stall",
326+
test_mongoc_tls_handshake_stall);
205327
}

0 commit comments

Comments
 (0)