1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "alloc-util.h"
25 #include "dns-domain.h"
27 #include "hexdecoct.h"
28 #include "resolved-dns-packet.h"
29 #include "resolved-dns-rr.h"
30 #include "string-util.h"
33 DnsResourceKey
* dns_resource_key_new(uint16_t class, uint16_t type
, const char *name
) {
40 k
= malloc0(sizeof(DnsResourceKey
) + l
+ 1);
48 strcpy((char*) k
+ sizeof(DnsResourceKey
), name
);
53 DnsResourceKey
* dns_resource_key_new_cname(const DnsResourceKey
*key
) {
56 return dns_resource_key_new(key
->class, DNS_TYPE_CNAME
, DNS_RESOURCE_KEY_NAME(key
));
59 DnsResourceKey
* dns_resource_key_new_redirect(const DnsResourceKey
*key
, const DnsResourceRecord
*cname
) {
65 assert(IN_SET(cname
->key
->type
, DNS_TYPE_CNAME
, DNS_TYPE_DNAME
));
67 if (cname
->key
->type
== DNS_TYPE_CNAME
)
68 return dns_resource_key_new(key
->class, key
->type
, cname
->cname
.name
);
71 char *destination
= NULL
;
73 r
= dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key
), DNS_RESOURCE_KEY_NAME(cname
->key
), cname
->dname
.name
, &destination
);
77 return dns_resource_key_ref((DnsResourceKey
*) key
);
79 k
= dns_resource_key_new_consume(key
->class, key
->type
, destination
);
89 int dns_resource_key_new_append_suffix(DnsResourceKey
**ret
, DnsResourceKey
*key
, char *name
) {
90 DnsResourceKey
*new_key
;
98 if (dns_name_is_root(name
)) {
99 *ret
= dns_resource_key_ref(key
);
103 r
= dns_name_concat(DNS_RESOURCE_KEY_NAME(key
), name
, &joined
);
107 new_key
= dns_resource_key_new_consume(key
->class, key
->type
, joined
);
117 DnsResourceKey
* dns_resource_key_new_consume(uint16_t class, uint16_t type
, char *name
) {
122 k
= new0(DnsResourceKey
, 1);
134 DnsResourceKey
* dns_resource_key_ref(DnsResourceKey
*k
) {
139 assert(k
->n_ref
> 0);
145 DnsResourceKey
* dns_resource_key_unref(DnsResourceKey
*k
) {
149 assert(k
->n_ref
> 0);
160 int dns_resource_key_equal(const DnsResourceKey
*a
, const DnsResourceKey
*b
) {
163 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(a
), DNS_RESOURCE_KEY_NAME(b
));
167 if (a
->class != b
->class)
170 if (a
->type
!= b
->type
)
176 int dns_resource_key_match_rr(const DnsResourceKey
*key
, const DnsResourceRecord
*rr
, const char *search_domain
) {
182 /* Checks if an rr matches the specified key. If a search
183 * domain is specified, it will also be checked if the key
184 * with the search domain suffixed might match the RR. */
186 if (rr
->key
->class != key
->class && key
->class != DNS_CLASS_ANY
)
189 if (rr
->key
->type
!= key
->type
&& key
->type
!= DNS_TYPE_ANY
)
192 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
197 _cleanup_free_
char *joined
= NULL
;
199 r
= dns_name_concat(DNS_RESOURCE_KEY_NAME(key
), search_domain
, &joined
);
203 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), joined
);
209 int dns_resource_key_match_cname(const DnsResourceKey
*key
, const DnsResourceRecord
*rr
, const char *search_domain
) {
215 if (rr
->key
->class != key
->class && key
->class != DNS_CLASS_ANY
)
218 if (rr
->key
->type
== DNS_TYPE_CNAME
)
219 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(key
), DNS_RESOURCE_KEY_NAME(rr
->key
));
220 else if (rr
->key
->type
== DNS_TYPE_DNAME
)
221 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(key
), DNS_RESOURCE_KEY_NAME(rr
->key
));
229 _cleanup_free_
char *joined
= NULL
;
231 r
= dns_name_concat(DNS_RESOURCE_KEY_NAME(key
), search_domain
, &joined
);
235 if (rr
->key
->type
== DNS_TYPE_CNAME
)
236 return dns_name_equal(joined
, DNS_RESOURCE_KEY_NAME(rr
->key
));
237 else if (rr
->key
->type
== DNS_TYPE_DNAME
)
238 return dns_name_endswith(joined
, DNS_RESOURCE_KEY_NAME(rr
->key
));
245 static void dns_resource_key_hash_func(const void *i
, struct siphash
*state
) {
246 const DnsResourceKey
*k
= i
;
250 dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k
), state
);
251 siphash24_compress(&k
->class, sizeof(k
->class), state
);
252 siphash24_compress(&k
->type
, sizeof(k
->type
), state
);
255 static int dns_resource_key_compare_func(const void *a
, const void *b
) {
256 const DnsResourceKey
*x
= a
, *y
= b
;
259 ret
= dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x
), DNS_RESOURCE_KEY_NAME(y
));
263 if (x
->type
< y
->type
)
265 if (x
->type
> y
->type
)
268 if (x
->class < y
->class)
270 if (x
->class > y
->class)
276 const struct hash_ops dns_resource_key_hash_ops
= {
277 .hash
= dns_resource_key_hash_func
,
278 .compare
= dns_resource_key_compare_func
281 int dns_resource_key_to_string(const DnsResourceKey
*key
, char **ret
) {
282 char cbuf
[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf
[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)];
286 c
= dns_class_to_string(key
->class);
288 sprintf(cbuf
, "CLASS%u", key
->class);
292 t
= dns_type_to_string(key
->type
);
294 sprintf(tbuf
, "TYPE%u", key
->type
);
298 if (asprintf(&s
, "%s %s %-5s", DNS_RESOURCE_KEY_NAME(key
), c
, t
) < 0)
305 DnsResourceRecord
* dns_resource_record_new(DnsResourceKey
*key
) {
306 DnsResourceRecord
*rr
;
308 rr
= new0(DnsResourceRecord
, 1);
313 rr
->key
= dns_resource_key_ref(key
);
318 DnsResourceRecord
* dns_resource_record_new_full(uint16_t class, uint16_t type
, const char *name
) {
319 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
321 key
= dns_resource_key_new(class, type
, name
);
325 return dns_resource_record_new(key
);
328 DnsResourceRecord
* dns_resource_record_ref(DnsResourceRecord
*rr
) {
332 assert(rr
->n_ref
> 0);
338 DnsResourceRecord
* dns_resource_record_unref(DnsResourceRecord
*rr
) {
342 assert(rr
->n_ref
> 0);
350 switch(rr
->key
->type
) {
370 dns_txt_item_free_all(rr
->txt
.items
);
379 free(rr
->mx
.exchange
);
387 free(rr
->sshfp
.fingerprint
);
390 case DNS_TYPE_DNSKEY
:
391 free(rr
->dnskey
.key
);
395 free(rr
->rrsig
.signer
);
396 free(rr
->rrsig
.signature
);
400 free(rr
->nsec
.next_domain_name
);
401 bitmap_free(rr
->nsec
.types
);
405 free(rr
->nsec3
.next_hashed_name
);
406 free(rr
->nsec3
.salt
);
407 bitmap_free(rr
->nsec3
.types
);
416 free(rr
->generic
.data
);
419 dns_resource_key_unref(rr
->key
);
427 int dns_resource_record_new_reverse(DnsResourceRecord
**ret
, int family
, const union in_addr_union
*address
, const char *hostname
) {
428 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
429 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
430 _cleanup_free_
char *ptr
= NULL
;
437 r
= dns_name_reverse(family
, address
, &ptr
);
441 key
= dns_resource_key_new_consume(DNS_CLASS_IN
, DNS_TYPE_PTR
, ptr
);
447 rr
= dns_resource_record_new(key
);
451 rr
->ptr
.name
= strdup(hostname
);
461 int dns_resource_record_new_address(DnsResourceRecord
**ret
, int family
, const union in_addr_union
*address
, const char *name
) {
462 DnsResourceRecord
*rr
;
468 if (family
== AF_INET
) {
470 rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_A
, name
);
474 rr
->a
.in_addr
= address
->in
;
476 } else if (family
== AF_INET6
) {
478 rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_AAAA
, name
);
482 rr
->aaaa
.in6_addr
= address
->in6
;
484 return -EAFNOSUPPORT
;
491 int dns_resource_record_equal(const DnsResourceRecord
*a
, const DnsResourceRecord
*b
) {
497 r
= dns_resource_key_equal(a
->key
, b
->key
);
501 if (a
->unparseable
!= b
->unparseable
)
504 switch (a
->unparseable
? _DNS_TYPE_INVALID
: a
->key
->type
) {
507 r
= dns_name_equal(a
->srv
.name
, b
->srv
.name
);
511 return a
->srv
.priority
== b
->srv
.priority
&&
512 a
->srv
.weight
== b
->srv
.weight
&&
513 a
->srv
.port
== b
->srv
.port
;
519 return dns_name_equal(a
->ptr
.name
, b
->ptr
.name
);
522 return strcaseeq(a
->hinfo
.cpu
, b
->hinfo
.cpu
) &&
523 strcaseeq(a
->hinfo
.os
, b
->hinfo
.os
);
525 case DNS_TYPE_SPF
: /* exactly the same as TXT */
527 return dns_txt_item_equal(a
->txt
.items
, b
->txt
.items
);
530 return memcmp(&a
->a
.in_addr
, &b
->a
.in_addr
, sizeof(struct in_addr
)) == 0;
533 return memcmp(&a
->aaaa
.in6_addr
, &b
->aaaa
.in6_addr
, sizeof(struct in6_addr
)) == 0;
536 r
= dns_name_equal(a
->soa
.mname
, b
->soa
.mname
);
539 r
= dns_name_equal(a
->soa
.rname
, b
->soa
.rname
);
543 return a
->soa
.serial
== b
->soa
.serial
&&
544 a
->soa
.refresh
== b
->soa
.refresh
&&
545 a
->soa
.retry
== b
->soa
.retry
&&
546 a
->soa
.expire
== b
->soa
.expire
&&
547 a
->soa
.minimum
== b
->soa
.minimum
;
550 if (a
->mx
.priority
!= b
->mx
.priority
)
553 return dns_name_equal(a
->mx
.exchange
, b
->mx
.exchange
);
556 assert(a
->loc
.version
== b
->loc
.version
);
558 return a
->loc
.size
== b
->loc
.size
&&
559 a
->loc
.horiz_pre
== b
->loc
.horiz_pre
&&
560 a
->loc
.vert_pre
== b
->loc
.vert_pre
&&
561 a
->loc
.latitude
== b
->loc
.latitude
&&
562 a
->loc
.longitude
== b
->loc
.longitude
&&
563 a
->loc
.altitude
== b
->loc
.altitude
;
566 return a
->ds
.key_tag
== b
->ds
.key_tag
&&
567 a
->ds
.algorithm
== b
->ds
.algorithm
&&
568 a
->ds
.digest_type
== b
->ds
.digest_type
&&
569 a
->ds
.digest_size
== b
->ds
.digest_size
&&
570 memcmp(a
->ds
.digest
, b
->ds
.digest
, a
->ds
.digest_size
) == 0;
573 return a
->sshfp
.algorithm
== b
->sshfp
.algorithm
&&
574 a
->sshfp
.fptype
== b
->sshfp
.fptype
&&
575 a
->sshfp
.fingerprint_size
== b
->sshfp
.fingerprint_size
&&
576 memcmp(a
->sshfp
.fingerprint
, b
->sshfp
.fingerprint
, a
->sshfp
.fingerprint_size
) == 0;
578 case DNS_TYPE_DNSKEY
:
579 return a
->dnskey
.flags
== b
->dnskey
.flags
&&
580 a
->dnskey
.protocol
== b
->dnskey
.protocol
&&
581 a
->dnskey
.algorithm
== b
->dnskey
.algorithm
&&
582 a
->dnskey
.key_size
== b
->dnskey
.key_size
&&
583 memcmp(a
->dnskey
.key
, b
->dnskey
.key
, a
->dnskey
.key_size
) == 0;
586 /* do the fast comparisons first */
587 if (a
->rrsig
.type_covered
!= b
->rrsig
.type_covered
||
588 a
->rrsig
.algorithm
!= b
->rrsig
.algorithm
||
589 a
->rrsig
.labels
!= b
->rrsig
.labels
||
590 a
->rrsig
.original_ttl
!= b
->rrsig
.original_ttl
||
591 a
->rrsig
.expiration
!= b
->rrsig
.expiration
||
592 a
->rrsig
.inception
!= b
->rrsig
.inception
||
593 a
->rrsig
.key_tag
!= b
->rrsig
.key_tag
||
594 a
->rrsig
.signature_size
!= b
->rrsig
.signature_size
||
595 memcmp(a
->rrsig
.signature
, b
->rrsig
.signature
, a
->rrsig
.signature_size
) != 0)
598 return dns_name_equal(a
->rrsig
.signer
, b
->rrsig
.signer
);
601 return dns_name_equal(a
->nsec
.next_domain_name
, b
->nsec
.next_domain_name
) &&
602 bitmap_equal(a
->nsec
.types
, b
->nsec
.types
);
605 return a
->nsec3
.algorithm
== b
->nsec3
.algorithm
&&
606 a
->nsec3
.flags
== b
->nsec3
.flags
&&
607 a
->nsec3
.iterations
== b
->nsec3
.iterations
&&
608 a
->nsec3
.salt_size
== b
->nsec3
.salt_size
&&
609 memcmp(a
->nsec3
.salt
, b
->nsec3
.salt
, a
->nsec3
.salt_size
) == 0 &&
610 memcmp(a
->nsec3
.next_hashed_name
, b
->nsec3
.next_hashed_name
, a
->nsec3
.next_hashed_name_size
) == 0 &&
611 bitmap_equal(a
->nsec3
.types
, b
->nsec3
.types
);
614 return a
->generic
.size
== b
->generic
.size
&&
615 memcmp(a
->generic
.data
, b
->generic
.data
, a
->generic
.size
) == 0;
619 static char* format_location(uint32_t latitude
, uint32_t longitude
, uint32_t altitude
,
620 uint8_t size
, uint8_t horiz_pre
, uint8_t vert_pre
) {
622 char NS
= latitude
>= 1U<<31 ? 'N' : 'S';
623 char EW
= longitude
>= 1U<<31 ? 'E' : 'W';
625 int lat
= latitude
>= 1U<<31 ? (int) (latitude
- (1U<<31)) : (int) ((1U<<31) - latitude
);
626 int lon
= longitude
>= 1U<<31 ? (int) (longitude
- (1U<<31)) : (int) ((1U<<31) - longitude
);
627 double alt
= altitude
>= 10000000u ? altitude
- 10000000u : -(double)(10000000u - altitude
);
628 double siz
= (size
>> 4) * exp10((double) (size
& 0xF));
629 double hor
= (horiz_pre
>> 4) * exp10((double) (horiz_pre
& 0xF));
630 double ver
= (vert_pre
>> 4) * exp10((double) (vert_pre
& 0xF));
632 if (asprintf(&s
, "%d %d %.3f %c %d %d %.3f %c %.2fm %.2fm %.2fm %.2fm",
635 (lat
% 60000) / 1000.,
639 (lon
% 60000) / 1000.,
650 static int format_timestamp_dns(char *buf
, size_t l
, time_t sec
) {
654 assert(l
> strlen("YYYYMMDDHHmmSS"));
656 if (!gmtime_r(&sec
, &tm
))
659 if (strftime(buf
, l
, "%Y%m%d%H%M%S", &tm
) <= 0)
665 static char *format_types(Bitmap
*types
) {
666 _cleanup_strv_free_
char **strv
= NULL
;
667 _cleanup_free_
char *str
= NULL
;
672 BITMAP_FOREACH(type
, types
, i
) {
673 if (dns_type_to_string(type
)) {
674 r
= strv_extend(&strv
, dns_type_to_string(type
));
680 r
= asprintf(&t
, "TYPE%u", type
);
684 r
= strv_consume(&strv
, t
);
690 str
= strv_join(strv
, " ");
694 return strjoin("( ", str
, " )", NULL
);
697 static char *format_txt(DnsTxtItem
*first
) {
702 LIST_FOREACH(items
, i
, first
)
703 c
+= i
->length
* 4 + 3;
705 p
= s
= new(char, c
);
709 LIST_FOREACH(items
, i
, first
) {
717 for (j
= 0; j
< i
->length
; j
++) {
718 if (i
->data
[j
] < ' ' || i
->data
[j
] == '"' || i
->data
[j
] >= 127) {
720 *(p
++) = '0' + (i
->data
[j
] / 100);
721 *(p
++) = '0' + ((i
->data
[j
] / 10) % 10);
722 *(p
++) = '0' + (i
->data
[j
] % 10);
734 int dns_resource_record_to_string(const DnsResourceRecord
*rr
, char **ret
) {
735 _cleanup_free_
char *k
= NULL
, *t
= NULL
;
741 r
= dns_resource_key_to_string(rr
->key
, &k
);
745 switch (rr
->unparseable
? _DNS_TYPE_INVALID
: rr
->key
->type
) {
748 r
= asprintf(&s
, "%s %u %u %u %s",
753 strna(rr
->srv
.name
));
762 s
= strjoin(k
, " ", rr
->ptr
.name
, NULL
);
769 s
= strjoin(k
, " ", rr
->hinfo
.cpu
, " ", rr
->hinfo
.os
, NULL
);
774 case DNS_TYPE_SPF
: /* exactly the same as TXT */
776 t
= format_txt(rr
->txt
.items
);
780 s
= strjoin(k
, " ", t
, NULL
);
786 _cleanup_free_
char *x
= NULL
;
788 r
= in_addr_to_string(AF_INET
, (const union in_addr_union
*) &rr
->a
.in_addr
, &x
);
792 s
= strjoin(k
, " ", x
, NULL
);
799 r
= in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &rr
->aaaa
.in6_addr
, &t
);
803 s
= strjoin(k
, " ", t
, NULL
);
809 r
= asprintf(&s
, "%s %s %s %u %u %u %u %u",
811 strna(rr
->soa
.mname
),
812 strna(rr
->soa
.rname
),
823 r
= asprintf(&s
, "%s %u %s",
832 assert(rr
->loc
.version
== 0);
834 t
= format_location(rr
->loc
.latitude
,
843 s
= strjoin(k
, " ", t
, NULL
);
849 t
= hexmem(rr
->ds
.digest
, rr
->ds
.digest_size
);
853 r
= asprintf(&s
, "%s %u %u %u %s",
864 t
= hexmem(rr
->sshfp
.fingerprint
, rr
->sshfp
.fingerprint_size
);
868 r
= asprintf(&s
, "%s %u %u %s",
877 case DNS_TYPE_DNSKEY
: {
880 alg
= dnssec_algorithm_to_string(rr
->dnskey
.algorithm
);
882 t
= base64mem(rr
->dnskey
.key
, rr
->dnskey
.key_size
);
886 r
= asprintf(&s
, "%s %u %u %.*s%.*u %s",
891 alg
? 0 : 1, alg
? 0u : (unsigned) rr
->dnskey
.algorithm
,
898 case DNS_TYPE_RRSIG
: {
899 const char *type
, *alg
;
900 char expiration
[strlen("YYYYMMDDHHmmSS") + 1], inception
[strlen("YYYYMMDDHHmmSS") + 1];
902 type
= dns_type_to_string(rr
->rrsig
.type_covered
);
903 alg
= dnssec_algorithm_to_string(rr
->rrsig
.algorithm
);
905 t
= base64mem(rr
->rrsig
.signature
, rr
->rrsig
.signature_size
);
909 r
= format_timestamp_dns(expiration
, sizeof(expiration
), rr
->rrsig
.expiration
);
913 r
= format_timestamp_dns(inception
, sizeof(inception
), rr
->rrsig
.inception
);
918 * http://tools.ietf.org/html/rfc3597#section-5 */
920 r
= asprintf(&s
, "%s %s%.*u %.*s%.*u %u %u %s %s %u %s %s",
923 type
? 0 : 1, type
? 0u : (unsigned) rr
->rrsig
.type_covered
,
925 alg
? 0 : 1, alg
? 0u : (unsigned) rr
->rrsig
.algorithm
,
927 rr
->rrsig
.original_ttl
,
939 t
= format_types(rr
->nsec
.types
);
943 r
= asprintf(&s
, "%s %s %s",
945 rr
->nsec
.next_domain_name
,
951 case DNS_TYPE_NSEC3
: {
952 _cleanup_free_
char *salt
= NULL
, *hash
= NULL
;
954 if (rr
->nsec3
.salt_size
> 0) {
955 salt
= hexmem(rr
->nsec3
.salt
, rr
->nsec3
.salt_size
);
960 hash
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
964 t
= format_types(rr
->nsec3
.types
);
968 r
= asprintf(&s
, "%s %"PRIu8
" %"PRIu8
" %"PRIu16
" %s %s %s",
972 rr
->nsec3
.iterations
,
973 rr
->nsec3
.salt_size
> 0 ? salt
: "-",
983 t
= hexmem(rr
->generic
.data
, rr
->generic
.size
);
987 r
= asprintf(&s
, "%s \\# %zu %s", k
, rr
->generic
.size
, t
);
997 const char *dns_class_to_string(uint16_t class) {
1011 int dns_class_from_string(const char *s
, uint16_t *class) {
1015 if (strcaseeq(s
, "IN"))
1016 *class = DNS_CLASS_IN
;
1017 else if (strcaseeq(s
, "ANY"))
1018 *class = DNS_CLASS_ANY
;
1025 DnsTxtItem
*dns_txt_item_free_all(DnsTxtItem
*i
) {
1034 return dns_txt_item_free_all(n
);
1037 bool dns_txt_item_equal(DnsTxtItem
*a
, DnsTxtItem
*b
) {
1045 if (a
->length
!= b
->length
)
1048 if (memcmp(a
->data
, b
->data
, a
->length
) != 0)
1051 return dns_txt_item_equal(a
->items_next
, b
->items_next
);