From 8fbf67d13826f73be75fa301d8bd27f4dba8b52c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 3 Sep 2019 13:49:47 +0200 Subject: [PATCH] shared: add nm_utils_parse_inaddr_bin_full() to support legacy IPv4 formats as inet_aton() inet_aton() also supports IPv4 addresses in octal (with a leading '0') or where not all 4 digits of the address are present. Add nm_utils_parse_inaddr_bin_full() to optionally fallback to parse the address with inet_aton(). Note taht inet_aton() also supports all crazy formats, including ignoring trailing garbage after a whitespace. We don't want to accept that in general. Note that even in legacy format we: - accept everything that inet_pton() would accept - additionally, we also accept some forms which inet_aton() would accept, but not all. That means, the legacy format that we accept is a superset of inet_pton() and a subset of inet_aton(). Which is desirable. --- shared/nm-glib-aux/nm-shared-utils.c | 99 ++++++++++++++++++++++++++-- shared/nm-glib-aux/nm-shared-utils.h | 17 +++-- 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index be9fbdc4a..8e1c8b580 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -683,11 +683,80 @@ nm_utils_ip_is_site_local (int addr_family, /*****************************************************************************/ +static gboolean +_parse_legacy_addr4 (const char *text, in_addr_t *out_addr) +{ + gs_free char *s_free = NULL; + struct in_addr a1; + guint8 bin[sizeof (a1)]; + char *s; + int i; + + if (inet_aton (text, &a1) != 1) + return FALSE; + + /* OK, inet_aton() accepted the format. That's good, because we want + * to accept IPv4 addresses in octal format, like 255.255.000.000. + * That's what "legacy" means here. inet_pton() doesn't accept those. + * + * But inet_aton() also ignores trailing garbage and formats with fewer than + * 4 digits. That is just too crazy and we don't do that. Perform additional checks + * and reject some forms that inet_aton() accepted. + * + * Note that we still should (of course) accept everything that inet_pton() + * accepts. However this code never gets called if inet_pton() succeeds + * (see below, aside the assertion code). */ + + if (NM_STRCHAR_ANY (text, ch, ( !(ch >= '0' && ch <= '9') + && !NM_IN_SET (ch, '.', 'x')))) { + /* We only accepts '.', digits, and 'x' for "0x". */ + return FALSE; + } + + s = nm_memdup_maybe_a (300, text, strlen (text) + 1, &s_free); + + for (i = 0; i < G_N_ELEMENTS (bin); i++) { + char *current_token = s; + gint32 v; + + s = strchr (s, '.'); + if (s) { + s[0] = '\0'; + s++; + } + + if ((i == G_N_ELEMENTS (bin) - 1) != (s == NULL)) { + /* Exactly for the last digit, we expect to have no more following token. + * But this isn't the case. Abort. */ + return FALSE; + } + + v = _nm_utils_ascii_str_to_int64 (current_token, 0, 0, 0xFF, -1); + if (v == -1) { + /* we do accept octal and hex (even with leading "0x"). But something + * about this token is wrong. */ + return FALSE; + } + + bin[i] = v; + } + + if (memcmp (bin, &a1, sizeof (bin)) != 0) { + /* our parsing did not agree with what inet_aton() gave. Something + * is wrong. Abort. */ + return FALSE; + } + + *out_addr = a1.s_addr; + return TRUE; +} + gboolean -nm_utils_parse_inaddr_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr) +nm_utils_parse_inaddr_bin_full (int addr_family, + gboolean accept_legacy, + const char *text, + int *out_addr_family, + gpointer out_addr) { NMIPAddr addrbin; @@ -699,8 +768,26 @@ nm_utils_parse_inaddr_bin (int addr_family, } else g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE); - if (inet_pton (addr_family, text, &addrbin) != 1) - return FALSE; + if (inet_pton (addr_family, text, &addrbin) != 1) { + if ( accept_legacy + && addr_family == AF_INET + && _parse_legacy_addr4 (text, &addrbin.addr4)) { + /* The address is in some legacy format which inet_aton() accepts, but not inet_pton(). + * Most likely octal digits (leading zeros). We accept the address. */ + } else + return FALSE; + } + +#if NM_MORE_ASSERTS > 10 + if (addr_family == AF_INET) { + in_addr_t a; + + /* The legacy parser should accept everything that inet_pton() accepts too. Meaning, + * it should strictly parse *more* formats. And of course, parse it the same way. */ + nm_assert (_parse_legacy_addr4 (text, &a)); + nm_assert (addrbin.addr4 == a); + } +#endif NM_SET_OUT (out_addr_family, addr_family); if (out_addr) diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 4abd2a004..3bd1afaea 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -542,10 +542,19 @@ gboolean nm_utils_ip_is_site_local (int addr_family, /*****************************************************************************/ -gboolean nm_utils_parse_inaddr_bin (int addr_family, - const char *text, - int *out_addr_family, - gpointer out_addr); +gboolean nm_utils_parse_inaddr_bin_full (int addr_family, + gboolean accept_legacy, + const char *text, + int *out_addr_family, + gpointer out_addr); +static inline gboolean +nm_utils_parse_inaddr_bin (int addr_family, + const char *text, + int *out_addr_family, + gpointer out_addr) +{ + return nm_utils_parse_inaddr_bin_full (addr_family, FALSE, text, out_addr_family, out_addr); +} gboolean nm_utils_parse_inaddr (int addr_family, const char *text,