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 "dns-domain.h"
28 #include "parse-util.h"
29 #include "string-util.h"
31 int dns_label_unescape(const char **name
, char *dest
, size_t sz
) {
55 if (r
>= DNS_LABEL_MAX
)
59 /* Escaped character */
67 else if (*n
== '\\' || *n
== '.') {
68 /* Escaped backslash or dot */
73 } else if (n
[0] >= '0' && n
[0] <= '9') {
76 /* Escaped literal ASCII character */
78 if (!(n
[1] >= '0' && n
[1] <= '9') ||
79 !(n
[2] >= '0' && n
[2] <= '9'))
82 k
= ((unsigned) (n
[0] - '0') * 100) +
83 ((unsigned) (n
[1] - '0') * 10) +
84 ((unsigned) (n
[2] - '0'));
86 /* Don't allow CC characters or anything that doesn't fit in 8bit */
87 if (k
< ' ' || k
> 255 || k
== 127)
98 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
100 /* Normal character */
108 /* Empty label that is not at the end? */
119 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
120 * the previous label (always skipping one dot) or to NULL if there are no more
122 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
123 const char *terminal
;
127 assert(label_terminal
);
131 if (!*label_terminal
) {
138 assert(**label_terminal
== '.' || **label_terminal
== 0);
140 /* skip current terminal character */
141 terminal
= *label_terminal
- 1;
143 /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
145 if (terminal
< name
) {
146 /* reached the first label, so indicate that there are no more */
151 /* find the start of the last label */
152 if (*terminal
== '.') {
154 unsigned slashes
= 0;
156 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
159 if (slashes
% 2 == 0) {
160 /* the '.' was not escaped */
172 r
= dns_label_unescape(&name
, dest
, sz
);
176 *label_terminal
= terminal
;
181 int dns_label_escape(const char *p
, size_t l
, char **ret
) {
182 _cleanup_free_
char *s
= NULL
;
189 if (l
> DNS_LABEL_MAX
)
192 s
= malloc(l
* 4 + 1);
199 if (*p
== '.' || *p
== '\\') {
201 /* Dot or backslash */
205 } else if (*p
== '_' ||
207 (*p
>= '0' && *p
<= '9') ||
208 (*p
>= 'a' && *p
<= 'z') ||
209 (*p
>= 'A' && *p
<= 'Z')) {
211 /* Proper character */
213 } else if ((uint8_t) *p
>= (uint8_t) ' ' && *p
!= 127) {
215 /* Everything else */
217 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
218 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
219 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
236 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
238 _cleanup_free_
uint32_t *input
= NULL
;
241 bool contains_8bit
= false;
245 assert(decoded_max
>= DNS_LABEL_MAX
);
247 if (encoded_size
<= 0)
250 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
251 if ((uint8_t) *p
> 127)
252 contains_8bit
= true;
257 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
261 if (idna_to_ascii_4i(input
, input_size
, decoded
, 0) != 0)
264 return strlen(decoded
);
270 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
272 size_t input_size
, output_size
;
273 _cleanup_free_
uint32_t *input
= NULL
;
274 _cleanup_free_
char *result
= NULL
;
275 uint32_t *output
= NULL
;
278 /* To be invoked after unescaping */
283 if (encoded_size
< sizeof(IDNA_ACE_PREFIX
)-1)
286 if (memcmp(encoded
, IDNA_ACE_PREFIX
, sizeof(IDNA_ACE_PREFIX
) -1) != 0)
289 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
293 output_size
= input_size
;
294 output
= newa(uint32_t, output_size
);
296 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
298 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
303 if (w
+1 > decoded_max
)
306 memcpy(decoded
, result
, w
+1);
313 int dns_name_concat(const char *a
, const char *b
, char **_ret
) {
314 _cleanup_free_
char *ret
= NULL
;
315 size_t n
= 0, allocated
= 0;
323 _cleanup_free_
char *t
= NULL
;
324 char label
[DNS_LABEL_MAX
];
327 r
= dns_label_unescape(&p
, label
, sizeof(label
));
335 /* Now continue with the second string, if there is one */
344 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
350 r
= dns_label_escape(label
, r
, &t
);
355 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ strlen(t
) + 1))
363 memcpy(ret
+ n
, t
, r
);
369 if (n
> DNS_NAME_MAX
)
373 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
384 void dns_name_hash_func(const void *s
, struct siphash
*state
) {
391 char label
[DNS_LABEL_MAX
+1];
394 r
= dns_label_unescape(&p
, label
, sizeof(label
));
398 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
408 ascii_strlower(label
);
410 string_hash_func(label
, state
);
413 /* enforce that all names are terminated by the empty label */
414 string_hash_func("", state
);
417 int dns_name_compare_func(const void *a
, const void *b
) {
424 x
= (const char *) a
+ strlen(a
);
425 y
= (const char *) b
+ strlen(b
);
428 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
430 if (x
== NULL
&& y
== NULL
)
433 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
434 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
438 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
439 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
448 r
= strcasecmp(la
, lb
);
454 const struct hash_ops dns_name_hash_ops
= {
455 .hash
= dns_name_hash_func
,
456 .compare
= dns_name_compare_func
459 int dns_name_equal(const char *x
, const char *y
) {
466 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
468 if (*x
== 0 && *y
== 0)
471 r
= dns_label_unescape(&x
, la
, sizeof(la
));
475 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
481 q
= dns_label_unescape(&y
, lb
, sizeof(lb
));
484 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
491 if (strcasecmp(la
, lb
))
496 int dns_name_endswith(const char *name
, const char *suffix
) {
497 const char *n
, *s
, *saved_n
= NULL
;
507 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
509 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
512 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
521 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
524 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
530 if (r
== 0 && q
== 0)
532 if (r
== 0 && saved_n
== n
)
537 if (r
!= q
|| strcasecmp(ln
, ls
)) {
539 /* Not the same, let's jump back, and try with the next label again */
547 int dns_name_between(const char *a
, const char *b
, const char *c
) {
550 /* Determine if b is strictly greater than a and strictly smaller than c.
551 We consider the order of names to be circular, so that if a is
552 strictly greater than c, we consider b to be between them if it is
553 either greater than a or smaller than c. This is how the canonical
554 DNS name order used in NSEC records work. */
556 n
= dns_name_compare_func(a
, c
);
561 return dns_name_compare_func(a
, b
) < 0 &&
562 dns_name_compare_func(b
, c
) < 0;
564 /* <--b--c a--b--> */
565 return dns_name_compare_func(b
, c
) < 0 ||
566 dns_name_compare_func(a
, b
) < 0;
569 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
576 p
= (const uint8_t*) a
;
578 if (family
== AF_INET
)
579 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
580 else if (family
== AF_INET6
)
581 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",
582 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
583 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
584 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
585 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
586 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
587 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
588 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
589 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
591 return -EAFNOSUPPORT
;
598 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
605 r
= dns_name_endswith(p
, "in-addr.arpa");
612 for (i
= 0; i
< ELEMENTSOF(a
); i
++) {
613 char label
[DNS_LABEL_MAX
+1];
615 r
= dns_label_unescape(&p
, label
, sizeof(label
));
623 r
= safe_atou8(label
, &a
[i
]);
628 r
= dns_name_equal(p
, "in-addr.arpa");
633 address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
634 ((uint32_t) a
[2] << 16) |
635 ((uint32_t) a
[1] << 8) |
641 r
= dns_name_endswith(p
, "ip6.arpa");
648 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
649 char label
[DNS_LABEL_MAX
+1];
652 r
= dns_label_unescape(&p
, label
, sizeof(label
));
657 x
= unhexchar(label
[0]);
661 r
= dns_label_unescape(&p
, label
, sizeof(label
));
666 y
= unhexchar(label
[0]);
670 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
673 r
= dns_name_equal(p
, "ip6.arpa");
685 int dns_name_root(const char *name
) {
686 char label
[DNS_LABEL_MAX
+1];
691 r
= dns_label_unescape(&name
, label
, sizeof(label
));
695 return r
== 0 && *name
== 0;
698 int dns_name_single_label(const char *name
) {
699 char label
[DNS_LABEL_MAX
+1];
704 r
= dns_label_unescape(&name
, label
, sizeof(label
));
710 r
= dns_label_unescape(&name
, label
, sizeof(label
));
714 return r
== 0 && *name
== 0;