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"
34 int dns_label_unescape(const char **name
, char *dest
, size_t sz
) {
58 if (r
>= DNS_LABEL_MAX
)
62 /* Escaped character */
70 else if (*n
== '\\' || *n
== '.') {
71 /* Escaped backslash or dot */
76 } else if (n
[0] >= '0' && n
[0] <= '9') {
79 /* Escaped literal ASCII character */
81 if (!(n
[1] >= '0' && n
[1] <= '9') ||
82 !(n
[2] >= '0' && n
[2] <= '9'))
85 k
= ((unsigned) (n
[0] - '0') * 100) +
86 ((unsigned) (n
[1] - '0') * 10) +
87 ((unsigned) (n
[2] - '0'));
89 /* Don't allow CC characters or anything that doesn't fit in 8bit */
90 if (k
< ' ' || k
> 255 || k
== 127)
101 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
103 /* Normal character */
111 /* Empty label that is not at the end? */
122 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
123 * the previous label (always skipping one dot) or to NULL if there are no more
125 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
126 const char *terminal
;
130 assert(label_terminal
);
134 if (!*label_terminal
) {
141 assert(**label_terminal
== '.' || **label_terminal
== 0);
143 /* skip current terminal character */
144 terminal
= *label_terminal
- 1;
146 /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
148 if (terminal
< name
) {
149 /* reached the first label, so indicate that there are no more */
154 /* find the start of the last label */
155 if (*terminal
== '.') {
157 unsigned slashes
= 0;
159 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
162 if (slashes
% 2 == 0) {
163 /* the '.' was not escaped */
175 r
= dns_label_unescape(&name
, dest
, sz
);
179 *label_terminal
= terminal
;
184 int dns_label_escape(const char *p
, size_t l
, char **ret
) {
185 _cleanup_free_
char *s
= NULL
;
192 if (l
> DNS_LABEL_MAX
)
195 s
= malloc(l
* 4 + 1);
202 if (*p
== '.' || *p
== '\\') {
204 /* Dot or backslash */
208 } else if (*p
== '_' ||
210 (*p
>= '0' && *p
<= '9') ||
211 (*p
>= 'a' && *p
<= 'z') ||
212 (*p
>= 'A' && *p
<= 'Z')) {
214 /* Proper character */
216 } else if ((uint8_t) *p
>= (uint8_t) ' ' && *p
!= 127) {
218 /* Everything else */
220 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
221 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
222 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
239 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
241 _cleanup_free_
uint32_t *input
= NULL
;
244 bool contains_8bit
= false;
248 assert(decoded_max
>= DNS_LABEL_MAX
);
250 if (encoded_size
<= 0)
253 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
254 if ((uint8_t) *p
> 127)
255 contains_8bit
= true;
260 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
264 if (idna_to_ascii_4i(input
, input_size
, decoded
, 0) != 0)
267 return strlen(decoded
);
273 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
275 size_t input_size
, output_size
;
276 _cleanup_free_
uint32_t *input
= NULL
;
277 _cleanup_free_
char *result
= NULL
;
278 uint32_t *output
= NULL
;
281 /* To be invoked after unescaping */
286 if (encoded_size
< sizeof(IDNA_ACE_PREFIX
)-1)
289 if (memcmp(encoded
, IDNA_ACE_PREFIX
, sizeof(IDNA_ACE_PREFIX
) -1) != 0)
292 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
296 output_size
= input_size
;
297 output
= newa(uint32_t, output_size
);
299 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
301 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
306 if (w
+1 > decoded_max
)
309 memcpy(decoded
, result
, w
+1);
316 int dns_name_concat(const char *a
, const char *b
, char **_ret
) {
317 _cleanup_free_
char *ret
= NULL
;
318 size_t n
= 0, allocated
= 0;
326 _cleanup_free_
char *t
= NULL
;
327 char label
[DNS_LABEL_MAX
];
330 r
= dns_label_unescape(&p
, label
, sizeof(label
));
338 /* Now continue with the second string, if there is one */
347 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
353 r
= dns_label_escape(label
, r
, &t
);
358 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ strlen(t
) + 1))
366 memcpy(ret
+ n
, t
, r
);
372 if (n
> DNS_NAME_MAX
)
376 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
387 void dns_name_hash_func(const void *s
, struct siphash
*state
) {
394 char label
[DNS_LABEL_MAX
+1];
397 r
= dns_label_unescape(&p
, label
, sizeof(label
));
401 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
411 ascii_strlower(label
);
413 string_hash_func(label
, state
);
416 /* enforce that all names are terminated by the empty label */
417 string_hash_func("", state
);
420 int dns_name_compare_func(const void *a
, const void *b
) {
427 x
= (const char *) a
+ strlen(a
);
428 y
= (const char *) b
+ strlen(b
);
431 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
433 if (x
== NULL
&& y
== NULL
)
436 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
437 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
441 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
442 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
451 r
= strcasecmp(la
, lb
);
457 const struct hash_ops dns_name_hash_ops
= {
458 .hash
= dns_name_hash_func
,
459 .compare
= dns_name_compare_func
462 int dns_name_equal(const char *x
, const char *y
) {
469 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
471 if (*x
== 0 && *y
== 0)
474 r
= dns_label_unescape(&x
, la
, sizeof(la
));
478 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
484 q
= dns_label_unescape(&y
, lb
, sizeof(lb
));
487 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
494 if (strcasecmp(la
, lb
))
499 int dns_name_endswith(const char *name
, const char *suffix
) {
500 const char *n
, *s
, *saved_n
= NULL
;
510 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
512 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
515 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
524 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
527 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
533 if (r
== 0 && q
== 0)
535 if (r
== 0 && saved_n
== n
)
540 if (r
!= q
|| strcasecmp(ln
, ls
)) {
542 /* Not the same, let's jump back, and try with the next label again */
550 int dns_name_change_suffix(const char *name
, const char *old_suffix
, const char *new_suffix
, char **ret
) {
551 const char *n
, *s
, *saved_before
= NULL
, *saved_after
= NULL
, *prefix
;
563 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
568 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
571 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
580 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
583 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
589 if (r
== 0 && q
== 0)
591 if (r
== 0 && saved_after
== n
) {
592 *ret
= NULL
; /* doesn't match */
598 if (r
!= q
|| strcasecmp(ln
, ls
)) {
600 /* Not the same, let's jump back, and try with the next label again */
603 saved_after
= saved_before
= NULL
;
607 /* Found it! Now generate the new name */
608 prefix
= strndupa(name
, saved_before
- name
);
610 r
= dns_name_concat(prefix
, new_suffix
, ret
);
617 int dns_name_between(const char *a
, const char *b
, const char *c
) {
620 /* Determine if b is strictly greater than a and strictly smaller than c.
621 We consider the order of names to be circular, so that if a is
622 strictly greater than c, we consider b to be between them if it is
623 either greater than a or smaller than c. This is how the canonical
624 DNS name order used in NSEC records work. */
626 n
= dns_name_compare_func(a
, c
);
631 return dns_name_compare_func(a
, b
) < 0 &&
632 dns_name_compare_func(b
, c
) < 0;
634 /* <--b--c a--b--> */
635 return dns_name_compare_func(b
, c
) < 0 ||
636 dns_name_compare_func(a
, b
) < 0;
639 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
646 p
= (const uint8_t*) a
;
648 if (family
== AF_INET
)
649 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
650 else if (family
== AF_INET6
)
651 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",
652 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
653 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
654 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
655 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
656 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
657 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
658 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
659 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
661 return -EAFNOSUPPORT
;
668 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
675 r
= dns_name_endswith(p
, "in-addr.arpa");
682 for (i
= 0; i
< ELEMENTSOF(a
); i
++) {
683 char label
[DNS_LABEL_MAX
+1];
685 r
= dns_label_unescape(&p
, label
, sizeof(label
));
693 r
= safe_atou8(label
, &a
[i
]);
698 r
= dns_name_equal(p
, "in-addr.arpa");
703 address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
704 ((uint32_t) a
[2] << 16) |
705 ((uint32_t) a
[1] << 8) |
711 r
= dns_name_endswith(p
, "ip6.arpa");
718 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
719 char label
[DNS_LABEL_MAX
+1];
722 r
= dns_label_unescape(&p
, label
, sizeof(label
));
727 x
= unhexchar(label
[0]);
731 r
= dns_label_unescape(&p
, label
, sizeof(label
));
736 y
= unhexchar(label
[0]);
740 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
743 r
= dns_name_equal(p
, "ip6.arpa");
755 int dns_name_root(const char *name
) {
756 char label
[DNS_LABEL_MAX
+1];
761 r
= dns_label_unescape(&name
, label
, sizeof(label
));
765 return r
== 0 && *name
== 0;
768 int dns_name_single_label(const char *name
) {
769 char label
[DNS_LABEL_MAX
+1];
774 r
= dns_label_unescape(&name
, label
, sizeof(label
));
780 r
= dns_label_unescape(&name
, label
, sizeof(label
));
784 return r
== 0 && *name
== 0;
787 /* Encode a domain name according to RFC 1035 Section 3.1 */
788 int dns_name_to_wire_format(const char *domain
, uint8_t *buffer
, size_t len
) {
789 uint8_t *label_length
;
793 assert_return(buffer
, -EINVAL
);
794 assert_return(domain
, -EINVAL
);
795 assert_return(domain
[0], -EINVAL
);
800 /* reserve a byte for label length */
807 /* convert and copy a single label */
808 r
= dns_label_unescape(&domain
, (char *) out
, len
);
812 /* fill label length, move forward */
821 static bool srv_type_label_is_valid(const char *label
, size_t n
) {
826 if (n
< 2) /* Label needs to be at least 2 chars long */
829 if (label
[0] != '_') /* First label char needs to be underscore */
832 /* Second char must be a letter */
833 if (!(label
[1] >= 'A' && label
[1] <= 'Z') &&
834 !(label
[1] >= 'a' && label
[1] <= 'z'))
837 /* Third and further chars must be alphanumeric or a hyphen */
838 for (k
= 2; k
< n
; k
++) {
839 if (!(label
[k
] >= 'A' && label
[k
] <= 'Z') &&
840 !(label
[k
] >= 'a' && label
[k
] <= 'z') &&
841 !(label
[k
] >= '0' && label
[k
] <= '9') &&
849 int dns_srv_type_verify(const char *name
) {
857 char label
[DNS_LABEL_MAX
];
859 /* This more or less implements RFC 6335, Section 5.1 */
861 r
= dns_label_unescape(&name
, label
, sizeof(label
));
872 if (!srv_type_label_is_valid(label
, r
))
878 return c
== 2; /* exactly two labels */
881 bool dns_service_name_is_valid(const char *name
) {
884 /* This more or less implements RFC 6763, Section 4.1.1 */
889 if (!utf8_is_valid(name
))
892 if (string_has_cc(name
, NULL
))
904 int dns_service_join(const char *name
, const char *type
, const char *domain
, char **ret
) {
905 _cleanup_free_
char *escaped
= NULL
, *n
= NULL
;
912 if (!dns_srv_type_verify(type
))
916 return dns_name_concat(type
, domain
, ret
);
918 if (!dns_service_name_is_valid(name
))
921 r
= dns_label_escape(name
, strlen(name
), &escaped
);
925 r
= dns_name_concat(type
, domain
, &n
);
929 return dns_name_concat(escaped
, n
, ret
);
932 static bool dns_service_name_label_is_valid(const char *label
, size_t n
) {
937 if (memchr(label
, 0, n
))
940 s
= strndupa(label
, n
);
941 return dns_service_name_is_valid(s
);
944 int dns_service_split(const char *joined
, char **_name
, char **_type
, char **_domain
) {
945 _cleanup_free_
char *name
= NULL
, *type
= NULL
, *domain
= NULL
;
946 const char *p
= joined
, *q
= NULL
, *d
= NULL
;
947 char a
[DNS_LABEL_MAX
], b
[DNS_LABEL_MAX
], c
[DNS_LABEL_MAX
];
953 /* Get first label from the full name */
954 an
= dns_label_unescape(&p
, a
, sizeof(a
));
961 /* If there was a first label, try to get the second one */
962 bn
= dns_label_unescape(&p
, b
, sizeof(b
));
969 /* If there was a second label, try to get the third one */
971 cn
= dns_label_unescape(&p
, c
, sizeof(c
));
982 if (x
>= 2 && srv_type_label_is_valid(b
, bn
)) {
984 if (x
>= 3 && srv_type_label_is_valid(c
, cn
)) {
986 if (dns_service_name_label_is_valid(a
, an
)) {
988 /* OK, got <name> . <type> . <type2> . <domain> */
990 name
= strndup(a
, an
);
994 type
= new(char, bn
+1+cn
+1);
997 strcpy(stpcpy(stpcpy(type
, b
), "."), c
);
1003 } else if (srv_type_label_is_valid(a
, an
)) {
1005 /* OK, got <type> . <type2> . <domain> */
1009 type
= new(char, an
+1+bn
+1);
1012 strcpy(stpcpy(stpcpy(type
, a
), "."), b
);
1024 r
= dns_name_normalize(d
, &domain
);