Skip to content

Commit 3c091ac

Browse files
CDRIVER-4816 Error parsing large IPv6 literal (#1526)
* add regression test * account for extra 2 bytes in IPv6 literal * check error logged and returned in test * check return of `bson_snprintf` * use format macros * include `inttypes.h` --------- Co-authored-by: Ezra Chung <[email protected]>
1 parent 5e5784d commit 3c091ac

File tree

2 files changed

+96
-12
lines changed

2 files changed

+96
-12
lines changed

src/libmongoc/src/mongoc/mongoc-host-list.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Copyright 2015 MongoDB Inc.
3-
*
43
* Licensed under the Apache License, Version 2.0 (the "License");
4+
*
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include <inttypes.h> // PRIu16
18+
1719
#include "mongoc-host-list-private.h"
1820
/* strcasecmp on windows */
1921
#include "mongoc-util-private.h"
@@ -343,13 +345,27 @@ _mongoc_host_list_from_hostport_with_err (mongoc_host_list_t *link_,
343345
if (strchr (host, ':')) {
344346
link_->family = AF_INET6;
345347

346-
mongoc_lowercase (link_->host, link_->host);
347-
bson_snprintf (link_->host_and_port,
348-
sizeof link_->host_and_port,
349-
"[%s]:%hu",
350-
link_->host,
351-
link_->port);
348+
// Check that IPv6 literal is two less than the max to account for `[` and
349+
// `]` added below.
350+
if (host_len > BSON_HOST_NAME_MAX - 2) {
351+
bson_set_error (
352+
error,
353+
MONGOC_ERROR_STREAM,
354+
MONGOC_ERROR_STREAM_NAME_RESOLUTION,
355+
"IPv6 literal provided in URI is too long, max is %d chars",
356+
BSON_HOST_NAME_MAX - 2);
357+
return false;
358+
}
352359

360+
mongoc_lowercase (link_->host, link_->host);
361+
int req = bson_snprintf (link_->host_and_port,
362+
sizeof link_->host_and_port,
363+
"[%s]:%" PRIu16,
364+
link_->host,
365+
link_->port);
366+
BSON_ASSERT (bson_in_range_size_t_signed (req));
367+
// Use `<`, not `<=` to account for NULL byte.
368+
BSON_ASSERT ((size_t) req < sizeof link_->host_and_port);
353369
} else if (strchr (host, '/') && strstr (host, ".sock")) {
354370
link_->family = AF_UNIX;
355371
bson_strncpy (link_->host_and_port, link_->host, host_len + 1);
@@ -358,11 +374,14 @@ _mongoc_host_list_from_hostport_with_err (mongoc_host_list_t *link_,
358374
link_->family = AF_UNSPEC;
359375

360376
mongoc_lowercase (link_->host, link_->host);
361-
bson_snprintf (link_->host_and_port,
362-
sizeof link_->host_and_port,
363-
"%s:%hu",
364-
link_->host,
365-
link_->port);
377+
int req = bson_snprintf (link_->host_and_port,
378+
sizeof link_->host_and_port,
379+
"%s:%" PRIu16,
380+
link_->host,
381+
link_->port);
382+
BSON_ASSERT (bson_in_range_size_t_signed (req));
383+
// Use `<`, not `<=` to account for NULL byte.
384+
BSON_ASSERT ((size_t) req < sizeof link_->host_and_port);
366385
}
367386

368387
link_->next = NULL;

src/libmongoc/tests/test-mongoc-uri.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,6 +2740,70 @@ test_casing_options (void)
27402740
mongoc_uri_destroy (uri);
27412741
}
27422742

2743+
static void
2744+
test_parses_long_ipv6 (void)
2745+
{
2746+
// Test parsing long malformed IPv6 literals. This is a regression test for
2747+
// CDRIVER-4816.
2748+
bson_error_t error;
2749+
2750+
// Test the largest permitted IPv6 literal.
2751+
{
2752+
// Construct a string of repeating `:`.
2753+
bson_string_t *host = bson_string_new (NULL);
2754+
for (int i = 0; i < BSON_HOST_NAME_MAX - 2; i++) {
2755+
// Max IPv6 literal is two less due to including `[` and `]`.
2756+
bson_string_append (host, ":");
2757+
}
2758+
2759+
char *host_and_port = bson_strdup_printf ("[%s]:27017", host->str);
2760+
char *uri_string = bson_strdup_printf ("mongodb://%s", host_and_port);
2761+
mongoc_uri_t *uri = mongoc_uri_new_with_error (uri_string, &error);
2762+
ASSERT_OR_PRINT (uri, error);
2763+
const mongoc_host_list_t *hosts = mongoc_uri_get_hosts (uri);
2764+
ASSERT_CMPSTR (hosts->host, host->str);
2765+
ASSERT_CMPSTR (hosts->host_and_port, host_and_port);
2766+
ASSERT_CMPUINT16 (hosts->port, ==, 27017);
2767+
ASSERT (!hosts->next);
2768+
2769+
mongoc_uri_destroy (uri);
2770+
bson_free (uri_string);
2771+
bson_free (host_and_port);
2772+
bson_string_free (host, true /* free_segment */);
2773+
}
2774+
2775+
// Test one character more than the largest IPv6 literal.
2776+
{
2777+
// Construct a string of repeating `:`.
2778+
bson_string_t *host = bson_string_new (NULL);
2779+
for (int i = 0; i < BSON_HOST_NAME_MAX - 2 + 1; i++) {
2780+
bson_string_append (host, ":");
2781+
}
2782+
2783+
char *host_and_port = bson_strdup_printf ("[%s]:27017", host->str);
2784+
char *uri_string = bson_strdup_printf ("mongodb://%s", host_and_port);
2785+
capture_logs (true);
2786+
mongoc_uri_t *uri = mongoc_uri_new_with_error (uri_string, &error);
2787+
// Expect error parsing IPv6 literal is logged.
2788+
ASSERT_CAPTURED_LOG ("parsing IPv6",
2789+
MONGOC_LOG_LEVEL_ERROR,
2790+
"IPv6 literal provided in URI is too long");
2791+
capture_logs (false);
2792+
2793+
// Expect a generic parsing error is also returned.
2794+
ASSERT (!uri);
2795+
ASSERT_ERROR_CONTAINS (error,
2796+
MONGOC_ERROR_COMMAND,
2797+
MONGOC_ERROR_COMMAND_INVALID_ARG,
2798+
"Invalid host string in URI");
2799+
2800+
mongoc_uri_destroy (uri);
2801+
bson_free (uri_string);
2802+
bson_free (host_and_port);
2803+
bson_string_free (host, true /* free_segment */);
2804+
}
2805+
}
2806+
27432807
void
27442808
test_uri_install (TestSuite *suite)
27452809
{
@@ -2774,4 +2838,5 @@ test_uri_install (TestSuite *suite)
27742838
"/Uri/one_tls_option_enables_tls",
27752839
test_one_tls_option_enables_tls);
27762840
TestSuite_Add (suite, "/Uri/options_casing", test_casing_options);
2841+
TestSuite_Add (suite, "/Uri/parses_long_ipv6", test_parses_long_ipv6);
27772842
}

0 commit comments

Comments
 (0)