LCOV - code coverage report
Current view: top level - shared - dns-domain.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 604 669 90.3 %
Date: 2019-08-22 15:41:25 Functions: 32 32 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #if HAVE_LIBIDN2
       4             : #  include <idn2.h>
       5             : #elif HAVE_LIBIDN
       6             : #  include <idna.h>
       7             : #  include <stringprep.h>
       8             : #endif
       9             : 
      10             : #include <endian.h>
      11             : #include <netinet/in.h>
      12             : #include <stdio.h>
      13             : #include <string.h>
      14             : #include <sys/socket.h>
      15             : 
      16             : #include "alloc-util.h"
      17             : #include "dns-domain.h"
      18             : #include "hashmap.h"
      19             : #include "hexdecoct.h"
      20             : #include "hostname-util.h"
      21             : #include "in-addr-util.h"
      22             : #include "macro.h"
      23             : #include "parse-util.h"
      24             : #include "string-util.h"
      25             : #include "strv.h"
      26             : #include "utf8.h"
      27             : 
      28        9006 : int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
      29             :         const char *n;
      30        9006 :         char *d, last_char = 0;
      31        9006 :         int r = 0;
      32             : 
      33        9006 :         assert(name);
      34        9006 :         assert(*name);
      35             : 
      36        9006 :         n = *name;
      37        9006 :         d = dest;
      38             : 
      39             :         for (;;) {
      40       47283 :                 if (IN_SET(*n, 0, '.')) {
      41        8963 :                         if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
      42             :                                 /* Trailing dash */
      43           8 :                                 return -EINVAL;
      44             : 
      45        8955 :                         if (*n == '.')
      46        4603 :                                 n++;
      47        8955 :                         break;
      48             :                 }
      49             : 
      50       38320 :                 if (r >= DNS_LABEL_MAX)
      51           2 :                         return -EINVAL;
      52             : 
      53       38318 :                 if (sz <= 0)
      54           5 :                         return -ENOBUFS;
      55             : 
      56       38313 :                 if (*n == '\\') {
      57             :                         /* Escaped character */
      58          57 :                         if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
      59           6 :                                 return -EINVAL;
      60             : 
      61          51 :                         n++;
      62             : 
      63          51 :                         if (*n == 0)
      64             :                                 /* Ending NUL */
      65           4 :                                 return -EINVAL;
      66             : 
      67          47 :                         else if (IN_SET(*n, '\\', '.')) {
      68             :                                 /* Escaped backslash or dot */
      69             : 
      70           8 :                                 if (FLAGS_SET(flags, DNS_LABEL_LDH))
      71           1 :                                         return -EINVAL;
      72             : 
      73           7 :                                 last_char = *n;
      74           7 :                                 if (d)
      75           7 :                                         *(d++) = *n;
      76           7 :                                 sz--;
      77           7 :                                 r++;
      78           7 :                                 n++;
      79             : 
      80          39 :                         } else if (n[0] >= '0' && n[0] <= '9') {
      81             :                                 unsigned k;
      82             : 
      83             :                                 /* Escaped literal ASCII character */
      84             : 
      85          38 :                                 if (!(n[1] >= '0' && n[1] <= '9') ||
      86          38 :                                     !(n[2] >= '0' && n[2] <= '9'))
      87           0 :                                         return -EINVAL;
      88             : 
      89         114 :                                 k = ((unsigned) (n[0] - '0') * 100) +
      90          76 :                                         ((unsigned) (n[1] - '0') * 10) +
      91          38 :                                         ((unsigned) (n[2] - '0'));
      92             : 
      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
      98             :                                  * inside. */
      99          38 :                                 if (k > 255)
     100           0 :                                         return -EINVAL;
     101             : 
     102          38 :                                 if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
     103           3 :                                     !valid_ldh_char((char) k))
     104           2 :                                         return -EINVAL;
     105             : 
     106          36 :                                 last_char = (char) k;
     107          36 :                                 if (d)
     108          36 :                                         *(d++) = (char) k;
     109          36 :                                 sz--;
     110          36 :                                 r++;
     111             : 
     112          36 :                                 n += 3;
     113             :                         } else
     114           1 :                                 return -EINVAL;
     115             : 
     116       38256 :                 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
     117             : 
     118             :                         /* Normal character */
     119             : 
     120       38246 :                         if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
     121        1647 :                                 if (!valid_ldh_char(*n))
     122           2 :                                         return -EINVAL;
     123        1645 :                                 if (r == 0 && *n == '-')
     124             :                                         /* Leading dash */
     125          10 :                                         return -EINVAL;
     126             :                         }
     127             : 
     128       38234 :                         last_char = *n;
     129       38234 :                         if (d)
     130       37886 :                                 *(d++) = *n;
     131       38234 :                         sz--;
     132       38234 :                         r++;
     133       38234 :                         n++;
     134             :                 } else
     135          10 :                         return -EINVAL;
     136             :         }
     137             : 
     138             :         /* Empty label that is not at the end? */
     139        8955 :         if (r == 0 && *n)
     140          18 :                 return -EINVAL;
     141             : 
     142             :         /* More than one trailing dot? */
     143        8937 :         if (*n == '.')
     144          18 :                 return -EINVAL;
     145             : 
     146        8919 :         if (sz >= 1 && d)
     147        8750 :                 *d = 0;
     148             : 
     149        8919 :         *name = n;
     150        8919 :         return r;
     151             : }
     152             : 
     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
     155             :  *                  labels. */
     156         584 : int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) {
     157             :         const char *terminal;
     158             :         int r;
     159             : 
     160         584 :         assert(name);
     161         584 :         assert(label_terminal);
     162         584 :         assert(dest);
     163             : 
     164             :         /* no more labels */
     165         584 :         if (!*label_terminal) {
     166          45 :                 if (sz >= 1)
     167          45 :                         *dest = 0;
     168             : 
     169          45 :                 return 0;
     170             :         }
     171             : 
     172         539 :         terminal = *label_terminal;
     173         539 :         assert(IN_SET(*terminal, 0, '.'));
     174             : 
     175             :         /* Skip current terminal character (and accept domain names ending it ".") */
     176         539 :         if (*terminal == 0)
     177         302 :                 terminal--;
     178         539 :         if (terminal >= name && *terminal == '.')
     179         248 :                 terminal--;
     180             : 
     181             :         /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
     182             :         for (;;) {
     183        3145 :                 if (terminal < name) {
     184             :                         /* Reached the first label, so indicate that there are no more */
     185         227 :                         terminal = NULL;
     186         227 :                         break;
     187             :                 }
     188             : 
     189             :                 /* Find the start of the last label */
     190        2918 :                 if (*terminal == '.') {
     191             :                         const char *y;
     192         315 :                         unsigned slashes = 0;
     193             : 
     194         322 :                         for (y = terminal - 1; y >= name && *y == '\\'; y--)
     195           7 :                                 slashes++;
     196             : 
     197         315 :                         if (slashes % 2 == 0) {
     198             :                                 /* The '.' was not escaped */
     199         312 :                                 name = terminal + 1;
     200         312 :                                 break;
     201             :                         } else {
     202           3 :                                 terminal = y;
     203           3 :                                 continue;
     204             :                         }
     205             :                 }
     206             : 
     207        2603 :                 terminal--;
     208             :         }
     209             : 
     210         539 :         r = dns_label_unescape(&name, dest, sz, 0);
     211         539 :         if (r < 0)
     212          10 :                 return r;
     213             : 
     214         529 :         *label_terminal = terminal;
     215             : 
     216         529 :         return r;
     217             : }
     218             : 
     219        2393 : int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
     220             :         char *q;
     221             : 
     222             :         /* DNS labels must be between 1 and 63 characters long. A
     223             :          * zero-length label does not exist. See RFC 2182, Section
     224             :          * 11. */
     225             : 
     226        2393 :         if (l <= 0 || l > DNS_LABEL_MAX)
     227           0 :                 return -EINVAL;
     228        2393 :         if (sz < 1)
     229           0 :                 return -ENOBUFS;
     230             : 
     231        2393 :         assert(p);
     232        2393 :         assert(dest);
     233             : 
     234        2393 :         q = dest;
     235       16823 :         while (l > 0) {
     236             : 
     237       14430 :                 if (IN_SET(*p, '.', '\\')) {
     238             : 
     239             :                         /* Dot or backslash */
     240             : 
     241           1 :                         if (sz < 3)
     242           0 :                                 return -ENOBUFS;
     243             : 
     244           1 :                         *(q++) = '\\';
     245           1 :                         *(q++) = *p;
     246             : 
     247           1 :                         sz -= 2;
     248             : 
     249       14429 :                 } else if (IN_SET(*p, '_', '-') ||
     250       14238 :                            (*p >= '0' && *p <= '9') ||
     251       11613 :                            (*p >= 'a' && *p <= 'z') ||
     252         159 :                            (*p >= 'A' && *p <= 'Z')) {
     253             : 
     254             :                         /* Proper character */
     255             : 
     256       14415 :                         if (sz < 2)
     257           0 :                                 return -ENOBUFS;
     258             : 
     259       14415 :                         *(q++) = *p;
     260       14415 :                         sz -= 1;
     261             : 
     262             :                 } else {
     263             : 
     264             :                         /* Everything else */
     265             : 
     266          14 :                         if (sz < 5)
     267           0 :                                 return -ENOBUFS;
     268             : 
     269          14 :                         *(q++) = '\\';
     270          14 :                         *(q++) = '0' + (char) ((uint8_t) *p / 100);
     271          14 :                         *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
     272          14 :                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
     273             : 
     274          14 :                         sz -= 4;
     275             :                 }
     276             : 
     277       14430 :                 p++;
     278       14430 :                 l--;
     279             :         }
     280             : 
     281        2393 :         *q = 0;
     282        2393 :         return (int) (q - dest);
     283             : }
     284             : 
     285           4 : int dns_label_escape_new(const char *p, size_t l, char **ret) {
     286           4 :         _cleanup_free_ char *s = NULL;
     287             :         int r;
     288             : 
     289           4 :         assert(p);
     290           4 :         assert(ret);
     291             : 
     292           4 :         if (l <= 0 || l > DNS_LABEL_MAX)
     293           1 :                 return -EINVAL;
     294             : 
     295           3 :         s = new(char, DNS_LABEL_ESCAPED_MAX);
     296           3 :         if (!s)
     297           0 :                 return -ENOMEM;
     298             : 
     299           3 :         r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
     300           3 :         if (r < 0)
     301           0 :                 return r;
     302             : 
     303           3 :         *ret = TAKE_PTR(s);
     304             : 
     305           3 :         return r;
     306             : }
     307             : 
     308             : #if HAVE_LIBIDN
     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;
     312             :         const char *p;
     313             :         bool contains_8bit = false;
     314             :         char buffer[DNS_LABEL_MAX+1];
     315             : 
     316             :         assert(encoded);
     317             :         assert(decoded);
     318             : 
     319             :         /* Converts an U-label into an A-label */
     320             : 
     321             :         if (encoded_size <= 0)
     322             :                 return -EINVAL;
     323             : 
     324             :         for (p = encoded; p < encoded + encoded_size; p++)
     325             :                 if ((uint8_t) *p > 127)
     326             :                         contains_8bit = true;
     327             : 
     328             :         if (!contains_8bit) {
     329             :                 if (encoded_size > DNS_LABEL_MAX)
     330             :                         return -EINVAL;
     331             : 
     332             :                 return 0;
     333             :         }
     334             : 
     335             :         input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
     336             :         if (!input)
     337             :                 return -ENOMEM;
     338             : 
     339             :         if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0)
     340             :                 return -EINVAL;
     341             : 
     342             :         l = strlen(buffer);
     343             : 
     344             :         /* Verify that the result is not longer than one DNS label. */
     345             :         if (l <= 0 || l > DNS_LABEL_MAX)
     346             :                 return -EINVAL;
     347             :         if (l > decoded_max)
     348             :                 return -ENOBUFS;
     349             : 
     350             :         memcpy(decoded, buffer, l);
     351             : 
     352             :         /* If there's room, append a trailing NUL byte, but only then */
     353             :         if (decoded_max > l)
     354             :                 decoded[l] = 0;
     355             : 
     356             :         return (int) l;
     357             : }
     358             : 
     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;
     364             :         size_t w;
     365             : 
     366             :         /* To be invoked after unescaping. Converts an A-label into an U-label. */
     367             : 
     368             :         assert(encoded);
     369             :         assert(decoded);
     370             : 
     371             :         if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX)
     372             :                 return -EINVAL;
     373             : 
     374             :         if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX))
     375             :                 return 0;
     376             : 
     377             :         input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size);
     378             :         if (!input)
     379             :                 return -ENOMEM;
     380             : 
     381             :         output_size = input_size;
     382             :         output = newa(uint32_t, output_size);
     383             : 
     384             :         idna_to_unicode_44i(input, input_size, output, &output_size, 0);
     385             : 
     386             :         result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w);
     387             :         if (!result)
     388             :                 return -ENOMEM;
     389             :         if (w <= 0)
     390             :                 return -EINVAL;
     391             :         if (w > decoded_max)
     392             :                 return -ENOBUFS;
     393             : 
     394             :         memcpy(decoded, result, w);
     395             : 
     396             :         /* Append trailing NUL byte if there's space, but only then. */
     397             :         if (decoded_max > w)
     398             :                 decoded[w] = 0;
     399             : 
     400             :         return w;
     401             : }
     402             : #endif
     403             : 
     404         193 : int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
     405         193 :         _cleanup_free_ char *ret = NULL;
     406         193 :         size_t n = 0, allocated = 0;
     407             :         const char *p;
     408         193 :         bool first = true;
     409             :         int r;
     410             : 
     411         193 :         if (a)
     412         190 :                 p = a;
     413           3 :         else if (b)
     414           2 :                 p = TAKE_PTR(b);
     415             :         else
     416           1 :                 goto finish;
     417             : 
     418         531 :         for (;;) {
     419             :                 char label[DNS_LABEL_MAX];
     420             : 
     421         723 :                 r = dns_label_unescape(&p, label, sizeof label, flags);
     422         723 :                 if (r < 0)
     423          37 :                         return r;
     424         686 :                 if (r == 0) {
     425         185 :                         if (*p != 0)
     426           0 :                                 return -EINVAL;
     427             : 
     428         185 :                         if (b) {
     429             :                                 /* Now continue with the second string, if there is one */
     430          30 :                                 p = TAKE_PTR(b);
     431          30 :                                 continue;
     432             :                         }
     433             : 
     434         155 :                         break;
     435             :                 }
     436             : 
     437         501 :                 if (_ret) {
     438         118 :                         if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
     439           0 :                                 return -ENOMEM;
     440             : 
     441         118 :                         r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
     442         118 :                         if (r < 0)
     443           0 :                                 return r;
     444             : 
     445         118 :                         if (!first)
     446          72 :                                 ret[n] = '.';
     447             :                 } else {
     448             :                         char escaped[DNS_LABEL_ESCAPED_MAX];
     449             : 
     450         383 :                         r = dns_label_escape(label, r, escaped, sizeof(escaped));
     451         383 :                         if (r < 0)
     452           0 :                                 return r;
     453             :                 }
     454             : 
     455         501 :                 if (!first)
     456         351 :                         n++;
     457             :                 else
     458         150 :                         first = false;
     459             : 
     460         501 :                 n += r;
     461             :         }
     462             : 
     463         156 : finish:
     464         156 :         if (n > DNS_HOSTNAME_MAX)
     465           6 :                 return -EINVAL;
     466             : 
     467         150 :         if (_ret) {
     468          60 :                 if (n == 0) {
     469             :                         /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
     470          14 :                         if (!GREEDY_REALLOC(ret, allocated, 2))
     471           0 :                                 return -ENOMEM;
     472             : 
     473          14 :                         ret[n++] = '.';
     474             :                 } else {
     475          46 :                         if (!GREEDY_REALLOC(ret, allocated, n + 1))
     476           0 :                                 return -ENOMEM;
     477             :                 }
     478             : 
     479          60 :                 ret[n] = 0;
     480          60 :                 *_ret = TAKE_PTR(ret);
     481             :         }
     482             : 
     483         150 :         return 0;
     484             : }
     485             : 
     486         865 : void dns_name_hash_func(const char *p, struct siphash *state) {
     487             :         int r;
     488             : 
     489         865 :         assert(p);
     490             : 
     491        2030 :         for (;;) {
     492             :                 char label[DNS_LABEL_MAX+1];
     493             : 
     494        2895 :                 r = dns_label_unescape(&p, label, sizeof label, 0);
     495        2895 :                 if (r < 0)
     496           0 :                         break;
     497        2895 :                 if (r == 0)
     498         865 :                         break;
     499             : 
     500        2030 :                 ascii_strlower_n(label, r);
     501        2030 :                 siphash24_compress(label, r, state);
     502        2030 :                 siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */
     503             :         }
     504             : 
     505             :         /* enforce that all names are terminated by the empty label */
     506         865 :         string_hash_func("", state);
     507         865 : }
     508             : 
     509         140 : int dns_name_compare_func(const char *a, const char *b) {
     510             :         const char *x, *y;
     511             :         int r, q;
     512             : 
     513         140 :         assert(a);
     514         140 :         assert(b);
     515             : 
     516         140 :         x = a + strlen(a);
     517         140 :         y = b + strlen(b);
     518             : 
     519         169 :         for (;;) {
     520             :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
     521             : 
     522         309 :                 if (x == NULL && y == NULL)
     523         140 :                         return 0;
     524             : 
     525         273 :                 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
     526         273 :                 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
     527         273 :                 if (r < 0 || q < 0)
     528           0 :                         return CMP(r, q);
     529             : 
     530         273 :                 r = ascii_strcasecmp_nn(la, r, lb, q);
     531         273 :                 if (r != 0)
     532         104 :                         return r;
     533             :         }
     534             : }
     535             : 
     536             : DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
     537             : 
     538         366 : int dns_name_equal(const char *x, const char *y) {
     539             :         int r, q;
     540             : 
     541         366 :         assert(x);
     542         366 :         assert(y);
     543             : 
     544        1017 :         for (;;) {
     545             :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
     546             : 
     547        1383 :                 r = dns_label_unescape(&x, la, sizeof la, 0);
     548        1383 :                 if (r < 0)
     549         366 :                         return r;
     550             : 
     551        1381 :                 q = dns_label_unescape(&y, lb, sizeof lb, 0);
     552        1381 :                 if (q < 0)
     553           0 :                         return q;
     554             : 
     555        1381 :                 if (r != q)
     556           9 :                         return false;
     557        1372 :                 if (r == 0)
     558         350 :                         return true;
     559             : 
     560        1022 :                 if (ascii_strcasecmp_n(la, lb, r) != 0)
     561           5 :                         return false;
     562             :         }
     563             : }
     564             : 
     565          29 : int dns_name_endswith(const char *name, const char *suffix) {
     566          29 :         const char *n, *s, *saved_n = NULL;
     567             :         int r, q;
     568             : 
     569          29 :         assert(name);
     570          29 :         assert(suffix);
     571             : 
     572          29 :         n = name;
     573          29 :         s = suffix;
     574             : 
     575         201 :         for (;;) {
     576             :                 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
     577             : 
     578         230 :                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
     579         230 :                 if (r < 0)
     580          29 :                         return r;
     581             : 
     582         229 :                 if (!saved_n)
     583         201 :                         saved_n = n;
     584             : 
     585         229 :                 q = dns_label_unescape(&s, ls, sizeof ls, 0);
     586         229 :                 if (q < 0)
     587           0 :                         return q;
     588             : 
     589         229 :                 if (r == 0 && q == 0)
     590          18 :                         return true;
     591         211 :                 if (r == 0 && saved_n == n)
     592          10 :                         return false;
     593             : 
     594         201 :                 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
     595             : 
     596             :                         /* Not the same, let's jump back, and try with the next label again */
     597         173 :                         s = suffix;
     598         173 :                         n = TAKE_PTR(saved_n);
     599             :                 }
     600             :         }
     601             : }
     602             : 
     603          12 : int dns_name_startswith(const char *name, const char *prefix) {
     604             :         const char *n, *p;
     605             :         int r, q;
     606             : 
     607          12 :         assert(name);
     608          12 :         assert(prefix);
     609             : 
     610          12 :         n = name;
     611          12 :         p = prefix;
     612             : 
     613           6 :         for (;;) {
     614             :                 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
     615             : 
     616          18 :                 r = dns_label_unescape(&p, lp, sizeof lp, 0);
     617          18 :                 if (r < 0)
     618          12 :                         return r;
     619          18 :                 if (r == 0)
     620           8 :                         return true;
     621             : 
     622          10 :                 q = dns_label_unescape(&n, ln, sizeof ln, 0);
     623          10 :                 if (q < 0)
     624           0 :                         return q;
     625             : 
     626          10 :                 if (r != q)
     627           1 :                         return false;
     628           9 :                 if (ascii_strcasecmp_n(ln, lp, r) != 0)
     629           3 :                         return false;
     630             :         }
     631             : }
     632             : 
     633           9 : int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
     634           9 :         const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
     635             :         int r, q;
     636             : 
     637           9 :         assert(name);
     638           9 :         assert(old_suffix);
     639           9 :         assert(new_suffix);
     640           9 :         assert(ret);
     641             : 
     642           9 :         n = name;
     643           9 :         s = old_suffix;
     644             : 
     645          23 :         for (;;) {
     646             :                 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
     647             : 
     648          32 :                 if (!saved_before)
     649          21 :                         saved_before = n;
     650             : 
     651          32 :                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
     652          32 :                 if (r < 0)
     653           1 :                         return r;
     654             : 
     655          32 :                 if (!saved_after)
     656          21 :                         saved_after = n;
     657             : 
     658          32 :                 q = dns_label_unescape(&s, ls, sizeof ls, 0);
     659          32 :                 if (q < 0)
     660           0 :                         return q;
     661             : 
     662          32 :                 if (r == 0 && q == 0)
     663           8 :                         break;
     664          24 :                 if (r == 0 && saved_after == n) {
     665           1 :                         *ret = NULL; /* doesn't match */
     666           1 :                         return 0;
     667             :                 }
     668             : 
     669          23 :                 if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
     670             : 
     671             :                         /* Not the same, let's jump back, and try with the next label again */
     672          12 :                         s = old_suffix;
     673          12 :                         n = TAKE_PTR(saved_after);
     674          12 :                         saved_before = NULL;
     675             :                 }
     676             :         }
     677             : 
     678             :         /* Found it! Now generate the new name */
     679           8 :         prefix = strndupa(name, saved_before - name);
     680             : 
     681           8 :         r = dns_name_concat(prefix, new_suffix, 0, ret);
     682           8 :         if (r < 0)
     683           0 :                 return r;
     684             : 
     685           8 :         return 1;
     686             : }
     687             : 
     688          28 : 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. */
     694             : 
     695          28 :         if (dns_name_compare_func(a, c) < 0)
     696             :                 /*
     697             :                    a and c are properly ordered:
     698             :                    a<---b--->c
     699             :                 */
     700          22 :                 return dns_name_compare_func(a, b) < 0 &&
     701          10 :                        dns_name_compare_func(b, c) < 0;
     702             :         else
     703             :                 /*
     704             :                    a and c are equal or 'reversed':
     705             :                    <--b--c         a----->
     706             :                    or:
     707             :                    <-----c         a--b-->
     708             :                 */
     709          31 :                 return dns_name_compare_func(b, c) < 0 ||
     710          15 :                        dns_name_compare_func(a, b) < 0;
     711             : }
     712             : 
     713           4 : int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
     714             :         const uint8_t *p;
     715             :         int r;
     716             : 
     717           4 :         assert(a);
     718           4 :         assert(ret);
     719             : 
     720           4 :         p = (const uint8_t*) a;
     721             : 
     722           4 :         if (family == AF_INET)
     723           2 :                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
     724           2 :         else if (family == AF_INET6)
     725          64 :                 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           8 :                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
     727           8 :                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
     728           8 :                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
     729           8 :                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
     730           8 :                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
     731           8 :                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
     732           8 :                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
     733           2 :                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
     734             :         else
     735           0 :                 return -EAFNOSUPPORT;
     736           4 :         if (r < 0)
     737           0 :                 return -ENOMEM;
     738             : 
     739           4 :         return 0;
     740             : }
     741             : 
     742           4 : int dns_name_address(const char *p, int *family, union in_addr_union *address) {
     743             :         int r;
     744             : 
     745           4 :         assert(p);
     746           4 :         assert(family);
     747           4 :         assert(address);
     748             : 
     749           4 :         r = dns_name_endswith(p, "in-addr.arpa");
     750           4 :         if (r < 0)
     751           0 :                 return r;
     752           4 :         if (r > 0) {
     753             :                 uint8_t a[4];
     754             :                 unsigned i;
     755             : 
     756          10 :                 for (i = 0; i < ELEMENTSOF(a); i++) {
     757             :                         char label[DNS_LABEL_MAX+1];
     758             : 
     759           8 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     760           8 :                         if (r < 0)
     761           0 :                                 return r;
     762           8 :                         if (r == 0)
     763           0 :                                 return -EINVAL;
     764           8 :                         if (r > 3)
     765           0 :                                 return -EINVAL;
     766             : 
     767           8 :                         r = safe_atou8(label, &a[i]);
     768           8 :                         if (r < 0)
     769           0 :                                 return r;
     770             :                 }
     771             : 
     772           2 :                 r = dns_name_equal(p, "in-addr.arpa");
     773           2 :                 if (r <= 0)
     774           0 :                         return r;
     775             : 
     776           2 :                 *family = AF_INET;
     777           2 :                 address->in.s_addr = htobe32(((uint32_t) a[3] << 24) |
     778             :                                              ((uint32_t) a[2] << 16) |
     779             :                                              ((uint32_t) a[1] << 8) |
     780             :                                               (uint32_t) a[0]);
     781             : 
     782           2 :                 return 1;
     783             :         }
     784             : 
     785           2 :         r = dns_name_endswith(p, "ip6.arpa");
     786           2 :         if (r < 0)
     787           0 :                 return r;
     788           2 :         if (r > 0) {
     789             :                 struct in6_addr a;
     790             :                 unsigned i;
     791             : 
     792          34 :                 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
     793             :                         char label[DNS_LABEL_MAX+1];
     794             :                         int x, y;
     795             : 
     796          32 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     797          32 :                         if (r <= 0)
     798           0 :                                 return r;
     799          32 :                         if (r != 1)
     800           0 :                                 return -EINVAL;
     801          32 :                         x = unhexchar(label[0]);
     802          32 :                         if (x < 0)
     803           0 :                                 return -EINVAL;
     804             : 
     805          32 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     806          32 :                         if (r <= 0)
     807           0 :                                 return r;
     808          32 :                         if (r != 1)
     809           0 :                                 return -EINVAL;
     810          32 :                         y = unhexchar(label[0]);
     811          32 :                         if (y < 0)
     812           0 :                                 return -EINVAL;
     813             : 
     814          32 :                         a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
     815             :                 }
     816             : 
     817           2 :                 r = dns_name_equal(p, "ip6.arpa");
     818           2 :                 if (r <= 0)
     819           0 :                         return r;
     820             : 
     821           2 :                 *family = AF_INET6;
     822           2 :                 address->in6 = a;
     823           2 :                 return 1;
     824             :         }
     825             : 
     826           0 :         return 0;
     827             : }
     828             : 
     829        2820 : bool dns_name_is_root(const char *name) {
     830             : 
     831        2820 :         assert(name);
     832             : 
     833             :         /* There are exactly two ways to encode the root domain name:
     834             :          * as empty string, or with a single dot. */
     835             : 
     836        2820 :         return STR_IN_SET(name, "", ".");
     837             : }
     838             : 
     839           8 : bool dns_name_is_single_label(const char *name) {
     840             :         int r;
     841             : 
     842           8 :         assert(name);
     843             : 
     844           8 :         r = dns_name_parent(&name);
     845           8 :         if (r <= 0)
     846           3 :                 return false;
     847             : 
     848           5 :         return dns_name_is_root(name);
     849             : }
     850             : 
     851             : /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
     852          25 : int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) {
     853             :         uint8_t *label_length, *out;
     854             :         int r;
     855             : 
     856          25 :         assert(domain);
     857          25 :         assert(buffer);
     858             : 
     859          25 :         out = buffer;
     860             : 
     861             :         do {
     862             :                 /* Reserve a byte for label length */
     863         120 :                 if (len <= 0)
     864           1 :                         return -ENOBUFS;
     865         119 :                 len--;
     866         119 :                 label_length = out;
     867         119 :                 out++;
     868             : 
     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         119 :                 r = dns_label_unescape(&domain, (char *) out, len, 0);
     874         119 :                 if (r < 0)
     875           1 :                         return r;
     876             : 
     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. */
     880         118 :                 if (canonical)
     881          33 :                         ascii_strlower_n((char*) out, (size_t) r);
     882             : 
     883             :                 /* Fill label length, move forward */
     884         118 :                 *label_length = r;
     885         118 :                 out += r;
     886         118 :                 len -= r;
     887             : 
     888         118 :         } while (r != 0);
     889             : 
     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
     893             :          * time. */
     894          23 :         if (out - buffer > DNS_HOSTNAME_MAX + 2)
     895           1 :                 return -EINVAL;
     896             : 
     897          22 :         return out - buffer;
     898             : }
     899             : 
     900          89 : static bool srv_type_label_is_valid(const char *label, size_t n) {
     901             :         size_t k;
     902             : 
     903          89 :         assert(label);
     904             : 
     905          89 :         if (n < 2) /* Label needs to be at least 2 chars long */
     906           4 :                 return false;
     907             : 
     908          85 :         if (label[0] != '_') /* First label char needs to be underscore */
     909           5 :                 return false;
     910             : 
     911             :         /* Second char must be a letter */
     912          80 :         if (!(label[1] >= 'A' && label[1] <= 'Z') &&
     913          80 :             !(label[1] >= 'a' && label[1] <= 'z'))
     914           6 :                 return false;
     915             : 
     916             :         /* Third and further chars must be alphanumeric or a hyphen */
     917         242 :         for (k = 2; k < n; k++) {
     918         170 :                 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
     919         170 :                     !(label[k] >= 'a' && label[k] <= 'z') &&
     920          18 :                     !(label[k] >= '0' && label[k] <= '9') &&
     921           6 :                     label[k] != '-')
     922           2 :                         return false;
     923             :         }
     924             : 
     925          72 :         return true;
     926             : }
     927             : 
     928          51 : bool dns_srv_type_is_valid(const char *name) {
     929          51 :         unsigned c = 0;
     930             :         int r;
     931             : 
     932          51 :         if (!name)
     933           2 :                 return false;
     934             : 
     935          54 :         for (;;) {
     936             :                 char label[DNS_LABEL_MAX];
     937             : 
     938             :                 /* This more or less implements RFC 6335, Section 5.1 */
     939             : 
     940         103 :                 r = dns_label_unescape(&name, label, sizeof label, 0);
     941         103 :                 if (r < 0)
     942          17 :                         return false;
     943         101 :                 if (r == 0)
     944          32 :                         break;
     945             : 
     946          69 :                 if (c >= 2)
     947           2 :                         return false;
     948             : 
     949          67 :                 if (!srv_type_label_is_valid(label, r))
     950          13 :                         return false;
     951             : 
     952          54 :                 c++;
     953             :         }
     954             : 
     955          32 :         return c == 2; /* exactly two labels */
     956             : }
     957             : 
     958          19 : bool dnssd_srv_type_is_valid(const char *name) {
     959          27 :         return dns_srv_type_is_valid(name) &&
     960           8 :                 ((dns_name_endswith(name, "_tcp") > 0) ||
     961           2 :                  (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
     962             : }
     963             : 
     964          19 : bool dns_service_name_is_valid(const char *name) {
     965             :         size_t l;
     966             : 
     967             :         /* This more or less implements RFC 6763, Section 4.1.1 */
     968             : 
     969          19 :         if (!name)
     970           1 :                 return false;
     971             : 
     972          18 :         if (!utf8_is_valid(name))
     973           1 :                 return false;
     974             : 
     975          17 :         if (string_has_cc(name, NULL))
     976           1 :                 return false;
     977             : 
     978          16 :         l = strlen(name);
     979          16 :         if (l <= 0)
     980           3 :                 return false;
     981          13 :         if (l > 63)
     982           1 :                 return false;
     983             : 
     984          12 :         return true;
     985             : }
     986             : 
     987          14 : int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
     988             :         char escaped[DNS_LABEL_ESCAPED_MAX];
     989          14 :         _cleanup_free_ char *n = NULL;
     990             :         int r;
     991             : 
     992          14 :         assert(type);
     993          14 :         assert(domain);
     994          14 :         assert(ret);
     995             : 
     996          14 :         if (!dns_srv_type_is_valid(type))
     997           3 :                 return -EINVAL;
     998             : 
     999          11 :         if (!name)
    1000           4 :                 return dns_name_concat(type, domain, 0, ret);
    1001             : 
    1002           7 :         if (!dns_service_name_is_valid(name))
    1003           2 :                 return -EINVAL;
    1004             : 
    1005           5 :         r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
    1006           5 :         if (r < 0)
    1007           0 :                 return r;
    1008             : 
    1009           5 :         r = dns_name_concat(type, domain, 0, &n);
    1010           5 :         if (r < 0)
    1011           0 :                 return r;
    1012             : 
    1013           5 :         return dns_name_concat(escaped, n, 0, ret);
    1014             : }
    1015             : 
    1016           5 : static bool dns_service_name_label_is_valid(const char *label, size_t n) {
    1017             :         char *s;
    1018             : 
    1019           5 :         assert(label);
    1020             : 
    1021           5 :         if (memchr(label, 0, n))
    1022           0 :                 return false;
    1023             : 
    1024           5 :         s = strndupa(label, n);
    1025           5 :         return dns_service_name_is_valid(s);
    1026             : }
    1027             : 
    1028          13 : int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
    1029          13 :         _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
    1030          13 :         const char *p = joined, *q = NULL, *d = NULL;
    1031             :         char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
    1032             :         int an, bn, cn, r;
    1033          13 :         unsigned x = 0;
    1034             : 
    1035          13 :         assert(joined);
    1036             : 
    1037             :         /* Get first label from the full name */
    1038          13 :         an = dns_label_unescape(&p, a, sizeof(a), 0);
    1039          13 :         if (an < 0)
    1040           0 :                 return an;
    1041             : 
    1042          13 :         if (an > 0) {
    1043          12 :                 x++;
    1044             : 
    1045             :                 /* If there was a first label, try to get the second one */
    1046          12 :                 bn = dns_label_unescape(&p, b, sizeof(b), 0);
    1047          12 :                 if (bn < 0)
    1048           0 :                         return bn;
    1049             : 
    1050          12 :                 if (bn > 0) {
    1051          11 :                         x++;
    1052             : 
    1053             :                         /* If there was a second label, try to get the third one */
    1054          11 :                         q = p;
    1055          11 :                         cn = dns_label_unescape(&p, c, sizeof(c), 0);
    1056          11 :                         if (cn < 0)
    1057           0 :                                 return cn;
    1058             : 
    1059          11 :                         if (cn > 0)
    1060           7 :                                 x++;
    1061             :                 } else
    1062           1 :                         cn = 0;
    1063             :         } else
    1064           1 :                 an = 0;
    1065             : 
    1066          13 :         if (x >= 2 && srv_type_label_is_valid(b, bn)) {
    1067             : 
    1068           9 :                 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
    1069             : 
    1070           5 :                         if (dns_service_name_label_is_valid(a, an)) {
    1071             :                                 /* OK, got <name> . <type> . <type2> . <domain> */
    1072             : 
    1073           5 :                                 name = strndup(a, an);
    1074           5 :                                 if (!name)
    1075           0 :                                         return -ENOMEM;
    1076             : 
    1077           5 :                                 type = strjoin(b, ".", c);
    1078           5 :                                 if (!type)
    1079           0 :                                         return -ENOMEM;
    1080             : 
    1081           5 :                                 d = p;
    1082           5 :                                 goto finish;
    1083             :                         }
    1084             : 
    1085           4 :                 } else if (srv_type_label_is_valid(a, an)) {
    1086             : 
    1087             :                         /* OK, got <type> . <type2> . <domain> */
    1088             : 
    1089           4 :                         name = NULL;
    1090             : 
    1091           4 :                         type = strjoin(a, ".", b);
    1092           4 :                         if (!type)
    1093           0 :                                 return -ENOMEM;
    1094             : 
    1095           4 :                         d = q;
    1096           4 :                         goto finish;
    1097             :                 }
    1098             :         }
    1099             : 
    1100           4 :         name = NULL;
    1101           4 :         type = NULL;
    1102           4 :         d = joined;
    1103             : 
    1104          13 : finish:
    1105          13 :         r = dns_name_normalize(d, 0, &domain);
    1106          13 :         if (r < 0)
    1107           0 :                 return r;
    1108             : 
    1109          13 :         if (_domain)
    1110          13 :                 *_domain = TAKE_PTR(domain);
    1111             : 
    1112          13 :         if (_type)
    1113          13 :                 *_type = TAKE_PTR(type);
    1114             : 
    1115          13 :         if (_name)
    1116          13 :                 *_name = TAKE_PTR(name);
    1117             : 
    1118          13 :         return 0;
    1119             : }
    1120             : 
    1121          36 : static int dns_name_build_suffix_table(const char *name, const char *table[]) {
    1122             :         const char *p;
    1123          36 :         unsigned n = 0;
    1124             :         int r;
    1125             : 
    1126          36 :         assert(name);
    1127          36 :         assert(table);
    1128             : 
    1129          36 :         p = name;
    1130             :         for (;;) {
    1131          86 :                 if (n > DNS_N_LABELS_MAX)
    1132           0 :                         return -EINVAL;
    1133             : 
    1134          86 :                 table[n] = p;
    1135          86 :                 r = dns_name_parent(&p);
    1136          86 :                 if (r < 0)
    1137           0 :                         return r;
    1138          86 :                 if (r == 0)
    1139          36 :                         break;
    1140             : 
    1141          50 :                 n++;
    1142             :         }
    1143             : 
    1144          36 :         return (int) n;
    1145             : }
    1146             : 
    1147          16 : int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) {
    1148             :         const char* labels[DNS_N_LABELS_MAX+1];
    1149             :         int n;
    1150             : 
    1151          16 :         assert(name);
    1152          16 :         assert(ret);
    1153             : 
    1154          16 :         n = dns_name_build_suffix_table(name, labels);
    1155          16 :         if (n < 0)
    1156           0 :                 return n;
    1157             : 
    1158          16 :         if ((unsigned) n < n_labels)
    1159           6 :                 return -EINVAL;
    1160             : 
    1161          10 :         *ret = labels[n - n_labels];
    1162          10 :         return (int) (n - n_labels);
    1163             : }
    1164             : 
    1165          24 : int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
    1166             :         int r;
    1167             : 
    1168          24 :         assert(a);
    1169          24 :         assert(ret);
    1170             : 
    1171          41 :         for (; n_labels > 0; n_labels--) {
    1172          24 :                 r = dns_name_parent(&a);
    1173          24 :                 if (r < 0)
    1174           0 :                         return r;
    1175          24 :                 if (r == 0) {
    1176           7 :                         *ret = "";
    1177           7 :                         return 0;
    1178             :                 }
    1179             :         }
    1180             : 
    1181          17 :         *ret = a;
    1182          17 :         return 1;
    1183             : }
    1184             : 
    1185          17 : int dns_name_count_labels(const char *name) {
    1186          17 :         unsigned n = 0;
    1187             :         const char *p;
    1188             :         int r;
    1189             : 
    1190          17 :         assert(name);
    1191             : 
    1192          17 :         p = name;
    1193             :         for (;;) {
    1194          45 :                 r = dns_name_parent(&p);
    1195          45 :                 if (r < 0)
    1196           1 :                         return r;
    1197          44 :                 if (r == 0)
    1198          16 :                         break;
    1199             : 
    1200          28 :                 if (n >= DNS_N_LABELS_MAX)
    1201           0 :                         return -EINVAL;
    1202             : 
    1203          28 :                 n++;
    1204             :         }
    1205             : 
    1206          16 :         return (int) n;
    1207             : }
    1208             : 
    1209          20 : int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
    1210             :         int r;
    1211             : 
    1212          20 :         assert(a);
    1213          20 :         assert(b);
    1214             : 
    1215          20 :         r = dns_name_skip(a, n_labels, &a);
    1216          20 :         if (r <= 0)
    1217           7 :                 return r;
    1218             : 
    1219          13 :         return dns_name_equal(a, b);
    1220             : }
    1221             : 
    1222          10 : 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          10 :         int n = 0, m = 0, k = 0, r, q;
    1225             : 
    1226          10 :         assert(a);
    1227          10 :         assert(b);
    1228          10 :         assert(ret);
    1229             : 
    1230             :         /* Determines the common suffix of domain names a and b */
    1231             : 
    1232          10 :         n = dns_name_build_suffix_table(a, a_labels);
    1233          10 :         if (n < 0)
    1234           0 :                 return n;
    1235             : 
    1236          10 :         m = dns_name_build_suffix_table(b, b_labels);
    1237          10 :         if (m < 0)
    1238           0 :                 return m;
    1239             : 
    1240           6 :         for (;;) {
    1241             :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
    1242             :                 const char *x, *y;
    1243             : 
    1244          16 :                 if (k >= n || k >= m) {
    1245           6 :                         *ret = a_labels[n - k];
    1246          10 :                         return 0;
    1247             :                 }
    1248             : 
    1249          10 :                 x = a_labels[n - 1 - k];
    1250          10 :                 r = dns_label_unescape(&x, la, sizeof la, 0);
    1251          10 :                 if (r < 0)
    1252           0 :                         return r;
    1253             : 
    1254          10 :                 y = b_labels[m - 1 - k];
    1255          10 :                 q = dns_label_unescape(&y, lb, sizeof lb, 0);
    1256          10 :                 if (q < 0)
    1257           0 :                         return q;
    1258             : 
    1259          10 :                 if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
    1260           4 :                         *ret = a_labels[n - k];
    1261           4 :                         return 0;
    1262             :                 }
    1263             : 
    1264           6 :                 k++;
    1265             :         }
    1266             : }
    1267             : 
    1268          15 : int dns_name_apply_idna(const char *name, char **ret) {
    1269             :         /* Return negative on error, 0 if not implemented, positive on success. */
    1270             : 
    1271             : #if HAVE_LIBIDN2
    1272             :         int r;
    1273          15 :         _cleanup_free_ char *t = NULL;
    1274             : 
    1275          15 :         assert(name);
    1276          15 :         assert(ret);
    1277             : 
    1278          15 :         r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
    1279             :                            IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
    1280          15 :         log_debug("idn2_lookup_u8: %s → %s", name, t);
    1281          15 :         if (r == IDN2_OK) {
    1282          14 :                 if (!startswith(name, "xn--")) {
    1283          13 :                         _cleanup_free_ char *s = NULL;
    1284             : 
    1285          13 :                         r = idn2_to_unicode_8z8z(t, &s, 0);
    1286          13 :                         if (r != IDN2_OK) {
    1287           0 :                                 log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
    1288             :                                           t, r, idn2_strerror(r));
    1289           0 :                                 return 0;
    1290             :                         }
    1291             : 
    1292          13 :                         if (!streq_ptr(name, s)) {
    1293           0 :                                 log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
    1294             :                                           name, t, s);
    1295           0 :                                 return 0;
    1296             :                         }
    1297             :                 }
    1298             : 
    1299          14 :                 *ret = TAKE_PTR(t);
    1300             : 
    1301          14 :                 return 1; /* *ret has been written */
    1302             :         }
    1303             : 
    1304           1 :         log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
    1305           1 :         if (r == IDN2_2HYPHEN)
    1306             :                 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
    1307           1 :                 return 0;
    1308           0 :         if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
    1309           0 :                 return -ENOSPC;
    1310           0 :         return -EINVAL;
    1311             : #elif HAVE_LIBIDN
    1312             :         _cleanup_free_ char *buf = NULL;
    1313             :         size_t n = 0, allocated = 0;
    1314             :         bool first = true;
    1315             :         int r, q;
    1316             : 
    1317             :         assert(name);
    1318             :         assert(ret);
    1319             : 
    1320             :         for (;;) {
    1321             :                 char label[DNS_LABEL_MAX];
    1322             : 
    1323             :                 r = dns_label_unescape(&name, label, sizeof label, 0);
    1324             :                 if (r < 0)
    1325             :                         return r;
    1326             :                 if (r == 0)
    1327             :                         break;
    1328             : 
    1329             :                 q = dns_label_apply_idna(label, r, label, sizeof label);
    1330             :                 if (q < 0)
    1331             :                         return q;
    1332             :                 if (q > 0)
    1333             :                         r = q;
    1334             : 
    1335             :                 if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
    1336             :                         return -ENOMEM;
    1337             : 
    1338             :                 r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
    1339             :                 if (r < 0)
    1340             :                         return r;
    1341             : 
    1342             :                 if (first)
    1343             :                         first = false;
    1344             :                 else
    1345             :                         buf[n++] = '.';
    1346             : 
    1347             :                 n += r;
    1348             :         }
    1349             : 
    1350             :         if (n > DNS_HOSTNAME_MAX)
    1351             :                 return -EINVAL;
    1352             : 
    1353             :         if (!GREEDY_REALLOC(buf, allocated, n + 1))
    1354             :                 return -ENOMEM;
    1355             : 
    1356             :         buf[n] = 0;
    1357             :         *ret = TAKE_PTR(buf);
    1358             : 
    1359             :         return 1;
    1360             : #else
    1361             :         return 0;
    1362             : #endif
    1363             : }
    1364             : 
    1365          25 : 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 */
    1367             : 
    1368          25 :         if (isempty(name))
    1369           2 :                 return 0;
    1370             : 
    1371          23 :         if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
    1372           3 :                 return 1;
    1373             : 
    1374          20 :         return dns_name_is_valid(name);
    1375             : }

Generated by: LCOV version 1.14