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_gc_timer(struct uloop_timeout *timeout) 80 { 81 struct cache_record *r, *p; 82 struct cache_service *s, *t; 83 84 avl_for_each_element_safe(&records, r, avl, p) { 85 if (!cache_is_expired(r->time, r->ttl, r->refresh)) 86 continue; 87 /* Records other than A(AAA) are handled as services */ 88 if (r->type != TYPE_A && r->type != TYPE_AAAA) { 89 if (cache_is_expired(r->time, r->ttl, 100)) 90 cache_record_free(r); 91 continue; 92 } 93 if (r->refresh >= 100) { 94 cache_record_free(r); 95 continue; 96 } 97 r->refresh += 50; 98 dns_send_question(r->iface, (struct sockaddr *)&r->from, r->record, r->type, 0); 99 } 100 101 avl_for_each_element_safe(&services, s, avl, t) { 102 if (!s->host) 103 continue; 104 if (!cache_is_expired(s->time, s->ttl, s->refresh)) 105 continue; 106 if (s->refresh >= 100) { 107 cache_service_free(s); 108 continue; 109 } 110 s->refresh += 50; 111 dns_send_question(s->iface, NULL, s->entry, TYPE_PTR, 0); 112 } 113 114 uloop_timeout_set(timeout, 10000); 115 } 116 117 int 118 cache_init(void) 119 { 120 avl_init(&services, avl_strcasecmp, true, NULL); 121 122 cache_gc.cb = cache_gc_timer; 123 uloop_timeout_set(&cache_gc, 10000); 124 125 return 0; 126 } 127 128 void cache_cleanup(struct interface *iface) 129 { 130 struct cache_record *r, *p; 131 struct cache_service *s, *t; 132 133 avl_for_each_element_safe(&services, s, avl, t) 134 if (!iface || iface == s->iface) 135 cache_service_free(s); 136 137 avl_for_each_element_safe(&records, r, avl, p) 138 if (!iface || iface == r->iface) 139 cache_record_free(r); 140 } 141 142 void 143 cache_update(void) 144 { 145 struct interface *iface; 146 147 vlist_for_each_element(&interfaces, iface, node) { 148 dns_send_question(iface, NULL, C_DNS_SD, TYPE_ANY, 0); 149 dns_send_question(iface, NULL, C_DNS_SD, TYPE_PTR, 0); 150 } 151 } 152 153 static struct cache_service* 154 cache_service(struct interface *iface, char *entry, int hlen, int ttl) 155 { 156 struct cache_service *s, *t; 157 char *entry_buf; 158 char *host_buf; 159 char *type; 160 161 avl_for_each_element_safe(&services, s, avl, t) 162 if (!strcmp(s->entry, entry)) { 163 s->refresh = 50; 164 s->time = monotonic_time(); 165 s->ttl = ttl; 166 return s; 167 } 168 169 s = calloc_a(sizeof(*s), 170 &entry_buf, strlen(entry) + 1, 171 &host_buf, hlen ? hlen + 1 : 0); 172 173 s->avl.key = s->entry = strcpy(entry_buf, entry); 174 s->time = monotonic_time(); 175 s->ttl = ttl; 176 s->iface = iface; 177 s->refresh = 50; 178 179 if (hlen) 180 s->host = strncpy(host_buf, s->entry, hlen); 181 182 type = strstr(entry_buf, "._"); 183 if (type) 184 type++; 185 if (type) 186 s->avl.key = type; 187 avl_insert(&services, &s->avl); 188 189 if (!hlen) 190 dns_send_question(iface, NULL, entry, TYPE_PTR, interface_multicast(iface)); 191 192 return s; 193 } 194 195 static struct cache_record* 196 cache_record_find(char *record, int type, int port, int rdlength, uint8_t *rdata) 197 { 198 struct cache_record *l = avl_find_element(&records, record, l, avl); 199 200 while (l && !strcmp(l->record, record)) { 201 struct cache_record *r = l; 202 203 l = !avl_is_last(&records, &l->avl) ? avl_next_element(l, avl) : NULL; 204 if (r->type != type) 205 continue; 206 207 if (r->type == TYPE_TXT || (r->type == TYPE_SRV)) 208 return r; 209 210 if (r->port != port) 211 continue; 212 213 if (r->rdlength != rdlength) 214 continue; 215 216 if (!!r->rdata != !!rdata) 217 continue; 218 219 if (!r->rdata || !rdata || memcmp(r->rdata, rdata, rdlength)) 220 continue; 221 222 return r; 223 } 224 225 return NULL; 226 } 227 228 int 229 cache_host_is_known(char *record) 230 { 231 struct cache_record *l = avl_find_element(&records, record, l, avl); 232 233 while (l && !strcmp(l->record, record)) { 234 struct cache_record *r = l; 235 236 l = !avl_is_last(&records, &l->avl) ? avl_next_element(l, avl) : NULL; 237 if ((r->type != TYPE_A) && (r->type != TYPE_AAAA)) 238 continue; 239 return 1; 240 } 241 242 return 0; 243 } 244 245 void cache_answer(struct interface *iface, struct sockaddr *from, uint8_t *base, 246 int blen, char *name, struct dns_answer *a, uint8_t *rdata, 247 int flush) 248 { 249 struct dns_srv_data *dsd = (struct dns_srv_data *) rdata; 250 struct cache_record *r; 251 int port = 0, dlen = 0, tlen = 0, nlen, rdlength; 252 char *p = NULL; 253 char *name_buf; 254 void *rdata_ptr, *txt_ptr; 255 int host_len = 0; 256 static char *rdata_buffer = (char *) mdns_buf; 257 time_t now = monotonic_time(); 258 259 nlen = strlen(name); 260 261 switch (a->type) { 262 case TYPE_PTR: 263 if (a->rdlength < 2) 264 return; 265 266 if (dn_expand(base, base + blen, rdata, rdata_buffer, MAX_DATA_LEN) < 0) { 267 perror("process_answer/dn_expand"); 268 return; 269 } 270 271 DBG(1, "A -> %s %s %s ttl:%d\n", dns_type_string(a->type), name, rdata_buffer, a->ttl); 272 273 rdlength = strlen(rdata_buffer); 274 275 if (strcmp(C_DNS_SD, name) != 0 && 276 nlen + 1 < rdlength && !strcmp(rdata_buffer + rdlength - nlen, name)) 277 host_len = rdlength - nlen - 1; 278 279 if (name[0] == '_') 280 cache_service(iface, rdata_buffer, host_len, a->ttl); 281 282 dlen = strlen(rdata_buffer) + 1; 283 rdata = (uint8_t*)rdata_buffer; 284 break; 285 286 case TYPE_SRV: 287 if (a->rdlength < 8) 288 return; 289 290 port = be16_to_cpu(dsd->port); 291 memcpy(rdata_buffer, dsd, sizeof(*dsd)); 292 if (dn_expand(base, base + blen, (const uint8_t*)&dsd[1], 293 &rdata_buffer[sizeof(*dsd)], MAX_DATA_LEN - sizeof(*dsd)) < 0) { 294 perror("process_answer/dn_expand"); 295 return; 296 } 297 dlen = sizeof(*dsd) + strlen(&rdata_buffer[sizeof(*dsd)]) + 1; 298 rdata = (uint8_t*)rdata_buffer; 299 break; 300 301 case TYPE_TXT: 302 rdlength = a->rdlength; 303 if (rdlength <= 2) 304 return; 305 306 memcpy(rdata_buffer, &rdata[1], rdlength-1); 307 rdata_buffer[rdlength] = rdata_buffer[rdlength + 1] = '\0'; 308 tlen = rdlength + 1; 309 p = &rdata_buffer[*rdata]; 310 311 do { 312 uint8_t v = *p; 313 314 *p = '\0'; 315 if (v && p + v < &rdata_buffer[rdlength]) 316 p += v + 1; 317 } while (*p); 318 break; 319 320 case TYPE_A: 321 if (a->rdlength != 4) 322 return; 323 dlen = 4; 324 break; 325 326 case TYPE_AAAA: 327 if (a->rdlength != 16) 328 return; 329 dlen = 16; 330 break; 331 332 default: 333 return; 334 } 335 336 r = cache_record_find(name, a->type, port, dlen, rdata); 337 if (r) { 338 if (!a->ttl) { 339 DBG(1, "D -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 340 r->time = now + 1 - r->ttl; 341 r->refresh = 100; 342 } else { 343 r->ttl = a->ttl; 344 r->time = now; 345 r->refresh = 50; 346 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 347 } 348 return; 349 } 350 351 if (!a->ttl) 352 return; 353 354 r = calloc_a(sizeof(*r), 355 &name_buf, strlen(name) + 1, 356 &txt_ptr, tlen, 357 &rdata_ptr, dlen); 358 359 r->avl.key = r->record = strcpy(name_buf, name); 360 r->type = a->type; 361 r->ttl = a->ttl; 362 r->port = port; 363 r->rdlength = dlen; 364 r->time = now; 365 r->iface = iface; 366 if (interface_ipv6(iface)) 367 memcpy(&r->from, from, sizeof(struct sockaddr_in6)); 368 else 369 memcpy(&r->from, from, sizeof(struct sockaddr_in)); 370 r->refresh = 50; 371 372 if (tlen) 373 r->txt = memcpy(txt_ptr, rdata_buffer, tlen); 374 375 if (dlen) 376 r->rdata = memcpy(rdata_ptr, rdata, dlen); 377 378 if (avl_insert(&records, &r->avl)) 379 free(r); 380 else 381 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r->type), r->record, r->ttl); 382 } 383 384 void 385 cache_dump_records(struct blob_buf *buf, const char *name, int array, 386 const char **hostname) 387 { 388 struct cache_record *r, *last, *next; 389 const char *txt; 390 char buffer[INET6_ADDRSTRLEN]; 391 void *c = NULL; 392 393 last = avl_last_element(&records, last, avl); 394 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 395 switch (r->type) { 396 case TYPE_A: 397 if (!c && array) 398 c = blobmsg_open_array(buf, "ipv4"); 399 if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buffer, INET6_ADDRSTRLEN)) 400 blobmsg_add_string(buf, "ipv4", buffer); 401 break; 402 } 403 404 if (r == last) 405 break; 406 407 next = avl_next_element(r, avl); 408 if (strcmp(r->record, next->record) != 0) 409 break; 410 } 411 412 if (c) { 413 blobmsg_close_array(buf, c); 414 c = NULL; 415 } 416 417 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 418 switch (r->type) { 419 case TYPE_AAAA: 420 if (!c && array) 421 c = blobmsg_open_array(buf, "ipv6"); 422 if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buffer, INET6_ADDRSTRLEN)) 423 blobmsg_add_string(buf, "ipv6", buffer); 424 break; 425 } 426 427 if (r == last) 428 break; 429 430 next = avl_next_element(r, avl); 431 if (strcmp(r->record, next->record) != 0) 432 break; 433 } 434 435 if (c) { 436 blobmsg_close_array(buf, c); 437 c = NULL; 438 } 439 440 for (r = avl_find_element(&records, name, r, avl); r; r = next) { 441 switch (r->type) { 442 case TYPE_TXT: 443 if (r->txt && strlen(r->txt)) { 444 if (array) 445 c = blobmsg_open_array(buf, "txt"); 446 447 txt = r->txt; 448 do { 449 blobmsg_add_string(buf, "txt", txt); 450 txt = &txt[strlen(txt) + 1]; 451 } while (*txt); 452 if (array) 453 blobmsg_close_array(buf, c); 454 } 455 break; 456 457 case TYPE_SRV: 458 if (r->rdata) { 459 blobmsg_add_string(buf, "host", (char *)r->rdata + sizeof(struct dns_srv_data)); 460 if (hostname) 461 *hostname = (char *)r->rdata + sizeof(struct dns_srv_data); 462 } 463 if (r->port) 464 blobmsg_add_u32(buf, "port", r->port); 465 break; 466 } 467 468 if (r == last) 469 break; 470 471 next = avl_next_element(r, avl); 472 if (strcmp(r->record, next->record) != 0) 473 break; 474 } 475 } 476 477 void 478 cache_dump_recursive(struct blob_buf *b, const char *name, uint16_t type, struct interface *iface) 479 { 480 time_t now = monotonic_time(); 481 for (struct cache_record *r = avl_find_ge_element(&records, name, r, avl); 482 r && !strcmp(r->record, name); 483 r = !avl_is_last(&records, &r->avl) ? avl_next_element(r, avl) : NULL) { 484 int32_t ttl = r->ttl - (now - r->time); 485 if (ttl <= 0 || (iface && iface->ifindex != r->iface->ifindex) || 486 (type != TYPE_ANY && type != r->type)) 487 continue; 488 489 const char *txt; 490 char buf[INET6_ADDRSTRLEN]; 491 void *k = blobmsg_open_table(b, NULL), *l; 492 const struct dns_srv_data *dsd = (const struct dns_srv_data*)r->rdata; 493 494 blobmsg_add_string(b, "name", r->record); 495 blobmsg_add_string(b, "type", dns_type_string(r->type)); 496 blobmsg_add_u32(b, "ttl", ttl); 497 498 switch (r->type) { 499 case TYPE_TXT: 500 if ((txt = r->txt) && strlen(txt)) { 501 l = blobmsg_open_array(b, "data"); 502 do { 503 blobmsg_add_string(b, NULL, txt); 504 txt = &txt[strlen(txt) + 1]; 505 } while (*txt); 506 blobmsg_close_array(b, l); 507 } 508 break; 509 510 case TYPE_SRV: 511 if (r->rdlength > sizeof(*dsd)) { 512 blobmsg_add_u32(b, "priority", be16_to_cpu(dsd->priority)); 513 blobmsg_add_u32(b, "weight", be16_to_cpu(dsd->weight)); 514 blobmsg_add_u32(b, "port", be16_to_cpu(dsd->port)); 515 blobmsg_add_string(b, "target", (const char*)&dsd[1]); 516 } 517 break; 518 519 case TYPE_PTR: 520 if (r->rdlength > 0) 521 blobmsg_add_string(b, "target", (const char*)r->rdata); 522 break; 523 524 case TYPE_A: 525 if ((r->rdlength == 4) && inet_ntop(AF_INET, r->rdata, buf, sizeof(buf))) 526 blobmsg_add_string(b, "target", buf); 527 break; 528 529 case TYPE_AAAA: 530 if ((r->rdlength == 16) && inet_ntop(AF_INET6, r->rdata, buf, sizeof(buf))) 531 blobmsg_add_string(b, "target", buf); 532 break; 533 } 534 535 blobmsg_close_table(b, k); 536 537 538 if (r->type == TYPE_PTR) { 539 cache_dump_recursive(b, (const char*)r->rdata, TYPE_SRV, iface); 540 cache_dump_recursive(b, (const char*)r->rdata, TYPE_TXT, iface); 541 } 542 543 if (r->type == TYPE_SRV) { 544 cache_dump_recursive(b, (const char*)&dsd[1], TYPE_A, iface); 545 cache_dump_recursive(b, (const char*)&dsd[1], TYPE_AAAA, iface); 546 } 547 } 548 } 549
This page was automatically generated by LXR 0.3.1. • OpenWrt