4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
42 /* Read the first label from string *name, unescape "\" and write it to dest */
43 char *avahi_unescape_label(const char **name, char *dest, size_t size) {
66 /* Escaped character */
74 else if (**name == '\\' || **name == '.') {
75 /* Escaped backslash or dot */
76 *(d++) = *((*name) ++);
78 } else if (isdigit(**name)) {
81 /* Escaped literal ASCII character */
83 if (!isdigit(*(*name+1)) || !isdigit(*(*name+2)))
86 n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0'));
88 if (n > 255 || n == 0)
100 /* Normal character */
102 *(d++) = *((*name) ++);
111 if (!avahi_utf8_valid(dest))
117 /* Escape "\" and ".", append \0 */
118 char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) {
125 assert(*ret_size > 0);
129 while (src_length > 0) {
130 if (*src == '.' || *src == '\\') {
132 /* Dot or backslash */
137 *((*ret_name) ++) = '\\';
138 *((*ret_name) ++) = *src;
144 (*src >= '0' && *src <= '9') ||
145 (*src >= 'a' && *src <= 'z') ||
146 (*src >= 'A' && *src <= 'Z')) {
148 /* Proper character */
153 *((*ret_name)++) = *src;
158 /* Everything else */
163 *((*ret_name) ++) = '\\';
164 *((*ret_name) ++) = '0' + (char) ((uint8_t) *src / 100);
165 *((*ret_name) ++) = '0' + (char) (((uint8_t) *src / 10) % 10);
166 *((*ret_name) ++) = '0' + (char) ((uint8_t) *src % 10);
180 char *avahi_normalize_name(const char *s, char *ret_s, size_t size) {
192 char label[AVAHI_LABEL_MAX];
194 if (!(avahi_unescape_label(&s, label, sizeof(label))))
199 if (*s == 0 && empty)
215 avahi_escape_label(label, strlen(label), &r, &size);
221 char *avahi_normalize_name_strdup(const char *s) {
222 char t[AVAHI_DOMAIN_NAME_MAX];
225 if (!(avahi_normalize_name(s, t, sizeof(t))))
228 return avahi_strdup(t);
231 int avahi_domain_equal(const char *a, const char *b) {
239 char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r;
241 r = avahi_unescape_label(&a, ca, sizeof(ca));
243 r = avahi_unescape_label(&b, cb, sizeof(cb));
246 if (strcasecmp(ca, cb))
256 int avahi_is_valid_service_type_generic(const char *t) {
259 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
263 char label[AVAHI_LABEL_MAX];
265 if (!(avahi_unescape_label(&t, label, sizeof(label))))
268 if (strlen(label) <= 2 || label[0] != '_')
276 int avahi_is_valid_service_type_strict(const char *t) {
277 char label[AVAHI_LABEL_MAX];
280 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
283 /* Application name */
285 if (!(avahi_unescape_label(&t, label, sizeof(label))))
288 if (strlen(label) <= 2 || label[0] != '_')
294 /* _tcp or _udp boilerplate */
296 if (!(avahi_unescape_label(&t, label, sizeof(label))))
299 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
308 const char *avahi_get_type_from_subtype(const char *t) {
309 char label[AVAHI_LABEL_MAX];
313 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
318 if (!(avahi_unescape_label(&t, label, sizeof(label))))
321 if (strlen(label) <= 2 || label[0] != '_')
329 if (!(avahi_unescape_label(&t, label, sizeof(label))))
332 if (strcasecmp(label, "_sub"))
340 /* Application name */
342 if (!(avahi_unescape_label(&t, label, sizeof(label))))
345 if (strlen(label) <= 2 || label[0] != '_')
351 /* _tcp or _udp boilerplate */
353 if (!(avahi_unescape_label(&t, label, sizeof(label))))
356 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp"))
365 int avahi_is_valid_service_subtype(const char *t) {
368 return !!avahi_get_type_from_subtype(t);
371 int avahi_is_valid_domain_name(const char *t) {
375 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
379 char label[AVAHI_LABEL_MAX];
381 if (!(avahi_unescape_label(&t, label, sizeof(label))))
384 /* Explicitly allow the root domain name */
385 if (is_first && label[0] == 0 && *t == 0)
398 int avahi_is_valid_service_name(const char *t) {
401 if (strlen(t) >= AVAHI_LABEL_MAX || !*t)
407 int avahi_is_valid_host_name(const char *t) {
408 char label[AVAHI_LABEL_MAX];
411 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t)
414 if (!(avahi_unescape_label(&t, label, sizeof(label))))
417 if (strlen(label) < 1)
426 unsigned avahi_domain_hash(const char *s) {
430 char c[AVAHI_LABEL_MAX], *p, *r;
432 r = avahi_unescape_label(&s, c, sizeof(c));
436 hash = 31 * hash + tolower(*p);
442 int avahi_service_name_join(char *p, size_t size, const char *name, const char *type, const char *domain) {
443 char escaped_name[AVAHI_LABEL_MAX*4];
444 char normalized_type[AVAHI_DOMAIN_NAME_MAX];
445 char normalized_domain[AVAHI_DOMAIN_NAME_MAX];
449 /* Validity checks */
451 if ((name && !avahi_is_valid_service_name(name)))
452 return AVAHI_ERR_INVALID_SERVICE_NAME;
454 if (!avahi_is_valid_service_type_generic(type))
455 return AVAHI_ERR_INVALID_SERVICE_TYPE;
457 if (!avahi_is_valid_domain_name(domain))
458 return AVAHI_ERR_INVALID_DOMAIN_NAME;
463 size_t l = sizeof(escaped_name);
464 char *e = escaped_name, *r;
465 r = avahi_escape_label(name, strlen(name), &e, &l);
469 if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type))))
470 return AVAHI_ERR_INVALID_SERVICE_TYPE;
472 if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain))))
473 return AVAHI_ERR_INVALID_DOMAIN_NAME;
477 snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain);
484 static size_t strlcpy(char *dest, const char *src, size_t n) {
489 strncpy(dest, src, n-1);
498 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) {
504 int type_empty = 1, domain_empty = 1;
508 assert(type_size > 0);
510 assert(domain_size > 0);
513 assert(name_size > 0);
524 if (!(avahi_unescape_label(&p, buf, sizeof(buf))))
529 strlcpy(name, buf, name_size);
539 return AVAHI_ERR_NO_MEMORY;
547 if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size)))
548 return AVAHI_ERR_NO_MEMORY;
560 return AVAHI_ERR_NO_MEMORY;
567 if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size)))
568 return AVAHI_ERR_NO_MEMORY;
577 int avahi_is_valid_fqdn(const char *t) {
578 char label[AVAHI_LABEL_MAX];
579 char normalized[AVAHI_DOMAIN_NAME_MAX];
584 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX)
587 if (!avahi_is_valid_domain_name(t))
590 /* Check if there are at least two labels*/
591 if (!(avahi_unescape_label(&k, label, sizeof(label))))
594 if (label[0] == 0 || !k)
597 if (!(avahi_unescape_label(&k, label, sizeof(label))))
600 if (label[0] == 0 || !k)
603 /* Make sure that the name is not an IP address */
604 if (!(avahi_normalize_name(t, normalized, sizeof(normalized))))
607 if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a))