1 /* 2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2.1 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #define _GNU_SOURCE 15 #include <sys/types.h> 16 #include <sys/stat.h> 17 18 #include <fcntl.h> 19 #include <time.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 #include <arpa/nameser.h> 28 #include <resolv.h> 29 #include <time.h> 30 31 #include <libubox/usock.h> 32 #include <libubox/uloop.h> 33 #include <libubox/avl-cmp.h> 34 #include <libubox/blobmsg_json.h> 35 #include <libubox/kvlist.h> 36 #include <libubus.h> 37 38 #include "cache.h" 39 #include "util.h" 40 #include "dns.h" 41 #include "interface.h" 42 43 static struct uloop_timeout cache_gc; 44 struct avl_tree services; 45 46 static int avl_strcasecmp(const void *k1, const void *k2, void *ptr) 47 { 48 return strcasecmp(k1, k2); 49 } 50 51 AVL_TREE(records, avl_strcasecmp, true, NULL); 52 53 static void 54 cache_record_free(struct cache_record *r) 55 { 56 DBG(2, "%s %s\n", dns_type_string(r->type), r->record); 57 avl_delete(&records, &r->avl); 58 free(r); 59 } 60 61 static void 62 cache_service_free(struct cache_service *s) 63 { 64 DBG(2, "%s\n", s->entry); 65 avl_delete(&services, &s->avl); 66 free(s); 67 } 68 69 static int 70 cache_is_expired(time_t t, uint32_t ttl, int frac) 71 { 72 if (monotonic_time() - t >= ttl * frac / 100) 73 return 1; 74 75 return 0; 76 } 77 78 static void 79 cache_refresh_service(struct cache_service *s) 80 { 81 dns_query(s->entry, TYPE_PTR); 82 83 if (!s->host) 84 return; 85 86 dns_query(s->host, TYPE_A); 87 dns_query(s->host, TYPE_AAAA); 88 } 89 90 static void 91 cache_gc_timer(struct uloop_timeout *timeout) 92 { 93 struct cache_record *r, *p; 94 struct cache_service *s, *t; 95 96 avl_for_each_element_safe(&records, r, avl, p) { 97 if (!cache_is_expired(r->time, r->ttl, r->refresh)) 98 continue; 99 /* Records other than A(AAA) are handled as services */ 100 if (r->type != TYPE_A && r->type != TYPE_AAAA) { 101 if (cache_is_expired(r->time, r->ttl, 100)) 102 cache_record_free(r); 103 continue; 104 } 105 if (r->refresh >= 100) { 106 cache_record_free(r); 107 continue; 108 } 109 r->refresh += 50; 110 dns_send_question(r->iface, (struct sockaddr *)&r->from, r->record, r->type, 0); 111 } 112 113 avl_for_each_element_safe(&services, s, avl, t) { 114 if (!s->host) 115 continue; 116 if (!cache_is_expired(s->time, s->ttl, s->refresh)) 117 continue; 118 if (s->refresh >= 100) { 119 cache_service_free(s); 120 continue; 121 } 122 s->refresh += 50; 123 cache_refresh_service(s); 124 } 125 126 uloop_timeout_set(timeout, 10000); 127 } 128 129 int 130 cache_init(void) 131 { 132 avl_init(&services, avl_strcasecmp, true, NULL); 133 134 cache_gc.cb = cache_gc_timer; 135 uloop_timeout_set(&cache_gc, 10000); 136 137 return 0; 138 } 139 140 void cache_cleanup(struct interface *iface) 141 { 142 struct cache_record *r, *p; 143 struct cache_service *s, *t; 144 145 avl_for_each_element_safe(&services, s, avl, t) 146 if (!iface || iface == s->iface) 147 cache_service_free(s); 148 149 avl_for_each_element_safe(&records, r, avl, p) 150 if (!iface || iface == r->iface) 151 cache_record_free(r); 152 } 153 154 void 155 cache_update(void) 156 { 157 struct cache_service *s; 158 159 dns_query(C_DNS_SD, TYPE_ANY); 160 dns_query(C_DNS_SD, TYPE_PTR); 161 avl_for_each_element(&services, s, avl) 162 cache_refresh_service(s); 163 } 164 165 static struct cache_service* 166 cache_service(struct interface *iface, char *entry, int hlen, int ttl) 167 { 168 struct cache_service *s, *t; 169 char *entry_buf; 170 char *host_buf; 171 char *type; 172 173 avl_for_each_element_safe(&services, s, avl, t) 174 if (!strcmp(s->entry, entry)) { 175 s->refresh = 50; 176 s->time = monotonic_time(); 177 s->ttl = ttl; 178 return s; 179 } 180 181 s = calloc_a(sizeof(*s), 182 &entry_buf, strlen(entry) + 1, 183 &host_buf, hlen ? hlen + sizeof(".local") + 1 : 0); 184 185 s->avl.key = s->entry = strcpy(entry_buf, entry); 186 s->time = monotonic_time(); 187 s->ttl = ttl; 188 s->iface = iface; 189 s->refresh = 50; 190 191 if (hlen) { 192 s->host = strncpy(host_buf, s->entry, hlen); 193 strcpy(host_buf + hlen, ".local"); 194 } 195 196 type = strstr(entry_buf, "._"); 197 if (type) 198 type++; 199 if (type) 200 s->avl.key = type; 201 avl_insert(&services, &s->avl); 202 203 cache_refresh_service(s); 204 205 return s; 206 } 207 208 static struct cache_record* 209 cache_record_find(char *record, int type, int port, int rdlength, uint8_t *rdata) 210 { 211 struct cache_record *l = avl_find_element(&records, record, l, avl); 212 213 while (l && !strcmp(l->record, record)) { 214 struct cache_record *r = l; 215 216 l = !avl_is_last(&records, &l->avl) ? avl_next_element(l, avl) : NULL; 217 if (r->type != type) 218 continue; 219 220 if (r->type == TYPE_TXT || (r->type == TYPE_SRV)) 221 return r; 222 223 if (r->port != port) 224 continue; 225 226 if (r->rdlength != rdlength) 227 continue; 228 229 if (!!r->rdata != !!rdata) 230 continue; 231 232 if (!r->rdata || !rdata || memcmp(r->rdata, rdata, rdlength)) 233 continue; 234 235 return r; 236 } 237 238 return NULL; 239 } 240 241 int 242 cache_host_is_known(char *record) 243 { 244 struct cache_record *l = avl_find_element(&records, record, l, avl); 245 246 while (l && !strcmp(l->record, record)) { 247 struct cache_record *r = l; 248 249 l = !avl_is_last(&records, &l->avl) ? avl_next_element(l, avl) : NULL; 250 if ((r->type != TYPE_A) && (r->type != TYPE_AAAA)) 251 continue; 252 return 1; 253 } 254 255 return 0; 256 } 257 258 void cache_answer(struct interface *iface, struct sockaddr *from, uint8_t *base, 259 int blen, char *name, struct dns_answer *a, uint8_t *rdata, 260 int flush) 261 { 262 struct dns_srv_data *dsd = (struct dns_srv_data *) rdata; 263 struct cache_record *r; 264 int port = 0, dlen = 0, tlen = 0, nlen, rdlength; 265 char *p = NULL; 266 char *name_buf; 267 void *rdata_ptr, *txt_ptr; 268 int host_len = 0; 269 static char *rdata_buffer = (char *) mdns_buf; 270 time_t now = monotonic_time(); 271 272 nlen = strlen(name); 273 274 switch (a->type) { 275 case TYPE_PTR: 276 if (a->rdlength < 2) 277 return; 278 279 if (dn_expand(base, base + blen, rdata, rdata_buffer, MAX_DATA_LEN) < 0) { 280 perror("process_answer/dn_expand"); 281 return; 282 } 283 284 DBG(1, "A -> %s %s %s ttl:%d\n", dns_type_string(a->type), name, rdata_buffer, a->ttl); 285 286 rdlength = strlen(rdata_buffer); 287 288 if (strcmp(C_DNS_SD, name) != 0 && 289 nlen + 1 < rdlength && !strcmp(rdata_buffer + rdlength - nlen, name)) 290 host_len = rdlength - nlen - 1; 291 292 if (name[0] == '_') 293 cache_service(iface, rdata_buffer, host_len, a->ttl); 294 295 dlen = strlen(rdata_buffer) + 1; 296 rdata = (uint8_t*)rdata_buffer; 297 break; 298 299 case TYPE_SRV: 300 if (a->rdlength < 8) 301 return; 302 303 port = be16_to_cpu(dsd->port); 304 memcpy(rdata_buffer, dsd, sizeof(*dsd)); 305 if (dn_expand(base, base + blen, (const uint8_t*)&dsd[1], 306 &rdata_buffer[sizeof(*dsd)], MAX_DATA_LEN - sizeof(*dsd)) < 0) { 307 perror("process_answer/dn_expand"); 308 return; 309 } 310 dlen = sizeof(*dsd) + strlen(&rdata_buffer[sizeof(*dsd)]) + 1; 311 rdata = (uint8_t*)rdata_buffer; 312 break; 313 314 case TYPE_TXT: 315 rdlength = a->rdlength; 316 if (rdlength <= 2) 317 return; 318 319 memcpy(rdata_buffer, &rdata[1], rdlength-1); 320 rdata_buffer[rdlength] = rdata_buffer[rdlength + 1] = '\0'; 321 tlen = rdlength + 1; 322 p = &rdata_buffer[*rdata]; 323 324 do { 325 uint8_t v = *p; 326 327 *p = '\0'; 328 if (v && p + v < &rdata_buffer[rdlength]) 329 p += v + 1; 330 } while (*p); 331 break; 332 333 case TYPE_A: 334 if (a->rdlength != 4) 335 return; 336 dlen = 4; 337 break; 338 339 case TYPE_AAAA: 340 if (a->rdlength != 16) 341 return; 342 dlen = 16; 343 break; 344 345 default: 346 return; 347 } 348 349 r = cache_record_find(name, a->type, port, dlen, rdata); 350 if (r) { 351 if (!a->ttl) { 352 DBG(1, "D -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 353 r->time = now + 1 - r->ttl; 354 r->refresh = 100; 355 } else { 356 r->ttl = a->ttl; 357 r->time = now; 358 r->refresh = 50; 359 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 360 } 361 return; 362 } 363 364 if (!a->ttl) 365 return; 366 367 r = calloc_a(sizeof(*r), 368 &name_buf, strlen(name) + 1, 369 &txt_ptr, tlen, 370 &rdata_ptr, dlen); 371 372 r->avl.key = r->record = strcpy(name_buf, name); 373 r->type = a->type; 374 r->ttl = a->ttl; 375 r->port = port; 376 r->rdlength = dlen; 377 r->time = now; 378 r->iface = iface; 379 if (interface_ipv6(iface)) 380 memcpy(&r->from, from, sizeof(struct sockaddr_in6)); 381 else 382 memcpy(&r->from, from, sizeof(struct sockaddr_in)); 383 r->refresh = 50; 384 385 if (tlen) 386 r->txt = memcpy(txt_ptr, rdata_buffer, tlen); 387 388 if (dlen) 389 r->rdata = memcpy(rdata_ptr, rdata, dlen); 390 391 if (avl_insert(&records, &r->avl)) 392 free(r); 393 else 394 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 395 } 396 397 void 398 cache_dump_records(struct blob_buf *buf, const char *name, int array, 399 const char **hostname) 400 { 401 struct cache_record *r, *last, *next; 402 const char *txt; 403 char buffer[INET6_ADDRSTRLEN]; 404 void *c = NULL; 405 406 last = avl_last_element(&records, last, avl); 407 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 408 switch (r->type) { 409 case TYPE_A: 410 if (!c && array) 411 c = blobmsg_open_array(buf, "ipv4"); 412 if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buffer, INET6_ADDRSTRLEN)) 413 blobmsg_add_string(buf, "ipv4", buffer); 414 break; 415 } 416 417 if (r == last) 418 break; 419 420 next = avl_next_element(r, avl); 421 if (strcmp(r->record, next->record) != 0) 422 break; 423 } 424 425 if (c) { 426 blobmsg_close_array(buf, c); 427 c = NULL; 428 } 429 430 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 431 switch (r->type) { 432 case TYPE_AAAA: 433 if (!c && array) 434 c = blobmsg_open_array(buf, "ipv6"); 435 if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buffer, INET6_ADDRSTRLEN)) 436 blobmsg_add_string(buf, "ipv6", buffer); 437 break; 438 } 439 440 if (r == last) 441 break; 442 443 next = avl_next_element(r, avl); 444 if (strcmp(r->record, next->record) != 0) 445 break; 446 } 447 448 if (c) { 449 blobmsg_close_array(buf, c); 450 c = NULL; 451 } 452 453 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 454 switch (r->type) { 455 case TYPE_TXT: 456 if (r->txt && strlen(r->txt)) { 457 if (array) 458 c = blobmsg_open_array(buf, "txt"); 459 460 txt = r->txt; 461 do { 462 blobmsg_add_string(buf, "txt", txt); 463 txt = &txt[strlen(txt) + 1]; 464 } while (*txt); 465 if (array) 466 blobmsg_close_array(buf, c); 467 } 468 break; 469 470 case TYPE_SRV: 471 if (r->rdata) { 472 blobmsg_add_string(buf, "host", (char *)r->rdata + sizeof(struct dns_srv_data)); 473 if (hostname) 474 *hostname = (char *)r->rdata + sizeof(struct dns_srv_data); 475 } 476 477 478 if (r->record) { 479 const struct dns_srv_data *dsd; 480 char *domain = NULL; 481 482 domain = strstr(r->record, "._udp."); 483 484 if (!domain) 485 domain = strstr(r->record, "._tcp."); 486 487 if (!domain) 488 break; 489 490 domain = domain + strlen("._udp."); 491 blobmsg_add_string(buf, "domain", domain); 492 493 if (r->port) 494 blobmsg_add_u32(buf, "port", r->port); 495 496 if (r->ttl) 497 blobmsg_add_u32(buf, "ttl", r->ttl); 498 499 if (r->time) { 500 struct tm *local_time; 501 char str_tm[32] = {0}; 502 time_t last_update = time(NULL) - (monotonic_time() - r->time); 503 local_time = localtime(&last_update); 504 strftime(str_tm, sizeof(str_tm), "%Y-%m-%dT%H:%M:%SZ", local_time); 505 506 blobmsg_add_string(buf, "last_update", str_tm); 507 } 508 509 dsd = (const struct dns_srv_data*)r->rdata; 510 511 if (r->rdlength > sizeof(*dsd)) { 512 blobmsg_add_u32(buf, "priority", be16_to_cpu(dsd->priority)); 513 blobmsg_add_u32(buf, "weight", be16_to_cpu(dsd->weight)); 514 } 515 } 516 517 break; 518 } 519 520 if (r == last) 521 break; 522 523 next = avl_next_element(r, avl); 524 if (strcmp(r->record, next->record) != 0) 525 break; 526 } 527 } 528 529 void 530 cache_dump_recursive(struct blob_buf *b, const char *name, uint16_t type, struct interface *iface) 531 { 532 time_t now = monotonic_time(); 533 for (struct cache_record *r = avl_find_ge_element(&records, name, r, avl); 534 r && !strcmp(r->record, name); 535 r = !avl_is_last(&records, &r->avl) ? avl_next_element(r, avl) : NULL) { 536 int32_t ttl = r->ttl - (now - r->time); 537 if (ttl <= 0 || (iface && iface->ifindex != r->iface->ifindex) || 538 (type != TYPE_ANY && type != r->type)) 539 continue; 540 541 const char *txt; 542 char buf[INET6_ADDRSTRLEN]; 543 void *k = blobmsg_open_table(b, NULL), *l; 544 const struct dns_srv_data *dsd = (const struct dns_srv_data*)r->rdata; 545 546 blobmsg_add_string(b, "name", r->record); 547 blobmsg_add_string(b, "type", dns_type_string(r->type)); 548 blobmsg_add_u32(b, "ttl", ttl); 549 550 switch (r->type) { 551 case TYPE_TXT: 552 if ((txt = r->txt) && strlen(txt)) { 553 l = blobmsg_open_array(b, "data"); 554 do { 555 blobmsg_add_string(b, NULL, txt); 556 txt = &txt[strlen(txt) + 1]; 557 } while (*txt); 558 blobmsg_close_array(b, l); 559 } 560 break; 561 562 case TYPE_SRV: 563 if (r->rdlength > sizeof(*dsd)) { 564 blobmsg_add_u32(b, "priority", be16_to_cpu(dsd->priority)); 565 blobmsg_add_u32(b, "weight", be16_to_cpu(dsd->weight)); 566 blobmsg_add_u32(b, "port", be16_to_cpu(dsd->port)); 567 blobmsg_add_string(b, "target", (const char*)&dsd[1]); 568 } 569 break; 570 571 case TYPE_PTR: 572 if (r->rdlength > 0) 573 blobmsg_add_string(b, "target", (const char*)r->rdata); 574 break; 575 576 case TYPE_A: 577 if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buf, sizeof(buf))) 578 blobmsg_add_string(b, "target", buf); 579 break; 580 581 case TYPE_AAAA: 582 if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buf, sizeof(buf))) 583 blobmsg_add_string(b, "target", buf); 584 break; 585 } 586 587 blobmsg_close_table(b, k); 588 589 590 if (r->type == TYPE_PTR) { 591 cache_dump_recursive(b, (const char*)r->rdata, TYPE_SRV, iface); 592 cache_dump_recursive(b, (const char*)r->rdata, TYPE_TXT, iface); 593 } 594 595 if (r->type == TYPE_SRV) { 596 cache_dump_recursive(b, (const char*)&dsd[1], TYPE_A, iface); 597 cache_dump_recursive(b, (const char*)&dsd[1], TYPE_AAAA, iface); 598 } 599 } 600 } 601
This page was automatically generated by LXR 0.3.1. • OpenWrt