1/* $Id$ */ 2 3/*** 4 This file is part of avahi. 5 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. 10 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. 15 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 19 USA. 20***/ 21 22#ifdef HAVE_CONFIG_H 23#include <config.h> 24#endif 25 26#include <string.h> 27#include <unistd.h> 28#include <fcntl.h> 29#include <errno.h> 30#include <limits.h> 31#include <stdio.h> 32#include <ctype.h> 33#include <stdlib.h> 34#include <assert.h> 35 36#include "domain.h" 37#include "malloc.h" 38#include "error.h" 39#include "address.h" 40#include "utf8.h" 41 42/* Read the first label from string *name, unescape "\" and write it to dest */ 43char *avahi_unescape_label(const char **name, char *dest, size_t size) { 44 unsigned i = 0; 45 char *d; 46 47 assert(dest); 48 assert(size > 0); 49 assert(name); 50 51 d = dest; 52 53 for (;;) { 54 if (i >= size) 55 return NULL; 56 57 if (**name == '.') { 58 (*name)++; 59 break; 60 } 61 62 if (**name == 0) 63 break; 64 65 if (**name == '\\') { 66 /* Escaped character */ 67 68 (*name) ++; 69 70 if (**name == 0) 71 /* Ending NUL */ 72 return NULL; 73 74 else if (**name == '\\' || **name == '.') { 75 /* Escaped backslash or dot */ 76 *(d++) = *((*name) ++); 77 i++; 78 } else if (isdigit(**name)) { 79 int n; 80 81 /* Escaped literal ASCII character */ 82 83 if (!isdigit(*(*name+1)) || !isdigit(*(*name+2))) 84 return NULL; 85 86 n = ((uint8_t) (**name - '0') * 100) + ((uint8_t) (*(*name+1) - '0') * 10) + ((uint8_t) (*(*name +2) - '0')); 87 88 if (n > 255 || n == 0) 89 return NULL; 90 91 *(d++) = (char) n; 92 i++; 93 94 (*name) += 3; 95 } else 96 return NULL; 97 98 } else { 99 100 /* Normal character */ 101 102 *(d++) = *((*name) ++); 103 i++; 104 } 105 } 106 107 assert(i < size); 108 109 *d = 0; 110 111 if (!avahi_utf8_valid(dest)) 112 return NULL; 113 114 return dest; 115} 116 117/* Escape "\" and ".", append \0 */ 118char *avahi_escape_label(const char* src, size_t src_length, char **ret_name, size_t *ret_size) { 119 char *r; 120 121 assert(src); 122 assert(ret_name); 123 assert(*ret_name); 124 assert(ret_size); 125 assert(*ret_size > 0); 126 127 r = *ret_name; 128 129 while (src_length > 0) { 130 if (*src == '.' || *src == '\\') { 131 132 /* Dot or backslash */ 133 134 if (*ret_size < 3) 135 return NULL; 136 137 *((*ret_name) ++) = '\\'; 138 *((*ret_name) ++) = *src; 139 (*ret_size) -= 2; 140 141 } else if ( 142 *src == '_' || 143 *src == '-' || 144 (*src >= '0' && *src <= '9') || 145 (*src >= 'a' && *src <= 'z') || 146 (*src >= 'A' && *src <= 'Z')) { 147 148 /* Proper character */ 149 150 if (*ret_size < 2) 151 return NULL; 152 153 *((*ret_name)++) = *src; 154 (*ret_size) --; 155 156 } else { 157 158 /* Everything else */ 159 160 if (*ret_size < 5) 161 return NULL; 162 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); 167 168 (*ret_size) -= 4; 169 } 170 171 src_length --; 172 src++; 173 } 174 175 **ret_name = 0; 176 177 return r; 178} 179 180char *avahi_normalize_name(const char *s, char *ret_s, size_t size) { 181 int empty = 1; 182 char *r; 183 184 assert(s); 185 assert(ret_s); 186 assert(size > 0); 187 188 r = ret_s; 189 *ret_s = 0; 190 191 while (*s) { 192 char label[AVAHI_LABEL_MAX]; 193 194 if (!(avahi_unescape_label(&s, label, sizeof(label)))) 195 return NULL; 196 197 if (label[0] == 0) { 198 199 if (*s == 0 && empty) 200 return ret_s; 201 202 return NULL; 203 } 204 205 if (!empty) { 206 if (size < 1) 207 return NULL; 208 209 *(r++) = '.'; 210 size--; 211 212 } else 213 empty = 0; 214 215 avahi_escape_label(label, strlen(label), &r, &size); 216 } 217 218 return ret_s; 219} 220 221char *avahi_normalize_name_strdup(const char *s) { 222 char t[AVAHI_DOMAIN_NAME_MAX]; 223 assert(s); 224 225 if (!(avahi_normalize_name(s, t, sizeof(t)))) 226 return NULL; 227 228 return avahi_strdup(t); 229} 230 231int avahi_domain_equal(const char *a, const char *b) { 232 assert(a); 233 assert(b); 234 235 if (a == b) 236 return 1; 237 238 for (;;) { 239 char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *r; 240 241 r = avahi_unescape_label(&a, ca, sizeof(ca)); 242 assert(r); 243 r = avahi_unescape_label(&b, cb, sizeof(cb)); 244 assert(r); 245 246 if (strcasecmp(ca, cb)) 247 return 0; 248 249 if (!*a && !*b) 250 return 1; 251 } 252 253 return 1; 254} 255 256int avahi_is_valid_service_type_generic(const char *t) { 257 assert(t); 258 259 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) 260 return 0; 261 262 do { 263 char label[AVAHI_LABEL_MAX]; 264 265 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 266 return 0; 267 268 if (strlen(label) <= 2 || label[0] != '_') 269 return 0; 270 271 } while (*t); 272 273 return 1; 274} 275 276int avahi_is_valid_service_type_strict(const char *t) { 277 char label[AVAHI_LABEL_MAX]; 278 assert(t); 279 280 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) 281 return 0; 282 283 /* Application name */ 284 285 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 286 return 0; 287 288 if (strlen(label) <= 2 || label[0] != '_') 289 return 0; 290 291 if (!*t) 292 return 0; 293 294 /* _tcp or _udp boilerplate */ 295 296 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 297 return 0; 298 299 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) 300 return 0; 301 302 if (*t) 303 return 0; 304 305 return 1; 306} 307 308const char *avahi_get_type_from_subtype(const char *t) { 309 char label[AVAHI_LABEL_MAX]; 310 const char *ret; 311 assert(t); 312 313 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) 314 return NULL; 315 316 /* Subtype name */ 317 318 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 319 return NULL; 320 321 if (strlen(label) <= 2 || label[0] != '_') 322 return NULL; 323 324 if (!*t) 325 return NULL; 326 327 /* String "_sub" */ 328 329 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 330 return NULL; 331 332 if (strcasecmp(label, "_sub")) 333 return NULL; 334 335 if (!*t) 336 return NULL; 337 338 ret = t; 339 340 /* Application name */ 341 342 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 343 return NULL; 344 345 if (strlen(label) <= 2 || label[0] != '_') 346 return NULL; 347 348 if (!*t) 349 return NULL; 350 351 /* _tcp or _udp boilerplate */ 352 353 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 354 return NULL; 355 356 if (strcasecmp(label, "_tcp") && strcasecmp(label, "_udp")) 357 return NULL; 358 359 if (*t) 360 return NULL; 361 362 return ret; 363} 364 365int avahi_is_valid_service_subtype(const char *t) { 366 assert(t); 367 368 return !!avahi_get_type_from_subtype(t); 369} 370 371int avahi_is_valid_domain_name(const char *t) { 372 int is_first = 1; 373 assert(t); 374 375 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) 376 return 0; 377 378 do { 379 char label[AVAHI_LABEL_MAX]; 380 381 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 382 return 0; 383 384 /* Explicitly allow the root domain name */ 385 if (is_first && label[0] == 0 && *t == 0) 386 return 1; 387 388 is_first = 0; 389 390 if (label[0] == 0) 391 return 0; 392 393 } while (*t); 394 395 return 1; 396} 397 398int avahi_is_valid_service_name(const char *t) { 399 assert(t); 400 401 if (strlen(t) >= AVAHI_LABEL_MAX || !*t) 402 return 0; 403 404 return 1; 405} 406 407int avahi_is_valid_host_name(const char *t) { 408 char label[AVAHI_LABEL_MAX]; 409 assert(t); 410 411 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX || !*t) 412 return 0; 413 414 if (!(avahi_unescape_label(&t, label, sizeof(label)))) 415 return 0; 416 417 if (strlen(label) < 1) 418 return 0; 419 420 if (*t) 421 return 0; 422 423 return 1; 424} 425 426unsigned avahi_domain_hash(const char *s) { 427 unsigned hash = 0; 428 429 while (*s) { 430 char c[AVAHI_LABEL_MAX], *p, *r; 431 432 r = avahi_unescape_label(&s, c, sizeof(c)); 433 assert(r); 434 435 for (p = c; *p; p++) 436 hash = 31 * hash + tolower(*p); 437 } 438 439 return hash; 440} 441 442int 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]; 446 447 assert(p); 448 449 /* Validity checks */ 450 451 if ((name && !avahi_is_valid_service_name(name))) 452 return AVAHI_ERR_INVALID_SERVICE_NAME; 453 454 if (!avahi_is_valid_service_type_generic(type)) 455 return AVAHI_ERR_INVALID_SERVICE_TYPE; 456 457 if (!avahi_is_valid_domain_name(domain)) 458 return AVAHI_ERR_INVALID_DOMAIN_NAME; 459 460 /* Preparation */ 461 462 if (name) { 463 size_t l = sizeof(escaped_name); 464 char *e = escaped_name, *r; 465 r = avahi_escape_label(name, strlen(name), &e, &l); 466 assert(r); 467 } 468 469 if (!(avahi_normalize_name(type, normalized_type, sizeof(normalized_type)))) 470 return AVAHI_ERR_INVALID_SERVICE_TYPE; 471 472 if (!(avahi_normalize_name(domain, normalized_domain, sizeof(normalized_domain)))) 473 return AVAHI_ERR_INVALID_DOMAIN_NAME; 474 475 /* Concatenation */ 476 477 snprintf(p, size, "%s%s%s.%s", name ? escaped_name : "", name ? "." : "", normalized_type, normalized_domain); 478 479 return AVAHI_OK; 480} 481 482#ifndef HAVE_STRLCPY 483 484static size_t strlcpy(char *dest, const char *src, size_t n) { 485 assert(dest); 486 assert(src); 487 488 if (n > 0) { 489 strncpy(dest, src, n-1); 490 dest[n-1] = 0; 491 } 492 493 return strlen(src); 494} 495 496#endif 497 498int 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) { 499 enum { 500 NAME, 501 TYPE, 502 DOMAIN 503 } state; 504 int type_empty = 1, domain_empty = 1; 505 506 assert(p); 507 assert(type); 508 assert(type_size > 0); 509 assert(domain); 510 assert(domain_size > 0); 511 512 if (name) { 513 assert(name_size > 0); 514 *name = 0; 515 state = NAME; 516 } else 517 state = TYPE; 518 519 *type = *domain = 0; 520 521 while (*p) { 522 char buf[64]; 523 524 if (!(avahi_unescape_label(&p, buf, sizeof(buf)))) 525 return -1; 526 527 switch (state) { 528 case NAME: 529 strlcpy(name, buf, name_size); 530 state = TYPE; 531 break; 532 533 case TYPE: 534 535 if (buf[0] == '_') { 536 537 if (!type_empty) { 538 if (!type_size) 539 return AVAHI_ERR_NO_MEMORY; 540 541 *(type++) = '.'; 542 type_size --; 543 544 } else 545 type_empty = 0; 546 547 if (!(avahi_escape_label(buf, strlen(buf), &type, &type_size))) 548 return AVAHI_ERR_NO_MEMORY; 549 550 break; 551 } 552 553 state = DOMAIN; 554 /* fall through */ 555 556 case DOMAIN: 557 558 if (!domain_empty) { 559 if (!domain_size) 560 return AVAHI_ERR_NO_MEMORY; 561 562 *(domain++) = '.'; 563 domain_size --; 564 } else 565 domain_empty = 0; 566 567 if (!(avahi_escape_label(buf, strlen(buf), &domain, &domain_size))) 568 return AVAHI_ERR_NO_MEMORY; 569 570 break; 571 } 572 } 573 574 return 0; 575} 576 577int avahi_is_valid_fqdn(const char *t) { 578 char label[AVAHI_LABEL_MAX]; 579 char normalized[AVAHI_DOMAIN_NAME_MAX]; 580 const char *k = t; 581 AvahiAddress a; 582 assert(t); 583 584 if (strlen(t) >= AVAHI_DOMAIN_NAME_MAX) 585 return 0; 586 587 if (!avahi_is_valid_domain_name(t)) 588 return 0; 589 590 /* Check if there are at least two labels*/ 591 if (!(avahi_unescape_label(&k, label, sizeof(label)))) 592 return 0; 593 594 if (label[0] == 0 || !k) 595 return 0; 596 597 if (!(avahi_unescape_label(&k, label, sizeof(label)))) 598 return 0; 599 600 if (label[0] == 0 || !k) 601 return 0; 602 603 /* Make sure that the name is not an IP address */ 604 if (!(avahi_normalize_name(t, normalized, sizeof(normalized)))) 605 return 0; 606 607 if (avahi_address_parse(normalized, AVAHI_PROTO_UNSPEC, &a)) 608 return 0; 609 610 return 1; 611} 612