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 <stringprep.h>
28 #include <netinet/in.h>
31 #include <sys/socket.h>
33 #include "alloc-util.h"
34 #include "dns-domain.h"
36 #include "hexdecoct.h"
37 #include "in-addr-util.h"
39 #include "parse-util.h"
40 #include "string-util.h"
44 int dns_label_unescape(const char **name
, char *dest
, size_t sz
) {
64 if (r
>= DNS_LABEL_MAX
)
71 /* Escaped character */
79 else if (*n
== '\\' || *n
== '.') {
80 /* Escaped backslash or dot */
88 } else if (n
[0] >= '0' && n
[0] <= '9') {
91 /* Escaped literal ASCII character */
93 if (!(n
[1] >= '0' && n
[1] <= '9') ||
94 !(n
[2] >= '0' && n
[2] <= '9'))
97 k
= ((unsigned) (n
[0] - '0') * 100) +
98 ((unsigned) (n
[1] - '0') * 10) +
99 ((unsigned) (n
[2] - '0'));
101 /* Don't allow anything that doesn't
102 * fit in 8bit. Note that we do allow
103 * control characters, as some servers
104 * (e.g. cloudflare) are happy to
105 * generate labels with them
119 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
121 /* Normal character */
132 /* Empty label that is not at the end? */
143 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
144 * the previous label (always skipping one dot) or to NULL if there are no more
146 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
147 const char *terminal
;
151 assert(label_terminal
);
155 if (!*label_terminal
) {
162 terminal
= *label_terminal
;
163 assert(*terminal
== '.' || *terminal
== 0);
165 /* Skip current terminal character (and accept domain names ending it ".") */
168 if (terminal
>= name
&& *terminal
== '.')
171 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
173 if (terminal
< name
) {
174 /* Reached the first label, so indicate that there are no more */
179 /* Find the start of the last label */
180 if (*terminal
== '.') {
182 unsigned slashes
= 0;
184 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
187 if (slashes
% 2 == 0) {
188 /* The '.' was not escaped */
200 r
= dns_label_unescape(&name
, dest
, sz
);
204 *label_terminal
= terminal
;
209 int dns_label_escape(const char *p
, size_t l
, char *dest
, size_t sz
) {
212 /* DNS labels must be between 1 and 63 characters long. A
213 * zero-length label does not exist. See RFC 2182, Section
216 if (l
<= 0 || l
> DNS_LABEL_MAX
)
227 if (*p
== '.' || *p
== '\\') {
229 /* Dot or backslash */
239 } else if (*p
== '_' ||
241 (*p
>= '0' && *p
<= '9') ||
242 (*p
>= 'a' && *p
<= 'z') ||
243 (*p
>= 'A' && *p
<= 'Z')) {
245 /* Proper character */
255 /* Everything else */
261 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
262 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
263 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
273 return (int) (q
- dest
);
276 int dns_label_escape_new(const char *p
, size_t l
, char **ret
) {
277 _cleanup_free_
char *s
= NULL
;
283 if (l
<= 0 || l
> DNS_LABEL_MAX
)
286 s
= new(char, DNS_LABEL_ESCAPED_MAX
);
290 r
= dns_label_escape(p
, l
, s
, DNS_LABEL_ESCAPED_MAX
);
300 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
302 _cleanup_free_
uint32_t *input
= NULL
;
303 size_t input_size
, l
;
305 bool contains_8bit
= false;
306 char buffer
[DNS_LABEL_MAX
+1];
311 /* Converts an U-label into an A-label */
313 if (encoded_size
<= 0)
316 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
317 if ((uint8_t) *p
> 127)
318 contains_8bit
= true;
320 if (!contains_8bit
) {
321 if (encoded_size
> DNS_LABEL_MAX
)
327 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
331 if (idna_to_ascii_4i(input
, input_size
, buffer
, 0) != 0)
336 /* Verify that the the result is not longer than one DNS label. */
337 if (l
<= 0 || l
> DNS_LABEL_MAX
)
342 memcpy(decoded
, buffer
, l
);
344 /* If there's room, append a trailing NUL byte, but only then */
354 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
356 size_t input_size
, output_size
;
357 _cleanup_free_
uint32_t *input
= NULL
;
358 _cleanup_free_
char *result
= NULL
;
359 uint32_t *output
= NULL
;
362 /* To be invoked after unescaping. Converts an A-label into an U-label. */
367 if (encoded_size
<= 0 || encoded_size
> DNS_LABEL_MAX
)
370 if (encoded_size
< sizeof(IDNA_ACE_PREFIX
)-1)
373 if (memcmp(encoded
, IDNA_ACE_PREFIX
, sizeof(IDNA_ACE_PREFIX
) -1) != 0)
376 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
380 output_size
= input_size
;
381 output
= newa(uint32_t, output_size
);
383 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
385 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
393 memcpy(decoded
, result
, w
);
395 /* Append trailing NUL byte if there's space, but only then. */
405 int dns_name_concat(const char *a
, const char *b
, char **_ret
) {
406 _cleanup_free_
char *ret
= NULL
;
407 size_t n
= 0, allocated
= 0;
415 char label
[DNS_LABEL_MAX
];
418 r
= dns_label_unescape(&p
, label
, sizeof(label
));
426 /* Now continue with the second string, if there is one */
435 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
442 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
445 r
= dns_label_escape(label
, r
, ret
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
452 char escaped
[DNS_LABEL_ESCAPED_MAX
];
454 r
= dns_label_escape(label
, r
, escaped
, sizeof(escaped
));
467 if (n
> DNS_HOSTNAME_MAX
)
471 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
482 void dns_name_hash_func(const void *s
, struct siphash
*state
) {
489 char label
[DNS_LABEL_MAX
+1];
492 r
= dns_label_unescape(&p
, label
, sizeof(label
));
498 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
504 ascii_strlower_n(label
, r
);
505 siphash24_compress(label
, r
, state
);
506 siphash24_compress_byte(0, state
); /* make sure foobar and foo.bar result in different hashes */
509 /* enforce that all names are terminated by the empty label */
510 string_hash_func("", state
);
513 int dns_name_compare_func(const void *a
, const void *b
) {
520 x
= (const char *) a
+ strlen(a
);
521 y
= (const char *) b
+ strlen(b
);
524 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
526 if (x
== NULL
&& y
== NULL
)
529 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
530 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
534 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
535 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
544 r
= strcasecmp(la
, lb
);
550 const struct hash_ops dns_name_hash_ops
= {
551 .hash
= dns_name_hash_func
,
552 .compare
= dns_name_compare_func
555 int dns_name_equal(const char *x
, const char *y
) {
562 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
564 r
= dns_label_unescape(&x
, la
, sizeof(la
));
568 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
575 q
= dns_label_unescape(&y
, lb
, sizeof(lb
));
579 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
591 if (ascii_strcasecmp_n(la
, lb
, r
) != 0)
596 int dns_name_endswith(const char *name
, const char *suffix
) {
597 const char *n
, *s
, *saved_n
= NULL
;
607 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
609 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
613 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
623 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
627 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
634 if (r
== 0 && q
== 0)
636 if (r
== 0 && saved_n
== n
)
639 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
641 /* Not the same, let's jump back, and try with the next label again */
649 static int dns_label_unescape_undo_idna(const char **name
, char *dest
, size_t sz
) {
652 /* Clobbers all arguments on failure... */
654 r
= dns_label_unescape(name
, dest
, sz
);
658 k
= dns_label_undo_idna(dest
, r
, dest
, sz
);
661 if (k
== 0) /* not an IDNA name */
667 int dns_name_startswith(const char *name
, const char *prefix
) {
678 char ln
[DNS_LABEL_MAX
], lp
[DNS_LABEL_MAX
];
680 r
= dns_label_unescape_undo_idna(&p
, lp
, sizeof(lp
));
686 q
= dns_label_unescape_undo_idna(&n
, ln
, sizeof(ln
));
692 if (ascii_strcasecmp_n(ln
, lp
, r
) != 0)
697 int dns_name_change_suffix(const char *name
, const char *old_suffix
, const char *new_suffix
, char **ret
) {
698 const char *n
, *s
, *saved_before
= NULL
, *saved_after
= NULL
, *prefix
;
710 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
715 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
719 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
729 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
733 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
740 if (r
== 0 && q
== 0)
742 if (r
== 0 && saved_after
== n
) {
743 *ret
= NULL
; /* doesn't match */
747 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
749 /* Not the same, let's jump back, and try with the next label again */
752 saved_after
= saved_before
= NULL
;
756 /* Found it! Now generate the new name */
757 prefix
= strndupa(name
, saved_before
- name
);
759 r
= dns_name_concat(prefix
, new_suffix
, ret
);
766 int dns_name_between(const char *a
, const char *b
, const char *c
) {
769 /* Determine if b is strictly greater than a and strictly smaller than c.
770 We consider the order of names to be circular, so that if a is
771 strictly greater than c, we consider b to be between them if it is
772 either greater than a or smaller than c. This is how the canonical
773 DNS name order used in NSEC records work. */
775 n
= dns_name_compare_func(a
, c
);
780 return dns_name_compare_func(a
, b
) < 0 &&
781 dns_name_compare_func(b
, c
) < 0;
783 /* <--b--c a--b--> */
784 return dns_name_compare_func(b
, c
) < 0 ||
785 dns_name_compare_func(a
, b
) < 0;
788 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
795 p
= (const uint8_t*) a
;
797 if (family
== AF_INET
)
798 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
799 else if (family
== AF_INET6
)
800 r
= asprintf(ret
, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa",
801 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
802 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
803 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
804 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
805 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
806 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
807 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
808 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
810 return -EAFNOSUPPORT
;
817 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
824 r
= dns_name_endswith(p
, "in-addr.arpa");
831 for (i
= 0; i
< ELEMENTSOF(a
); i
++) {
832 char label
[DNS_LABEL_MAX
+1];
834 r
= dns_label_unescape(&p
, label
, sizeof(label
));
842 r
= safe_atou8(label
, &a
[i
]);
847 r
= dns_name_equal(p
, "in-addr.arpa");
852 address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
853 ((uint32_t) a
[2] << 16) |
854 ((uint32_t) a
[1] << 8) |
860 r
= dns_name_endswith(p
, "ip6.arpa");
867 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
868 char label
[DNS_LABEL_MAX
+1];
871 r
= dns_label_unescape(&p
, label
, sizeof(label
));
876 x
= unhexchar(label
[0]);
880 r
= dns_label_unescape(&p
, label
, sizeof(label
));
885 y
= unhexchar(label
[0]);
889 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
892 r
= dns_name_equal(p
, "ip6.arpa");
904 bool dns_name_is_root(const char *name
) {
908 /* There are exactly two ways to encode the root domain name:
909 * as empty string, or with a single dot. */
911 return STR_IN_SET(name
, "", ".");
914 bool dns_name_is_single_label(const char *name
) {
915 char label
[DNS_LABEL_MAX
+1];
920 r
= dns_label_unescape(&name
, label
, sizeof(label
));
924 return dns_name_is_root(name
);
927 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
928 int dns_name_to_wire_format(const char *domain
, uint8_t *buffer
, size_t len
, bool canonical
) {
929 uint8_t *label_length
, *out
;
938 /* Reserve a byte for label length */
945 /* Convert and copy a single label. Note that
946 * dns_label_unescape() returns 0 when it hits the end
947 * of the domain name, which we rely on here to encode
948 * the trailing NUL byte. */
949 r
= dns_label_unescape(&domain
, (char *) out
, len
);
953 /* Optionally, output the name in DNSSEC canonical
954 * format, as described in RFC 4034, section 6.2. Or
955 * in other words: in lower-case. */
957 ascii_strlower_n((char*) out
, (size_t) r
);
959 /* Fill label length, move forward */
966 /* Verify the maximum size of the encoded name. The trailing
967 * dot + NUL byte account are included this time, hence
968 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
970 if (out
- buffer
> DNS_HOSTNAME_MAX
+ 2)
976 static bool srv_type_label_is_valid(const char *label
, size_t n
) {
981 if (n
< 2) /* Label needs to be at least 2 chars long */
984 if (label
[0] != '_') /* First label char needs to be underscore */
987 /* Second char must be a letter */
988 if (!(label
[1] >= 'A' && label
[1] <= 'Z') &&
989 !(label
[1] >= 'a' && label
[1] <= 'z'))
992 /* Third and further chars must be alphanumeric or a hyphen */
993 for (k
= 2; k
< n
; k
++) {
994 if (!(label
[k
] >= 'A' && label
[k
] <= 'Z') &&
995 !(label
[k
] >= 'a' && label
[k
] <= 'z') &&
996 !(label
[k
] >= '0' && label
[k
] <= '9') &&
1004 bool dns_srv_type_is_valid(const char *name
) {
1012 char label
[DNS_LABEL_MAX
];
1014 /* This more or less implements RFC 6335, Section 5.1 */
1016 r
= dns_label_unescape(&name
, label
, sizeof(label
));
1025 if (!srv_type_label_is_valid(label
, r
))
1031 return c
== 2; /* exactly two labels */
1034 bool dns_service_name_is_valid(const char *name
) {
1037 /* This more or less implements RFC 6763, Section 4.1.1 */
1042 if (!utf8_is_valid(name
))
1045 if (string_has_cc(name
, NULL
))
1057 int dns_service_join(const char *name
, const char *type
, const char *domain
, char **ret
) {
1058 char escaped
[DNS_LABEL_ESCAPED_MAX
];
1059 _cleanup_free_
char *n
= NULL
;
1066 if (!dns_srv_type_is_valid(type
))
1070 return dns_name_concat(type
, domain
, ret
);
1072 if (!dns_service_name_is_valid(name
))
1075 r
= dns_label_escape(name
, strlen(name
), escaped
, sizeof(escaped
));
1079 r
= dns_name_concat(type
, domain
, &n
);
1083 return dns_name_concat(escaped
, n
, ret
);
1086 static bool dns_service_name_label_is_valid(const char *label
, size_t n
) {
1091 if (memchr(label
, 0, n
))
1094 s
= strndupa(label
, n
);
1095 return dns_service_name_is_valid(s
);
1098 int dns_service_split(const char *joined
, char **_name
, char **_type
, char **_domain
) {
1099 _cleanup_free_
char *name
= NULL
, *type
= NULL
, *domain
= NULL
;
1100 const char *p
= joined
, *q
= NULL
, *d
= NULL
;
1101 char a
[DNS_LABEL_MAX
], b
[DNS_LABEL_MAX
], c
[DNS_LABEL_MAX
];
1107 /* Get first label from the full name */
1108 an
= dns_label_unescape(&p
, a
, sizeof(a
));
1115 /* If there was a first label, try to get the second one */
1116 bn
= dns_label_unescape(&p
, b
, sizeof(b
));
1123 /* If there was a second label, try to get the third one */
1125 cn
= dns_label_unescape(&p
, c
, sizeof(c
));
1136 if (x
>= 2 && srv_type_label_is_valid(b
, bn
)) {
1138 if (x
>= 3 && srv_type_label_is_valid(c
, cn
)) {
1140 if (dns_service_name_label_is_valid(a
, an
)) {
1142 /* OK, got <name> . <type> . <type2> . <domain> */
1144 name
= strndup(a
, an
);
1148 type
= new(char, bn
+1+cn
+1);
1151 strcpy(stpcpy(stpcpy(type
, b
), "."), c
);
1157 } else if (srv_type_label_is_valid(a
, an
)) {
1159 /* OK, got <type> . <type2> . <domain> */
1163 type
= new(char, an
+1+bn
+1);
1166 strcpy(stpcpy(stpcpy(type
, a
), "."), b
);
1178 r
= dns_name_normalize(d
, &domain
);
1200 int dns_name_suffix(const char *name
, unsigned n_labels
, const char **ret
) {
1201 const char* labels
[DNS_N_LABELS_MAX
+1];
1211 if (n
> DNS_N_LABELS_MAX
)
1216 r
= dns_name_parent(&p
);
1228 *ret
= labels
[n
- n_labels
];
1229 return (int) (n
- n_labels
);
1232 int dns_name_count_labels(const char *name
) {
1241 r
= dns_name_parent(&p
);
1247 if (n
>= DNS_N_LABELS_MAX
)
1256 int dns_name_equal_skip(const char *a
, unsigned n_labels
, const char *b
) {
1262 while (n_labels
> 0) {
1264 r
= dns_name_parent(&a
);
1271 return dns_name_equal(a
, b
);