/lib/avahi/avahi-0.6.19/avahi-common/domain.c
C | 611 lines | 388 code | 181 blank | 42 comment | 144 complexity | 85ba261a320705d6af69f5cbff950959 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-3.0, Unlicense, GPL-2.0, GPL-3.0, CC-BY-SA-3.0, AGPL-1.0, ISC, MIT, 0BSD, LGPL-2.0
- /* $Id: domain.c 1202 2006-04-24 21:52:34Z lennart $ */
- /***
- 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
- USA.
- ***/
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <assert.h>
- #include "domain.h"
- #include "malloc.h"
- #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);
- d = dest;
-
- for (;;) {
- if (i >= size)
- return NULL;
- if (**name == '.') {
- (*name)++;
- break;
- }
-
- if (**name == 0)
- break;
-
- if (**name == '\\') {
- /* Escaped character */
- (*name) ++;
- if (**name == 0)
- /* 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++;
- }
- }
- assert(i < size);
- *d = 0;
- if (!avahi_utf8_valid(dest))
- return NULL;
- return dest;
- }
- /* Escape "\" and ".", append \0 */
- char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) {
- char *r;
- assert(src);
- assert(ret_name);
- assert(*ret_name);
- assert(ret_size);
- assert(*ret_size > 0);
- r = *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) --;
-
- } 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++;
- }
- **ret_name = 0;
- 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[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r;
- r = avahi_unescape_label(&a, ca, sizeof(ca));
- assert(r);
- r = avahi_unescape_label(&b, cb, sizeof(cb));
- assert(r);
- if (strcasecmp(ca, cb))
- return 0;
-
- if (!*a && !*b)
- return 1;
- }
- return 1;
- }
- int avahi_is_valid_service_type_generic(const char *t) {
- assert(t);
- if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
- return 0;
- do {
- char label[AVAHI_LABEL_MAX];
- if (!(avahi_unescape_label(&t, label, sizeof(label))))
- return 0;
- if (strlen(label) <= 2 || label[0] != '_')
- return 0;
-
- } while (*t);
- return 1;
- }
- int avahi_is_valid_service_type_strict(const char *t) {
- char label[AVAHI_LABEL_MAX];
- assert(t);
- if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
- return 0;
- /* Application name */
-
- if (!(avahi_unescape_label(&t, label, sizeof(label))))
- return 0;
- if (strlen(label) <= 2 || label[0] != '_')
- return 0;
- if (!*t)
- return 0;
- /* _tcp or _udp boilerplate */
-
- if (!(avahi_unescape_label(&t, label, sizeof(label))))
- return 0;
- if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
- return 0;
- if (*t)
- return 0;
-
- return 1;
- }
- const char *avahi_get_type_from_subtype(const char *t) {
- char label[AVAHI_LABEL_MAX];
- const char *ret;
- assert(t);
- if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
- return NULL;
- /* Subtype name */
-
- if (!(avahi_unescape_label(&t, label, sizeof(label))))
- return NULL;
- if (strlen(label) <= 2 || label[0] != '_')
- return NULL;
- if (!*t)
- return NULL;
- /* String "_sub" */
-
- 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);
- 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;
- 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 (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 (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
- return 0;
- if (!(avahi_unescape_label(&t, label, sizeof(label))))
- return 0;
- if (strlen(label) < 1)
- return 0;
- if (*t)
- return 0;
- return 1;
- }
- unsigned avahi_domain_hash(const char *s) {
- unsigned hash = 0;
-
- while (*s) {
- char c[AVAHI_LABEL_MAX], *p, *r;
- 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;
- }