From d1dd07145bb3db5c6fd77c55aea762eb79ab3b5c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 10 Oct 2005 22:34:06 +0000 Subject: * rename avahi_service_name_snprint() to avahi_service_name_join() * add avahi_service_name_split() and make everything use it * change avahi_normalize_name() to work on a supplied buffer instead of malloc'ed memory. * add avahi_normalize_name_strdup() that retains the old behaviour avahi_normalize_name() * same thing for avahi_get_host_name()/avahi_get_host_name_strdup() * Rewrite domain name escaping and validity checking code * Remove superfluous memory allocationsfrom various browsers and do some other major cleanups * add new global macro AVAHI_CHECK_VALIDITY_RETURN_NULL() and modify many things to make use of it * add AVAHI_LABEL_MAX * patch everything to make use of AVAHI_LABEL_MAX and AVAHI_DOMAIN_NAME_MAX * add pretty printing for NS records * add partial pretty printing for SOA records * add many more validity checks to various user API functions git-svn-id: file:///home/lennart/svn/public/avahi/trunk@708 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-common/domain-test.c | 53 ++++- avahi-common/domain.c | 495 +++++++++++++++++++++++++++------------------ avahi-common/domain.h | 24 ++- 3 files changed, 362 insertions(+), 210 deletions(-) (limited to 'avahi-common') diff --git a/avahi-common/domain-test.c b/avahi-common/domain-test.c index 35ce6d6..444f553 100644 --- a/avahi-common/domain-test.c +++ b/avahi-common/domain-test.c @@ -24,42 +24,73 @@ #endif #include +#include +#include #include "domain.h" #include "malloc.h" int main(int argc, char *argv[]) { char *s; - char t[256]; + char t[256], r[256]; + const char *p; + size_t size; + char name[64], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX]; - printf("host name: %s\n", s = avahi_get_host_name()); + printf("host name: %s\n", s = avahi_get_host_name_strdup()); avahi_free(s); - printf("%s\n", s = avahi_normalize_name("foo.foo.")); + printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\046.")); avahi_free(s); - printf("%s\n", s = avahi_normalize_name("foo\.foo.")); + printf("%s\n", s = avahi_normalize_name_strdup("foo.foo\\.foo.")); avahi_free(s); - printf("%s\n", s = avahi_normalize_name("\\f\\o\\\\o\\..\\f\\ \\o\\o.")); + printf("%s\n", s = avahi_normalize_name_strdup("fo\\\\o\\..f oo.")); avahi_free(s); - printf("%i\n", avahi_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff")); - printf("%i\n", avahi_domain_equal("\\A", "a")); + printf("%i\n", avahi_domain_equal("\\065aa bbb\\.\\046cc.cc\\\\.dee.fff.", "Aaa BBB\\.\\.cc.cc\\\\.dee.fff")); + printf("%i\n", avahi_domain_equal("A", "a")); printf("%i\n", avahi_domain_equal("a", "aaa")); - printf("%u = %u\n", avahi_domain_hash("\\Aaaab\\\\."), avahi_domain_hash("aaaa\\b\\\\")); + printf("%u = %u\n", avahi_domain_hash("ccc\\065aa.aa\\.b\\\\."), avahi_domain_hash("cccAaa.aa\\.b\\\\")); - avahi_service_name_snprint(t, sizeof(t), "foo.foo.foo \.", "_http._tcp", "test.local"); + avahi_service_name_join(t, sizeof(t), "foo.foo.foo \.", "_http._tcp", "test.local"); printf("<%s>\n", t); - - avahi_service_name_snprint(t, sizeof(t), NULL, "_http._tcp", "one.two\. .local"); + avahi_service_name_split(t, name, sizeof(name), type, sizeof(type), domain, sizeof(domain)); + printf("name: <%s>; type: <%s>; domain <%s>\n", name, type, domain); + + avahi_service_name_join(t, sizeof(t), NULL, "_http._tcp", "one.two\. .local"); printf("<%s>\n", t); + avahi_service_name_split(t, NULL, 0, type, sizeof(type), domain, sizeof(domain)); + printf("name: <>; type: <%s>; domain <%s>\n", type, domain); + + + p = "--:---\\\\\\123\\065_äöü\\064\\.\\\\sjöödfhh.sdfjhskjdf"; + printf("unescaped: <%s>, rest: %s\n", avahi_unescape_label(&p, t, sizeof(t)), p); + + size = sizeof(r); + s = r; + printf("escaped: <%s>\n", avahi_escape_label(t, strlen(t), &s, &size)); + + p = r; + printf("unescaped: <%s>\n", avahi_unescape_label(&p, t, sizeof(t))); + + assert(avahi_domain_ends_with("foo.bar.\\065\\\\\\.aaaa", "\\065\\\\\\.aaaa")); + + assert(avahi_is_valid_service_type("_foo._bar._waldo")); + assert(!avahi_is_valid_service_type("_foo._bar.waldo")); + assert(!avahi_is_valid_service_type("")); + + assert(!avahi_is_valid_host_name("sf.ooo.")); + assert(avahi_is_valid_host_name("sfooo.")); + assert(avahi_is_valid_host_name("sfooo")); + return 0; } diff --git a/avahi-common/domain.c b/avahi-common/domain.c index bffe563..b72a898 100644 --- a/avahi-common/domain.c +++ b/avahi-common/domain.c @@ -37,66 +37,31 @@ #include "malloc.h" #include "error.h" -char *avahi_get_host_name(void) { +char *avahi_get_host_name(char *ret_s, size_t size) { #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(ret_s); 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; + gethostname(t, sizeof(t)); + t[sizeof(t)-1] = 0; - return ret_dest; + return avahi_normalize_name(t, ret_s, size); } -char *avahi_normalize_name(const char *s) { - char tmp[256]; - size_t l; - - assert(s); - - unescape_uneeded(s, tmp, sizeof(tmp)); - - l = strlen(tmp); +char *avahi_get_host_name_strdup(void) { + char t[AVAHI_DOMAIN_NAME_MAX]; - while (l > 0 && tmp[l-1] == '.') - tmp[--l] = 0; + if (!(avahi_get_host_name(t, sizeof(t)))) + return NULL; - return avahi_strdup(tmp); + return avahi_strdup(t); } - /* 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; @@ -106,9 +71,6 @@ char *avahi_unescape_label(const char **name, char *dest, size_t size) { assert(size > 0); assert(name); - if (!**name) - return NULL; - d = dest; for (;;) { @@ -124,14 +86,45 @@ char *avahi_unescape_label(const char **name, char *dest, size_t size) { 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); @@ -142,7 +135,7 @@ char *avahi_unescape_label(const char **name, char *dest, size_t size) { } /* 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); @@ -155,18 +148,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) --; - } + + } else { - if (*ret_size < 2) - return NULL; - - *((*ret_name)++) = *src; - (*ret_size) --; + /* 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++; @@ -177,6 +197,53 @@ 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; + while (*s) { + char label[AVAHI_LABEL_MAX]; + + if (!(avahi_unescape_label(&s, label, sizeof(label)))) + return NULL; + + if (strlen(label) > 0) { + + if (!empty) { + if (size < 1) + return NULL; + + *(r++) = '.'; + size--; + + } else + empty = 0; + + avahi_escape_label(label, strlen(label), &r, &size); + } + } + + if (empty) + return NULL; + + 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); @@ -185,18 +252,18 @@ int avahi_domain_equal(const char *a, const char *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)) - return 0; - - if (strcasecmp(pa, pb)) + if (strcasecmp(ca, cb)) return 0; + + if (!*a && !*b) + return 1; } return 1; @@ -210,113 +277,85 @@ int avahi_binary_domain_cmp(const char *a, const char *b) { return 0; for (;;) { - char ca[65], cb[65], *pa, *pb; + char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p; int r; - pa = avahi_unescape_label(&a, ca, sizeof(ca)); - pb = avahi_unescape_label(&b, cb, sizeof(cb)); + p = avahi_unescape_label(&a, ca, sizeof(ca)); + assert(p); + p = avahi_unescape_label(&b, cb, sizeof(cb)); + assert(p); - if (!pa && !pb) - return 0; - else if (pa && !pb) - return 1; - else if (!pa && pb) - return -1; - - if ((r = strcmp(pa, pb))) + if ((r = strcmp(ca, cb))) return r; + + if (!*a && !*b) + return 0; } } int avahi_is_valid_service_type(const char *t) { - const char *p; assert(t); - if (strlen(t) < 5) - return 0; - - if (*t != '_') + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) return 0; - if (!(p = strchr(t, '.'))) - return 0; + do { + char label[AVAHI_LABEL_MAX]; - if (p - t > 63 || p - t < 2) - return 0; - - if (*(++p) != '_') - return 0; + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; - if (strchr(p, '.')) - return 0; + if (strlen(label) <= 2 || label[0] != '_') + return 0; + + } while (*t); - if (strlen(p) > 63 || strlen(p) < 2) - return 0; - return 1; } int avahi_is_valid_domain_name(const char *t) { - const char *p, *dp; - int dot = 0; - assert(t); - if (*t == 0) - return 0; - - /* Domains may not start with a dot */ - if (*t == '.') + if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) return 0; - dp = t; + do { + char label[AVAHI_LABEL_MAX]; - for (p = t; *p; p++) { - - if (*p == '.') { - if (dot) /* Two subsequent dots */ - return 0; - - if (p - dp > 63) - return 0; - - dot = 1; - dp = p + 1; - } else - dot = 0; - - } + if (!(avahi_unescape_label(&t, label, sizeof(label)))) + return 0; - if (p - dp > 63) - return 0; + if (strlen(label) < 1) + return 0; + + } while (*t); - /* A trailing dot IS allowed */ - return 1; } int avahi_is_valid_service_name(const char *t) { assert(t); - if (*t == 0) + if (strlen(t) >= AVAHI_LABEL_MAX || !*t) return 0; - - if (strlen(t) > 63) - 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; @@ -325,100 +364,166 @@ 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_domain_ends_with(const char *domain, const char *suffix) { assert(domain); assert(suffix); - assert(avahi_is_valid_domain_name(domain)); - assert(avahi_is_valid_domain_name(suffix)); - for (;;) { - char dummy[64]; + char dummy[AVAHI_LABEL_MAX], *r; + + if (*domain == 0) + return 0; if (avahi_domain_equal(domain, suffix)) return 1; - if (!(avahi_unescape_label(&domain, dummy, sizeof(dummy)))) - return 0; + r = avahi_unescape_label(&domain, dummy, sizeof(dummy)); + assert(r); } } -static void escape_service_name(char *d, size_t size, const char *s) { - assert(d); - assert(size); - assert(s); +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]; + char normalized_type[AVAHI_DOMAIN_NAME_MAX]; + char normalized_domain[AVAHI_DOMAIN_NAME_MAX]; + + assert(p); - while (*s && size >= 2) { - if (*s == '.' || *s == '\\') { - if (size < 3) - break; + /* Validity checks */ + + if ((name && !avahi_is_valid_service_name(name))) + return AVAHI_ERR_INVALID_SERVICE_NAME; - *(d++) = '\\'; - size--; - } - - *(d++) = *(s++); - size--; + if (!avahi_is_valid_service_type(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); } - assert(size > 0); - *(d++) = 0; -} + 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; -int avahi_service_name_snprint(char *p, size_t size, const char *name, const char *type, const char *domain) { - char *t = NULL, *d = NULL; - char ename[64]; - int ret; + /* Concatenation */ - assert(p); + snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); - if ((name && !avahi_is_valid_service_name(name))) { - ret = AVAHI_ERR_INVALID_SERVICE_NAME; - goto fail; - } + return AVAHI_OK; +} - if (!avahi_is_valid_service_type(type)) { - ret = AVAHI_ERR_INVALID_SERVICE_TYPE; - goto fail; - } - - if (!avahi_is_valid_domain_name(domain)) { - ret = AVAHI_ERR_INVALID_DOMAIN_NAME; - goto fail; - } + +char *avahi_strlcpy(char *dest, const char *src, size_t n) { + assert(dest); + assert(src); + + if (n == 0) + return dest; - if (name) - escape_service_name(ename, sizeof(ename), name); + strncpy(dest, src, n-1); + dest[n-1] = 0; + return dest; +} + +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; - if (!(d = avahi_normalize_name(domain)) || - !(t = avahi_normalize_name(type))) { - ret = AVAHI_ERR_NO_MEMORY; - goto fail; - } + 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; - snprintf(p, size, "%s%s%s.%s", name ? ename : "", name ? "." : "", t, d); + switch (state) { + case NAME: + avahi_strlcpy(name, buf, name_size); + state = TYPE; + break; - ret = AVAHI_OK; - -fail: + case TYPE: + + if (buf[0] == '_') { + + if (!type_empty) { + if (!type_size) + return AVAHI_ERR_NO_MEMORY; + + *(type++) = '.'; + type_size --; - avahi_free(t); - avahi_free(d); + } else + type_empty = 0; + + if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) + return AVAHI_ERR_NO_MEMORY; - return ret; + 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; } + diff --git a/avahi-common/domain.h b/avahi-common/domain.h index 59765ef..bcd036e 100644 --- a/avahi-common/domain.h +++ b/avahi-common/domain.h @@ -44,13 +44,23 @@ AVAHI_C_DECL_BEGIN * the string brings us to 1014. */ #define AVAHI_DOMAIN_NAME_MAX 1014 +/** Maxium size of an unescaped label */ +#define AVAHI_LABEL_MAX 64 + +/** Normalize a domain name into canonical form. This drops trailing + * dots and removes useless backslash escapes. */ +char *avahi_normalize_name(const char *s, char *ret_s, size_t size); + /** Normalize a domain name into canonical form. This drops trailing * dots and removes useless backslash escapes. avahi_free() the * result! */ -char *avahi_normalize_name(const char *s); +char *avahi_normalize_name_strdup(const char *s); + +/** Return the local host name. */ +char *avahi_get_host_name(char *ret_s, size_t size); /** Return the local host name. avahi_free() the result! */ -char *avahi_get_host_name(void); +char *avahi_get_host_name_strdup(void); /** Return 1 when the specified domain names are equal, 0 otherwise */ int avahi_domain_equal(const char *a, const char *b); @@ -63,7 +73,7 @@ int avahi_binary_domain_cmp(const char *a, const char *b); char *avahi_unescape_label(const char **name, char *dest, size_t size); /** Escape the domain name in *src and write it to *ret_name */ -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); /** Return 1 when the specified string contains a valid service type, 0 otherwise */ int avahi_is_valid_service_type(const char *t); @@ -84,7 +94,13 @@ unsigned avahi_domain_hash(const char *name); int avahi_domain_ends_with(const char *domain, const char *suffix); /** Construct a valid complete service name from a name, a type and a domain */ -int avahi_service_name_snprint(char *p, size_t size, const char *name, const char *type, const char *domain); +int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain); + +/** Split a full service name into name, type and domain */ +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); + +/** Just like OpenBSD strlcpy */ +char *avahi_strlcpy(char *dest, const char *src, size_t n); #ifndef DOXYGEN_SHOULD_SKIP_THIS AVAHI_C_DECL_END -- cgit