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"
29 int dns_label_unescape(const char **name
, char *dest
, size_t sz
) {
53 if (r
>= DNS_LABEL_MAX
)
57 /* Escaped character */
65 else if (*n
== '\\' || *n
== '.') {
66 /* Escaped backslash or dot */
71 } else if (n
[0] >= '0' && n
[0] <= '9') {
74 /* Escaped literal ASCII character */
76 if (!(n
[1] >= '0' && n
[1] <= '9') ||
77 !(n
[2] >= '0' && n
[2] <= '9'))
80 k
= ((unsigned) (n
[0] - '0') * 100) +
81 ((unsigned) (n
[1] - '0') * 10) +
82 ((unsigned) (n
[2] - '0'));
84 /* Don't allow CC characters or anything that doesn't fit in 8bit */
85 if (k
< ' ' || k
> 255 || k
== 127)
96 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
98 /* Normal character */
106 /* Empty label that is not at the end? */
117 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
118 * the previous label (always skipping one dot) or to NULL if there are no more
120 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
121 const char *terminal
;
125 assert(label_terminal
);
129 if (!*label_terminal
) {
136 assert(**label_terminal
== '.' || **label_terminal
== 0);
138 /* skip current terminal character */
139 terminal
= *label_terminal
- 1;
141 /* point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
143 if (terminal
< name
) {
144 /* reached the first label, so indicate that there are no more */
149 /* find the start of the last label */
150 if (*terminal
== '.') {
152 unsigned slashes
= 0;
154 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
157 if (slashes
% 2 == 0) {
158 /* the '.' was not escaped */
170 r
= dns_label_unescape(&name
, dest
, sz
);
174 *label_terminal
= terminal
;
179 int dns_label_escape(const char *p
, size_t l
, char **ret
) {
180 _cleanup_free_
char *s
= NULL
;
187 if (l
> DNS_LABEL_MAX
)
190 s
= malloc(l
* 4 + 1);
197 if (*p
== '.' || *p
== '\\') {
199 /* Dot or backslash */
203 } else if (*p
== '_' ||
205 (*p
>= '0' && *p
<= '9') ||
206 (*p
>= 'a' && *p
<= 'z') ||
207 (*p
>= 'A' && *p
<= 'Z')) {
209 /* Proper character */
211 } else if ((uint8_t) *p
>= (uint8_t) ' ' && *p
!= 127) {
213 /* Everything else */
215 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
216 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
217 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
234 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
236 _cleanup_free_
uint32_t *input
= NULL
;
239 bool contains_8bit
= false;
243 assert(decoded_max
>= DNS_LABEL_MAX
);
245 if (encoded_size
<= 0)
248 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
249 if ((uint8_t) *p
> 127)
250 contains_8bit
= true;
255 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
259 if (idna_to_ascii_4i(input
, input_size
, decoded
, 0) != 0)
262 return strlen(decoded
);
268 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
270 size_t input_size
, output_size
;
271 _cleanup_free_
uint32_t *input
= NULL
;
272 _cleanup_free_
char *result
= NULL
;
273 uint32_t *output
= NULL
;
276 /* To be invoked after unescaping */
281 if (encoded_size
< sizeof(IDNA_ACE_PREFIX
)-1)
284 if (memcmp(encoded
, IDNA_ACE_PREFIX
, sizeof(IDNA_ACE_PREFIX
) -1) != 0)
287 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
291 output_size
= input_size
;
292 output
= newa(uint32_t, output_size
);
294 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
296 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
301 if (w
+1 > decoded_max
)
304 memcpy(decoded
, result
, w
+1);
311 int dns_name_normalize(const char *s
, char **_ret
) {
312 _cleanup_free_
char *ret
= NULL
;
313 size_t n
= 0, allocated
= 0;
321 _cleanup_free_
char *t
= NULL
;
322 char label
[DNS_LABEL_MAX
];
325 r
= dns_label_unescape(&p
, label
, sizeof(label
));
334 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
340 r
= dns_label_escape(label
, r
, &t
);
344 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ strlen(t
) + 1))
352 memcpy(ret
+ n
, t
, r
);
356 if (n
> DNS_NAME_MAX
)
359 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
372 unsigned long dns_name_hash_func(const void *s
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
374 unsigned long ul
= hash_key
[0];
380 char label
[DNS_LABEL_MAX
+1];
383 r
= dns_label_unescape(&p
, label
, sizeof(label
));
387 k
= dns_label_undo_idna(label
, r
, label
, sizeof(label
));
394 ascii_strlower(label
);
396 ul
= ul
* hash_key
[1] + ul
+ string_hash_func(label
, hash_key
);
402 int dns_name_compare_func(const void *a
, const void *b
) {
409 x
= (const char *) a
+ strlen(a
);
410 y
= (const char *) b
+ strlen(b
);
413 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
415 if (x
== NULL
&& y
== NULL
)
418 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
419 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
423 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
424 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
433 r
= strcasecmp(la
, lb
);
439 const struct hash_ops dns_name_hash_ops
= {
440 .hash
= dns_name_hash_func
,
441 .compare
= dns_name_compare_func
444 int dns_name_equal(const char *x
, const char *y
) {
451 char la
[DNS_LABEL_MAX
+1], lb
[DNS_LABEL_MAX
+1];
453 if (*x
== 0 && *y
== 0)
456 r
= dns_label_unescape(&x
, la
, sizeof(la
));
460 k
= dns_label_undo_idna(la
, r
, la
, sizeof(la
));
466 q
= dns_label_unescape(&y
, lb
, sizeof(lb
));
469 w
= dns_label_undo_idna(lb
, q
, lb
, sizeof(lb
));
476 if (strcasecmp(la
, lb
))
481 int dns_name_endswith(const char *name
, const char *suffix
) {
482 const char *n
, *s
, *saved_n
= NULL
;
492 char ln
[DNS_LABEL_MAX
+1], ls
[DNS_LABEL_MAX
+1];
494 r
= dns_label_unescape(&n
, ln
, sizeof(ln
));
497 k
= dns_label_undo_idna(ln
, r
, ln
, sizeof(ln
));
506 q
= dns_label_unescape(&s
, ls
, sizeof(ls
));
509 w
= dns_label_undo_idna(ls
, q
, ls
, sizeof(ls
));
515 if (r
== 0 && q
== 0)
517 if (r
== 0 && saved_n
== n
)
522 if (r
!= q
|| strcasecmp(ln
, ls
)) {
524 /* Not the same, let's jump back, and try with the next label again */
532 int dns_name_between(const char *a
, const char *b
, const char *c
) {
535 /* Determine if b is strictly greater than a and strictly smaller than c.
536 We consider the order of names to be circular, so that if a is
537 strictly greater than c, we consider b to be between them if it is
538 either greater than a or smaller than c. This is how the canonical
539 DNS name order used in NSEC records work. */
541 n
= dns_name_compare_func(a
, c
);
546 return dns_name_compare_func(a
, b
) < 0 &&
547 dns_name_compare_func(b
, c
) < 0;
549 /* <--b--c a--b--> */
550 return dns_name_compare_func(b
, c
) < 0 ||
551 dns_name_compare_func(a
, b
) < 0;
554 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
561 p
= (const uint8_t*) a
;
563 if (family
== AF_INET
)
564 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
565 else if (family
== AF_INET6
)
566 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",
567 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
568 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
569 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
570 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
571 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
572 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
573 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
574 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
576 return -EAFNOSUPPORT
;
583 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
590 r
= dns_name_endswith(p
, "in-addr.arpa");
597 for (i
= 0; i
< ELEMENTSOF(a
); i
++) {
598 char label
[DNS_LABEL_MAX
+1];
600 r
= dns_label_unescape(&p
, label
, sizeof(label
));
608 r
= safe_atou8(label
, &a
[i
]);
613 r
= dns_name_equal(p
, "in-addr.arpa");
618 address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
619 ((uint32_t) a
[2] << 16) |
620 ((uint32_t) a
[1] << 8) |
626 r
= dns_name_endswith(p
, "ip6.arpa");
633 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
634 char label
[DNS_LABEL_MAX
+1];
637 r
= dns_label_unescape(&p
, label
, sizeof(label
));
642 x
= unhexchar(label
[0]);
646 r
= dns_label_unescape(&p
, label
, sizeof(label
));
651 y
= unhexchar(label
[0]);
655 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
658 r
= dns_name_equal(p
, "ip6.arpa");
670 int dns_name_root(const char *name
) {
671 char label
[DNS_LABEL_MAX
+1];
676 r
= dns_label_unescape(&name
, label
, sizeof(label
));
680 return r
== 0 && *name
== 0;
683 int dns_name_single_label(const char *name
) {
684 char label
[DNS_LABEL_MAX
+1];
689 r
= dns_label_unescape(&name
, label
, sizeof(label
));
695 r
= dns_label_unescape(&name
, label
, sizeof(label
));
699 return r
== 0 && *name
== 0;