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