From 2f4d8e577ca7bc51fb054b8c2c8dd57c2e188a41 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Feb 2021 21:32:13 +0100 Subject: [PATCH 1/6] resolved: don't follow CNAMEs in the stub anymore CNAME following was broken by 775ae35403f8f3c01b7ac13387fe8aac1759993f where we'd not properly collect RRs along the CNAME path. Good thing though is that we don't have to anymore: since we nowadays propagate all sections of the upstream replies into the cache and back to stub clients all the information should already be available anyway, and there's no need for us to collect it. Fixes: #18690 --- src/resolve/resolved-dns-stub.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 02ac29dbb9..8f3349cbce 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -684,27 +684,13 @@ static void dns_stub_query_complete(DnsQuery *q) { } } + /* Note that we don't bother with following CNAMEs here. We propagate the authoritative/additional + * sections from the upstream answer however, hence if the upstream server collected that information + * already we don't have to collect it ourselves anymore. */ + switch (q->state) { case DNS_TRANSACTION_SUCCESS: - /* Follow CNAMEs, and accumulate answers. Except if DNSSEC is requested, then let the client do that. */ - if (!DNS_PACKET_DO(q->request_packet)) { - r = dns_query_process_cname(q); - if (r == -ELOOP) { /* CNAME loop */ - (void) dns_stub_send_reply(q, DNS_RCODE_SERVFAIL); - break; - } - if (r < 0) { - log_debug_errno(r, "Failed to process CNAME: %m"); - break; - } - if (r == DNS_QUERY_RESTARTED) - return; - } - - (void) dns_stub_send_reply(q, q->answer_rcode); - break; - case DNS_TRANSACTION_RCODE_FAILURE: (void) dns_stub_send_reply(q, q->answer_rcode); break; @@ -843,7 +829,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea r = dns_query_new(m, &q, p->question, p->question, NULL, 0, SD_RESOLVED_PROTOCOLS_ALL| SD_RESOLVED_NO_SEARCH| - (DNS_PACKET_DO(p) ? SD_RESOLVED_NO_CNAME|SD_RESOLVED_REQUIRE_PRIMARY : 0)| + SD_RESOLVED_NO_CNAME| + (DNS_PACKET_DO(p) ? SD_RESOLVED_REQUIRE_PRIMARY : 0)| SD_RESOLVED_CLAMP_TTL); if (r < 0) { log_error_errno(r, "Failed to generate query object: %m"); From c6ebf89b8f14431200a0d45497b3dfcd2c36707a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Feb 2021 22:51:32 +0100 Subject: [PATCH 2/6] resolved: update comment in DNS stub code a bit There's no "answer_auxiliary" object anymore, it's all one "answer" object, and we have per-item flags that tell us which section things are from, i.e. from the main answer section, or the additional or authoritative ones. --- src/resolve/resolved-dns-stub.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 8f3349cbce..b6e66f7f67 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -246,17 +246,17 @@ static int dns_stub_assign_sections( assert(q); assert(question); - /* Let's assign the 'answer' and 'answer_auxiliary' RRs we collected to their respective sections in - * the reply datagram. We try to reproduce a section assignment similar to what the upstream DNS - * server responded to us. We use the DNS_ANSWER_SECTION_xyz flags to match things up, which is where - * the original upstream's packet section assignment is stored in the DnsAnswer object. Not all RRs - * in the 'answer' and 'answer_auxiliary' objects come with section information though (for example, - * because they were synthesized locally, and not from a DNS packet). To deal with that we extend the - * assignment logic a bit: anything from the 'answer' object that directly matches the original - * question is always put in the ANSWER section, regardless if it carries section info, or what that - * section info says. Then, anything from the 'answer' and 'answer_auxiliary' objects that is from - * the ANSWER or AUTHORITY sections, and wasn't already added to the ANSWER section is placed in the - * AUTHORITY section. Everything else from either object is added to the ADDITIONAL section. */ + /* Let's assign the 'answer' RRs we collected to their respective sections in the reply datagram. We + * try to reproduce a section assignment similar to what the upstream DNS server responded to us. We + * use the DNS_ANSWER_SECTION_xyz flags to match things up, which is where the original upstream's + * packet section assignment is stored in the DnsAnswer object. Not all RRs in the 'answer' objects + * come with section information though (for example, because they were synthesized locally, and not + * from a DNS packet). To deal with that we extend the assignment logic a bit: anything from the + * 'answer' object that directly matches the original question is always put in the ANSWER section, + * regardless if it carries section info, or what that section info says. Then, anything from the + * 'answer' objects that is from the ANSWER or AUTHORITY sections, and wasn't already added to the + * ANSWER section is placed in the AUTHORITY section. Everything else from either object is added to + * the ADDITIONAL section. */ /* Include all RRs that directly answer the question in the answer section */ r = dns_stub_collect_answer_by_question( From 990e7e87ad93e791f8e77c526ccafd761b5f319b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Feb 2021 23:07:22 +0100 Subject: [PATCH 3/6] resolved: remove duplicate comment Practically the same comment is a few lines up covering both parts anyway, let's remove one. --- src/resolve/resolved-dns-stub.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index b6e66f7f67..4f2088bb76 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -277,9 +277,6 @@ static int dns_stub_assign_sections( edns0_do); if (r < 0) return r; - - /* Include all RRs that originate from the answer or authority sections, and aren't listed in the - * answer section, in the authority section */ r = dns_stub_collect_answer_by_section( &q->reply_authoritative, q->answer, From d1f8fbea9fcba048338aeb3c26f57593b96021b5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Feb 2021 23:07:01 +0100 Subject: [PATCH 4/6] resolved: add dns_resource_record_get_cname_target() helper This determines the redirection target from a CNAME or DNAME RR given it matches some given RR key. --- src/resolve/resolved-dns-rr.c | 42 +++++++++++++++++++++++++++++++++++ src/resolve/resolved-dns-rr.h | 2 ++ src/resolve/test-dns-packet.c | 35 +++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index d844f7c9aa..9b94ab59ea 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -1722,6 +1722,7 @@ int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) { } bool dns_resource_record_is_link_local_address(DnsResourceRecord *rr) { + assert(rr); if (rr->key->class != DNS_CLASS_IN) return false; @@ -1735,6 +1736,47 @@ bool dns_resource_record_is_link_local_address(DnsResourceRecord *rr) { return false; } +int dns_resource_record_get_cname_target(DnsResourceKey *key, DnsResourceRecord *cname, char **ret) { + _cleanup_free_ char *d = NULL; + int r; + + assert(key); + assert(cname); + + if (key->class != cname->key->class && key->class != DNS_CLASS_ANY) + return -EUNATCH; + + if (cname->key->type == DNS_TYPE_CNAME) { + r = dns_name_equal(dns_resource_key_name(key), + dns_resource_key_name(cname->key)); + if (r < 0) + return r; + if (r == 0) + return -EUNATCH; /* CNAME RR key doesn't actually match the original key */ + + d = strdup(cname->cname.name); + if (!d) + return -ENOMEM; + + } else if (cname->key->type == DNS_TYPE_DNAME) { + + r = dns_name_change_suffix( + dns_resource_key_name(key), + dns_resource_key_name(cname->key), + cname->dname.name, + &d); + if (r < 0) + return r; + if (r == 0) + return -EUNATCH; /* DNAME RR key doesn't actually match the original key */ + + } else + return -EUNATCH; /* Not a CNAME/DNAME RR, hence doesn't match the proposition either */ + + *ret = TAKE_PTR(d); + return 0; +} + DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) { DnsTxtItem *n; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index d788d797fc..d3edcadf67 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -326,6 +326,8 @@ int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl); bool dns_resource_record_is_link_local_address(DnsResourceRecord *rr); +int dns_resource_record_get_cname_target(DnsResourceKey *key, DnsResourceRecord *cname, char **ret); + DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i); diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 47c7d671cd..72b6d4a657 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -90,6 +90,39 @@ static void test_packet_from_file(const char* filename, bool canonical) { } } +static void test_dns_resource_record_get_cname_target(void) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL; + _cleanup_free_ char *target = NULL; + + assert_se(cname = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "quux.foobar")); + assert_se(cname->cname.name = strdup("wuff.wuff")); + + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "waldo"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "foobar"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, ""), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "."), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "nope.quux.foobar"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux.foobar"), cname, &target) == 0); + assert_se(streq(target, "wuff.wuff")); + target = mfree(target); + + assert_se(cname = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "quux.foobar")); + assert_se(cname->dname.name = strdup("wuff.wuff")); + + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "waldo"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "foobar"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux"), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, ""), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "."), cname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "yupp.quux.foobar"), cname, &target) == 0); + assert_se(streq(target, "yupp.wuff.wuff")); + target = mfree(target); + + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux.foobar"), cname, &target) == 0); + assert_se(streq(target, "wuff.wuff")); +} + int main(int argc, char **argv) { int i, N; _cleanup_globfree_ glob_t g = {}; @@ -116,5 +149,7 @@ int main(int argc, char **argv) { puts(""); } + test_dns_resource_record_get_cname_target(); + return EXIT_SUCCESS; } From 5bd7ebb3322062f36b0b2a2a809ebfd4d9d9618c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 22 Feb 2021 23:07:57 +0100 Subject: [PATCH 5/6] resolved: stick CNAME targets into main answer section in stub replies --- src/resolve/resolved-dns-answer.h | 4 + src/resolve/resolved-dns-stub.c | 118 ++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 31 deletions(-) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index f90ca67019..c2fd0c078f 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -22,6 +22,10 @@ typedef enum DnsAnswerFlags { DNS_ANSWER_SECTION_ANSWER = 1 << 5, /* When parsing: RR originates from answer section */ DNS_ANSWER_SECTION_AUTHORITY = 1 << 6, /* When parsing: RR originates from authority section */ DNS_ANSWER_SECTION_ADDITIONAL = 1 << 7, /* When parsing: RR originates from additional section */ + + DNS_ANSWER_MASK_SECTIONS = DNS_ANSWER_SECTION_ANSWER| + DNS_ANSWER_SECTION_AUTHORITY| + DNS_ANSWER_SECTION_ADDITIONAL, } DnsAnswerFlags; struct DnsAnswerItem { diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 4f2088bb76..78f7cb854f 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -139,12 +139,38 @@ static int stub_packet_compare_func(const DnsPacket *x, const DnsPacket *y) { DEFINE_HASH_OPS(stub_packet_hash_ops, DnsPacket, stub_packet_hash_func, stub_packet_compare_func); +static int reply_add_with_rrsig( + DnsAnswer **reply, + DnsResourceRecord *rr, + int ifindex, + DnsAnswerFlags flags, + DnsResourceRecord *rrsig, + bool with_rrsig) { + int r; + + assert(reply); + assert(rr); + + r = dns_answer_add_extend(reply, rr, ifindex, flags, rrsig); + if (r < 0) + return r; + + if (with_rrsig && rrsig) { + r = dns_answer_add_extend(reply, rrsig, ifindex, flags, NULL); + if (r < 0) + return r; + } + + return 0; +} + static int dns_stub_collect_answer_by_question( DnsAnswer **reply, DnsAnswer *answer, DnsQuestion *question, bool with_rrsig) { /* Add RRSIG RR matching each RR */ + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *redirected_key = NULL; DnsAnswerItem *item; int r; @@ -153,36 +179,71 @@ static int dns_stub_collect_answer_by_question( /* Copies all RRs from 'answer' into 'reply', if they match 'question'. */ DNS_ANSWER_FOREACH_ITEM(item, answer) { - if (question) { - bool match = false; - r = dns_question_matches_rr(question, item->rr, NULL); if (r < 0) return r; - else if (r > 0) - match = true; - else { - r = dns_question_matches_cname_or_dname(question, item->rr, NULL); + if (r == 0) { + _cleanup_free_ char *target = NULL; + + /* OK, so the RR doesn't directly match. Let's see if the RR is a matching + * CNAME or DNAME */ + + r = dns_resource_record_get_cname_target( + question->keys[0], + item->rr, + &target); + if (r == -EUNATCH) + continue; /* Not a CNAME/DNAME or doesn't match */ if (r < 0) return r; - if (r > 0) - match = true; - } - if (!match) - continue; + dns_resource_key_unref(redirected_key); + + /* There can only be one CNAME per name, hence no point in storing more than one here */ + redirected_key = dns_resource_key_new(question->keys[0]->class, question->keys[0]->type, target); + if (!redirected_key) + return -ENOMEM; + } } - r = dns_answer_add_extend(reply, item->rr, item->ifindex, item->flags, item->rrsig); + /* Mask the section info, we want the primary answers to always go without section info, so + * that it is added to the answer section when we synthesize a reply. */ + + r = reply_add_with_rrsig( + reply, + item->rr, + item->ifindex, + item->flags & ~DNS_ANSWER_MASK_SECTIONS, + item->rrsig, + with_rrsig); if (r < 0) return r; + } - if (with_rrsig && item->rrsig) { - r = dns_answer_add_extend(reply, item->rrsig, item->ifindex, item->flags, NULL); - if (r < 0) - return r; - } + if (!redirected_key) + return 0; + + /* This is a CNAME/DNAME answer. In this case also append where the redirections point to to the main + * answer section */ + + DNS_ANSWER_FOREACH_ITEM(item, answer) { + + r = dns_resource_key_match_rr(redirected_key, item->rr, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + r = reply_add_with_rrsig( + reply, + item->rr, + item->ifindex, + item->flags & ~DNS_ANSWER_MASK_SECTIONS, + item->rrsig, + with_rrsig); + if (r < 0) + return r; } return 0; @@ -197,7 +258,6 @@ static int dns_stub_collect_answer_by_section( bool with_dnssec) { /* Include DNSSEC RRs. RRSIG, NSEC, … */ DnsAnswerItem *item; - unsigned c = 0; int r; assert(reply); @@ -218,22 +278,18 @@ static int dns_stub_collect_answer_by_section( if (((item->flags ^ section) & (DNS_ANSWER_SECTION_ANSWER|DNS_ANSWER_SECTION_AUTHORITY|DNS_ANSWER_SECTION_ADDITIONAL)) != 0) continue; - r = dns_answer_add_extend(reply, item->rr, item->ifindex, item->flags, item->rrsig); + r = reply_add_with_rrsig( + reply, + item->rr, + item->ifindex, + item->flags, + item->rrsig, + with_dnssec); if (r < 0) return r; - - c++; - - if (with_dnssec && item->rrsig) { - r = dns_answer_add_extend(reply, item->rrsig, item->ifindex, item->flags, NULL); - if (r < 0) - return r; - - c++; - } } - return (int) c; + return 0; } static int dns_stub_assign_sections( From d6191ed529375d70ec91ce30e790d9e57092a629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 23 Feb 2021 01:06:08 +0100 Subject: [PATCH 6/6] test-dns-packet: fix leak of DnsResourceRecord object --- src/resolve/test-dns-packet.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 72b6d4a657..01f15ca88a 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -91,7 +91,7 @@ static void test_packet_from_file(const char* filename, bool canonical) { } static void test_dns_resource_record_get_cname_target(void) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *dname = NULL; _cleanup_free_ char *target = NULL; assert_se(cname = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_CNAME, "quux.foobar")); @@ -107,15 +107,15 @@ static void test_dns_resource_record_get_cname_target(void) { assert_se(streq(target, "wuff.wuff")); target = mfree(target); - assert_se(cname = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "quux.foobar")); - assert_se(cname->dname.name = strdup("wuff.wuff")); + assert_se(dname = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNAME, "quux.foobar")); + assert_se(dname->dname.name = strdup("wuff.wuff")); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "waldo"), cname, &target) == -EUNATCH); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "foobar"), cname, &target) == -EUNATCH); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux"), cname, &target) == -EUNATCH); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, ""), cname, &target) == -EUNATCH); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "."), cname, &target) == -EUNATCH); - assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "yupp.quux.foobar"), cname, &target) == 0); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "waldo"), dname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "foobar"), dname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "quux"), dname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, ""), dname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "."), dname, &target) == -EUNATCH); + assert_se(dns_resource_record_get_cname_target(&DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_A, "yupp.quux.foobar"), dname, &target) == 0); assert_se(streq(target, "yupp.wuff.wuff")); target = mfree(target); @@ -124,7 +124,7 @@ static void test_dns_resource_record_get_cname_target(void) { } int main(int argc, char **argv) { - int i, N; + int N; _cleanup_globfree_ glob_t g = {}; char **fnames; @@ -141,7 +141,7 @@ int main(int argc, char **argv) { fnames = g.gl_pathv; } - for (i = 0; i < N; i++) { + for (int i = 0; i < N; i++) { test_packet_from_file(fnames[i], false); puts(""); test_packet_from_file(fnames[i], true);