about summary refs log tree commit diff
path: root/src/libnm-core-impl/tests
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2025-02-12 13:46:50 +0100
committerMichael Biebl <biebl@debian.org>2025-02-12 13:46:50 +0100
commit8bdf070ff046f482f6eb5e2b15ebc216f5d1e3da (patch)
treef706478d189d54c6532e8863d4b0d5ff5575af60 /src/libnm-core-impl/tests
parent818258cf34b83fbc754633295e1052d4752d7b15 (diff)
New upstream version 1.51.90 upstream/1.51.90
Diffstat (limited to 'src/libnm-core-impl/tests')
-rw-r--r--src/libnm-core-impl/tests/test-general.c351
-rw-r--r--src/libnm-core-impl/tests/test-keyfile.c6
-rw-r--r--src/libnm-core-impl/tests/test-setting.c135
3 files changed, 296 insertions, 196 deletions
diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c
index 8d4ea069..d581fc79 100644
--- a/src/libnm-core-impl/tests/test-general.c
+++ b/src/libnm-core-impl/tests/test-general.c
@@ -560,13 +560,13 @@ test_nm_hash(void)
 
     g_assert_cmpmem(NM_HASH_SEED_16(55, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
                     16,
-                    ((guint8[16]){55, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
+                    ((guint8[16]) {55, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
                     16);
 
-    g_assert_cmpmem(NM_HASH_SEED_16_U64(1), 16, ((guint8[16]){0, 0, 0, 0, 0, 0, 0, 1, 0}), 16);
+    g_assert_cmpmem(NM_HASH_SEED_16_U64(1), 16, ((guint8[16]) {0, 0, 0, 0, 0, 0, 0, 1, 0}), 16);
     g_assert_cmpmem(NM_HASH_SEED_16_U64(0x1234567890ABCDEFu),
                     16,
-                    ((guint8[16]){0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0}),
+                    ((guint8[16]) {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0}),
                     16);
 
     g_assert_cmpint(c_siphash_hash(NM_HASH_SEED_16_U64(0x780E21E45489CC6Fu), (guint8 *) "foo", 3),
@@ -1702,7 +1702,7 @@ static const NMDedupMultiObjClass dedup_obj_class = {
 };
 
 #define DEDUP_OBJ_INIT(val_val, other_other)              \
-    (&((DedupObj){                                        \
+    (&((DedupObj) {                                       \
         .parent =                                         \
             {                                             \
                 .klass      = &dedup_obj_class,           \
@@ -1904,7 +1904,7 @@ _dedup_entry_assert_all(const NMDedupMultiEntry *entry,
     }
 }
 #define _dedup_entry_assert_all(entry, expected_idx, ...) \
-    _dedup_entry_assert_all(entry, expected_idx, (const DedupObj *const[]){__VA_ARGS__, NULL})
+    _dedup_entry_assert_all(entry, expected_idx, (const DedupObj *const[]) {__VA_ARGS__, NULL})
 
 static void
 test_dedup_multi(void)
@@ -3962,7 +3962,7 @@ typedef struct {
 
 typedef struct {
     const char *name;
-    DiffKey     keys[33];
+    DiffKey     keys[41];
 } DiffSetting;
 
 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
@@ -4037,6 +4037,9 @@ test_connection_diff_a_only(void)
           {NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_DOWN_ON_POWEROFF, NM_SETTING_DIFF_RESULT_IN_A},
+          {NM_SETTING_CONNECTION_IP_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
+          {NM_SETTING_CONNECTION_IP_PING_ADDRESSES, NM_SETTING_DIFF_RESULT_IN_A},
+          {NM_SETTING_CONNECTION_IP_PING_ADDRESSES_REQUIRE_ALL, NM_SETTING_DIFF_RESULT_IN_A},
           {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}}},
         {NM_SETTING_WIRED_SETTING_NAME,
          {
@@ -4075,6 +4078,7 @@ test_connection_diff_a_only(void)
              {NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME_V2, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_HOSTNAME_FLAGS, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP4_CONFIG_DHCP_FQDN, NM_SETTING_DIFF_RESULT_IN_A},
@@ -4088,9 +4092,13 @@ test_connection_diff_a_only(void)
              {NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_REJECT_SERVERS, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP4_CONFIG_LINK_LOCAL, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP4_CONFIG_DHCP_IPV6_ONLY_PREFERRED, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_REPLACE_LOCAL_RULE, NM_SETTING_DIFF_RESULT_IN_A},
              {NM_SETTING_IP_CONFIG_DHCP_SEND_RELEASE, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_ROUTED_DNS, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_SHARED_DHCP_RANGE, NM_SETTING_DIFF_RESULT_IN_A},
+             {NM_SETTING_IP_CONFIG_SHARED_DHCP_LEASE_TIME, NM_SETTING_DIFF_RESULT_IN_A},
              {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN},
          }},
     };
@@ -8671,9 +8679,11 @@ test_nm_utils_ascii_str_to_int64(void)
 static void
 test_nm_utils_strstrdictkey(void)
 {
-#define _VALUES_STATIC(_v1, _v2)                                                    \
-    {                                                                               \
-        .v1 = _v1, .v2 = _v2, .v_static = _nm_utils_strstrdictkey_static(_v1, _v2), \
+#define _VALUES_STATIC(_v1, _v2)                              \
+    {                                                         \
+        .v1       = _v1,                                      \
+        .v2       = _v2,                                      \
+        .v_static = _nm_utils_strstrdictkey_static(_v1, _v2), \
     }
     const struct {
         const char           *v1;
@@ -10613,7 +10623,7 @@ test_integrate_maincontext(gconstpointer test_data)
 
         loop1 = g_main_loop_new(c1, FALSE);
 
-        d = (IntegData){
+        d = (IntegData) {
             .loop1 = loop1,
             .c2    = c2,
         };
@@ -11389,176 +11399,179 @@ test_connection_path(void)
 /*****************************************************************************/
 
 static void
-_t_dnsname_1(const char *str, const char *exp_addr, const char *exp_server_name)
-{
-    int           addr_family;
-    NMIPAddr      exp_addr_bin;
-    gboolean      addr_family_request;
-    gboolean      r;
-    int           detect_addr_family;
-    NMIPAddr      detect_addr;
-    const char   *detect_server_name;
-    int          *p_detect_addr_family = &detect_addr_family;
-    NMIPAddr     *p_detect_addr        = &detect_addr;
-    const char  **p_detect_server_name = &detect_server_name;
-    char          str_construct_buf[100];
-    char          str_construct_buf2[100];
-    const char   *str_construct;
-    const char   *str_construct2;
-    gsize         l;
-    const char   *str_normalized;
-    gs_free char *str_normalized_alloc = NULL;
+t_dns_0(const char *str)
+{
+    NMDnsServer server = {};
+    gboolean    ret;
 
-    g_assert(str);
-    g_assert(exp_addr);
+    ret = nm_dns_uri_parse(AF_UNSPEC, str, &server);
 
-    r = nm_inet_parse_bin(AF_UNSPEC, exp_addr, &addr_family, &exp_addr_bin);
-    g_assert(r);
-    g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
+    g_assert(!ret);
+}
 
-    addr_family_request = nmtst_get_rand_bool();
-    if (nmtst_get_rand_bool())
-        p_detect_addr = NULL;
-    if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool())
-        p_detect_addr_family = NULL;
-    if (nmtst_get_rand_bool())
-        p_detect_server_name = NULL;
-
-    r = nm_utils_dnsname_parse(addr_family_request ? addr_family : AF_UNSPEC,
-                               str,
-                               p_detect_addr_family,
-                               p_detect_addr,
-                               p_detect_server_name);
-    g_assert(r);
-
-    if (p_detect_addr_family)
-        g_assert_cmpint(addr_family, ==, detect_addr_family);
-    if (p_detect_addr)
-        g_assert_cmpstr(nmtst_inet_to_string(addr_family, &detect_addr), ==, exp_addr);
-    if (p_detect_server_name)
-        g_assert_cmpstr(detect_server_name, ==, exp_server_name);
-
-    r = nm_utils_dnsname_parse(addr_family == AF_INET ? AF_INET6 : AF_INET,
-                               str,
-                               p_detect_addr_family,
-                               p_detect_addr,
-                               p_detect_server_name);
-    g_assert(!r);
-
-    /* Construct the expected value. */
-    str_construct = nm_utils_dnsname_construct(addr_family,
-                                               &exp_addr_bin,
-                                               exp_server_name,
-                                               str_construct_buf,
-                                               sizeof(str_construct_buf));
-    g_assert(str_construct);
-    g_assert(str_construct == str_construct_buf);
-    g_assert(strlen(str_construct) < sizeof(str_construct_buf));
-
-    /* Check that a too short buffer causes truncation. */
-    l              = nmtst_get_rand_uint32() % (strlen(str_construct) + 10);
-    str_construct2 = nm_utils_dnsname_construct(addr_family,
-                                                &exp_addr_bin,
-                                                exp_server_name,
-                                                str_construct_buf2,
-                                                l);
-    if (str_construct2) {
-        g_assert(str_construct2 == str_construct_buf2);
-        g_assert_cmpstr(str_construct2, ==, str_construct);
-        g_assert(l > strlen(str_construct));
-    } else
-        g_assert(l <= strlen(str_construct));
+static void
+dns_uri_parse_ok(const char    *str,
+                 int            addr_family,
+                 NMDnsUriScheme scheme,
+                 const char    *addr,
+                 int            port,
+                 const char    *sname,
+                 const char    *ifname)
+{
+    NMDnsServer dns = {};
+    char        addrstr[NM_INET_ADDRSTRLEN];
+    gboolean    ret;
 
-    if (!nm_streq(str_construct, str)) {
-        _t_dnsname_1(str_construct, exp_addr, exp_server_name);
-    }
+    for (int i = 0; i < 2; i++) {
+        gboolean af_unspec = i;
 
-    str_normalized = nm_utils_dnsname_normalize(nmtst_get_rand_bool() ? addr_family : AF_UNSPEC,
-                                                str,
-                                                &str_normalized_alloc);
-    g_assert(str_normalized);
-    if (str_normalized_alloc) {
-        g_assert(str_normalized == str_normalized_alloc);
-        g_assert_cmpstr(str_normalized, !=, str);
-    } else {
-        g_assert(str == str_normalized);
+        ret = nm_dns_uri_parse(af_unspec ? AF_UNSPEC : addr_family, str, &dns);
+        g_assert(ret);
+
+        g_assert_cmpint(addr_family, ==, dns.addr_family);
+        g_assert_cmpint(port, ==, dns.port);
+        g_assert_cmpstr(sname, ==, dns.servername);
+        g_assert_cmpstr(ifname ?: "", ==, dns.interface);
+
+        nm_inet_ntop(dns.addr_family, &dns.addr, addrstr);
+        g_assert_cmpstr(addrstr, ==, addr);
+
+        /* Parse with the wrong address family must fail */
+        ret = nm_dns_uri_parse(addr_family == AF_INET ? AF_INET6 : AF_INET, str, &dns);
+        g_assert(!ret);
     }
-    g_assert_cmpstr(str_normalized, ==, str_construct);
+}
+
+#define t_dns_1(str, af, scheme, addr, port, sname, ifname) \
+    dns_uri_parse_ok((str),                                 \
+                     (AF_##af),                             \
+                     (NM_DNS_URI_SCHEME_##scheme),          \
+                     (addr),                                \
+                     (port),                                \
+                     (sname),                               \
+                     (ifname))
+
+static void
+test_dns_uri_parse(void)
+{
+    /* clang-format off */
+    t_dns_1("dns+tls://8.8.8.8",                   INET,  TLS,  "8.8.8.8",            -1, NULL,      NULL);
+    t_dns_1("dns+tls://8.8.8.8",                   INET,  TLS,  "8.8.8.8",            -1, NULL,      NULL);
+    t_dns_1("dns+tls://1.2.3.4#name",              INET,  TLS,  "1.2.3.4",            -1, "name",    NULL);
+    t_dns_1("dns+tls://1.2.3.4#a.b.c",             INET,  TLS,  "1.2.3.4",            -1, "a.b.c",   NULL);
+    t_dns_1("dns+tls://1.2.3.4:53",                INET,  TLS,  "1.2.3.4",            53, NULL,      NULL);
+    t_dns_1("dns+tls://1.2.3.4:53#foobar",         INET,  TLS,  "1.2.3.4",            53, "foobar",  NULL);
+    t_dns_1("dns+tls://192.168.120.250:99",        INET,  TLS,  "192.168.120.250",    99, NULL,      NULL);
+    t_dns_1("dns+udp://8.8.8.8:65535",             INET,  UDP,  "8.8.8.8",         65535, NULL,      NULL);
+
+    t_dns_1("dns+udp://[fd01::1]",                 INET6, UDP,  "fd01::1",            -1, NULL,      NULL);
+    t_dns_1("dns+tls://[fd01::2]:5353",            INET6, UDP,  "fd01::2",          5353, NULL,      NULL);
+    t_dns_1("dns+tls://[::1]#name",                INET6, UDP,  "::1",                -1, "name",    NULL);
+    t_dns_1("dns+tls://[::2]:65535#name",          INET6, UDP,  "::2",             65535, "name",    NULL);
+    t_dns_1("dns+udp://[::ffff:1.2.3.4]",          INET6, UDP,  "::ffff:1.2.3.4",     -1, NULL,      NULL);
+    t_dns_1("dns+tls://[fe80::1%eth0]",            INET6, UDP,  "fe80::1",            -1, NULL,      "eth0");
+    t_dns_1("dns+tls://[fe80::2%en1]:53#a",        INET6, UDP,  "fe80::2",            53, "a",       "en1");
+    t_dns_1("dns+tls://[fe80::1%en3456789012345]", INET6, UDP,  "fe80::1",            -1, NULL, "en3456789012345");
+
+    t_dns_1("1.2.3.4",                             INET,  NONE, "1.2.3.4",            -1, NULL,      NULL);
+    t_dns_1("1.2.3.4#foo",                         INET,  NONE, "1.2.3.4",            -1, "foo",     NULL);
+    t_dns_1("1::#x",                               INET6, NONE, "1::",                -1, "x",       NULL);
+    t_dns_1("1::0#x",                              INET6, NONE, "1::",                -1, "x",       NULL);
+    t_dns_1("192.168.0.1",                         INET,  NONE, "192.168.0.1",        -1, NULL,      NULL);
+    t_dns_1("192.168.0.1#tst.com",                 INET,  NONE, "192.168.0.1",        -1, "tst.com", NULL);
+    t_dns_1("fe80::18",                            INET6, NONE, "fe80::18",           -1, NULL,      NULL);
+    t_dns_1("fe80::18#foo.com",                    INET6, NONE, "fe80::18",           -1, "foo.com", NULL);
+    /* clang-format on */
 
-    nm_clear_g_free(&str_normalized_alloc);
-    str_normalized = nm_utils_dnsname_normalize(addr_family == AF_INET ? AF_INET6 : AF_INET,
-                                                str,
-                                                &str_normalized_alloc);
-    g_assert(!str_normalized);
-    g_assert(!str_normalized_alloc);
+    t_dns_0("http://8.8.8.8");              /* unsupported schema */
+    t_dns_0("dns+udp://1.2.3.4#name");      /* servername not supported for plain UDP */
+    t_dns_0("dns+tls://1.2.3");             /* invalid address */
+    t_dns_0("dns+tls://fd01::1");           /* IPv6 requires brackets */
+    t_dns_0("dns+tls://[fd13:a:aaaa]");     /* invalid address */
+    t_dns_0("dns+tls://1.2.3.4:1:1");       /* invalid syntax */
+    t_dns_0("dns+tls://1.2.3.4#name#name"); /* invalid syntax */
+    t_dns_0("dns+tls://1.2.3.4%eth0");      /* interface only allowed for IPv6 */
+    t_dns_0("dns+tls://[2001::1%eth0]");    /* interface only allowed for IPv6 link-local */
+    t_dns_0("dns+tls://[fe80::1%en34567890123456]"); /* interface name too long */
+    t_dns_0("1.2.3.4#");
+    t_dns_0("1::0#");
+    t_dns_0("192.168.0.1:53");
+    t_dns_0("192.168.0.1:53#example.com");
+    t_dns_0("fe80::18%19");
+    t_dns_0("fe80::18%lo");
+    t_dns_0("[fe80::18]:53");
+    t_dns_0("[fe80::18]:53%19");
+    t_dns_0("[fe80::18]:53%lo");
+    t_dns_0("fe80::18%19#hoge.com");
+    t_dns_0("[fe80::18]:53#hoge.com");
+    t_dns_0("[fe80::18]:53%19");
+    t_dns_0("[fe80::18]:53%19#hoge.com");
+    t_dns_0("[fe80::18]:53%lo");
+    t_dns_0("[fe80::18]:53%lo#hoge.com");
+}
+
+static void
+test_dns_uri_parse_plain(void)
+{
+    struct {
+        const char *input;
+        int         input_af;
+        gboolean    result;
+        const char *addrstr;
+    } values[] = {
+        {"1.2.3.4", AF_INET, TRUE, "1.2.3.4"},
+        {"1.2.3.4", AF_INET6, FALSE, NULL},
+        {"1.2.3.4", AF_UNSPEC, TRUE, "1.2.3.4"},
+        {"1234:5555:ffff:dddd::4321", AF_INET, FALSE, NULL},
+        {"1234:5555:ffff:dddd::4321", AF_INET6, TRUE, "1234:5555:ffff:dddd::4321"},
+        {"1234:5555:ffff:dddd::4321", AF_UNSPEC, TRUE, "1234:5555:ffff:dddd::4321"},
+        {"192.0.2.1#example.com", AF_INET, TRUE, "192.0.2.1"},
+        {"192.0.2.1#example.com", AF_UNSPEC, TRUE, "192.0.2.1"},
+        {"192.0.2.1#example.com", AF_INET6, FALSE, NULL},
+        {"dns+tls://1.2.3.4", AF_INET, FALSE, NULL},
+        {"dns+tls://[fd01::1]", AF_INET, FALSE, NULL},
+        {"dns+udp://1.2.3.4:53", AF_INET, TRUE, "1.2.3.4"},
+        {"dns+udp://1.2.3.4:54", AF_INET, FALSE, NULL},
+        {"dns+udp://[fd01::1]", AF_INET6, TRUE, "fd01::1"},
+        {"dns+udp://[fd01::1]:53", AF_INET6, TRUE, "fd01::1"},
+        {"dns+udp://[fd01::1]:60000", AF_INET, FALSE, NULL},
+    };
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS(values); i++) {
+        char     addrstr[NM_INET_ADDRSTRLEN];
+        gboolean result;
+        NMIPAddr addr;
+
+        result = nm_dns_uri_parse_plain(values[i].input_af, values[i].input, addrstr, &addr);
+        g_assert_cmpint(result, ==, values[i].result);
+        if (result) {
+            char buf[NM_INET_ADDRSTRLEN];
+
+            nm_inet_ntop(strchr(addrstr, ':') ? AF_INET6 : AF_INET, addr.addr_ptr, buf);
+            g_assert_cmpstr(buf, ==, addrstr);
+            g_assert_cmpstr(addrstr, ==, values[i].addrstr);
+        }
+    }
 }
 
 static void
-_t_dnsname_0(const char *str)
+t_dns_uri_normalize(const char *input, const char *expected)
 {
-    gboolean      addr_family_request;
-    int           detect_addr_family;
-    NMIPAddr      detect_addr;
-    const char   *detect_server_name;
-    int          *p_detect_addr_family = &detect_addr_family;
-    NMIPAddr     *p_detect_addr        = &detect_addr;
-    const char  **p_detect_server_name = &detect_server_name;
-    const char   *str_normalized;
-    gs_free char *str_normalized_alloc = NULL;
-    gboolean      r;
+    const char   *str;
+    gs_free char *str_free = NULL;
 
-    g_assert(str);
+    str = nm_dns_uri_normalize(AF_UNSPEC, input, &str_free);
+    g_assert_cmpstr(str, ==, expected);
+}
 
-    addr_family_request = nmtst_get_rand_bool();
-    if (nmtst_get_rand_bool())
-        p_detect_addr = NULL;
-    if ((addr_family_request || !p_detect_addr) && nmtst_get_rand_bool())
-        p_detect_addr_family = NULL;
-    if (nmtst_get_rand_bool())
-        p_detect_server_name = NULL;
-
-    r = nm_utils_dnsname_parse(addr_family_request ? nmtst_rand_select(AF_INET, AF_INET6)
-                                                   : AF_UNSPEC,
-                               str,
-                               p_detect_addr_family,
-                               p_detect_addr,
-                               p_detect_server_name);
-    g_assert(!r);
-
-    str_normalized = nm_utils_dnsname_normalize(nmtst_rand_select(AF_UNSPEC, AF_INET, AF_INET6),
-                                                str,
-                                                &str_normalized_alloc);
-    g_assert(!str_normalized);
-    g_assert(!str_normalized_alloc);
-}
-
-static void
-test_dnsname(void)
-{
-    _t_dnsname_1("1.2.3.4", "1.2.3.4", NULL);
-    _t_dnsname_1("1.2.3.4#foo", "1.2.3.4", "foo");
-    _t_dnsname_1("1::#x", "1::", "x");
-    _t_dnsname_1("1::0#x", "1::", "x");
-    _t_dnsname_1("192.168.0.1", "192.168.0.1", NULL);
-    _t_dnsname_1("192.168.0.1#test.com", "192.168.0.1", "test.com");
-    _t_dnsname_1("fe80::18", "fe80::18", NULL);
-    _t_dnsname_1("fe80::18#hoge.com", "fe80::18", "hoge.com");
-
-    _t_dnsname_0("1.2.3.4#");
-    _t_dnsname_0("1::0#");
-    _t_dnsname_0("192.168.0.1:53");
-    _t_dnsname_0("192.168.0.1:53#example.com");
-    _t_dnsname_0("fe80::18%19");
-    _t_dnsname_0("fe80::18%lo");
-    _t_dnsname_0("[fe80::18]:53");
-    _t_dnsname_0("[fe80::18]:53%19");
-    _t_dnsname_0("[fe80::18]:53%lo");
-    _t_dnsname_0("fe80::18%19#hoge.com");
-    _t_dnsname_0("[fe80::18]:53#hoge.com");
-    _t_dnsname_0("[fe80::18]:53%19");
-    _t_dnsname_0("[fe80::18]:53%19#hoge.com");
-    _t_dnsname_0("[fe80::18]:53%lo");
-    _t_dnsname_0("[fe80::18]:53%lo#hoge.com");
+static void
+test_dns_uri_normalize(void)
+{
+    t_dns_uri_normalize("8.8.8.8", "8.8.8.8");
+    t_dns_uri_normalize("dns+tls://[2001:0:0::1234]:999#name", "dns+tls://[2001::1234]:999#name");
+    t_dns_uri_normalize("dns+udp://[0::1]:0123", "dns+udp://[::1]:123");
+    t_dns_uri_normalize("8.8.8.888", NULL);
 }
 
 /*****************************************************************************/
@@ -11937,7 +11950,9 @@ main(int argc, char **argv)
     g_test_add_func("/core/general/test_system_encodings", test_system_encodings);
     g_test_add_func("/core/general/test_direct_string_is_refstr", test_direct_string_is_refstr);
     g_test_add_func("/core/general/test_connection_path", test_connection_path);
-    g_test_add_func("/core/general/test_dnsname", test_dnsname);
+    g_test_add_func("/core/general/test_dns_uri_parse", test_dns_uri_parse);
+    g_test_add_func("/core/general/test_dns_uri_get_legacy", test_dns_uri_parse_plain);
+    g_test_add_func("/core/general/test_dns_uri_normalize", test_dns_uri_normalize);
     g_test_add_func("/core/general/test_dhcp_iaid_hexstr", test_dhcp_iaid_hexstr);
 
     return g_test_run();
diff --git a/src/libnm-core-impl/tests/test-keyfile.c b/src/libnm-core-impl/tests/test-keyfile.c
index 1912782e..5c66196c 100644
--- a/src/libnm-core-impl/tests/test-keyfile.c
+++ b/src/libnm-core-impl/tests/test-keyfile.c
@@ -943,7 +943,7 @@ test_invalid_option(void)
 
     nm_setting_option_set_boolean(s_ethtool, NM_ETHTOOL_OPTNAME_PAUSE_RX, TRUE);
 
-    data = (InvalidOptionWriteData){};
+    data = (InvalidOptionWriteData) {};
     kf   = nm_keyfile_write(con,
                           NM_KEYFILE_HANDLER_FLAGS_NONE,
                           _invalid_option_write_handler,
@@ -956,7 +956,7 @@ test_invalid_option(void)
 
     nmtst_assert_connection_verifies_without_normalization(con);
 
-    data = (InvalidOptionWriteData){};
+    data = (InvalidOptionWriteData) {};
     kf   = nm_keyfile_write(con,
                           NM_KEYFILE_HANDLER_FLAGS_NONE,
                           _invalid_option_write_handler,
@@ -967,7 +967,7 @@ test_invalid_option(void)
 
     nm_setting_option_set(s_ethtool, "bogus", g_variant_new_int64(0));
 
-    data = (InvalidOptionWriteData){
+    data = (InvalidOptionWriteData) {
         .expect = TRUE,
     };
     kf = nm_keyfile_write(con,
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index f3309166..2064162a 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -799,29 +799,29 @@ static void
 test_bond_compare(void)
 {
     test_bond_compare_options(TRUE,
-                              ((const char *[]){"mode", "balance-rr", "miimon", "1", NULL}),
-                              ((const char *[]){"mode", "balance-rr", "miimon", "1", NULL}));
+                              ((const char *[]) {"mode", "balance-rr", "miimon", "1", NULL}),
+                              ((const char *[]) {"mode", "balance-rr", "miimon", "1", NULL}));
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"mode", "balance-rr", "miimon", "1", NULL}),
-                              ((const char *[]){"mode", "balance-rr", "miimon", "2", NULL}));
+                              ((const char *[]) {"mode", "balance-rr", "miimon", "1", NULL}),
+                              ((const char *[]) {"mode", "balance-rr", "miimon", "2", NULL}));
 
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"miimon", "1", NULL}),
-                              ((const char *[]){"miimon", "1", "updelay", "0", NULL}));
+                              ((const char *[]) {"miimon", "1", NULL}),
+                              ((const char *[]) {"miimon", "1", "updelay", "0", NULL}));
 
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"num_grat_arp", "2", NULL}),
-                              ((const char *[]){"num_grat_arp", "1", NULL}));
+                              ((const char *[]) {"num_grat_arp", "2", NULL}),
+                              ((const char *[]) {"num_grat_arp", "1", NULL}));
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"num_grat_arp", "3", NULL}),
-                              ((const char *[]){"num_unsol_na", "3", NULL}));
+                              ((const char *[]) {"num_grat_arp", "3", NULL}),
+                              ((const char *[]) {"num_unsol_na", "3", NULL}));
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"num_grat_arp", "4", NULL}),
-                              ((const char *[]){"num_unsol_na", "4", "num_grat_arp", "4", NULL}));
+                              ((const char *[]) {"num_grat_arp", "4", NULL}),
+                              ((const char *[]) {"num_unsol_na", "4", "num_grat_arp", "4", NULL}));
 
     test_bond_compare_options(FALSE,
-                              ((const char *[]){"mode", "balance-rr", "miimon", "100", NULL}),
-                              ((const char *[]){"mode", "balance-rr", NULL}));
+                              ((const char *[]) {"mode", "balance-rr", "miimon", "100", NULL}),
+                              ((const char *[]) {"mode", "balance-rr", NULL}));
 }
 
 static void
@@ -856,20 +856,25 @@ static void
 test_bond_normalize(void)
 {
     test_bond_normalize_options(
-        ((const char *[]){"mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL}),
-        ((const char *[]){"mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL}));
-    test_bond_normalize_options(((const char *[]){"mode", "1", "miimon", "1", NULL}),
-                                ((const char *[]){"mode", "active-backup", "miimon", "1", NULL}));
+        ((const char *[]) {"mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL}),
+        ((const char *[]) {"mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL}));
+    test_bond_normalize_options(((const char *[]) {"mode", "1", "miimon", "1", NULL}),
+                                ((const char *[]) {"mode", "active-backup", "miimon", "1", NULL}));
     test_bond_normalize_options(
-        ((const char *[]){"mode", "balance-alb", "tlb_dynamic_lb", "1", NULL}),
-        ((const char *[]){"mode", "balance-alb", "tlb_dynamic_lb", "1", NULL}));
+        ((const char *[]) {"mode", "balance-alb", "tlb_dynamic_lb", "1", NULL}),
+        ((const char *[]) {"mode", "balance-alb", "tlb_dynamic_lb", "1", NULL}));
     test_bond_normalize_options(
-        ((const char *[]){"mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL}),
-        ((const char *[]){"mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL}));
+        ((const char *[]) {"mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL}),
+        ((const char *[]) {"mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL}));
     test_bond_normalize_options(
-        ((const char
-              *[]){"mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL}),
-        ((const char *[]){"mode", "balance-rr", "packets_per_slave", "3", NULL}));
+        ((const char *[]) {"mode",
+                           "balance-rr",
+                           "ad_actor_sys_prio",
+                           "4",
+                           "packets_per_slave",
+                           "3",
+                           NULL}),
+        ((const char *[]) {"mode", "balance-rr", "packets_per_slave", "3", NULL}));
 }
 
 /*****************************************************************************/
@@ -2379,6 +2384,85 @@ test_ethtool_eee(void)
 /*****************************************************************************/
 
 static void
+test_ethtool_fec(void)
+{
+    gs_unref_object NMConnection   *con     = NULL;
+    gs_unref_object NMConnection   *con2    = NULL;
+    gs_unref_object NMConnection   *con3    = NULL;
+    gs_unref_variant GVariant      *variant = NULL;
+    gs_free_error GError           *error   = NULL;
+    nm_auto_unref_keyfile GKeyFile *keyfile = NULL;
+    NMSettingConnection            *s_con;
+    NMSettingEthtool               *s_ethtool;
+    NMSettingEthtool               *s_ethtool2;
+    NMSettingEthtool               *s_ethtool3;
+    guint32                         out_value;
+    guint32                         expected_fec_mode =
+        NM_SETTING_ETHTOOL_FEC_MODE_AUTO | NM_SETTING_ETHTOOL_FEC_MODE_BASER;
+
+    con =
+        nmtst_create_minimal_connection("ethtool-fec", NULL, NM_SETTING_WIRED_SETTING_NAME, &s_con);
+    s_ethtool = NM_SETTING_ETHTOOL(nm_setting_ethtool_new());
+    nm_connection_add_setting(con, NM_SETTING(s_ethtool));
+
+    nm_setting_option_set_uint32(NM_SETTING(s_ethtool),
+                                 NM_ETHTOOL_OPTNAME_FEC_MODE,
+                                 expected_fec_mode);
+
+    g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool),
+                                               NM_ETHTOOL_OPTNAME_FEC_MODE,
+                                               &out_value));
+    g_assert_true(out_value == expected_fec_mode);
+
+    nmtst_connection_normalize(con);
+
+    variant = nm_connection_to_dbus(con, NM_CONNECTION_SERIALIZE_ALL);
+
+    con2 = nm_simple_connection_new_from_dbus(variant, &error);
+    nmtst_assert_success(con2, error);
+
+    s_ethtool2 = NM_SETTING_ETHTOOL(nm_connection_get_setting(con2, NM_TYPE_SETTING_ETHTOOL));
+
+    g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool2),
+                                               NM_ETHTOOL_OPTNAME_FEC_MODE,
+                                               &out_value));
+    g_assert_true(out_value == expected_fec_mode);
+
+    nmtst_assert_connection_verifies_without_normalization(con2);
+
+    nmtst_assert_connection_equals(con, FALSE, con2, FALSE);
+
+    con2 = nm_simple_connection_new_from_dbus(variant, &error);
+    nmtst_assert_success(con2, error);
+
+    keyfile = nm_keyfile_write(con, NM_KEYFILE_HANDLER_FLAGS_NONE, NULL, NULL, &error);
+    nmtst_assert_success(keyfile, error);
+
+    con3 = nm_keyfile_read(keyfile,
+                           "/ignored/current/working/directory/for/loading/relative/paths",
+                           NM_KEYFILE_HANDLER_FLAGS_NONE,
+                           NULL,
+                           NULL,
+                           &error);
+    nmtst_assert_success(con3, error);
+
+    nm_keyfile_read_ensure_id(con3, "unused-because-already-has-id");
+    nm_keyfile_read_ensure_uuid(con3, "unused-because-already-has-uuid");
+
+    nmtst_connection_normalize(con3);
+
+    nmtst_assert_connection_equals(con, FALSE, con3, FALSE);
+
+    s_ethtool3 = NM_SETTING_ETHTOOL(nm_connection_get_setting(con3, NM_TYPE_SETTING_ETHTOOL));
+
+    g_assert_true(nm_setting_option_get_uint32(NM_SETTING(s_ethtool3),
+                                               NM_ETHTOOL_OPTNAME_FEC_MODE,
+                                               &out_value));
+    g_assert_true(out_value == expected_fec_mode);
+}
+/*****************************************************************************/
+
+static void
 test_sriov_vf(void)
 {
     NMSriovVF *vf1, *vf2;
@@ -5481,6 +5565,7 @@ main(int argc, char **argv)
     g_test_add_func("/libnm/settings/ethtool/ring", test_ethtool_ring);
     g_test_add_func("/libnm/settings/ethtool/pause", test_ethtool_pause);
     g_test_add_func("/libnm/settings/ethtool/eee", test_ethtool_eee);
+    g_test_add_func("/libnm/settings/ethtool/fec", test_ethtool_fec);
 
     g_test_add_func("/libnm/settings/6lowpan/1", test_6lowpan_1);