]> git.meshlink.io Git - catta/blobdiff - avahi-common/domain.c
forgot to pull the publish_no_reverse change to the example.
[catta] / avahi-common / domain.c
index 5a51a39ae58cd4f40e7eb24d393caca68b92fc26..3b1ab683494e9e5caf6094944bd4e2e41ae96bdd 100644 (file)
@@ -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
 
 #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,434 @@ 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;
 }
 
+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) {
-    const char *p, *dp;
-    int dot = 0;
-        
+    int is_first = 1;
     assert(t);
 
-    if (*t == 0)
+    if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
         return 0;
 
-    /* Domains may not start with a dot */
-    if (*t == '.')
-        return 0;
+    do {
+        char label[AVAHI_LABEL_MAX];
 
-    dp = t; 
+        if (!(avahi_unescape_label(&t, label, sizeof(label))))
+            return 0;
 
-    for (p = t; *p; p++) {
+        /* Explicitly allow the root domain name */
+        if (is_first && label[0] == 0 && *t == 0)
+            return 1;
 
-        if (*p == '.') {
-            if (dot) /* Two subsequent dots */
-                return 0;
+        is_first = 0;
 
-            if (p - dp > 63)
-                return 0;
+        if (label[0] == 0)
+            return 0;
 
-            dot = 1;
-            dp = p + 1;
-        } else
-            dot = 0;
+    } while (*t);
 
-    }
+    return 1;
+}
 
-    if (p - dp > 63)
+int avahi_is_valid_service_name(const char *t) {
+    assert(t);
+
+    if (strlen(t) >= AVAHI_LABEL_MAX || !*t)
         return 0;
 
-    /* A trailing dot IS allowed */
-    
     return 1;
 }
 
-int avahi_is_valid_service_name(const char *t) {
+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(label) < 1)
         return 0;
 
-    if (strlen(t) > 63)
+    if (*t)
         return 0;
 
     return 1;
 }
 
-int avahi_is_valid_host_name(const char *t) {
+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 (*t == 0)
+    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;
 
-    if (strlen(t) > 63)
+    /* Make sure that the name is not an IP address */
+    if (!(avahi_normalize_name(t, normalized, sizeof(normalized))))
         return 0;
 
-    if (strchr(t, '.'))
+    if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a))
         return 0;
 
     return 1;