1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 # include <stringprep.h>
11 #include <netinet/in.h>
13 #include <sys/socket.h>
15 #include "alloc-util.h"
16 #include "dns-domain.h"
18 #include "hexdecoct.h"
19 #include "hostname-util.h"
20 #include "in-addr-util.h"
22 #include "parse-util.h"
23 #include "string-util.h"
27 int dns_label_unescape(const char **name
, char *dest
, size_t sz
, DNSLabelFlags flags
) {
29 char *d
, last_char
= 0;
39 if (IN_SET(*n
, 0, '.')) {
40 if (FLAGS_SET(flags
, DNS_LABEL_LDH
) && last_char
== '-')
44 if (n
[0] == '.' && (n
[1] != 0 || !FLAGS_SET(flags
, DNS_LABEL_LEAVE_TRAILING_DOT
)))
50 if (r
>= DNS_LABEL_MAX
)
57 /* Escaped character */
58 if (FLAGS_SET(flags
, DNS_LABEL_NO_ESCAPES
))
67 else if (IN_SET(*n
, '\\', '.')) {
68 /* Escaped backslash or dot */
70 if (FLAGS_SET(flags
, DNS_LABEL_LDH
))
80 } else if (n
[0] >= '0' && n
[0] <= '9') {
83 /* Escaped literal ASCII character */
85 if (!(n
[1] >= '0' && n
[1] <= '9') ||
86 !(n
[2] >= '0' && n
[2] <= '9'))
89 k
= ((unsigned) (n
[0] - '0') * 100) +
90 ((unsigned) (n
[1] - '0') * 10) +
91 ((unsigned) (n
[2] - '0'));
93 /* Don't allow anything that doesn't
94 * fit in 8bit. Note that we do allow
95 * control characters, as some servers
96 * (e.g. cloudflare) are happy to
97 * generate labels with them
102 if (FLAGS_SET(flags
, DNS_LABEL_LDH
) &&
103 !valid_ldh_char((char) k
))
106 last_char
= (char) k
;
116 } else if ((uint8_t) *n
>= (uint8_t) ' ' && *n
!= 127) {
118 /* Normal character */
120 if (FLAGS_SET(flags
, DNS_LABEL_LDH
)) {
121 if (!valid_ldh_char(*n
))
123 if (r
== 0 && *n
== '-')
138 /* Empty label that is not at the end? */
142 /* More than one trailing dot? */
143 if (n
[0] == '.' && !FLAGS_SET(flags
, DNS_LABEL_LEAVE_TRAILING_DOT
))
153 /* @label_terminal: terminal character of a label, updated to point to the terminal character of
154 * the previous label (always skipping one dot) or to NULL if there are no more
156 int dns_label_unescape_suffix(const char *name
, const char **label_terminal
, char *dest
, size_t sz
) {
157 const char *terminal
;
161 assert(label_terminal
);
165 if (!*label_terminal
) {
172 terminal
= *label_terminal
;
173 assert(IN_SET(*terminal
, 0, '.'));
175 /* Skip current terminal character (and accept domain names ending it ".") */
178 if (terminal
>= name
&& *terminal
== '.')
181 /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
183 if (terminal
< name
) {
184 /* Reached the first label, so indicate that there are no more */
189 /* Find the start of the last label */
190 if (*terminal
== '.') {
192 unsigned slashes
= 0;
194 for (y
= terminal
- 1; y
>= name
&& *y
== '\\'; y
--)
197 if (slashes
% 2 == 0) {
198 /* The '.' was not escaped */
210 r
= dns_label_unescape(&name
, dest
, sz
, 0);
214 *label_terminal
= terminal
;
219 int dns_label_escape(const char *p
, size_t l
, char *dest
, size_t sz
) {
222 /* DNS labels must be between 1 and 63 characters long. A
223 * zero-length label does not exist. See RFC 2182, Section
226 if (l
<= 0 || l
> DNS_LABEL_MAX
)
237 if (IN_SET(*p
, '.', '\\')) {
239 /* Dot or backslash */
249 } else if (IN_SET(*p
, '_', '-') ||
250 (*p
>= '0' && *p
<= '9') ||
251 (*p
>= 'a' && *p
<= 'z') ||
252 (*p
>= 'A' && *p
<= 'Z')) {
254 /* Proper character */
264 /* Everything else */
270 *(q
++) = '0' + (char) ((uint8_t) *p
/ 100);
271 *(q
++) = '0' + (char) (((uint8_t) *p
/ 10) % 10);
272 *(q
++) = '0' + (char) ((uint8_t) *p
% 10);
282 return (int) (q
- dest
);
285 int dns_label_escape_new(const char *p
, size_t l
, char **ret
) {
286 _cleanup_free_
char *s
= NULL
;
292 if (l
<= 0 || l
> DNS_LABEL_MAX
)
295 s
= new(char, DNS_LABEL_ESCAPED_MAX
);
299 r
= dns_label_escape(p
, l
, s
, DNS_LABEL_ESCAPED_MAX
);
309 int dns_label_apply_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
310 _cleanup_free_
uint32_t *input
= NULL
;
311 size_t input_size
, l
;
313 bool contains_8bit
= false;
314 char buffer
[DNS_LABEL_MAX
+1];
319 /* Converts an U-label into an A-label */
321 if (encoded_size
<= 0)
324 for (p
= encoded
; p
< encoded
+ encoded_size
; p
++)
325 if ((uint8_t) *p
> 127)
326 contains_8bit
= true;
328 if (!contains_8bit
) {
329 if (encoded_size
> DNS_LABEL_MAX
)
335 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
339 if (idna_to_ascii_4i(input
, input_size
, buffer
, 0) != 0)
344 /* Verify that the result is not longer than one DNS label. */
345 if (l
<= 0 || l
> DNS_LABEL_MAX
)
350 memcpy(decoded
, buffer
, l
);
352 /* If there's room, append a trailing NUL byte, but only then */
359 int dns_label_undo_idna(const char *encoded
, size_t encoded_size
, char *decoded
, size_t decoded_max
) {
360 size_t input_size
, output_size
;
361 _cleanup_free_
uint32_t *input
= NULL
;
362 _cleanup_free_
char *result
= NULL
;
363 uint32_t *output
= NULL
;
366 /* To be invoked after unescaping. Converts an A-label into an U-label. */
371 if (encoded_size
<= 0 || encoded_size
> DNS_LABEL_MAX
)
374 if (!memory_startswith(encoded
, encoded_size
, IDNA_ACE_PREFIX
))
377 input
= stringprep_utf8_to_ucs4(encoded
, encoded_size
, &input_size
);
381 output_size
= input_size
;
382 output
= newa(uint32_t, output_size
);
384 idna_to_unicode_44i(input
, input_size
, output
, &output_size
, 0);
386 result
= stringprep_ucs4_to_utf8(output
, output_size
, NULL
, &w
);
394 memcpy(decoded
, result
, w
);
396 /* Append trailing NUL byte if there's space, but only then. */
404 int dns_name_concat(const char *a
, const char *b
, DNSLabelFlags flags
, char **_ret
) {
405 _cleanup_free_
char *ret
= NULL
;
406 size_t n
= 0, allocated
= 0;
419 char label
[DNS_LABEL_MAX
];
421 r
= dns_label_unescape(&p
, label
, sizeof label
, flags
);
429 /* Now continue with the second string, if there is one */
438 if (!GREEDY_REALLOC(ret
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
441 r
= dns_label_escape(label
, r
, ret
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
448 char escaped
[DNS_LABEL_ESCAPED_MAX
];
450 r
= dns_label_escape(label
, r
, escaped
, sizeof(escaped
));
464 if (n
> DNS_HOSTNAME_MAX
)
469 /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
470 if (!GREEDY_REALLOC(ret
, allocated
, 2))
475 if (!GREEDY_REALLOC(ret
, allocated
, n
+ 1))
480 *_ret
= TAKE_PTR(ret
);
486 void dns_name_hash_func(const char *p
, struct siphash
*state
) {
492 char label
[DNS_LABEL_MAX
+1];
494 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
500 ascii_strlower_n(label
, r
);
501 siphash24_compress(label
, r
, state
);
502 siphash24_compress_byte(0, state
); /* make sure foobar and foo.bar result in different hashes */
505 /* enforce that all names are terminated by the empty label */
506 string_hash_func("", state
);
509 int dns_name_compare_func(const char *a
, const char *b
) {
520 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
522 if (x
== NULL
&& y
== NULL
)
525 r
= dns_label_unescape_suffix(a
, &x
, la
, sizeof(la
));
526 q
= dns_label_unescape_suffix(b
, &y
, lb
, sizeof(lb
));
530 r
= ascii_strcasecmp_nn(la
, r
, lb
, q
);
536 DEFINE_HASH_OPS(dns_name_hash_ops
, char, dns_name_hash_func
, dns_name_compare_func
);
538 int dns_name_equal(const char *x
, const char *y
) {
545 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
547 r
= dns_label_unescape(&x
, la
, sizeof la
, 0);
551 q
= dns_label_unescape(&y
, lb
, sizeof lb
, 0);
560 if (ascii_strcasecmp_n(la
, lb
, r
) != 0)
565 int dns_name_endswith(const char *name
, const char *suffix
) {
566 const char *n
, *s
, *saved_n
= NULL
;
576 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
578 r
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
585 q
= dns_label_unescape(&s
, ls
, sizeof ls
, 0);
589 if (r
== 0 && q
== 0)
591 if (r
== 0 && saved_n
== n
)
594 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
596 /* Not the same, let's jump back, and try with the next label again */
598 n
= TAKE_PTR(saved_n
);
603 int dns_name_startswith(const char *name
, const char *prefix
) {
614 char ln
[DNS_LABEL_MAX
], lp
[DNS_LABEL_MAX
];
616 r
= dns_label_unescape(&p
, lp
, sizeof lp
, 0);
622 q
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
628 if (ascii_strcasecmp_n(ln
, lp
, r
) != 0)
633 int dns_name_change_suffix(const char *name
, const char *old_suffix
, const char *new_suffix
, char **ret
) {
634 const char *n
, *s
, *saved_before
= NULL
, *saved_after
= NULL
, *prefix
;
646 char ln
[DNS_LABEL_MAX
], ls
[DNS_LABEL_MAX
];
651 r
= dns_label_unescape(&n
, ln
, sizeof ln
, 0);
658 q
= dns_label_unescape(&s
, ls
, sizeof ls
, 0);
662 if (r
== 0 && q
== 0)
664 if (r
== 0 && saved_after
== n
) {
665 *ret
= NULL
; /* doesn't match */
669 if (r
!= q
|| ascii_strcasecmp_n(ln
, ls
, r
) != 0) {
671 /* Not the same, let's jump back, and try with the next label again */
673 n
= TAKE_PTR(saved_after
);
678 /* Found it! Now generate the new name */
679 prefix
= strndupa(name
, saved_before
- name
);
681 r
= dns_name_concat(prefix
, new_suffix
, 0, ret
);
688 int dns_name_between(const char *a
, const char *b
, const char *c
) {
689 /* Determine if b is strictly greater than a and strictly smaller than c.
690 We consider the order of names to be circular, so that if a is
691 strictly greater than c, we consider b to be between them if it is
692 either greater than a or smaller than c. This is how the canonical
693 DNS name order used in NSEC records work. */
695 if (dns_name_compare_func(a
, c
) < 0)
697 a and c are properly ordered:
700 return dns_name_compare_func(a
, b
) < 0 &&
701 dns_name_compare_func(b
, c
) < 0;
704 a and c are equal or 'reversed':
709 return dns_name_compare_func(b
, c
) < 0 ||
710 dns_name_compare_func(a
, b
) < 0;
713 int dns_name_reverse(int family
, const union in_addr_union
*a
, char **ret
) {
720 p
= (const uint8_t*) a
;
722 if (family
== AF_INET
)
723 r
= asprintf(ret
, "%u.%u.%u.%u.in-addr.arpa", p
[3], p
[2], p
[1], p
[0]);
724 else if (family
== AF_INET6
)
725 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",
726 hexchar(p
[15] & 0xF), hexchar(p
[15] >> 4), hexchar(p
[14] & 0xF), hexchar(p
[14] >> 4),
727 hexchar(p
[13] & 0xF), hexchar(p
[13] >> 4), hexchar(p
[12] & 0xF), hexchar(p
[12] >> 4),
728 hexchar(p
[11] & 0xF), hexchar(p
[11] >> 4), hexchar(p
[10] & 0xF), hexchar(p
[10] >> 4),
729 hexchar(p
[ 9] & 0xF), hexchar(p
[ 9] >> 4), hexchar(p
[ 8] & 0xF), hexchar(p
[ 8] >> 4),
730 hexchar(p
[ 7] & 0xF), hexchar(p
[ 7] >> 4), hexchar(p
[ 6] & 0xF), hexchar(p
[ 6] >> 4),
731 hexchar(p
[ 5] & 0xF), hexchar(p
[ 5] >> 4), hexchar(p
[ 4] & 0xF), hexchar(p
[ 4] >> 4),
732 hexchar(p
[ 3] & 0xF), hexchar(p
[ 3] >> 4), hexchar(p
[ 2] & 0xF), hexchar(p
[ 2] >> 4),
733 hexchar(p
[ 1] & 0xF), hexchar(p
[ 1] >> 4), hexchar(p
[ 0] & 0xF), hexchar(p
[ 0] >> 4));
735 return -EAFNOSUPPORT
;
742 int dns_name_address(const char *p
, int *family
, union in_addr_union
*address
) {
749 r
= dns_name_endswith(p
, "in-addr.arpa");
756 for (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");
777 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");
792 for (i
= 0; i
< ELEMENTSOF(a
.s6_addr
); i
++) {
793 char label
[DNS_LABEL_MAX
+1];
796 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
801 x
= unhexchar(label
[0]);
805 r
= dns_label_unescape(&p
, label
, sizeof label
, 0);
810 y
= unhexchar(label
[0]);
814 a
.s6_addr
[ELEMENTSOF(a
.s6_addr
) - i
- 1] = (uint8_t) y
<< 4 | (uint8_t) x
;
817 r
= dns_name_equal(p
, "ip6.arpa");
829 bool dns_name_is_root(const char *name
) {
833 /* There are exactly two ways to encode the root domain name:
834 * as empty string, or with a single dot. */
836 return STR_IN_SET(name
, "", ".");
839 bool dns_name_is_single_label(const char *name
) {
844 r
= dns_name_parent(&name
);
848 return dns_name_is_root(name
);
851 /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
852 int dns_name_to_wire_format(const char *domain
, uint8_t *buffer
, size_t len
, bool canonical
) {
853 uint8_t *label_length
, *out
;
862 /* Reserve a byte for label length */
869 /* Convert and copy a single label. Note that
870 * dns_label_unescape() returns 0 when it hits the end
871 * of the domain name, which we rely on here to encode
872 * the trailing NUL byte. */
873 r
= dns_label_unescape(&domain
, (char *) out
, len
, 0);
877 /* Optionally, output the name in DNSSEC canonical
878 * format, as described in RFC 4034, section 6.2. Or
879 * in other words: in lower-case. */
881 ascii_strlower_n((char*) out
, (size_t) r
);
883 /* Fill label length, move forward */
890 /* Verify the maximum size of the encoded name. The trailing
891 * dot + NUL byte account are included this time, hence
892 * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this
894 if (out
- buffer
> DNS_HOSTNAME_MAX
+ 2)
900 static bool srv_type_label_is_valid(const char *label
, size_t n
) {
905 if (n
< 2) /* Label needs to be at least 2 chars long */
908 if (label
[0] != '_') /* First label char needs to be underscore */
911 /* Second char must be a letter */
912 if (!(label
[1] >= 'A' && label
[1] <= 'Z') &&
913 !(label
[1] >= 'a' && label
[1] <= 'z'))
916 /* Third and further chars must be alphanumeric or a hyphen */
917 for (k
= 2; k
< n
; k
++) {
918 if (!(label
[k
] >= 'A' && label
[k
] <= 'Z') &&
919 !(label
[k
] >= 'a' && label
[k
] <= 'z') &&
920 !(label
[k
] >= '0' && label
[k
] <= '9') &&
928 bool dns_srv_type_is_valid(const char *name
) {
936 char label
[DNS_LABEL_MAX
];
938 /* This more or less implements RFC 6335, Section 5.1 */
940 r
= dns_label_unescape(&name
, label
, sizeof label
, 0);
949 if (!srv_type_label_is_valid(label
, r
))
955 return c
== 2; /* exactly two labels */
958 bool dnssd_srv_type_is_valid(const char *name
) {
959 return dns_srv_type_is_valid(name
) &&
960 ((dns_name_endswith(name
, "_tcp") > 0) ||
961 (dns_name_endswith(name
, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
964 bool dns_service_name_is_valid(const char *name
) {
967 /* This more or less implements RFC 6763, Section 4.1.1 */
972 if (!utf8_is_valid(name
))
975 if (string_has_cc(name
, NULL
))
987 int dns_service_join(const char *name
, const char *type
, const char *domain
, char **ret
) {
988 char escaped
[DNS_LABEL_ESCAPED_MAX
];
989 _cleanup_free_
char *n
= NULL
;
996 if (!dns_srv_type_is_valid(type
))
1000 return dns_name_concat(type
, domain
, 0, ret
);
1002 if (!dns_service_name_is_valid(name
))
1005 r
= dns_label_escape(name
, strlen(name
), escaped
, sizeof(escaped
));
1009 r
= dns_name_concat(type
, domain
, 0, &n
);
1013 return dns_name_concat(escaped
, n
, 0, ret
);
1016 static bool dns_service_name_label_is_valid(const char *label
, size_t n
) {
1021 if (memchr(label
, 0, n
))
1024 s
= strndupa(label
, n
);
1025 return dns_service_name_is_valid(s
);
1028 int dns_service_split(const char *joined
, char **_name
, char **_type
, char **_domain
) {
1029 _cleanup_free_
char *name
= NULL
, *type
= NULL
, *domain
= NULL
;
1030 const char *p
= joined
, *q
= NULL
, *d
= NULL
;
1031 char a
[DNS_LABEL_MAX
], b
[DNS_LABEL_MAX
], c
[DNS_LABEL_MAX
];
1037 /* Get first label from the full name */
1038 an
= dns_label_unescape(&p
, a
, sizeof(a
), 0);
1045 /* If there was a first label, try to get the second one */
1046 bn
= dns_label_unescape(&p
, b
, sizeof(b
), 0);
1053 /* If there was a second label, try to get the third one */
1055 cn
= dns_label_unescape(&p
, c
, sizeof(c
), 0);
1066 if (x
>= 2 && srv_type_label_is_valid(b
, bn
)) {
1068 if (x
>= 3 && srv_type_label_is_valid(c
, cn
)) {
1070 if (dns_service_name_label_is_valid(a
, an
)) {
1071 /* OK, got <name> . <type> . <type2> . <domain> */
1073 name
= strndup(a
, an
);
1077 type
= strjoin(b
, ".", c
);
1085 } else if (srv_type_label_is_valid(a
, an
)) {
1087 /* OK, got <type> . <type2> . <domain> */
1091 type
= strjoin(a
, ".", b
);
1105 r
= dns_name_normalize(d
, 0, &domain
);
1110 *_domain
= TAKE_PTR(domain
);
1113 *_type
= TAKE_PTR(type
);
1116 *_name
= TAKE_PTR(name
);
1121 static int dns_name_build_suffix_table(const char *name
, const char *table
[]) {
1131 if (n
> DNS_N_LABELS_MAX
)
1135 r
= dns_name_parent(&p
);
1147 int dns_name_suffix(const char *name
, unsigned n_labels
, const char **ret
) {
1148 const char* labels
[DNS_N_LABELS_MAX
+1];
1154 n
= dns_name_build_suffix_table(name
, labels
);
1158 if ((unsigned) n
< n_labels
)
1161 *ret
= labels
[n
- n_labels
];
1162 return (int) (n
- n_labels
);
1165 int dns_name_skip(const char *a
, unsigned n_labels
, const char **ret
) {
1171 for (; n_labels
> 0; n_labels
--) {
1172 r
= dns_name_parent(&a
);
1185 int dns_name_count_labels(const char *name
) {
1194 r
= dns_name_parent(&p
);
1200 if (n
>= DNS_N_LABELS_MAX
)
1209 int dns_name_equal_skip(const char *a
, unsigned n_labels
, const char *b
) {
1215 r
= dns_name_skip(a
, n_labels
, &a
);
1219 return dns_name_equal(a
, b
);
1222 int dns_name_common_suffix(const char *a
, const char *b
, const char **ret
) {
1223 const char *a_labels
[DNS_N_LABELS_MAX
+1], *b_labels
[DNS_N_LABELS_MAX
+1];
1224 int n
= 0, m
= 0, k
= 0, r
, q
;
1230 /* Determines the common suffix of domain names a and b */
1232 n
= dns_name_build_suffix_table(a
, a_labels
);
1236 m
= dns_name_build_suffix_table(b
, b_labels
);
1241 char la
[DNS_LABEL_MAX
], lb
[DNS_LABEL_MAX
];
1244 if (k
>= n
|| k
>= m
) {
1245 *ret
= a_labels
[n
- k
];
1249 x
= a_labels
[n
- 1 - k
];
1250 r
= dns_label_unescape(&x
, la
, sizeof la
, 0);
1254 y
= b_labels
[m
- 1 - k
];
1255 q
= dns_label_unescape(&y
, lb
, sizeof lb
, 0);
1259 if (r
!= q
|| ascii_strcasecmp_n(la
, lb
, r
) != 0) {
1260 *ret
= a_labels
[n
- k
];
1268 int dns_name_apply_idna(const char *name
, char **ret
) {
1269 /* Return negative on error, 0 if not implemented, positive on success. */
1273 _cleanup_free_
char *t
= NULL
;
1278 r
= idn2_lookup_u8((uint8_t*) name
, (uint8_t**) &t
,
1279 IDN2_NFC_INPUT
| IDN2_NONTRANSITIONAL
);
1280 log_debug("idn2_lookup_u8: %s → %s", name
, t
);
1282 if (!startswith(name
, "xn--")) {
1283 _cleanup_free_
char *s
= NULL
;
1285 r
= idn2_to_unicode_8z8z(t
, &s
, 0);
1287 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
1288 t
, r
, idn2_strerror(r
));
1292 if (!streq_ptr(name
, s
)) {
1293 log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
1301 return 1; /* *ret has been written */
1304 log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name
, r
, idn2_strerror(r
));
1305 if (r
== IDN2_2HYPHEN
)
1306 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
1308 if (IN_SET(r
, IDN2_TOO_BIG_DOMAIN
, IDN2_TOO_BIG_LABEL
))
1312 _cleanup_free_
char *buf
= NULL
;
1313 size_t n
= 0, allocated
= 0;
1321 char label
[DNS_LABEL_MAX
];
1323 r
= dns_label_unescape(&name
, label
, sizeof label
, 0);
1329 q
= dns_label_apply_idna(label
, r
, label
, sizeof label
);
1335 if (!GREEDY_REALLOC(buf
, allocated
, n
+ !first
+ DNS_LABEL_ESCAPED_MAX
))
1338 r
= dns_label_escape(label
, r
, buf
+ n
+ !first
, DNS_LABEL_ESCAPED_MAX
);
1350 if (n
> DNS_HOSTNAME_MAX
)
1353 if (!GREEDY_REALLOC(buf
, allocated
, n
+ 1))
1357 *ret
= TAKE_PTR(buf
);
1365 int dns_name_is_valid_or_address(const char *name
) {
1366 /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
1371 if (in_addr_from_string_auto(name
, NULL
, NULL
) >= 0)
1374 return dns_name_is_valid(name
);
1377 int dns_name_dot_suffixed(const char *name
) {
1378 const char *p
= name
;
1385 r
= dns_label_unescape(&p
, NULL
, DNS_LABEL_MAX
, DNS_LABEL_LEAVE_TRAILING_DOT
);