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>
27 #include "alloc-util.h"
28 #include "dns-domain.h"
29 #include "hexdecoct.h"
30 #include "parse-util.h"
31 #include "string-util.h"
35 int dns_label_unescape(const char **name
, char *dest
, size_t sz
) {
59 if (r
>= DNS_LABEL_MAX
)
63 /* Escaped character */
71 else if (*n
== '\\' || *n
== '.') {
72 /* Escaped backslash or dot */
77 } else if (n
[0] >= '0' && n
[0] <= '9') {
80 /* Escaped literal ASCII character */
82 if (!(n
[1] >= '0' && n
[1] <= '9') ||
83 !(n
[2] >= '0' && n
[2] <= '9'))
86 k
= ((unsigned) (n
[0] - '0') * 100) +
87 ((unsigned) (n
[1] - '0') * 10) +
88 ((unsigned) (n
[2] - '0'));
90 /* Don't allow CC characters or anything that doesn't fit in 8bit */
91 if (k
< ' ' || k
> 255 || k
== 127)
102 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
104 /* Normal character */
112 /* Empty label that is not at the end? */
123 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
124 * the previous label (always skipping one dot) or to NULL if there are no more
126 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
127 const char *terminal
;
131 assert(label_terminal
);
135 if (!*label_terminal
) {
142 assert(**label_terminal
== '.' || **label_terminal
== 0);
144 /* skip current terminal character */
145 terminal
= *label_terminal
- 1;
147 /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
149 if (terminal
< name
) {
150 /* reached the first label, so indicate that there are no more */
155 /* find the start of the last label */
156 if (*terminal
== '.') {
158 unsigned slashes
= 0;
160 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
163 if (slashes
% 2 == 0) {
164 /* the '.' was not escaped */
176 r
= dns_label_unescape(&name
, dest
, sz
);
180 *label_terminal
= terminal
;
185 int dns_label_escape(const char *p
, size_t l
, char *dest
, size_t sz
) {
188 if (l
> DNS_LABEL_MAX
)
199 if (*p
== '.' || *p
== '\\') {
204 /* Dot or backslash */
210 } else if (*p
== '_' ||
212 (*p
>= '0' && *p
<= '9') ||
213 (*p
>= 'a' && *p
<= 'z') ||
214 (*p
>= 'A' && *p
<= 'Z')) {
216 /* Proper character */
224 } else if ((uint8_t) *p
>= (uint8_t) ' ' && *p
!= 127) {
226 /* Everything else */
232 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
233 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
234 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
246 return (int) (q
- dest
);
249 int dns_label_escape_new(const char *p
, size_t l
, char **ret
) {
250 _cleanup_free_
char *s
= NULL
;
256 if (l
> DNS_LABEL_MAX
)
259 s
= new(char, DNS_LABEL_ESCAPED_MAX
);
263 r
= dns_label_escape(p
, l
, s
, DNS_LABEL_ESCAPED_MAX
);
273 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
275 _cleanup_free_
uint32_t *input
= NULL
;
278 bool contains_8bit
= false;
282 assert(decoded_max
>= DNS_LABEL_MAX
);
284 if (encoded_size
<= 0)
287 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
288 if ((uint8_t) *p
> 127)
289 contains_8bit
= true;
294 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
298 if (idna_to_ascii_4i(input
, input_size
, decoded
, 0) != 0)
301 return strlen(decoded
);
307 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
309 size_t input_size
, output_size
;
310 _cleanup_free_
uint32_t *input
= NULL
;
311 _cleanup_free_
char *result
= NULL
;
312 uint32_t *output
= NULL
;
315 /* To be invoked after unescaping */
320 if (encoded_size
< sizeof(IDNA_ACE_PREFIX
)-1)
323 if (memcmp(encoded
, IDNA_ACE_PREFIX
, sizeof(IDNA_ACE_PREFIX
) -1) != 0)
326 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
330 output_size
= input_size
;
331 output
= newa(uint32_t, output_size
);
333 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
335 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
340 if (w
+1 > decoded_max
)
343 memcpy(decoded
, result
, w
+1);
350 int dns_name_concat(const char *a
, const char *b
, char **_ret
) {
351 _cleanup_free_
char *ret
= NULL
;
352 size_t n
= 0, allocated
= 0;
360 _cleanup_free_
char *t
= NULL
;
361 char label
[DNS_LABEL_MAX
];
364 r
= dns_label_unescape(&p
, label
, sizeof(label
));
372 /* Now continue with the second string, if there is one */
381 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
388 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
391 r
= dns_label_escape(label
, r
, ret
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
398 char escaped
[DNS_LABEL_ESCAPED_MAX
];
400 r
= dns_label_escape(label
, r
, escaped
, sizeof(escaped
));
414 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
425 void dns_name_hash_func(const void *s
, struct siphash
*state
) {
432 char label
[DNS_LABEL_MAX
+1];
435 r
= dns_label_unescape(&p
, label
, sizeof(label
));
439 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
449 ascii_strlower(label
);
451 string_hash_func(label
, state
);
454 /* enforce that all names are terminated by the empty label */
455 string_hash_func("", state
);
458 int dns_name_compare_func(const void *a
, const void *b
) {
465 x
= (const char *) a
+ strlen(a
);
466 y
= (const char *) b
+ strlen(b
);
469 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
471 if (x
== NULL
&& y
== NULL
)
474 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
475 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
479 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
480 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
489 r
= strcasecmp(la
, lb
);
495 const struct hash_ops dns_name_hash_ops
= {
496 .hash
= dns_name_hash_func
,
497 .compare
= dns_name_compare_func
500 int dns_name_equal(const char *x
, const char *y
) {
507 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
509 if (*x
== 0 && *y
== 0)
512 r
= dns_label_unescape(&x
, la
, sizeof(la
));
516 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
522 q
= dns_label_unescape(&y
, lb
, sizeof(lb
));
525 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
532 if (strcasecmp(la
, lb
))
537 int dns_name_endswith(const char *name
, const char *suffix
) {
538 const char *n
, *s
, *saved_n
= NULL
;
548 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
550 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
553 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
562 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
565 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
571 if (r
== 0 && q
== 0)
573 if (r
== 0 && saved_n
== n
)
578 if (r
!= q
|| strcasecmp(ln
, ls
)) {
580 /* Not the same, let's jump back, and try with the next label again */
588 int dns_name_change_suffix(const char *name
, const char *old_suffix
, const char *new_suffix
, char **ret
) {
589 const char *n
, *s
, *saved_before
= NULL
, *saved_after
= NULL
, *prefix
;
601 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
606 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
609 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
618 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
621 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
627 if (r
== 0 && q
== 0)
629 if (r
== 0 && saved_after
== n
) {
630 *ret
= NULL
; /* doesn't match */
636 if (r
!= q
|| strcasecmp(ln
, ls
)) {
638 /* Not the same, let's jump back, and try with the next label again */
641 saved_after
= saved_before
= NULL
;
645 /* Found it! Now generate the new name */
646 prefix
= strndupa(name
, saved_before
- name
);
648 r
= dns_name_concat(prefix
, new_suffix
, ret
);
655 int dns_name_between(const char *a
, const char *b
, const char *c
) {
658 /* Determine if b is strictly greater than a and strictly smaller than c.
659 We consider the order of names to be circular, so that if a is
660 strictly greater than c, we consider b to be between them if it is
661 either greater than a or smaller than c. This is how the canonical
662 DNS name order used in NSEC records work. */
664 n
= dns_name_compare_func(a
, c
);
669 return dns_name_compare_func(a
, b
) < 0 &&
670 dns_name_compare_func(b
, c
) < 0;
672 /* <--b--c a--b--> */
673 return dns_name_compare_func(b
, c
) < 0 ||
674 dns_name_compare_func(a
, b
) < 0;
677 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
684 p
= (const uint8_t*) a
;
686 if (family
== AF_INET
)
687 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
688 else if (family
== AF_INET6
)
689 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",
690 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
691 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
692 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
693 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
694 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
695 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
696 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
697 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
699 return -EAFNOSUPPORT
;
706 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
713 r
= dns_name_endswith(p
, "in-addr.arpa");
720 for (i
= 0; i
< ELEMENTSOF(a
); i
++) {
721 char label
[DNS_LABEL_MAX
+1];
723 r
= dns_label_unescape(&p
, label
, sizeof(label
));
731 r
= safe_atou8(label
, &a
[i
]);
736 r
= dns_name_equal(p
, "in-addr.arpa");
741 address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
742 ((uint32_t) a
[2] << 16) |
743 ((uint32_t) a
[1] << 8) |
749 r
= dns_name_endswith(p
, "ip6.arpa");
756 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
757 char label
[DNS_LABEL_MAX
+1];
760 r
= dns_label_unescape(&p
, label
, sizeof(label
));
765 x
= unhexchar(label
[0]);
769 r
= dns_label_unescape(&p
, label
, sizeof(label
));
774 y
= unhexchar(label
[0]);
778 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
781 r
= dns_name_equal(p
, "ip6.arpa");
793 bool dns_name_is_root(const char *name
) {
797 /* There are exactly two ways to encode the root domain name:
798 * as empty string, or with a single dot. */
800 return STR_IN_SET(name
, "", ".");
803 bool dns_name_is_single_label(const char *name
) {
804 char label
[DNS_LABEL_MAX
+1];
809 r
= dns_label_unescape(&name
, label
, sizeof(label
));
813 return dns_name_is_root(name
);
816 /* Encode a domain name according to RFC 1035 Section 3.1 */
817 int dns_name_to_wire_format(const char *domain
, uint8_t *buffer
, size_t len
) {
818 uint8_t *label_length
;
822 assert_return(buffer
, -EINVAL
);
823 assert_return(domain
, -EINVAL
);
824 assert_return(domain
[0], -EINVAL
);
829 /* reserve a byte for label length */
836 /* convert and copy a single label */
837 r
= dns_label_unescape(&domain
, (char *) out
, len
);
841 /* fill label length, move forward */
850 static bool srv_type_label_is_valid(const char *label
, size_t n
) {
855 if (n
< 2) /* Label needs to be at least 2 chars long */
858 if (label
[0] != '_') /* First label char needs to be underscore */
861 /* Second char must be a letter */
862 if (!(label
[1] >= 'A' && label
[1] <= 'Z') &&
863 !(label
[1] >= 'a' && label
[1] <= 'z'))
866 /* Third and further chars must be alphanumeric or a hyphen */
867 for (k
= 2; k
< n
; k
++) {
868 if (!(label
[k
] >= 'A' && label
[k
] <= 'Z') &&
869 !(label
[k
] >= 'a' && label
[k
] <= 'z') &&
870 !(label
[k
] >= '0' && label
[k
] <= '9') &&
878 bool dns_srv_type_is_valid(const char *name
) {
886 char label
[DNS_LABEL_MAX
];
888 /* This more or less implements RFC 6335, Section 5.1 */
890 r
= dns_label_unescape(&name
, label
, sizeof(label
));
899 if (!srv_type_label_is_valid(label
, r
))
905 return c
== 2; /* exactly two labels */
908 bool dns_service_name_is_valid(const char *name
) {
911 /* This more or less implements RFC 6763, Section 4.1.1 */
916 if (!utf8_is_valid(name
))
919 if (string_has_cc(name
, NULL
))
931 int dns_service_join(const char *name
, const char *type
, const char *domain
, char **ret
) {
932 char escaped
[DNS_LABEL_ESCAPED_MAX
];
933 _cleanup_free_
char *n
= NULL
;
940 if (!dns_srv_type_is_valid(type
))
944 return dns_name_concat(type
, domain
, ret
);
946 if (!dns_service_name_is_valid(name
))
949 r
= dns_label_escape(name
, strlen(name
), escaped
, sizeof(escaped
));
953 r
= dns_name_concat(type
, domain
, &n
);
957 return dns_name_concat(escaped
, n
, ret
);
960 static bool dns_service_name_label_is_valid(const char *label
, size_t n
) {
965 if (memchr(label
, 0, n
))
968 s
= strndupa(label
, n
);
969 return dns_service_name_is_valid(s
);
972 int dns_service_split(const char *joined
, char **_name
, char **_type
, char **_domain
) {
973 _cleanup_free_
char *name
= NULL
, *type
= NULL
, *domain
= NULL
;
974 const char *p
= joined
, *q
= NULL
, *d
= NULL
;
975 char a
[DNS_LABEL_MAX
], b
[DNS_LABEL_MAX
], c
[DNS_LABEL_MAX
];
981 /* Get first label from the full name */
982 an
= dns_label_unescape(&p
, a
, sizeof(a
));
989 /* If there was a first label, try to get the second one */
990 bn
= dns_label_unescape(&p
, b
, sizeof(b
));
997 /* If there was a second label, try to get the third one */
999 cn
= dns_label_unescape(&p
, c
, sizeof(c
));
1010 if (x
>= 2 && srv_type_label_is_valid(b
, bn
)) {
1012 if (x
>= 3 && srv_type_label_is_valid(c
, cn
)) {
1014 if (dns_service_name_label_is_valid(a
, an
)) {
1016 /* OK, got <name> . <type> . <type2> . <domain> */
1018 name
= strndup(a
, an
);
1022 type
= new(char, bn
+1+cn
+1);
1025 strcpy(stpcpy(stpcpy(type
, b
), "."), c
);
1031 } else if (srv_type_label_is_valid(a
, an
)) {
1033 /* OK, got <type> . <type2> . <domain> */
1037 type
= new(char, an
+1+bn
+1);
1040 strcpy(stpcpy(stpcpy(type
, a
), "."), b
);
1052 r
= dns_name_normalize(d
, &domain
);