summaryrefslogtreecommitdiff
path: root/src/libnm-glib-aux/nm-inet-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-glib-aux/nm-inet-utils.c')
-rw-r--r--src/libnm-glib-aux/nm-inet-utils.c151
1 files changed, 111 insertions, 40 deletions
diff --git a/src/libnm-glib-aux/nm-inet-utils.c b/src/libnm-glib-aux/nm-inet-utils.c
index 7f710f3527..87fe3ce050 100644
--- a/src/libnm-glib-aux/nm-inet-utils.c
+++ b/src/libnm-glib-aux/nm-inet-utils.c
@@ -6,6 +6,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <dlfcn.h>
/*****************************************************************************/
@@ -266,34 +267,118 @@ nm_ip6_addr_same_prefix_cmp(const struct in6_addr *addr_a,
/*****************************************************************************/
-static gboolean
-_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
+static int
+_inet_aton(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;
+ /* Call inet_aton() via dlopen.
+ *
+ * The inet_aton() API is discouraged, and ABI checkers warn when we call
+ * it.
+ *
+ * We want to use this function, but only for testing/asserting. To avoid
+ * the ABI checker's complain, dlopen() the symbol. This is not used for
+ * production.
+ */
+ static gpointer mod_handle = NULL;
+ static gpointer fcn_sym = NULL;
+ static gsize initialized = 0;
+ int (*fcn)(const char *text, struct in_addr *out_addr);
+ int r;
+ in_addr_t a;
+
+ if (g_once_init_enter(&initialized)) {
+ mod_handle = dlopen(NULL, RTLD_LAZY);
+ if (mod_handle) {
+ fcn_sym = dlsym(mod_handle, "inet_aton");
+ if (!fcn_sym)
+ dlclose(g_steal_pointer(&mod_handle));
+ }
+ g_once_init_leave(&initialized, 1);
+ }
- if (inet_aton(text, &a1) != 1) {
- g_set_error_literal(error,
- NM_UTILS_ERROR,
- NM_UTILS_ERROR_INVALID_ARGUMENT,
- "address invalid according to inet_aton()");
- return FALSE;
+ if (!fcn_sym)
+ return -ENOSYS;
+
+ g_assert(mod_handle);
+
+ fcn = fcn_sym;
+ r = fcn(text, (gpointer) &a);
+
+ if (r != 1)
+ return -EINVAL;
+
+ NM_SET_OUT(out_addr, a);
+ return 0;
+}
+
+int
+nmtst_inet_aton(const char *text, in_addr_t *out_addr)
+{
+ return _inet_aton(text, out_addr);
+}
+
+static void
+_nm_assert_legacy_addr4(const char *text, in_addr_t addr)
+{
+#if NM_MORE_ASSERTS > 20
+ char buf1[NM_INET_ADDRSTRLEN];
+ char buf2[NM_INET_ADDRSTRLEN];
+ int r;
+ in_addr_t a;
+
+ /* Our legacy parser accepted "text" as "addr".
+ *
+ * However, we want to ensure that whatever we parse is also parsed by old
+ * inet_aton(). So we want to be strictly more strict than inet_aton() in
+ * what we accept.
+ */
+
+ r = _inet_aton(text, &a);
+
+ if (r != 0) {
+ if (r == -ENOSYS)
+ return;
+ g_error("inet_aton(\"%s\") failed with \"%s\", but we expected %s",
+ text,
+ nm_strerror_native(-r),
+ nm_inet4_ntop(addr, buf2));
}
- /* 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.
+ if (a != addr) {
+ g_error("inet_aton(\"%s\") parsed %s, but we expected %s",
+ text,
+ nm_inet4_ntop(a, buf1),
+ nm_inet4_ntop(addr, buf2));
+ }
+#endif
+}
+
+static gboolean
+_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
+{
+ gs_free char *s_free = NULL;
+ union {
+ guint8 b[sizeof(in_addr_t)];
+ in_addr_t a;
+ } addr;
+ char *s;
+ int i;
+
+ /* inet_pton() does strict parsing of IPv4 address. Good.
*
- * 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.
+ * However, inet_aton() used to accept much more relaxed forms (e.g. octal
+ * and hex numbers, not having 4 components but fewer, ignore any trailing
+ * garbage).
+ *
+ * Some places where we accept input, we want to be slightly more forgiving
+ * than inet_pton() and accept some (not all!) forms of what inet_aton()
+ * would accept. For example, we want to accept 255.000.000.000.
+ *
+ * We reimplement that below.
*
* 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). */
+ * accepts. This is ensured because the caller only calls this function
+ * after inet_pton() failed. */
if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) {
/* We only accepts '.', digits, and 'x' for "0x". */
@@ -306,7 +391,7 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free);
- for (i = 0; i < G_N_ELEMENTS(bin); i++) {
+ for (i = 0; i < G_N_ELEMENTS(addr.b); i++) {
char *current_token = s;
gint32 v;
@@ -316,7 +401,7 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
s++;
}
- if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) {
+ if ((i == G_N_ELEMENTS(addr.b) - 1) != (s == NULL)) {
/* Exactly for the last digit, we expect to have no more following token.
* But this isn't the case. Abort. */
g_set_error(error,
@@ -344,26 +429,12 @@ _parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
return FALSE;
}
- bin[i] = v;
+ addr.b[i] = v;
}
- if (memcmp(bin, &a1, sizeof(bin)) != 0) {
- /* our parsing did not agree with what inet_aton() gave. Something
- * is wrong. Abort. */
- g_set_error(
- error,
- NM_UTILS_ERROR,
- NM_UTILS_ERROR_INVALID_ARGUMENT,
- "inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx",
- a1.s_addr,
- bin[0],
- bin[1],
- bin[2],
- bin[3]);
- return FALSE;
- }
+ _nm_assert_legacy_addr4(text, addr.a);
- *out_addr = a1.s_addr;
+ *out_addr = addr.a;
return TRUE;
}