Home | History | Annotate | Download | only in avahi-common
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <string.h>
     25 #include <unistd.h>
     26 #include <fcntl.h>
     27 #include <errno.h>
     28 #include <limits.h>
     29 #include <stdio.h>
     30 #include <ctype.h>
     31 #include <stdlib.h>
     32 #include <assert.h>
     33 
     34 #include "domain.h"
     35 #include "avahi-malloc.h"
     36 #include "error.h"
     37 #include "address.h"
     38 #include "utf8.h"
     39 
     40 /* Read the first label from string *name, unescape "\" and write it to dest */
     41 char *avahi_unescape_label(const char **name, char *dest, size_t size) {
     42     unsigned i = 0;
     43     char *d;
     44 
     45     assert(dest);
     46     assert(size > 0);
     47     assert(name);
     48 
     49     d = dest;
     50 
     51     for (;;) {
     52         if (i >= size)
     53             return NULL;
     54 
     55         if (**name == '.') {
     56             (*name)++;
     57             break;
     58         }
     59 
     60         if (**name == 0)
     61             break;
     62 
     63         if (**name == '\\') {
     64             /* Escaped character */
     65 
     66             (*name) ++;
     67 
     68             if (**name == 0)
     69                 /* Ending NUL */
     70                 return NULL;
     71 
     72             else if (**name == '\\' || **name == '.') {
     73                 /* Escaped backslash or dot */
     74                 *(d++) = *((*name) ++);
     75                 i++;
     76             } else if (isdigit(**name)) {
     77                 int n;
     78 
     79                 /* Escaped literal ASCII character */
     80 
     81                 if (!isdigit(*(*name+1)) || !isdigit(*(*name+2)))
     82                     return NULL;
     83 
     84                 n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0'));
     85 
     86                 if (n > 255 || n == 0)
     87                     return NULL;
     88 
     89                 *(d++) = (char) n;
     90                 i++;
     91 
     92                 (*name) += 3;
     93             } else
     94                 return NULL;
     95 
     96         } else {
     97 
     98             /* Normal character */
     99 
    100             *(d++) = *((*name) ++);
    101             i++;
    102         }
    103     }
    104 
    105     assert(i < size);
    106 
    107     *d = 0;
    108 
    109     if (!avahi_utf8_valid(dest))
    110         return NULL;
    111 
    112     return dest;
    113 }
    114 
    115 /* Escape "\" and ".", append \0 */
    116 char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) {
    117     char *r;
    118 
    119     assert(src);
    120     assert(ret_name);
    121     assert(*ret_name);
    122     assert(ret_size);
    123     assert(*ret_size > 0);
    124 
    125     r = *ret_name;
    126 
    127     while (src_length > 0) {
    128         if (*src == '.' || *src == '\\') {
    129 
    130             /* Dot or backslash */
    131 
    132             if (*ret_size < 3)
    133                 return NULL;
    134 
    135             *((*ret_name) ++) = '\\';
    136             *((*ret_name) ++) = *src;
    137             (*ret_size) -= 2;
    138 
    139         } else if (
    140             *src == '_' ||
    141             *src == '-' ||
    142             (*src >= '0' && *src <= '9') ||
    143             (*src >= 'a' && *src <= 'z') ||
    144             (*src >= 'A' && *src <= 'Z')) {
    145 
    146             /* Proper character */
    147 
    148             if (*ret_size < 2)
    149                 return NULL;
    150 
    151             *((*ret_name)++) = *src;
    152             (*ret_size) --;
    153 
    154         } else {
    155 
    156             /* Everything else */
    157 
    158             if (*ret_size < 5)
    159                 return NULL;
    160 
    161             *((*ret_name) ++) = '\\';
    162             *((*ret_name) ++) = '0' + (char)  ((uint8_t) *src / 100);
    163             *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10);
    164             *((*ret_name) ++) = '0' + (char)  ((uint8_t) *src % 10);
    165 
    166             (*ret_size) -= 4;
    167         }
    168 
    169         src_length --;
    170         src++;
    171     }
    172 
    173     **ret_name = 0;
    174 
    175     return r;
    176 }
    177 
    178 char *avahi_normalize_name(const char *s, char *ret_s, size_t size) {
    179     int empty = 1;
    180     char *r;
    181 
    182     assert(s);
    183     assert(ret_s);
    184     assert(size > 0);
    185 
    186     r = ret_s;
    187     *ret_s = 0;
    188 
    189     while (*s) {
    190         char label[AVAHI_LABEL_MAX];
    191 
    192         if (!(avahi_unescape_label(&s, label, sizeof(label))))
    193             return NULL;
    194 
    195         if (label[0] == 0) {
    196 
    197             if (*s == 0 && empty)
    198                 return ret_s;
    199 
    200             return NULL;
    201         }
    202 
    203         if (!empty) {
    204             if (size < 1)
    205                 return NULL;
    206 
    207             *(r++) = '.';
    208             size--;
    209 
    210         } else
    211             empty = 0;
    212 
    213         avahi_escape_label(label, strlen(label), &r, &size);
    214     }
    215 
    216     return ret_s;
    217 }
    218 
    219 char *avahi_normalize_name_strdup(const char *s) {
    220     char t[AVAHI_DOMAIN_NAME_MAX];
    221     assert(s);
    222 
    223     if (!(avahi_normalize_name(s, t, sizeof(t))))
    224         return NULL;
    225 
    226     return avahi_strdup(t);
    227 }
    228 
    229 int avahi_domain_equal(const char *a, const char *b) {
    230     assert(a);
    231     assert(b);
    232 
    233     if (a == b)
    234         return 1;
    235 
    236     for (;;) {
    237         char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r;
    238 
    239         r = avahi_unescape_label(&a, ca, sizeof(ca));
    240         assert(r);
    241         r = avahi_unescape_label(&b, cb, sizeof(cb));
    242         assert(r);
    243 
    244         if (strcasecmp(ca, cb))
    245             return 0;
    246 
    247         if (!*a && !*b)
    248             return 1;
    249     }
    250 
    251     return 1;
    252 }
    253 
    254 int avahi_is_valid_service_type_generic(const char *t) {
    255     assert(t);
    256 
    257     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
    258         return 0;
    259 
    260     do {
    261         char label[AVAHI_LABEL_MAX];
    262 
    263         if (!(avahi_unescape_label(&t, label, sizeof(label))))
    264             return 0;
    265 
    266         if (strlen(label) <= 2 || label[0] != '_')
    267             return 0;
    268 
    269     } while (*t);
    270 
    271     return 1;
    272 }
    273 
    274 int avahi_is_valid_service_type_strict(const char *t) {
    275     char label[AVAHI_LABEL_MAX];
    276     assert(t);
    277 
    278     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
    279         return 0;
    280 
    281     /* Application name */
    282 
    283     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    284         return 0;
    285 
    286     if (strlen(label) <= 2 || label[0] != '_')
    287         return 0;
    288 
    289     if (!*t)
    290         return 0;
    291 
    292     /* _tcp or _udp boilerplate */
    293 
    294     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    295         return 0;
    296 
    297     if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
    298         return 0;
    299 
    300     if (*t)
    301         return 0;
    302 
    303     return 1;
    304 }
    305 
    306 const char *avahi_get_type_from_subtype(const char *t) {
    307     char label[AVAHI_LABEL_MAX];
    308     const char *ret;
    309     assert(t);
    310 
    311     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
    312         return NULL;
    313 
    314     /* Subtype name */
    315 
    316     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    317         return NULL;
    318 
    319     if (strlen(label) <= 2 || label[0] != '_')
    320         return NULL;
    321 
    322     if (!*t)
    323         return NULL;
    324 
    325     /* String "_sub" */
    326 
    327     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    328         return NULL;
    329 
    330     if (strcasecmp(label, "_sub"))
    331         return NULL;
    332 
    333     if (!*t)
    334         return NULL;
    335 
    336     ret = t;
    337 
    338     /* Application name */
    339 
    340     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    341         return NULL;
    342 
    343     if (strlen(label) <= 2 || label[0] != '_')
    344         return NULL;
    345 
    346     if (!*t)
    347         return NULL;
    348 
    349     /* _tcp or _udp boilerplate */
    350 
    351     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    352         return NULL;
    353 
    354     if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
    355         return NULL;
    356 
    357     if (*t)
    358         return NULL;
    359 
    360     return ret;
    361 }
    362 
    363 int avahi_is_valid_service_subtype(const char *t) {
    364     assert(t);
    365 
    366     return !!avahi_get_type_from_subtype(t);
    367 }
    368 
    369 int avahi_is_valid_domain_name(const char *t) {
    370     int is_first = 1;
    371     assert(t);
    372 
    373     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
    374         return 0;
    375 
    376     do {
    377         char label[AVAHI_LABEL_MAX];
    378 
    379         if (!(avahi_unescape_label(&t, label, sizeof(label))))
    380             return 0;
    381 
    382         /* Explicitly allow the root domain name */
    383         if (is_first && label[0] == 0 && *t == 0)
    384             return 1;
    385 
    386         is_first = 0;
    387 
    388         if (label[0] == 0)
    389             return 0;
    390 
    391     } while (*t);
    392 
    393     return 1;
    394 }
    395 
    396 int avahi_is_valid_service_name(const char *t) {
    397     assert(t);
    398 
    399     if (strlen(t) >= AVAHI_LABEL_MAX || !*t)
    400         return 0;
    401 
    402     return 1;
    403 }
    404 
    405 int avahi_is_valid_host_name(const char *t) {
    406     char label[AVAHI_LABEL_MAX];
    407     assert(t);
    408 
    409     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
    410         return 0;
    411 
    412     if (!(avahi_unescape_label(&t, label, sizeof(label))))
    413         return 0;
    414 
    415     if (strlen(label) < 1)
    416         return 0;
    417 
    418     if (*t)
    419         return 0;
    420 
    421     return 1;
    422 }
    423 
    424 unsigned avahi_domain_hash(const char *s) {
    425     unsigned hash = 0;
    426 
    427     while (*s) {
    428         char c[AVAHI_LABEL_MAX], *p, *r;
    429 
    430         r = avahi_unescape_label(&s, c, sizeof(c));
    431         assert(r);
    432 
    433         for (p = c; *p; p++)
    434             hash = 31 * hash + tolower(*p);
    435     }
    436 
    437     return hash;
    438 }
    439 
    440 int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) {
    441     char escaped_name[AVAHI_LABEL_MAX*4];
    442     char normalized_type[AVAHI_DOMAIN_NAME_MAX];
    443     char normalized_domain[AVAHI_DOMAIN_NAME_MAX];
    444 
    445     assert(p);
    446 
    447     /* Validity checks */
    448 
    449     if ((name && !avahi_is_valid_service_name(name)))
    450         return AVAHI_ERR_INVALID_SERVICE_NAME;
    451 
    452     if (!avahi_is_valid_service_type_generic(type))
    453         return AVAHI_ERR_INVALID_SERVICE_TYPE;
    454 
    455     if (!avahi_is_valid_domain_name(domain))
    456         return AVAHI_ERR_INVALID_DOMAIN_NAME;
    457 
    458     /* Preparation */
    459 
    460     if (name) {
    461         size_t l = sizeof(escaped_name);
    462         char *e = escaped_name, *r;
    463         r = avahi_escape_label(name, strlen(name), &e, &l);
    464         assert(r);
    465     }
    466 
    467     if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type))))
    468         return AVAHI_ERR_INVALID_SERVICE_TYPE;
    469 
    470     if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain))))
    471         return AVAHI_ERR_INVALID_DOMAIN_NAME;
    472 
    473     /* Concatenation */
    474 
    475     snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain);
    476 
    477     return AVAHI_OK;
    478 }
    479 
    480 #ifndef HAVE_STRLCPY
    481 
    482 static size_t strlcpy(char *dest, const char *src, size_t n) {
    483     assert(dest);
    484     assert(src);
    485 
    486     if (n > 0) {
    487         strncpy(dest, src, n-1);
    488         dest[n-1] = 0;
    489     }
    490 
    491     return strlen(src);
    492 }
    493 
    494 #endif
    495 
    496 int avahi_service_name_split(const char *p, char *name, size_t name_size, char *type, size_t type_size, char *domain, size_t domain_size) {
    497     enum {
    498         NAME,
    499         TYPE,
    500         DOMAIN
    501     } state;
    502     int type_empty = 1, domain_empty = 1;
    503 
    504     assert(p);
    505     assert(type);
    506     assert(type_size > 0);
    507     assert(domain);
    508     assert(domain_size > 0);
    509 
    510     if (name) {
    511         assert(name_size > 0);
    512         *name = 0;
    513         state = NAME;
    514     } else
    515         state = TYPE;
    516 
    517     *type = *domain = 0;
    518 
    519     while (*p) {
    520         char buf[64];
    521 
    522         if (!(avahi_unescape_label(&p, buf, sizeof(buf))))
    523             return -1;
    524 
    525         switch (state) {
    526             case NAME:
    527                 strlcpy(name, buf, name_size);
    528                 state = TYPE;
    529                 break;
    530 
    531             case TYPE:
    532 
    533                 if (buf[0] == '_') {
    534 
    535                     if (!type_empty) {
    536                         if (!type_size)
    537                             return AVAHI_ERR_NO_MEMORY;
    538 
    539                         *(type++) = '.';
    540                         type_size --;
    541 
    542                     } else
    543                         type_empty = 0;
    544 
    545                     if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size)))
    546                         return AVAHI_ERR_NO_MEMORY;
    547 
    548                     break;
    549                 }
    550 
    551                 state = DOMAIN;
    552                 /* fall through */
    553 
    554             case DOMAIN:
    555 
    556                 if (!domain_empty) {
    557                     if (!domain_size)
    558                         return AVAHI_ERR_NO_MEMORY;
    559 
    560                     *(domain++) = '.';
    561                     domain_size --;
    562                 } else
    563                     domain_empty = 0;
    564 
    565                 if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size)))
    566                     return AVAHI_ERR_NO_MEMORY;
    567 
    568                 break;
    569         }
    570     }
    571 
    572     return 0;
    573 }
    574 
    575 int avahi_is_valid_fqdn(const char *t) {
    576     char label[AVAHI_LABEL_MAX];
    577     char normalized[AVAHI_DOMAIN_NAME_MAX];
    578     const char *k = t;
    579     AvahiAddress a;
    580     assert(t);
    581 
    582     if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
    583         return 0;
    584 
    585     if (!avahi_is_valid_domain_name(t))
    586         return 0;
    587 
    588     /* Check if there are at least two labels*/
    589     if (!(avahi_unescape_label(&k, label, sizeof(label))))
    590         return 0;
    591 
    592     if (label[0] == 0 || !k)
    593         return 0;
    594 
    595     if (!(avahi_unescape_label(&k, label, sizeof(label))))
    596         return 0;
    597 
    598     if (label[0] == 0 || !k)
    599         return 0;
    600 
    601     /* Make sure that the name is not an IP address */
    602     if (!(avahi_normalize_name(t, normalized, sizeof(normalized))))
    603         return 0;
    604 
    605     if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a))
    606         return 0;
    607 
    608     return 1;
    609 }
    610