X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=avahi-common%2Fdomain.c;h=3b1ab683494e9e5caf6094944bd4e2e41ae96bdd;hb=9c0f9c65093cfa53d45f9b68782321eb8063a032;hp=3703a0426f49352ab3e594beb2bc88d543876b0b;hpb=4f0a5e7572a4257894b4bfede42c26d65152609e;p=catta diff --git a/avahi-common/domain.c b/avahi-common/domain.c index 3703a04..3b1ab68 100644 --- a/avahi-common/domain.c +++ b/avahi-common/domain.c @@ -1,18 +1,16 @@ -/* $Id$ */ - /*** This file is part of avahi. - + avahi is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + avahi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with avahi; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -35,81 +33,21 @@ #include "domain.h" #include "malloc.h" - -char *avahi_get_host_name(void) { -#ifdef HOST_NAME_MAX - char t[HOST_NAME_MAX]; -#else - char t[256]; -#endif - gethostname(t, sizeof(t)); - t[sizeof(t)-1] = 0; - return avahi_normalize_name(t); -} - -static char *unescape_uneeded(const char *src, char *ret_dest, size_t size) { - int escaped = 0; - - assert(src); - assert(ret_dest); - assert(size > 0); - - for (; *src; src++) { - - if (!escaped && *src == '\\') - escaped = 1; - else if (escaped && (*src == '.' || *src == '\\')) { - - if ((size -= 2) <= 1) break; - - *(ret_dest++) = '\\'; - *(ret_dest++) = *src; - escaped = 0; - } else { - if (--size <= 1) break; - - *(ret_dest++) = *src; - escaped = 0; - } - - } - - *ret_dest = 0; - - return ret_dest; -} - -char *avahi_normalize_name(const char *s) { - char tmp[256]; - size_t l; - - assert(s); - - unescape_uneeded(s, tmp, sizeof(tmp)); - - l = strlen(tmp); - - while (l > 0 && tmp[l-1] == '.') - tmp[--l] = 0; - - return avahi_strdup(tmp); -} - +#include "error.h" +#include "address.h" +#include "utf8.h" /* Read the first label from string *name, unescape "\" and write it to dest */ char *avahi_unescape_label(const char **name, char *dest, size_t size) { unsigned i = 0; char *d; - + assert(dest); assert(size > 0); assert(name); - if (!**name) - return NULL; - d = dest; - + for (;;) { if (i >= size) return NULL; @@ -118,30 +56,64 @@ char *avahi_unescape_label(const char **name, char *dest, size_t size) { (*name)++; break; } - + if (**name == 0) break; - + if (**name == '\\') { + /* Escaped character */ + (*name) ++; - + if (**name == 0) - break; + /* Ending NUL */ + return NULL; + + else if (**name == '\\' || **name == '.') { + /* Escaped backslash or dot */ + *(d++) = *((*name) ++); + i++; + } else if (isdigit(**name)) { + int n; + + /* Escaped literal ASCII character */ + + if (!isdigit(*(*name+1)) || !isdigit(*(*name+2))) + return NULL; + + n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0')); + + if (n > 255 || n == 0) + return NULL; + + *(d++) = (char) n; + i++; + + (*name) += 3; + } else + return NULL; + + } else { + + /* Normal character */ + + *(d++) = *((*name) ++); + i++; } - - *(d++) = *((*name) ++); - i++; } assert(i < size); *d = 0; + if (!avahi_utf8_valid(dest)) + return NULL; + return dest; } /* Escape "\" and ".", append \0 */ -char *avahi_escape_label(const uint8_t* src, size_t src_length, char **ret_name, size_t *ret_size) { +char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) { char *r; assert(src); @@ -154,18 +126,45 @@ char *avahi_escape_label(const uint8_t* src, size_t src_length, char **ret_name, while (src_length > 0) { if (*src == '.' || *src == '\\') { + + /* Dot or backslash */ + if (*ret_size < 3) return NULL; - + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = *src; + (*ret_size) -= 2; + + } else if ( + *src == '_' || + *src == '-' || + (*src >= '0' && *src <= '9') || + (*src >= 'a' && *src <= 'z') || + (*src >= 'A' && *src <= 'Z')) { + + /* Proper character */ + + if (*ret_size < 2) + return NULL; + + *((*ret_name)++) = *src; (*ret_size) --; - } - if (*ret_size < 2) - return NULL; - - *((*ret_name)++) = *src; - (*ret_size) --; + } else { + + /* Everything else */ + + if (*ret_size < 5) + return NULL; + + *((*ret_name) ++) = '\\'; + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100); + *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10); + *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10); + + (*ret_size) -= 4; + } src_length --; src++; @@ -176,146 +175,247 @@ char *avahi_escape_label(const uint8_t* src, size_t src_length, char **ret_name, return r; } +char *avahi_normalize_name(const char *s, char *ret_s, size_t size) { + int empty = 1; + char *r; + + assert(s); + assert(ret_s); + assert(size > 0); + + r = ret_s; + *ret_s = 0; + + while (*s) { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&s, label, sizeof(label)))) + return NULL; + + if (label[0] == 0) { + + if (*s == 0 && empty) + return ret_s; + + return NULL; + } + + if (!empty) { + if (size < 1) + return NULL; + + *(r++) = '.'; + size--; + + } else + empty = 0; + + avahi_escape_label(label, strlen(label), &r, &size); + } + + return ret_s; +} + +char *avahi_normalize_name_strdup(const char *s) { + char t[AVAHI_DOMAIN_NAME_MAX]; + assert(s); + + if (!(avahi_normalize_name(s, t, sizeof(t)))) + return NULL; + + return avahi_strdup(t); +} + int avahi_domain_equal(const char *a, const char *b) { assert(a); assert(b); if (a == b) return 1; - + for (;;) { - char ca[65], cb[65], *pa, *pb; + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r; - pa = avahi_unescape_label(&a, ca, sizeof(ca)); - pb = avahi_unescape_label(&b, cb, sizeof(cb)); + r = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(r); + r = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(r); - if (!pa && !pb) - return 1; - else if ((pa && !pb) || (!pa && pb)) + if (strcasecmp(ca, cb)) return 0; - if (strcasecmp(pa, pb)) - return 0; + if (!*a && !*b) + return 1; } return 1; } -int avahi_binary_domain_cmp(const char *a, const char *b) { - assert(a); - assert(b); +int avahi_is_valid_service_type_generic(const char *t) { + assert(t); - if (a == b) + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) return 0; - for (;;) { - char ca[65], cb[65], *pa, *pb; - int r; + do { + char label[AVAHI_LABEL_MAX]; - pa = avahi_unescape_label(&a, ca, sizeof(ca)); - pb = avahi_unescape_label(&b, cb, sizeof(cb)); + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; - if (!pa && !pb) + if (strlen(label) <= 2 || label[0] != '_') return 0; - else if (pa && !pb) - return 1; - else if (!pa && pb) - return -1; - - if ((r = strcmp(pa, pb))) - return r; - } + + } while (*t); + + return 1; } -int avahi_is_valid_service_type(const char *t) { - const char *p; +int avahi_is_valid_service_type_strict(const char *t) { + char label[AVAHI_LABEL_MAX]; assert(t); - if (strlen(t) < 5) + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) return 0; - - if (*t != '_') + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) return 0; - if (!(p = strchr(t, '.'))) + if (strlen(label) <= 2 || label[0] != '_') return 0; - if (p - t > 63 || p - t < 2) + if (!*t) return 0; - if (*(++p) != '_') + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) return 0; - if (strchr(p, '.')) + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) return 0; - if (strlen(p) > 63 || strlen(p) < 2) + if (*t) return 0; - + return 1; } -int avahi_is_valid_domain_name(const char *t) { - const char *p, *dp; - int dot = 0; - +const char *avahi_get_type_from_subtype(const char *t) { + char label[AVAHI_LABEL_MAX]; + const char *ret; assert(t); - if (*t == 0) - return 0; + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return NULL; - /* Domains may not start with a dot */ - if (*t == '.') - return 0; + /* Subtype name */ - dp = t; + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; - for (p = t; *p; p++) { + if (strlen(label) <= 2 || label[0] != '_') + return NULL; - if (*p == '.') { - if (dot) /* Two subsequent dots */ - return 0; + if (!*t) + return NULL; - if (p - dp > 63) - return 0; + /* String "_sub" */ - dot = 1; - dp = p + 1; - } else - dot = 0; + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; - } + if (strcasecmp(label, "_sub")) + return NULL; + + if (!*t) + return NULL; + + ret = t; + + /* Application name */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strlen(label) <= 2 || label[0] != '_') + return NULL; + + if (!*t) + return NULL; + + /* _tcp or _udp boilerplate */ + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return NULL; + + if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) + return NULL; + + if (*t) + return NULL; + + return ret; +} + +int avahi_is_valid_service_subtype(const char *t) { + assert(t); - if (p - dp > 63) + return !!avahi_get_type_from_subtype(t); +} + +int avahi_is_valid_domain_name(const char *t) { + int is_first = 1; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) return 0; - /* A trailing dot IS allowed */ - + do { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; + + /* Explicitly allow the root domain name */ + if (is_first && label[0] == 0 && *t == 0) + return 1; + + is_first = 0; + + if (label[0] == 0) + return 0; + + } while (*t); + return 1; } int avahi_is_valid_service_name(const char *t) { assert(t); - if (*t == 0) - return 0; - - if (strlen(t) > 63) + if (strlen(t) >= AVAHI_LABEL_MAX || !*t) return 0; return 1; } int avahi_is_valid_host_name(const char *t) { + char label[AVAHI_LABEL_MAX]; assert(t); - if (*t == 0) + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) + return 0; + + if (!(avahi_unescape_label(&t, label, sizeof(label)))) return 0; - if (strlen(t) > 63) + if (strlen(label) < 1) return 0; - if (strchr(t, '.')) + if (*t) return 0; return 1; @@ -323,17 +423,187 @@ int avahi_is_valid_host_name(const char *t) { unsigned avahi_domain_hash(const char *s) { unsigned hash = 0; - - for (;;) { - char c[65], *p; - if (!avahi_unescape_label(&s, c, sizeof(c))) - return hash; + while (*s) { + char c[AVAHI_LABEL_MAX], *p, *r; - if (!c[0]) - continue; + r = avahi_unescape_label(&s, c, sizeof(c)); + assert(r); for (p = c; *p; p++) hash = 31 * hash + tolower(*p); } + + return hash; +} + +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) { + char escaped_name[AVAHI_LABEL_MAX*4]; + char normalized_type[AVAHI_DOMAIN_NAME_MAX]; + char normalized_domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(p); + + /* Validity checks */ + + if ((name && !avahi_is_valid_service_name(name))) + return AVAHI_ERR_INVALID_SERVICE_NAME; + + if (!avahi_is_valid_service_type_generic(type)) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!avahi_is_valid_domain_name(domain)) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Preparation */ + + if (name) { + size_t l = sizeof(escaped_name); + char *e = escaped_name, *r; + r = avahi_escape_label(name, strlen(name), &e, &l); + assert(r); + } + + if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type)))) + return AVAHI_ERR_INVALID_SERVICE_TYPE; + + if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain)))) + return AVAHI_ERR_INVALID_DOMAIN_NAME; + + /* Concatenation */ + + snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); + + return AVAHI_OK; +} + +#ifndef HAVE_STRLCPY + +static size_t strlcpy(char *dest, const char *src, size_t n) { + assert(dest); + assert(src); + + if (n > 0) { + strncpy(dest, src, n-1); + dest[n-1] = 0; + } + + return strlen(src); +} + +#endif + +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) { + enum { + NAME, + TYPE, + DOMAIN + } state; + int type_empty = 1, domain_empty = 1; + + assert(p); + assert(type); + assert(type_size > 0); + assert(domain); + assert(domain_size > 0); + + if (name) { + assert(name_size > 0); + *name = 0; + state = NAME; + } else + state = TYPE; + + *type = *domain = 0; + + while (*p) { + char buf[64]; + + if (!(avahi_unescape_label(&p, buf, sizeof(buf)))) + return -1; + + switch (state) { + case NAME: + strlcpy(name, buf, name_size); + state = TYPE; + break; + + case TYPE: + + if (buf[0] == '_') { + + if (!type_empty) { + if (!type_size) + return AVAHI_ERR_NO_MEMORY; + + *(type++) = '.'; + type_size --; + + } else + type_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + + state = DOMAIN; + /* fall through */ + + case DOMAIN: + + if (!domain_empty) { + if (!domain_size) + return AVAHI_ERR_NO_MEMORY; + + *(domain++) = '.'; + domain_size --; + } else + domain_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size))) + return AVAHI_ERR_NO_MEMORY; + + break; + } + } + + return 0; +} + +int avahi_is_valid_fqdn(const char *t) { + char label[AVAHI_LABEL_MAX]; + char normalized[AVAHI_DOMAIN_NAME_MAX]; + const char *k = t; + AvahiAddress a; + assert(t); + + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) + return 0; + + if (!avahi_is_valid_domain_name(t)) + return 0; + + /* Check if there are at least two labels*/ + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + if (!(avahi_unescape_label(&k, label, sizeof(label)))) + return 0; + + if (label[0] == 0 || !k) + return 0; + + /* Make sure that the name is not an IP address */ + if (!(avahi_normalize_name(t, normalized, sizeof(normalized)))) + return 0; + + if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a)) + return 0; + + return 1; }