LCOV - code coverage report
Current view: top level - shared - dns-domain.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 604 669 90.3 %
Date: 2019-08-23 13:36:53 Functions: 32 32 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 434 588 73.8 %

           Branch data     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                 :      36046 : int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
      29                 :            :         const char *n;
      30                 :      36046 :         char *d, last_char = 0;
      31                 :      36046 :         int r = 0;
      32                 :            : 
      33         [ -  + ]:      36046 :         assert(name);
      34         [ -  + ]:      36046 :         assert(*name);
      35                 :            : 
      36                 :      36046 :         n = *name;
      37                 :      36046 :         d = dest;
      38                 :            : 
      39                 :            :         for (;;) {
      40   [ +  +  +  + ]:     189386 :                 if (IN_SET(*n, 0, '.')) {
      41   [ +  +  +  + ]:      35874 :                         if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
      42                 :            :                                 /* Trailing dash */
      43                 :         32 :                                 return -EINVAL;
      44                 :            : 
      45         [ +  + ]:      35842 :                         if (*n == '.')
      46                 :      18412 :                                 n++;
      47                 :      35842 :                         break;
      48                 :            :                 }
      49                 :            : 
      50         [ +  + ]:     153512 :                 if (r >= DNS_LABEL_MAX)
      51                 :          8 :                         return -EINVAL;
      52                 :            : 
      53         [ +  + ]:     153504 :                 if (sz <= 0)
      54                 :         20 :                         return -ENOBUFS;
      55                 :            : 
      56         [ +  + ]:     153484 :                 if (*n == '\\') {
      57                 :            :                         /* Escaped character */
      58         [ +  + ]:        228 :                         if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
      59                 :         24 :                                 return -EINVAL;
      60                 :            : 
      61                 :        204 :                         n++;
      62                 :            : 
      63         [ +  + ]:        204 :                         if (*n == 0)
      64                 :            :                                 /* Ending NUL */
      65                 :         16 :                                 return -EINVAL;
      66                 :            : 
      67   [ +  +  +  + ]:        188 :                         else if (IN_SET(*n, '\\', '.')) {
      68                 :            :                                 /* Escaped backslash or dot */
      69                 :            : 
      70         [ +  + ]:         32 :                                 if (FLAGS_SET(flags, DNS_LABEL_LDH))
      71                 :          4 :                                         return -EINVAL;
      72                 :            : 
      73                 :         28 :                                 last_char = *n;
      74         [ +  - ]:         28 :                                 if (d)
      75                 :         28 :                                         *(d++) = *n;
      76                 :         28 :                                 sz--;
      77                 :         28 :                                 r++;
      78                 :         28 :                                 n++;
      79                 :            : 
      80   [ +  -  +  + ]:        156 :                         } else if (n[0] >= '0' && n[0] <= '9') {
      81                 :            :                                 unsigned k;
      82                 :            : 
      83                 :            :                                 /* Escaped literal ASCII character */
      84                 :            : 
      85   [ +  -  +  - ]:        152 :                                 if (!(n[1] >= '0' && n[1] <= '9') ||
      86   [ +  -  -  + ]:        152 :                                     !(n[2] >= '0' && n[2] <= '9'))
      87                 :          0 :                                         return -EINVAL;
      88                 :            : 
      89                 :        456 :                                 k = ((unsigned) (n[0] - '0') * 100) +
      90                 :        304 :                                         ((unsigned) (n[1] - '0') * 10) +
      91                 :        152 :                                         ((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         [ -  + ]:        152 :                                 if (k > 255)
     100                 :          0 :                                         return -EINVAL;
     101                 :            : 
     102         [ +  + ]:        152 :                                 if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
     103         [ +  + ]:         12 :                                     !valid_ldh_char((char) k))
     104                 :          8 :                                         return -EINVAL;
     105                 :            : 
     106                 :        144 :                                 last_char = (char) k;
     107         [ +  - ]:        144 :                                 if (d)
     108                 :        144 :                                         *(d++) = (char) k;
     109                 :        144 :                                 sz--;
     110                 :        144 :                                 r++;
     111                 :            : 
     112                 :        144 :                                 n += 3;
     113                 :            :                         } else
     114                 :          4 :                                 return -EINVAL;
     115                 :            : 
     116   [ +  +  +  - ]:     153256 :                 } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) {
     117                 :            : 
     118                 :            :                         /* Normal character */
     119                 :            : 
     120         [ +  + ]:     153216 :                         if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
     121         [ +  + ]:       6588 :                                 if (!valid_ldh_char(*n))
     122                 :          8 :                                         return -EINVAL;
     123   [ +  +  +  + ]:       6580 :                                 if (r == 0 && *n == '-')
     124                 :            :                                         /* Leading dash */
     125                 :         40 :                                         return -EINVAL;
     126                 :            :                         }
     127                 :            : 
     128                 :     153168 :                         last_char = *n;
     129         [ +  + ]:     153168 :                         if (d)
     130                 :     151776 :                                 *(d++) = *n;
     131                 :     153168 :                         sz--;
     132                 :     153168 :                         r++;
     133                 :     153168 :                         n++;
     134                 :            :                 } else
     135                 :         40 :                         return -EINVAL;
     136                 :            :         }
     137                 :            : 
     138                 :            :         /* Empty label that is not at the end? */
     139   [ +  +  +  + ]:      35842 :         if (r == 0 && *n)
     140                 :         72 :                 return -EINVAL;
     141                 :            : 
     142                 :            :         /* More than one trailing dot? */
     143         [ +  + ]:      35770 :         if (*n == '.')
     144                 :         72 :                 return -EINVAL;
     145                 :            : 
     146   [ +  +  +  + ]:      35698 :         if (sz >= 1 && d)
     147                 :      35022 :                 *d = 0;
     148                 :            : 
     149                 :      35698 :         *name = n;
     150                 :      35698 :         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                 :       2358 : 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         [ -  + ]:       2358 :         assert(name);
     161         [ -  + ]:       2358 :         assert(label_terminal);
     162         [ -  + ]:       2358 :         assert(dest);
     163                 :            : 
     164                 :            :         /* no more labels */
     165         [ +  + ]:       2358 :         if (!*label_terminal) {
     166         [ +  - ]:        180 :                 if (sz >= 1)
     167                 :        180 :                         *dest = 0;
     168                 :            : 
     169                 :        180 :                 return 0;
     170                 :            :         }
     171                 :            : 
     172                 :       2178 :         terminal = *label_terminal;
     173   [ +  -  -  + ]:       2178 :         assert(IN_SET(*terminal, 0, '.'));
     174                 :            : 
     175                 :            :         /* Skip current terminal character (and accept domain names ending it ".") */
     176         [ +  + ]:       2178 :         if (*terminal == 0)
     177                 :       1230 :                 terminal--;
     178   [ +  +  +  + ]:       2178 :         if (terminal >= name && *terminal == '.')
     179                 :        992 :                 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         [ +  + ]:      12834 :                 if (terminal < name) {
     184                 :            :                         /* Reached the first label, so indicate that there are no more */
     185                 :        907 :                         terminal = NULL;
     186                 :        907 :                         break;
     187                 :            :                 }
     188                 :            : 
     189                 :            :                 /* Find the start of the last label */
     190         [ +  + ]:      11927 :                 if (*terminal == '.') {
     191                 :            :                         const char *y;
     192                 :       1283 :                         unsigned slashes = 0;
     193                 :            : 
     194   [ +  +  +  + ]:       1311 :                         for (y = terminal - 1; y >= name && *y == '\\'; y--)
     195                 :         28 :                                 slashes++;
     196                 :            : 
     197         [ +  + ]:       1283 :                         if (slashes % 2 == 0) {
     198                 :            :                                 /* The '.' was not escaped */
     199                 :       1271 :                                 name = terminal + 1;
     200                 :       1271 :                                 break;
     201                 :            :                         } else {
     202                 :         12 :                                 terminal = y;
     203                 :         12 :                                 continue;
     204                 :            :                         }
     205                 :            :                 }
     206                 :            : 
     207                 :      10644 :                 terminal--;
     208                 :            :         }
     209                 :            : 
     210                 :       2178 :         r = dns_label_unescape(&name, dest, sz, 0);
     211         [ +  + ]:       2178 :         if (r < 0)
     212                 :         40 :                 return r;
     213                 :            : 
     214                 :       2138 :         *label_terminal = terminal;
     215                 :            : 
     216                 :       2138 :         return r;
     217                 :            : }
     218                 :            : 
     219                 :       9572 : 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   [ +  -  -  + ]:       9572 :         if (l <= 0 || l > DNS_LABEL_MAX)
     227                 :          0 :                 return -EINVAL;
     228         [ -  + ]:       9572 :         if (sz < 1)
     229                 :          0 :                 return -ENOBUFS;
     230                 :            : 
     231         [ -  + ]:       9572 :         assert(p);
     232         [ -  + ]:       9572 :         assert(dest);
     233                 :            : 
     234                 :       9572 :         q = dest;
     235         [ +  + ]:      67292 :         while (l > 0) {
     236                 :            : 
     237   [ +  +  +  + ]:      57720 :                 if (IN_SET(*p, '.', '\\')) {
     238                 :            : 
     239                 :            :                         /* Dot or backslash */
     240                 :            : 
     241         [ -  + ]:          4 :                         if (sz < 3)
     242                 :          0 :                                 return -ENOBUFS;
     243                 :            : 
     244                 :          4 :                         *(q++) = '\\';
     245                 :          4 :                         *(q++) = *p;
     246                 :            : 
     247                 :          4 :                         sz -= 2;
     248                 :            : 
     249   [ +  +  +  + ]:      57716 :                 } else if (IN_SET(*p, '_', '-') ||
     250   [ +  +  +  + ]:      56952 :                            (*p >= '0' && *p <= '9') ||
     251   [ +  +  -  + ]:      46452 :                            (*p >= 'a' && *p <= 'z') ||
     252   [ +  +  +  - ]:        636 :                            (*p >= 'A' && *p <= 'Z')) {
     253                 :            : 
     254                 :            :                         /* Proper character */
     255                 :            : 
     256         [ -  + ]:      57660 :                         if (sz < 2)
     257                 :          0 :                                 return -ENOBUFS;
     258                 :            : 
     259                 :      57660 :                         *(q++) = *p;
     260                 :      57660 :                         sz -= 1;
     261                 :            : 
     262                 :            :                 } else {
     263                 :            : 
     264                 :            :                         /* Everything else */
     265                 :            : 
     266         [ -  + ]:         56 :                         if (sz < 5)
     267                 :          0 :                                 return -ENOBUFS;
     268                 :            : 
     269                 :         56 :                         *(q++) = '\\';
     270                 :         56 :                         *(q++) = '0' + (char) ((uint8_t) *p / 100);
     271                 :         56 :                         *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10);
     272                 :         56 :                         *(q++) = '0' + (char) ((uint8_t) *p % 10);
     273                 :            : 
     274                 :         56 :                         sz -= 4;
     275                 :            :                 }
     276                 :            : 
     277                 :      57720 :                 p++;
     278                 :      57720 :                 l--;
     279                 :            :         }
     280                 :            : 
     281                 :       9572 :         *q = 0;
     282                 :       9572 :         return (int) (q - dest);
     283                 :            : }
     284                 :            : 
     285                 :         16 : int dns_label_escape_new(const char *p, size_t l, char **ret) {
     286                 :         16 :         _cleanup_free_ char *s = NULL;
     287                 :            :         int r;
     288                 :            : 
     289         [ -  + ]:         16 :         assert(p);
     290         [ -  + ]:         16 :         assert(ret);
     291                 :            : 
     292   [ +  +  -  + ]:         16 :         if (l <= 0 || l > DNS_LABEL_MAX)
     293                 :          4 :                 return -EINVAL;
     294                 :            : 
     295                 :         12 :         s = new(char, DNS_LABEL_ESCAPED_MAX);
     296         [ -  + ]:         12 :         if (!s)
     297                 :          0 :                 return -ENOMEM;
     298                 :            : 
     299                 :         12 :         r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX);
     300         [ -  + ]:         12 :         if (r < 0)
     301                 :          0 :                 return r;
     302                 :            : 
     303                 :         12 :         *ret = TAKE_PTR(s);
     304                 :            : 
     305                 :         12 :         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                 :        772 : int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
     405                 :        772 :         _cleanup_free_ char *ret = NULL;
     406                 :        772 :         size_t n = 0, allocated = 0;
     407                 :            :         const char *p;
     408                 :        772 :         bool first = true;
     409                 :            :         int r;
     410                 :            : 
     411         [ +  + ]:        772 :         if (a)
     412                 :        760 :                 p = a;
     413         [ +  + ]:         12 :         else if (b)
     414                 :          8 :                 p = TAKE_PTR(b);
     415                 :            :         else
     416                 :          4 :                 goto finish;
     417                 :            : 
     418                 :       2124 :         for (;;) {
     419                 :            :                 char label[DNS_LABEL_MAX];
     420                 :            : 
     421                 :       2892 :                 r = dns_label_unescape(&p, label, sizeof label, flags);
     422         [ +  + ]:       2892 :                 if (r < 0)
     423                 :        148 :                         return r;
     424         [ +  + ]:       2744 :                 if (r == 0) {
     425         [ -  + ]:        740 :                         if (*p != 0)
     426                 :          0 :                                 return -EINVAL;
     427                 :            : 
     428         [ +  + ]:        740 :                         if (b) {
     429                 :            :                                 /* Now continue with the second string, if there is one */
     430                 :        120 :                                 p = TAKE_PTR(b);
     431                 :        120 :                                 continue;
     432                 :            :                         }
     433                 :            : 
     434                 :        620 :                         break;
     435                 :            :                 }
     436                 :            : 
     437         [ +  + ]:       2004 :                 if (_ret) {
     438         [ -  + ]:        472 :                         if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
     439                 :          0 :                                 return -ENOMEM;
     440                 :            : 
     441                 :        472 :                         r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
     442         [ -  + ]:        472 :                         if (r < 0)
     443                 :          0 :                                 return r;
     444                 :            : 
     445         [ +  + ]:        472 :                         if (!first)
     446                 :        288 :                                 ret[n] = '.';
     447                 :            :                 } else {
     448                 :            :                         char escaped[DNS_LABEL_ESCAPED_MAX];
     449                 :            : 
     450                 :       1532 :                         r = dns_label_escape(label, r, escaped, sizeof(escaped));
     451         [ -  + ]:       1532 :                         if (r < 0)
     452                 :          0 :                                 return r;
     453                 :            :                 }
     454                 :            : 
     455         [ +  + ]:       2004 :                 if (!first)
     456                 :       1404 :                         n++;
     457                 :            :                 else
     458                 :        600 :                         first = false;
     459                 :            : 
     460                 :       2004 :                 n += r;
     461                 :            :         }
     462                 :            : 
     463                 :        624 : finish:
     464         [ +  + ]:        624 :         if (n > DNS_HOSTNAME_MAX)
     465                 :         24 :                 return -EINVAL;
     466                 :            : 
     467         [ +  + ]:        600 :         if (_ret) {
     468         [ +  + ]:        240 :                 if (n == 0) {
     469                 :            :                         /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
     470         [ -  + ]:         56 :                         if (!GREEDY_REALLOC(ret, allocated, 2))
     471                 :          0 :                                 return -ENOMEM;
     472                 :            : 
     473                 :         56 :                         ret[n++] = '.';
     474                 :            :                 } else {
     475         [ -  + ]:        184 :                         if (!GREEDY_REALLOC(ret, allocated, n + 1))
     476                 :          0 :                                 return -ENOMEM;
     477                 :            :                 }
     478                 :            : 
     479                 :        240 :                 ret[n] = 0;
     480                 :        240 :                 *_ret = TAKE_PTR(ret);
     481                 :            :         }
     482                 :            : 
     483                 :        600 :         return 0;
     484                 :            : }
     485                 :            : 
     486                 :       3460 : void dns_name_hash_func(const char *p, struct siphash *state) {
     487                 :            :         int r;
     488                 :            : 
     489         [ -  + ]:       3460 :         assert(p);
     490                 :            : 
     491                 :       8120 :         for (;;) {
     492                 :            :                 char label[DNS_LABEL_MAX+1];
     493                 :            : 
     494                 :      11580 :                 r = dns_label_unescape(&p, label, sizeof label, 0);
     495         [ -  + ]:      11580 :                 if (r < 0)
     496                 :          0 :                         break;
     497         [ +  + ]:      11580 :                 if (r == 0)
     498                 :       3460 :                         break;
     499                 :            : 
     500                 :       8120 :                 ascii_strlower_n(label, r);
     501                 :       8120 :                 siphash24_compress(label, r, state);
     502                 :       8120 :                 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                 :       3460 :         string_hash_func("", state);
     507                 :       3460 : }
     508                 :            : 
     509                 :        571 : int dns_name_compare_func(const char *a, const char *b) {
     510                 :            :         const char *x, *y;
     511                 :            :         int r, q;
     512                 :            : 
     513         [ -  + ]:        571 :         assert(a);
     514         [ -  + ]:        571 :         assert(b);
     515                 :            : 
     516                 :        571 :         x = a + strlen(a);
     517                 :        571 :         y = b + strlen(b);
     518                 :            : 
     519                 :        676 :         for (;;) {
     520                 :            :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
     521                 :            : 
     522   [ +  +  +  + ]:       1247 :                 if (x == NULL && y == NULL)
     523                 :        571 :                         return 0;
     524                 :            : 
     525                 :       1103 :                 r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
     526                 :       1103 :                 q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
     527   [ +  -  -  + ]:       1103 :                 if (r < 0 || q < 0)
     528         [ #  # ]:          0 :                         return CMP(r, q);
     529                 :            : 
     530                 :       1103 :                 r = ascii_strcasecmp_nn(la, r, lb, q);
     531         [ +  + ]:       1103 :                 if (r != 0)
     532                 :        427 :                         return r;
     533                 :            :         }
     534                 :            : }
     535                 :            : 
     536                 :            : DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
     537                 :            : 
     538                 :       1464 : int dns_name_equal(const char *x, const char *y) {
     539                 :            :         int r, q;
     540                 :            : 
     541         [ -  + ]:       1464 :         assert(x);
     542         [ -  + ]:       1464 :         assert(y);
     543                 :            : 
     544                 :       4068 :         for (;;) {
     545                 :            :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
     546                 :            : 
     547                 :       5532 :                 r = dns_label_unescape(&x, la, sizeof la, 0);
     548         [ +  + ]:       5532 :                 if (r < 0)
     549                 :       1464 :                         return r;
     550                 :            : 
     551                 :       5524 :                 q = dns_label_unescape(&y, lb, sizeof lb, 0);
     552         [ -  + ]:       5524 :                 if (q < 0)
     553                 :          0 :                         return q;
     554                 :            : 
     555         [ +  + ]:       5524 :                 if (r != q)
     556                 :         36 :                         return false;
     557         [ +  + ]:       5488 :                 if (r == 0)
     558                 :       1400 :                         return true;
     559                 :            : 
     560         [ +  + ]:       4088 :                 if (ascii_strcasecmp_n(la, lb, r) != 0)
     561                 :         20 :                         return false;
     562                 :            :         }
     563                 :            : }
     564                 :            : 
     565                 :        116 : int dns_name_endswith(const char *name, const char *suffix) {
     566                 :        116 :         const char *n, *s, *saved_n = NULL;
     567                 :            :         int r, q;
     568                 :            : 
     569         [ -  + ]:        116 :         assert(name);
     570         [ -  + ]:        116 :         assert(suffix);
     571                 :            : 
     572                 :        116 :         n = name;
     573                 :        116 :         s = suffix;
     574                 :            : 
     575                 :        804 :         for (;;) {
     576                 :            :                 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
     577                 :            : 
     578                 :        920 :                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
     579         [ +  + ]:        920 :                 if (r < 0)
     580                 :        116 :                         return r;
     581                 :            : 
     582         [ +  + ]:        916 :                 if (!saved_n)
     583                 :        804 :                         saved_n = n;
     584                 :            : 
     585                 :        916 :                 q = dns_label_unescape(&s, ls, sizeof ls, 0);
     586         [ -  + ]:        916 :                 if (q < 0)
     587                 :          0 :                         return q;
     588                 :            : 
     589   [ +  +  +  + ]:        916 :                 if (r == 0 && q == 0)
     590                 :         72 :                         return true;
     591   [ +  +  +  - ]:        844 :                 if (r == 0 && saved_n == n)
     592                 :         40 :                         return false;
     593                 :            : 
     594   [ +  +  +  + ]:        804 :                 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                 :        692 :                         s = suffix;
     598                 :        692 :                         n = TAKE_PTR(saved_n);
     599                 :            :                 }
     600                 :            :         }
     601                 :            : }
     602                 :            : 
     603                 :         48 : int dns_name_startswith(const char *name, const char *prefix) {
     604                 :            :         const char *n, *p;
     605                 :            :         int r, q;
     606                 :            : 
     607         [ -  + ]:         48 :         assert(name);
     608         [ -  + ]:         48 :         assert(prefix);
     609                 :            : 
     610                 :         48 :         n = name;
     611                 :         48 :         p = prefix;
     612                 :            : 
     613                 :         24 :         for (;;) {
     614                 :            :                 char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
     615                 :            : 
     616                 :         72 :                 r = dns_label_unescape(&p, lp, sizeof lp, 0);
     617         [ -  + ]:         72 :                 if (r < 0)
     618                 :         48 :                         return r;
     619         [ +  + ]:         72 :                 if (r == 0)
     620                 :         32 :                         return true;
     621                 :            : 
     622                 :         40 :                 q = dns_label_unescape(&n, ln, sizeof ln, 0);
     623         [ -  + ]:         40 :                 if (q < 0)
     624                 :          0 :                         return q;
     625                 :            : 
     626         [ +  + ]:         40 :                 if (r != q)
     627                 :          4 :                         return false;
     628         [ +  + ]:         36 :                 if (ascii_strcasecmp_n(ln, lp, r) != 0)
     629                 :         12 :                         return false;
     630                 :            :         }
     631                 :            : }
     632                 :            : 
     633                 :         36 : int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
     634                 :         36 :         const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
     635                 :            :         int r, q;
     636                 :            : 
     637         [ -  + ]:         36 :         assert(name);
     638         [ -  + ]:         36 :         assert(old_suffix);
     639         [ -  + ]:         36 :         assert(new_suffix);
     640         [ -  + ]:         36 :         assert(ret);
     641                 :            : 
     642                 :         36 :         n = name;
     643                 :         36 :         s = old_suffix;
     644                 :            : 
     645                 :         92 :         for (;;) {
     646                 :            :                 char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
     647                 :            : 
     648         [ +  + ]:        128 :                 if (!saved_before)
     649                 :         84 :                         saved_before = n;
     650                 :            : 
     651                 :        128 :                 r = dns_label_unescape(&n, ln, sizeof ln, 0);
     652         [ -  + ]:        128 :                 if (r < 0)
     653                 :          4 :                         return r;
     654                 :            : 
     655         [ +  + ]:        128 :                 if (!saved_after)
     656                 :         84 :                         saved_after = n;
     657                 :            : 
     658                 :        128 :                 q = dns_label_unescape(&s, ls, sizeof ls, 0);
     659         [ -  + ]:        128 :                 if (q < 0)
     660                 :          0 :                         return q;
     661                 :            : 
     662   [ +  +  +  + ]:        128 :                 if (r == 0 && q == 0)
     663                 :         32 :                         break;
     664   [ +  +  +  - ]:         96 :                 if (r == 0 && saved_after == n) {
     665                 :          4 :                         *ret = NULL; /* doesn't match */
     666                 :          4 :                         return 0;
     667                 :            :                 }
     668                 :            : 
     669   [ +  +  +  + ]:         92 :                 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                 :         48 :                         s = old_suffix;
     673                 :         48 :                         n = TAKE_PTR(saved_after);
     674                 :         48 :                         saved_before = NULL;
     675                 :            :                 }
     676                 :            :         }
     677                 :            : 
     678                 :            :         /* Found it! Now generate the new name */
     679                 :         32 :         prefix = strndupa(name, saved_before - name);
     680                 :            : 
     681                 :         32 :         r = dns_name_concat(prefix, new_suffix, 0, ret);
     682         [ -  + ]:         32 :         if (r < 0)
     683                 :          0 :                 return r;
     684                 :            : 
     685                 :         32 :         return 1;
     686                 :            : }
     687                 :            : 
     688                 :        112 : 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         [ +  + ]:        112 :         if (dns_name_compare_func(a, c) < 0)
     696                 :            :                 /*
     697                 :            :                    a and c are properly ordered:
     698                 :            :                    a<---b--->c
     699                 :            :                 */
     700   [ +  +  +  + ]:         88 :                 return dns_name_compare_func(a, b) < 0 &&
     701                 :         40 :                        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   [ +  +  +  + ]:        124 :                 return dns_name_compare_func(b, c) < 0 ||
     710                 :         60 :                        dns_name_compare_func(a, b) < 0;
     711                 :            : }
     712                 :            : 
     713                 :         16 : int dns_name_reverse(int family, const union in_addr_union *a, char **ret) {
     714                 :            :         const uint8_t *p;
     715                 :            :         int r;
     716                 :            : 
     717         [ -  + ]:         16 :         assert(a);
     718         [ -  + ]:         16 :         assert(ret);
     719                 :            : 
     720                 :         16 :         p = (const uint8_t*) a;
     721                 :            : 
     722         [ +  + ]:         16 :         if (family == AF_INET)
     723                 :          8 :                 r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]);
     724         [ +  - ]:          8 :         else if (family == AF_INET6)
     725                 :        256 :                 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                 :         32 :                              hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4),
     727                 :         32 :                              hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4),
     728                 :         32 :                              hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4),
     729                 :         32 :                              hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4),
     730                 :         32 :                              hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4),
     731                 :         32 :                              hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4),
     732                 :         32 :                              hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4),
     733                 :          8 :                              hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4));
     734                 :            :         else
     735                 :          0 :                 return -EAFNOSUPPORT;
     736         [ -  + ]:         16 :         if (r < 0)
     737                 :          0 :                 return -ENOMEM;
     738                 :            : 
     739                 :         16 :         return 0;
     740                 :            : }
     741                 :            : 
     742                 :         16 : int dns_name_address(const char *p, int *family, union in_addr_union *address) {
     743                 :            :         int r;
     744                 :            : 
     745         [ -  + ]:         16 :         assert(p);
     746         [ -  + ]:         16 :         assert(family);
     747         [ -  + ]:         16 :         assert(address);
     748                 :            : 
     749                 :         16 :         r = dns_name_endswith(p, "in-addr.arpa");
     750         [ -  + ]:         16 :         if (r < 0)
     751                 :          0 :                 return r;
     752         [ +  + ]:         16 :         if (r > 0) {
     753                 :            :                 uint8_t a[4];
     754                 :            :                 unsigned i;
     755                 :            : 
     756         [ +  + ]:         40 :                 for (i = 0; i < ELEMENTSOF(a); i++) {
     757                 :            :                         char label[DNS_LABEL_MAX+1];
     758                 :            : 
     759                 :         32 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     760         [ -  + ]:         32 :                         if (r < 0)
     761                 :          0 :                                 return r;
     762         [ -  + ]:         32 :                         if (r == 0)
     763                 :          0 :                                 return -EINVAL;
     764         [ -  + ]:         32 :                         if (r > 3)
     765                 :          0 :                                 return -EINVAL;
     766                 :            : 
     767                 :         32 :                         r = safe_atou8(label, &a[i]);
     768         [ -  + ]:         32 :                         if (r < 0)
     769                 :          0 :                                 return r;
     770                 :            :                 }
     771                 :            : 
     772                 :          8 :                 r = dns_name_equal(p, "in-addr.arpa");
     773         [ -  + ]:          8 :                 if (r <= 0)
     774                 :          0 :                         return r;
     775                 :            : 
     776                 :          8 :                 *family = AF_INET;
     777                 :          8 :                 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                 :          8 :                 return 1;
     783                 :            :         }
     784                 :            : 
     785                 :          8 :         r = dns_name_endswith(p, "ip6.arpa");
     786         [ -  + ]:          8 :         if (r < 0)
     787                 :          0 :                 return r;
     788         [ +  - ]:          8 :         if (r > 0) {
     789                 :            :                 struct in6_addr a;
     790                 :            :                 unsigned i;
     791                 :            : 
     792         [ +  + ]:        136 :                 for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) {
     793                 :            :                         char label[DNS_LABEL_MAX+1];
     794                 :            :                         int x, y;
     795                 :            : 
     796                 :        128 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     797         [ -  + ]:        128 :                         if (r <= 0)
     798                 :          0 :                                 return r;
     799         [ -  + ]:        128 :                         if (r != 1)
     800                 :          0 :                                 return -EINVAL;
     801                 :        128 :                         x = unhexchar(label[0]);
     802         [ -  + ]:        128 :                         if (x < 0)
     803                 :          0 :                                 return -EINVAL;
     804                 :            : 
     805                 :        128 :                         r = dns_label_unescape(&p, label, sizeof label, 0);
     806         [ -  + ]:        128 :                         if (r <= 0)
     807                 :          0 :                                 return r;
     808         [ -  + ]:        128 :                         if (r != 1)
     809                 :          0 :                                 return -EINVAL;
     810                 :        128 :                         y = unhexchar(label[0]);
     811         [ -  + ]:        128 :                         if (y < 0)
     812                 :          0 :                                 return -EINVAL;
     813                 :            : 
     814                 :        128 :                         a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x;
     815                 :            :                 }
     816                 :            : 
     817                 :          8 :                 r = dns_name_equal(p, "ip6.arpa");
     818         [ -  + ]:          8 :                 if (r <= 0)
     819                 :          0 :                         return r;
     820                 :            : 
     821                 :          8 :                 *family = AF_INET6;
     822                 :          8 :                 address->in6 = a;
     823                 :          8 :                 return 1;
     824                 :            :         }
     825                 :            : 
     826                 :          0 :         return 0;
     827                 :            : }
     828                 :            : 
     829                 :      11280 : bool dns_name_is_root(const char *name) {
     830                 :            : 
     831         [ -  + ]:      11280 :         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                 :      11280 :         return STR_IN_SET(name, "", ".");
     837                 :            : }
     838                 :            : 
     839                 :         32 : bool dns_name_is_single_label(const char *name) {
     840                 :            :         int r;
     841                 :            : 
     842         [ -  + ]:         32 :         assert(name);
     843                 :            : 
     844                 :         32 :         r = dns_name_parent(&name);
     845         [ +  + ]:         32 :         if (r <= 0)
     846                 :         12 :                 return false;
     847                 :            : 
     848                 :         20 :         return dns_name_is_root(name);
     849                 :            : }
     850                 :            : 
     851                 :            : /* Encode a domain name according to RFC 1035 Section 3.1, without compression */
     852                 :        100 : 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         [ -  + ]:        100 :         assert(domain);
     857         [ -  + ]:        100 :         assert(buffer);
     858                 :            : 
     859                 :        100 :         out = buffer;
     860                 :            : 
     861                 :            :         do {
     862                 :            :                 /* Reserve a byte for label length */
     863         [ +  + ]:        480 :                 if (len <= 0)
     864                 :          4 :                         return -ENOBUFS;
     865                 :        476 :                 len--;
     866                 :        476 :                 label_length = out;
     867                 :        476 :                 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                 :        476 :                 r = dns_label_unescape(&domain, (char *) out, len, 0);
     874         [ +  + ]:        476 :                 if (r < 0)
     875                 :          4 :                         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         [ +  + ]:        472 :                 if (canonical)
     881                 :        132 :                         ascii_strlower_n((char*) out, (size_t) r);
     882                 :            : 
     883                 :            :                 /* Fill label length, move forward */
     884                 :        472 :                 *label_length = r;
     885                 :        472 :                 out += r;
     886                 :        472 :                 len -= r;
     887                 :            : 
     888         [ +  + ]:        472 :         } 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         [ +  + ]:         92 :         if (out - buffer > DNS_HOSTNAME_MAX + 2)
     895                 :          4 :                 return -EINVAL;
     896                 :            : 
     897                 :         88 :         return out - buffer;
     898                 :            : }
     899                 :            : 
     900                 :        356 : static bool srv_type_label_is_valid(const char *label, size_t n) {
     901                 :            :         size_t k;
     902                 :            : 
     903         [ -  + ]:        356 :         assert(label);
     904                 :            : 
     905         [ +  + ]:        356 :         if (n < 2) /* Label needs to be at least 2 chars long */
     906                 :         16 :                 return false;
     907                 :            : 
     908         [ +  + ]:        340 :         if (label[0] != '_') /* First label char needs to be underscore */
     909                 :         20 :                 return false;
     910                 :            : 
     911                 :            :         /* Second char must be a letter */
     912   [ +  +  +  - ]:        320 :         if (!(label[1] >= 'A' && label[1] <= 'Z') &&
     913   [ +  +  -  + ]:        320 :             !(label[1] >= 'a' && label[1] <= 'z'))
     914                 :         24 :                 return false;
     915                 :            : 
     916                 :            :         /* Third and further chars must be alphanumeric or a hyphen */
     917         [ +  + ]:        968 :         for (k = 2; k < n; k++) {
     918   [ +  +  +  - ]:        680 :                 if (!(label[k] >= 'A' && label[k] <= 'Z') &&
     919   [ +  +  -  + ]:        680 :                     !(label[k] >= 'a' && label[k] <= 'z') &&
     920   [ +  +  -  + ]:         72 :                     !(label[k] >= '0' && label[k] <= '9') &&
     921         [ +  + ]:         24 :                     label[k] != '-')
     922                 :          8 :                         return false;
     923                 :            :         }
     924                 :            : 
     925                 :        288 :         return true;
     926                 :            : }
     927                 :            : 
     928                 :        204 : bool dns_srv_type_is_valid(const char *name) {
     929                 :        204 :         unsigned c = 0;
     930                 :            :         int r;
     931                 :            : 
     932         [ +  + ]:        204 :         if (!name)
     933                 :          8 :                 return false;
     934                 :            : 
     935                 :        216 :         for (;;) {
     936                 :            :                 char label[DNS_LABEL_MAX];
     937                 :            : 
     938                 :            :                 /* This more or less implements RFC 6335, Section 5.1 */
     939                 :            : 
     940                 :        412 :                 r = dns_label_unescape(&name, label, sizeof label, 0);
     941         [ +  + ]:        412 :                 if (r < 0)
     942                 :         68 :                         return false;
     943         [ +  + ]:        404 :                 if (r == 0)
     944                 :        128 :                         break;
     945                 :            : 
     946         [ +  + ]:        276 :                 if (c >= 2)
     947                 :          8 :                         return false;
     948                 :            : 
     949         [ +  + ]:        268 :                 if (!srv_type_label_is_valid(label, r))
     950                 :         52 :                         return false;
     951                 :            : 
     952                 :        216 :                 c++;
     953                 :            :         }
     954                 :            : 
     955                 :        128 :         return c == 2; /* exactly two labels */
     956                 :            : }
     957                 :            : 
     958                 :         76 : bool dnssd_srv_type_is_valid(const char *name) {
     959   [ +  +  +  + ]:        108 :         return dns_srv_type_is_valid(name) &&
     960         [ +  + ]:         32 :                 ((dns_name_endswith(name, "_tcp") > 0) ||
     961                 :          8 :                  (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */
     962                 :            : }
     963                 :            : 
     964                 :         76 : 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         [ +  + ]:         76 :         if (!name)
     970                 :          4 :                 return false;
     971                 :            : 
     972         [ +  + ]:         72 :         if (!utf8_is_valid(name))
     973                 :          4 :                 return false;
     974                 :            : 
     975         [ +  + ]:         68 :         if (string_has_cc(name, NULL))
     976                 :          4 :                 return false;
     977                 :            : 
     978                 :         64 :         l = strlen(name);
     979         [ +  + ]:         64 :         if (l <= 0)
     980                 :         12 :                 return false;
     981         [ +  + ]:         52 :         if (l > 63)
     982                 :          4 :                 return false;
     983                 :            : 
     984                 :         48 :         return true;
     985                 :            : }
     986                 :            : 
     987                 :         56 : int dns_service_join(const char *name, const char *type, const char *domain, char **ret) {
     988                 :            :         char escaped[DNS_LABEL_ESCAPED_MAX];
     989                 :         56 :         _cleanup_free_ char *n = NULL;
     990                 :            :         int r;
     991                 :            : 
     992         [ -  + ]:         56 :         assert(type);
     993         [ -  + ]:         56 :         assert(domain);
     994         [ -  + ]:         56 :         assert(ret);
     995                 :            : 
     996         [ +  + ]:         56 :         if (!dns_srv_type_is_valid(type))
     997                 :         12 :                 return -EINVAL;
     998                 :            : 
     999         [ +  + ]:         44 :         if (!name)
    1000                 :         16 :                 return dns_name_concat(type, domain, 0, ret);
    1001                 :            : 
    1002         [ +  + ]:         28 :         if (!dns_service_name_is_valid(name))
    1003                 :          8 :                 return -EINVAL;
    1004                 :            : 
    1005                 :         20 :         r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped));
    1006         [ -  + ]:         20 :         if (r < 0)
    1007                 :          0 :                 return r;
    1008                 :            : 
    1009                 :         20 :         r = dns_name_concat(type, domain, 0, &n);
    1010         [ -  + ]:         20 :         if (r < 0)
    1011                 :          0 :                 return r;
    1012                 :            : 
    1013                 :         20 :         return dns_name_concat(escaped, n, 0, ret);
    1014                 :            : }
    1015                 :            : 
    1016                 :         20 : static bool dns_service_name_label_is_valid(const char *label, size_t n) {
    1017                 :            :         char *s;
    1018                 :            : 
    1019         [ -  + ]:         20 :         assert(label);
    1020                 :            : 
    1021         [ -  + ]:         20 :         if (memchr(label, 0, n))
    1022                 :          0 :                 return false;
    1023                 :            : 
    1024                 :         20 :         s = strndupa(label, n);
    1025                 :         20 :         return dns_service_name_is_valid(s);
    1026                 :            : }
    1027                 :            : 
    1028                 :         52 : int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
    1029                 :         52 :         _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
    1030                 :         52 :         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                 :         52 :         unsigned x = 0;
    1034                 :            : 
    1035         [ -  + ]:         52 :         assert(joined);
    1036                 :            : 
    1037                 :            :         /* Get first label from the full name */
    1038                 :         52 :         an = dns_label_unescape(&p, a, sizeof(a), 0);
    1039         [ -  + ]:         52 :         if (an < 0)
    1040                 :          0 :                 return an;
    1041                 :            : 
    1042         [ +  + ]:         52 :         if (an > 0) {
    1043                 :         48 :                 x++;
    1044                 :            : 
    1045                 :            :                 /* If there was a first label, try to get the second one */
    1046                 :         48 :                 bn = dns_label_unescape(&p, b, sizeof(b), 0);
    1047         [ -  + ]:         48 :                 if (bn < 0)
    1048                 :          0 :                         return bn;
    1049                 :            : 
    1050         [ +  + ]:         48 :                 if (bn > 0) {
    1051                 :         44 :                         x++;
    1052                 :            : 
    1053                 :            :                         /* If there was a second label, try to get the third one */
    1054                 :         44 :                         q = p;
    1055                 :         44 :                         cn = dns_label_unescape(&p, c, sizeof(c), 0);
    1056         [ -  + ]:         44 :                         if (cn < 0)
    1057                 :          0 :                                 return cn;
    1058                 :            : 
    1059         [ +  + ]:         44 :                         if (cn > 0)
    1060                 :         28 :                                 x++;
    1061                 :            :                 } else
    1062                 :          4 :                         cn = 0;
    1063                 :            :         } else
    1064                 :          4 :                 an = 0;
    1065                 :            : 
    1066   [ +  +  +  + ]:         52 :         if (x >= 2 && srv_type_label_is_valid(b, bn)) {
    1067                 :            : 
    1068   [ +  +  +  + ]:         36 :                 if (x >= 3 && srv_type_label_is_valid(c, cn)) {
    1069                 :            : 
    1070         [ +  - ]:         20 :                         if (dns_service_name_label_is_valid(a, an)) {
    1071                 :            :                                 /* OK, got <name> . <type> . <type2> . <domain> */
    1072                 :            : 
    1073                 :         20 :                                 name = strndup(a, an);
    1074         [ -  + ]:         20 :                                 if (!name)
    1075                 :          0 :                                         return -ENOMEM;
    1076                 :            : 
    1077                 :         20 :                                 type = strjoin(b, ".", c);
    1078         [ -  + ]:         20 :                                 if (!type)
    1079                 :          0 :                                         return -ENOMEM;
    1080                 :            : 
    1081                 :         20 :                                 d = p;
    1082                 :         20 :                                 goto finish;
    1083                 :            :                         }
    1084                 :            : 
    1085         [ +  - ]:         16 :                 } else if (srv_type_label_is_valid(a, an)) {
    1086                 :            : 
    1087                 :            :                         /* OK, got <type> . <type2> . <domain> */
    1088                 :            : 
    1089                 :         16 :                         name = NULL;
    1090                 :            : 
    1091                 :         16 :                         type = strjoin(a, ".", b);
    1092         [ -  + ]:         16 :                         if (!type)
    1093                 :          0 :                                 return -ENOMEM;
    1094                 :            : 
    1095                 :         16 :                         d = q;
    1096                 :         16 :                         goto finish;
    1097                 :            :                 }
    1098                 :            :         }
    1099                 :            : 
    1100                 :         16 :         name = NULL;
    1101                 :         16 :         type = NULL;
    1102                 :         16 :         d = joined;
    1103                 :            : 
    1104                 :         52 : finish:
    1105                 :         52 :         r = dns_name_normalize(d, 0, &domain);
    1106         [ -  + ]:         52 :         if (r < 0)
    1107                 :          0 :                 return r;
    1108                 :            : 
    1109         [ +  - ]:         52 :         if (_domain)
    1110                 :         52 :                 *_domain = TAKE_PTR(domain);
    1111                 :            : 
    1112         [ +  - ]:         52 :         if (_type)
    1113                 :         52 :                 *_type = TAKE_PTR(type);
    1114                 :            : 
    1115         [ +  - ]:         52 :         if (_name)
    1116                 :         52 :                 *_name = TAKE_PTR(name);
    1117                 :            : 
    1118                 :         52 :         return 0;
    1119                 :            : }
    1120                 :            : 
    1121                 :        144 : static int dns_name_build_suffix_table(const char *name, const char *table[]) {
    1122                 :            :         const char *p;
    1123                 :        144 :         unsigned n = 0;
    1124                 :            :         int r;
    1125                 :            : 
    1126         [ -  + ]:        144 :         assert(name);
    1127         [ -  + ]:        144 :         assert(table);
    1128                 :            : 
    1129                 :        144 :         p = name;
    1130                 :            :         for (;;) {
    1131         [ -  + ]:        344 :                 if (n > DNS_N_LABELS_MAX)
    1132                 :          0 :                         return -EINVAL;
    1133                 :            : 
    1134                 :        344 :                 table[n] = p;
    1135                 :        344 :                 r = dns_name_parent(&p);
    1136         [ -  + ]:        344 :                 if (r < 0)
    1137                 :          0 :                         return r;
    1138         [ +  + ]:        344 :                 if (r == 0)
    1139                 :        144 :                         break;
    1140                 :            : 
    1141                 :        200 :                 n++;
    1142                 :            :         }
    1143                 :            : 
    1144                 :        144 :         return (int) n;
    1145                 :            : }
    1146                 :            : 
    1147                 :         64 : 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         [ -  + ]:         64 :         assert(name);
    1152         [ -  + ]:         64 :         assert(ret);
    1153                 :            : 
    1154                 :         64 :         n = dns_name_build_suffix_table(name, labels);
    1155         [ -  + ]:         64 :         if (n < 0)
    1156                 :          0 :                 return n;
    1157                 :            : 
    1158         [ +  + ]:         64 :         if ((unsigned) n < n_labels)
    1159                 :         24 :                 return -EINVAL;
    1160                 :            : 
    1161                 :         40 :         *ret = labels[n - n_labels];
    1162                 :         40 :         return (int) (n - n_labels);
    1163                 :            : }
    1164                 :            : 
    1165                 :         96 : int dns_name_skip(const char *a, unsigned n_labels, const char **ret) {
    1166                 :            :         int r;
    1167                 :            : 
    1168         [ -  + ]:         96 :         assert(a);
    1169         [ -  + ]:         96 :         assert(ret);
    1170                 :            : 
    1171         [ +  + ]:        164 :         for (; n_labels > 0; n_labels--) {
    1172                 :         96 :                 r = dns_name_parent(&a);
    1173         [ -  + ]:         96 :                 if (r < 0)
    1174                 :          0 :                         return r;
    1175         [ +  + ]:         96 :                 if (r == 0) {
    1176                 :         28 :                         *ret = "";
    1177                 :         28 :                         return 0;
    1178                 :            :                 }
    1179                 :            :         }
    1180                 :            : 
    1181                 :         68 :         *ret = a;
    1182                 :         68 :         return 1;
    1183                 :            : }
    1184                 :            : 
    1185                 :         68 : int dns_name_count_labels(const char *name) {
    1186                 :         68 :         unsigned n = 0;
    1187                 :            :         const char *p;
    1188                 :            :         int r;
    1189                 :            : 
    1190         [ -  + ]:         68 :         assert(name);
    1191                 :            : 
    1192                 :         68 :         p = name;
    1193                 :            :         for (;;) {
    1194                 :        180 :                 r = dns_name_parent(&p);
    1195         [ +  + ]:        180 :                 if (r < 0)
    1196                 :          4 :                         return r;
    1197         [ +  + ]:        176 :                 if (r == 0)
    1198                 :         64 :                         break;
    1199                 :            : 
    1200         [ -  + ]:        112 :                 if (n >= DNS_N_LABELS_MAX)
    1201                 :          0 :                         return -EINVAL;
    1202                 :            : 
    1203                 :        112 :                 n++;
    1204                 :            :         }
    1205                 :            : 
    1206                 :         64 :         return (int) n;
    1207                 :            : }
    1208                 :            : 
    1209                 :         80 : int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) {
    1210                 :            :         int r;
    1211                 :            : 
    1212         [ -  + ]:         80 :         assert(a);
    1213         [ -  + ]:         80 :         assert(b);
    1214                 :            : 
    1215                 :         80 :         r = dns_name_skip(a, n_labels, &a);
    1216         [ +  + ]:         80 :         if (r <= 0)
    1217                 :         28 :                 return r;
    1218                 :            : 
    1219                 :         52 :         return dns_name_equal(a, b);
    1220                 :            : }
    1221                 :            : 
    1222                 :         40 : 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                 :         40 :         int n = 0, m = 0, k = 0, r, q;
    1225                 :            : 
    1226         [ -  + ]:         40 :         assert(a);
    1227         [ -  + ]:         40 :         assert(b);
    1228         [ -  + ]:         40 :         assert(ret);
    1229                 :            : 
    1230                 :            :         /* Determines the common suffix of domain names a and b */
    1231                 :            : 
    1232                 :         40 :         n = dns_name_build_suffix_table(a, a_labels);
    1233         [ -  + ]:         40 :         if (n < 0)
    1234                 :          0 :                 return n;
    1235                 :            : 
    1236                 :         40 :         m = dns_name_build_suffix_table(b, b_labels);
    1237         [ -  + ]:         40 :         if (m < 0)
    1238                 :          0 :                 return m;
    1239                 :            : 
    1240                 :         24 :         for (;;) {
    1241                 :            :                 char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
    1242                 :            :                 const char *x, *y;
    1243                 :            : 
    1244   [ +  +  +  + ]:         64 :                 if (k >= n || k >= m) {
    1245                 :         24 :                         *ret = a_labels[n - k];
    1246                 :         40 :                         return 0;
    1247                 :            :                 }
    1248                 :            : 
    1249                 :         40 :                 x = a_labels[n - 1 - k];
    1250                 :         40 :                 r = dns_label_unescape(&x, la, sizeof la, 0);
    1251         [ -  + ]:         40 :                 if (r < 0)
    1252                 :          0 :                         return r;
    1253                 :            : 
    1254                 :         40 :                 y = b_labels[m - 1 - k];
    1255                 :         40 :                 q = dns_label_unescape(&y, lb, sizeof lb, 0);
    1256         [ -  + ]:         40 :                 if (q < 0)
    1257                 :          0 :                         return q;
    1258                 :            : 
    1259   [ +  +  +  + ]:         40 :                 if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) {
    1260                 :         16 :                         *ret = a_labels[n - k];
    1261                 :         16 :                         return 0;
    1262                 :            :                 }
    1263                 :            : 
    1264                 :         24 :                 k++;
    1265                 :            :         }
    1266                 :            : }
    1267                 :            : 
    1268                 :         60 : 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                 :         60 :         _cleanup_free_ char *t = NULL;
    1274                 :            : 
    1275         [ -  + ]:         60 :         assert(name);
    1276         [ -  + ]:         60 :         assert(ret);
    1277                 :            : 
    1278                 :         60 :         r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
    1279                 :            :                            IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
    1280         [ +  - ]:         60 :         log_debug("idn2_lookup_u8: %s → %s", name, t);
    1281         [ +  + ]:         60 :         if (r == IDN2_OK) {
    1282         [ +  + ]:         56 :                 if (!startswith(name, "xn--")) {
    1283         [ +  - ]:         52 :                         _cleanup_free_ char *s = NULL;
    1284                 :            : 
    1285                 :         52 :                         r = idn2_to_unicode_8z8z(t, &s, 0);
    1286         [ -  + ]:         52 :                         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         [ -  + ]:         52 :                         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                 :         56 :                 *ret = TAKE_PTR(t);
    1300                 :            : 
    1301                 :         56 :                 return 1; /* *ret has been written */
    1302                 :            :         }
    1303                 :            : 
    1304         [ +  - ]:          4 :         log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
    1305         [ +  - ]:          4 :         if (r == IDN2_2HYPHEN)
    1306                 :            :                 /* The name has two hyphens — forbidden by IDNA2008 in some cases */
    1307                 :          4 :                 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                 :        100 : 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         [ +  + ]:        100 :         if (isempty(name))
    1369                 :          8 :                 return 0;
    1370                 :            : 
    1371         [ +  + ]:         92 :         if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
    1372                 :         12 :                 return 1;
    1373                 :            : 
    1374                 :         80 :         return dns_name_is_valid(name);
    1375                 :            : }

Generated by: LCOV version 1.14