1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <netinet/in.h>
6 #include <sys/socket.h>
8 #include "alloc-util.h"
9 #include "dns-domain.h"
10 #include "glyph-util.h"
12 #include "hexdecoct.h"
13 #include "hostname-util.h"
15 #include "in-addr-util.h"
17 #include "parse-util.h"
18 #include "string-util.h"
22 int dns_label_unescape(const char **name
, char *dest
, size_t sz
, DNSLabelFlags flags
) {
24 char *d
, last_char
= 0;
34 if (IN_SET(*n
, 0, '.')) {
35 if (FLAGS_SET(flags
, DNS_LABEL_LDH
) && last_char
== '-')
39 if (n
[0] == '.' && (n
[1] != 0 || !FLAGS_SET(flags
, DNS_LABEL_LEAVE_TRAILING_DOT
)))
45 if (r
>= DNS_LABEL_MAX
)
52 /* Escaped character */
53 if (FLAGS_SET(flags
, DNS_LABEL_NO_ESCAPES
))
62 else if (IN_SET(*n
, '\\', '.')) {
63 /* Escaped backslash or dot */
65 if (FLAGS_SET(flags
, DNS_LABEL_LDH
))
75 } else if (n
[0] >= '0' && n
[0] <= '9') {
78 /* Escaped literal ASCII character */
80 if (!(n
[1] >= '0' && n
[1] <= '9') ||
81 !(n
[2] >= '0' && n
[2] <= '9'))
84 k
= ((unsigned) (n
[0] - '0') * 100) +
85 ((unsigned) (n
[1] - '0') * 10) +
86 ((unsigned) (n
[2] - '0'));
88 /* Don't allow anything that doesn't fit in 8 bits. Note that we do allow
89 * control characters, as some servers (e.g. cloudflare) are happy to
90 * generate labels with them inside. */
94 if (FLAGS_SET(flags
, DNS_LABEL_LDH
) &&
95 !valid_ldh_char((char) k
))
108 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
110 /* Normal character */
112 if (FLAGS_SET(flags
, DNS_LABEL_LDH
)) {
113 if (!valid_ldh_char(*n
))
115 if (r
== 0 && *n
== '-')
130 /* Empty label that is not at the end? */
134 /* More than one trailing dot? */
135 if (n
[0] == '.' && !FLAGS_SET(flags
, DNS_LABEL_LEAVE_TRAILING_DOT
))
145 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
146 * the previous label (always skipping one dot) or to NULL if there are no more
148 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
149 const char *terminal
;
153 assert(label_terminal
);
157 if (!*label_terminal
) {
164 terminal
= *label_terminal
;
165 assert(IN_SET(*terminal
, 0, '.'));
167 /* Skip current terminal character (and accept domain names ending it ".") */
169 terminal
= PTR_SUB1(terminal
, name
);
170 if (terminal
>= name
&& *terminal
== '.')
171 terminal
= PTR_SUB1(terminal
, name
);
173 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
175 /* Find the start of the last label */
176 if (*terminal
== '.') {
178 unsigned slashes
= 0;
180 for (y
= PTR_SUB1(terminal
, name
); y
&& *y
== '\\'; y
= PTR_SUB1(y
, name
))
183 if (slashes
% 2 == 0) {
184 /* The '.' was not escaped */
193 terminal
= PTR_SUB1(terminal
, name
);
196 r
= dns_label_unescape(&name
, dest
, sz
, 0);
200 *label_terminal
= terminal
;
205 int dns_label_escape(const char *p
, size_t l
, char *dest
, size_t sz
) {
208 /* DNS labels must be between 1 and 63 characters long. A
209 * zero-length label does not exist. See RFC 2181, Section
212 if (l
<= 0 || l
> DNS_LABEL_MAX
)
223 if (IN_SET(*p
, '.', '\\')) {
225 /* Dot or backslash */
235 } else if (IN_SET(*p
, '_', '-') ||
239 /* Proper character */
249 /* Everything else */
255 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
256 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
257 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
267 return (int) (q
- dest
);
270 int dns_label_escape_new(const char *p
, size_t l
, char **ret
) {
271 _cleanup_free_
char *s
= NULL
;
277 if (l
<= 0 || l
> DNS_LABEL_MAX
)
280 s
= new(char, DNS_LABEL_ESCAPED_MAX
);
284 r
= dns_label_escape(p
, l
, s
, DNS_LABEL_ESCAPED_MAX
);
294 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
295 _cleanup_free_
uint32_t *input
= NULL
;
296 size_t input_size
, l
;
297 bool contains_8_bit
= false;
298 char buffer
[DNS_LABEL_MAX
+1];
304 /* Converts a U-label into an A-label */
310 if (encoded_size
<= 0)
313 for (const char *p
= encoded
; p
< encoded
+ encoded_size
; p
++)
314 if ((uint8_t) *p
> 127)
315 contains_8_bit
= true;
317 if (!contains_8_bit
) {
318 if (encoded_size
> DNS_LABEL_MAX
)
324 input
= sym_stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
328 if (sym_idna_to_ascii_4i(input
, input_size
, buffer
, 0) != 0)
333 /* Verify that the result is not longer than one DNS label. */
334 if (l
<= 0 || l
> DNS_LABEL_MAX
)
339 memcpy(decoded
, buffer
, l
);
341 /* If there's room, append a trailing NUL byte, but only then */
348 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
349 size_t input_size
, output_size
;
350 _cleanup_free_
uint32_t *input
= NULL
;
351 _cleanup_free_
char *result
= NULL
;
352 uint32_t *output
= NULL
;
356 /* To be invoked after unescaping. Converts an A-label into a U-label. */
365 if (encoded_size
<= 0 || encoded_size
> DNS_LABEL_MAX
)
368 if (!memory_startswith(encoded
, encoded_size
, IDNA_ACE_PREFIX
))
371 input
= sym_stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
375 output_size
= input_size
;
376 output
= newa(uint32_t, output_size
);
378 sym_idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
380 result
= sym_stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
388 memcpy(decoded
, result
, w
);
390 /* Append trailing NUL byte if there's space, but only then. */
398 int dns_name_concat(const char *a
, const char *b
, DNSLabelFlags flags
, char **_ret
) {
399 _cleanup_free_
char *ret
= NULL
;
413 char label
[DNS_LABEL_MAX
];
415 r
= dns_label_unescape(&p
, label
, sizeof label
, flags
);
423 /* Now continue with the second string, if there is one */
432 if (!GREEDY_REALLOC(ret
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
435 r
= dns_label_escape(label
, r
, ret
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
442 char escaped
[DNS_LABEL_ESCAPED_MAX
];
444 r
= dns_label_escape(label
, r
, escaped
, sizeof(escaped
));
454 if (n
> DNS_HOSTNAME_MAX
)
459 /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
460 if (!GREEDY_REALLOC(ret
, 2))
465 if (!GREEDY_REALLOC(ret
, n
+ 1))
470 *_ret
= TAKE_PTR(ret
);
476 void dns_name_hash_func(const char *p
, struct siphash
*state
) {
482 char label
[DNS_LABEL_MAX
+1];
484 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
490 ascii_strlower_n(label
, r
);
491 siphash24_compress(label
, r
, state
);
492 siphash24_compress_byte(0, state
); /* make sure foobar and foo.bar result in different hashes */
495 /* enforce that all names are terminated by the empty label */
496 string_hash_func("", state
);
499 int dns_name_compare_func(const char *a
, const char *b
) {
510 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
512 if (x
== NULL
&& y
== NULL
)
515 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
516 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
520 r
= ascii_strcasecmp_nn(la
, r
, lb
, q
);
530 dns_name_compare_func
);
532 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
533 dns_name_hash_ops_free
,
536 dns_name_compare_func
,
539 int dns_name_equal(const char *x
, const char *y
) {
546 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
548 r
= dns_label_unescape(&x
, la
, sizeof la
, 0);
552 q
= dns_label_unescape(&y
, lb
, sizeof lb
, 0);
561 if (ascii_strcasecmp_n(la
, lb
, r
) != 0)
566 int dns_name_endswith(const char *name
, const char *suffix
) {
567 const char *n
, *s
, *saved_n
= NULL
;
577 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
579 r
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
586 q
= dns_label_unescape(&s
, ls
, sizeof ls
, 0);
590 if (r
== 0 && q
== 0)
592 if (r
== 0 && saved_n
== n
)
595 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
597 /* Not the same, let's jump back, and try with the next label again */
599 n
= TAKE_PTR(saved_n
);
604 int dns_name_startswith(const char *name
, const char *prefix
) {
615 char ln
[DNS_LABEL_MAX
], lp
[DNS_LABEL_MAX
];
617 r
= dns_label_unescape(&p
, lp
, sizeof lp
, 0);
623 q
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
629 if (ascii_strcasecmp_n(ln
, lp
, r
) != 0)
634 int dns_name_change_suffix(const char *name
, const char *old_suffix
, const char *new_suffix
, char **ret
) {
635 const char *n
, *s
, *saved_before
= NULL
, *saved_after
= NULL
, *prefix
;
647 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
652 r
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
659 q
= dns_label_unescape(&s
, ls
, sizeof ls
, 0);
663 if (r
== 0 && q
== 0)
665 if (r
== 0 && saved_after
== n
) {
666 *ret
= NULL
; /* doesn't match */
670 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
672 /* Not the same, let's jump back, and try with the next label again */
674 n
= TAKE_PTR(saved_after
);
679 /* Found it! Now generate the new name */
680 prefix
= strndupa_safe(name
, saved_before
- name
);
682 r
= dns_name_concat(prefix
, new_suffix
, 0, ret
);
689 int dns_name_between(const char *a
, const char *b
, const char *c
) {
690 /* Determine if b is strictly greater than a and strictly smaller than c.
691 We consider the order of names to be circular, so that if a is
692 strictly greater than c, we consider b to be between them if it is
693 either greater than a or smaller than c. This is how the canonical
694 DNS name order used in NSEC records work. */
696 if (dns_name_compare_func(a
, c
) < 0)
698 a and c are properly ordered:
701 return dns_name_compare_func(a
, b
) < 0 &&
702 dns_name_compare_func(b
, c
) < 0;
705 a and c are equal or 'reversed':
710 return dns_name_compare_func(b
, c
) < 0 ||
711 dns_name_compare_func(a
, b
) < 0;
714 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
721 p
= (const uint8_t*) a
;
723 if (family
== AF_INET
)
724 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
725 else if (family
== AF_INET6
)
726 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",
727 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
728 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
729 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
730 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
731 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
732 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
733 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
734 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
736 return -EAFNOSUPPORT
;
743 int dns_name_address(const char *p
, int *ret_family
, union in_addr_union
*ret_address
) {
750 r
= dns_name_endswith(p
, "in-addr.arpa");
756 for (size_t i
= 0; i
< ELEMENTSOF(a
); i
++) {
757 char label
[DNS_LABEL_MAX
+1];
759 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
767 r
= safe_atou8(label
, &a
[i
]);
772 r
= dns_name_equal(p
, "in-addr.arpa");
776 *ret_family
= AF_INET
;
777 ret_address
->in
.s_addr
= htobe32(((uint32_t) a
[3] << 24) |
778 ((uint32_t) a
[2] << 16) |
779 ((uint32_t) a
[1] << 8) |
785 r
= dns_name_endswith(p
, "ip6.arpa");
791 for (size_t i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
792 char label
[DNS_LABEL_MAX
+1];
795 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
800 x
= unhexchar(label
[0]);
804 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
809 y
= unhexchar(label
[0]);
813 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
816 r
= dns_name_equal(p
, "ip6.arpa");
820 *ret_family
= AF_INET6
;
821 ret_address
->in6
= a
;
825 *ret_family
= AF_UNSPEC
;
826 *ret_address
= IN_ADDR_NULL
;
831 bool dns_name_is_root(const char *name
) {
834 /* There are exactly two ways to encode the root domain name:
835 * as empty string, or with a single dot. */
837 return STR_IN_SET(name
, "", ".");
840 bool dns_name_is_single_label(const char *name
) {
845 r
= dns_name_parent(&name
);
849 return dns_name_is_root(name
);
852 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
853 int dns_name_to_wire_format(const char *domain
, uint8_t *buffer
, size_t len
, bool canonical
) {
854 uint8_t *label_length
, *out
;
863 /* Reserve a byte for label length */
870 /* Convert and copy a single label. Note that
871 * dns_label_unescape() returns 0 when it hits the end
872 * of the domain name, which we rely on here to encode
873 * the trailing NUL byte. */
874 r
= dns_label_unescape(&domain
, (char *) out
, len
, 0);
878 /* Optionally, output the name in DNSSEC canonical
879 * format, as described in RFC 4034, section 6.2. Or
880 * in other words: in lower-case. */
882 ascii_strlower_n((char*) out
, (size_t) r
);
884 /* Fill label length, move forward */
891 /* Verify the maximum size of the encoded name. The trailing
892 * dot + NUL byte account are included this time, hence
893 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
895 if (out
- buffer
> DNS_HOSTNAME_MAX
+ 2)
901 static bool srv_type_label_is_valid(const char *label
, size_t n
) {
904 if (n
< 2) /* Label needs to be at least 2 chars long */
907 if (label
[0] != '_') /* First label char needs to be underscore */
910 /* Second char must be a letter */
911 if (!ascii_isalpha(label
[1]))
914 /* Third and further chars must be alphanumeric or a hyphen */
915 for (size_t k
= 2; k
< n
; k
++)
916 if (!ascii_isalpha(label
[k
]) &&
917 !ascii_isdigit(label
[k
]) &&
924 bool dns_srv_type_is_valid(const char *name
) {
932 char label
[DNS_LABEL_MAX
];
934 /* This more or less implements RFC 6335, Section 5.1 */
936 r
= dns_label_unescape(&name
, label
, sizeof label
, 0);
945 if (!srv_type_label_is_valid(label
, r
))
951 return c
== 2; /* exactly two labels */
954 bool dnssd_srv_type_is_valid(const char *name
) {
955 return dns_srv_type_is_valid(name
) &&
956 ((dns_name_endswith(name
, "_tcp") > 0) ||
957 (dns_name_endswith(name
, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
960 bool dns_service_name_is_valid(const char *name
) {
963 /* This more or less implements RFC 6763, Section 4.1.1 */
968 if (!utf8_is_valid(name
))
971 if (string_has_cc(name
, NULL
))
977 if (l
> DNS_LABEL_MAX
)
983 int dns_service_join(const char *name
, const char *type
, const char *domain
, char **ret
) {
984 char escaped
[DNS_LABEL_ESCAPED_MAX
];
985 _cleanup_free_
char *n
= NULL
;
992 if (!dns_srv_type_is_valid(type
))
996 return dns_name_concat(type
, domain
, 0, ret
);
998 if (!dns_service_name_is_valid(name
))
1001 r
= dns_label_escape(name
, strlen(name
), escaped
, sizeof(escaped
));
1005 r
= dns_name_concat(type
, domain
, 0, &n
);
1009 return dns_name_concat(escaped
, n
, 0, ret
);
1012 static bool dns_service_name_label_is_valid(const char *label
, size_t n
) {
1017 if (memchr(label
, 0, n
))
1020 s
= strndupa_safe(label
, n
);
1021 return dns_service_name_is_valid(s
);
1024 int dns_service_split(const char *joined
, char **ret_name
, char **ret_type
, char **ret_domain
) {
1025 _cleanup_free_
char *name
= NULL
, *type
= NULL
, *domain
= NULL
;
1026 const char *p
= joined
, *q
= NULL
, *d
= joined
;
1027 char a
[DNS_LABEL_MAX
+1], b
[DNS_LABEL_MAX
+1], c
[DNS_LABEL_MAX
+1];
1033 /* Get first label from the full name */
1034 an
= dns_label_unescape(&p
, a
, sizeof(a
), 0);
1041 /* If there was a first label, try to get the second one */
1042 bn
= dns_label_unescape(&p
, b
, sizeof(b
), 0);
1047 if (!srv_type_label_is_valid(b
, bn
))
1052 /* If there was a second label, try to get the third one */
1054 cn
= dns_label_unescape(&p
, c
, sizeof(c
), 0);
1058 if (cn
> 0 && srv_type_label_is_valid(c
, cn
))
1065 if (!srv_type_label_is_valid(a
, an
))
1068 /* OK, got <type> . <type2> . <domain> */
1072 type
= strjoin(a
, ".", b
);
1080 if (!dns_service_name_label_is_valid(a
, an
))
1083 /* OK, got <name> . <type> . <type2> . <domain> */
1085 name
= strndup(a
, an
);
1089 type
= strjoin(b
, ".", c
);
1098 r
= dns_name_normalize(d
, 0, &domain
);
1103 *ret_domain
= TAKE_PTR(domain
);
1106 *ret_type
= TAKE_PTR(type
);
1109 *ret_name
= TAKE_PTR(name
);
1114 static int dns_name_build_suffix_table(const char *name
, const char *table
[]) {
1115 const char *p
= ASSERT_PTR(name
);
1122 if (n
> DNS_N_LABELS_MAX
)
1126 r
= dns_name_parent(&p
);
1138 int dns_name_suffix(const char *name
, unsigned n_labels
, const char **ret
) {
1139 const char* labels
[DNS_N_LABELS_MAX
+1];
1145 n
= dns_name_build_suffix_table(name
, labels
);
1149 if ((unsigned) n
< n_labels
)
1152 *ret
= labels
[n
- n_labels
];
1153 return (int) (n
- n_labels
);
1156 int dns_name_skip(const char *a
, unsigned n_labels
, const char **ret
) {
1162 for (; n_labels
> 0; n_labels
--) {
1163 r
= dns_name_parent(&a
);
1176 int dns_name_count_labels(const char *name
) {
1182 for (const char *p
= name
;;) {
1183 r
= dns_name_parent(&p
);
1189 if (n
>= DNS_N_LABELS_MAX
)
1198 int dns_name_equal_skip(const char *a
, unsigned n_labels
, const char *b
) {
1204 r
= dns_name_skip(a
, n_labels
, &a
);
1208 return dns_name_equal(a
, b
);
1211 int dns_name_common_suffix(const char *a
, const char *b
, const char **ret
) {
1212 const char *a_labels
[DNS_N_LABELS_MAX
+1], *b_labels
[DNS_N_LABELS_MAX
+1];
1213 int n
= 0, m
= 0, k
= 0, r
, q
;
1219 /* Determines the common suffix of domain names a and b */
1221 n
= dns_name_build_suffix_table(a
, a_labels
);
1225 m
= dns_name_build_suffix_table(b
, b_labels
);
1230 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
1233 if (k
>= n
|| k
>= m
) {
1234 *ret
= a_labels
[n
- k
];
1238 x
= a_labels
[n
- 1 - k
];
1239 r
= dns_label_unescape(&x
, la
, sizeof la
, 0);
1243 y
= b_labels
[m
- 1 - k
];
1244 q
= dns_label_unescape(&y
, lb
, sizeof lb
, 0);
1248 if (r
!= q
|| ascii_strcasecmp_n(la
, lb
, r
) != 0) {
1249 *ret
= a_labels
[n
- k
];
1257 int dns_name_apply_idna(const char *name
, char **ret
) {
1259 /* Return negative on error, 0 if not implemented, positive on success. */
1261 #if HAVE_LIBIDN2 || HAVE_LIBIDN2
1265 if (r
== -EOPNOTSUPP
) {
1274 _cleanup_free_
char *t
= NULL
;
1279 /* First, try non-transitional mode (i.e. IDN2008 rules) */
1280 r
= sym_idn2_lookup_u8((uint8_t*) name
, (uint8_t**) &t
,
1281 IDN2_NFC_INPUT
| IDN2_NONTRANSITIONAL
);
1282 if (r
== IDN2_DISALLOWED
) /* If that failed, because of disallowed characters, try transitional mode.
1283 * (i.e. IDN2003 rules which supports some unicode chars IDN2008 doesn't allow). */
1284 r
= sym_idn2_lookup_u8((uint8_t*) name
, (uint8_t**) &t
,
1285 IDN2_NFC_INPUT
| IDN2_TRANSITIONAL
);
1287 log_debug("idn2_lookup_u8: %s %s %s", name
, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), t
);
1289 if (!startswith(name
, "xn--")) {
1290 _cleanup_free_
char *s
= NULL
;
1292 r
= sym_idn2_to_unicode_8z8z(t
, &s
, 0);
1294 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1295 t
, r
, sym_idn2_strerror(r
));
1300 if (!streq_ptr(name
, s
)) {
1301 log_debug("idn2 roundtrip failed: \"%s\" %s \"%s\" %s \"%s\", ignoring.",
1302 name
, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), t
,
1303 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), s
);
1310 return 1; /* *ret has been written */
1313 log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name
, r
, sym_idn2_strerror(r
));
1314 if (r
== IDN2_2HYPHEN
)
1315 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
1317 if (IN_SET(r
, IDN2_TOO_BIG_DOMAIN
, IDN2_TOO_BIG_LABEL
))
1322 _cleanup_free_
char *buf
= NULL
;
1331 char label
[DNS_LABEL_MAX
];
1333 r
= dns_label_unescape(&name
, label
, sizeof label
, 0);
1339 q
= dns_label_apply_idna(label
, r
, label
, sizeof label
);
1345 if (!GREEDY_REALLOC(buf
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
1348 r
= dns_label_escape(label
, r
, buf
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
1360 if (n
> DNS_HOSTNAME_MAX
)
1363 if (!GREEDY_REALLOC(buf
, n
+ 1))
1367 *ret
= TAKE_PTR(buf
);
1376 int dns_name_is_valid_or_address(const char *name
) {
1377 /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1382 if (in_addr_from_string_auto(name
, NULL
, NULL
) >= 0)
1385 return dns_name_is_valid(name
);
1388 int dns_name_dot_suffixed(const char *name
) {
1389 const char *p
= name
;
1396 r
= dns_label_unescape(&p
, NULL
, DNS_LABEL_MAX
, DNS_LABEL_LEAVE_TRAILING_DOT
);
1404 bool dns_name_dont_resolve(const char *name
) {
1406 /* Never respond to some of the domains listed in RFC6303 */
1407 if (dns_name_endswith(name
, "0.in-addr.arpa") > 0 ||
1408 dns_name_equal(name
, "255.255.255.255.in-addr.arpa") > 0 ||
1409 dns_name_equal(name
, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
1412 /* Never respond to some of the domains listed in RFC6761 */
1413 if (dns_name_endswith(name
, "invalid") > 0)
1416 /* Never respond to some of the domains listed in RFC9476 */
1417 if (dns_name_endswith(name
, "alt") > 0)