2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
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
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.
15 #include <sys/types.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>
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>
41 #include "interface.h"
43 static struct uloop_timeout cache_gc
;
44 struct avl_tree services
;
46 static int avl_strcasecmp(const void *k1
, const void *k2
, void *ptr
)
48 return strcasecmp(k1
, k2
);
51 AVL_TREE(records
, avl_strcasecmp
, true, NULL
);
54 cache_record_free(struct cache_record
*r
)
56 DBG(2, "%s %s\n", dns_type_string(r
->type
), r
->record
);
57 avl_delete(&records
, &r
->avl
);
62 cache_service_free(struct cache_service
*s
)
64 DBG(2, "%s\n", s
->entry
);
65 avl_delete(&services
, &s
->avl
);
70 cache_is_expired(time_t t
, uint32_t ttl
, int frac
)
72 if (monotonic_time() - t
>= ttl
* frac
/ 100)
79 cache_gc_timer(struct uloop_timeout
*timeout
)
81 struct cache_record
*r
, *p
;
82 struct cache_service
*s
, *t
;
84 avl_for_each_element_safe(&records
, r
, avl
, p
) {
85 if (!cache_is_expired(r
->time
, r
->ttl
, r
->refresh
))
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))
93 if (r
->refresh
>= 100) {
98 dns_send_question(r
->iface
, (struct sockaddr
*)&r
->from
, r
->record
, r
->type
, 0);
101 avl_for_each_element_safe(&services
, s
, avl
, t
) {
104 if (!cache_is_expired(s
->time
, s
->ttl
, s
->refresh
))
106 if (s
->refresh
>= 100) {
107 cache_service_free(s
);
111 dns_send_question(s
->iface
, NULL
, s
->entry
, TYPE_PTR
, 0);
114 uloop_timeout_set(timeout
, 10000);
120 avl_init(&services
, avl_strcasecmp
, true, NULL
);
122 cache_gc
.cb
= cache_gc_timer
;
123 uloop_timeout_set(&cache_gc
, 10000);
128 void cache_cleanup(struct interface
*iface
)
130 struct cache_record
*r
, *p
;
131 struct cache_service
*s
, *t
;
133 avl_for_each_element_safe(&services
, s
, avl
, t
)
134 if (!iface
|| iface
== s
->iface
)
135 cache_service_free(s
);
137 avl_for_each_element_safe(&records
, r
, avl
, p
)
138 if (!iface
|| iface
== r
->iface
)
139 cache_record_free(r
);
145 struct interface
*iface
;
146 struct cache_service
*s
;
148 vlist_for_each_element(&interfaces
, iface
, node
)
149 avl_for_each_element(&services
, s
, avl
)
150 dns_send_question(iface
, NULL
, s
->entry
, TYPE_PTR
, 0);
153 static struct cache_service
*
154 cache_service(struct interface
*iface
, char *entry
, int hlen
, int ttl
)
156 struct cache_service
*s
, *t
;
161 avl_for_each_element_safe(&services
, s
, avl
, t
)
162 if (!strcmp(s
->entry
, entry
)) {
164 s
->time
= monotonic_time();
169 s
= calloc_a(sizeof(*s
),
170 &entry_buf
, strlen(entry
) + 1,
171 &host_buf
, hlen
? hlen
+ 1 : 0);
173 s
->avl
.key
= s
->entry
= strcpy(entry_buf
, entry
);
174 s
->time
= monotonic_time();
180 s
->host
= strncpy(host_buf
, s
->entry
, hlen
);
182 type
= strstr(entry_buf
, "._");
187 avl_insert(&services
, &s
->avl
);
190 dns_send_question(iface
, NULL
, entry
, TYPE_PTR
, interface_multicast(iface
));
195 static struct cache_record
*
196 cache_record_find(char *record
, int type
, int port
, int rdlength
, uint8_t *rdata
)
198 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
200 while (l
&& !strcmp(l
->record
, record
)) {
201 struct cache_record
*r
= l
;
203 l
= !avl_is_last(&records
, &l
->avl
) ? avl_next_element(l
, avl
) : NULL
;
207 if (r
->type
== TYPE_TXT
|| (r
->type
== TYPE_SRV
))
213 if (r
->rdlength
!= rdlength
)
216 if (!!r
->rdata
!= !!rdata
)
219 if (!r
->rdata
|| !rdata
|| memcmp(r
->rdata
, rdata
, rdlength
))
229 cache_host_is_known(char *record
)
231 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
233 while (l
&& !strcmp(l
->record
, record
)) {
234 struct cache_record
*r
= l
;
236 l
= !avl_is_last(&records
, &l
->avl
) ? avl_next_element(l
, avl
) : NULL
;
237 if ((r
->type
!= TYPE_A
) && (r
->type
!= TYPE_AAAA
))
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
,
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
;
254 void *rdata_ptr
, *txt_ptr
;
256 static char *rdata_buffer
= (char *) mdns_buf
;
257 time_t now
= monotonic_time();
266 if (dn_expand(base
, base
+ blen
, rdata
, rdata_buffer
, MAX_DATA_LEN
) < 0) {
267 perror("process_answer/dn_expand");
271 DBG(1, "A -> %s %s %s ttl:%d\n", dns_type_string(a
->type
), name
, rdata_buffer
, a
->ttl
);
273 rdlength
= strlen(rdata_buffer
);
275 if (strcmp(C_DNS_SD
, name
) != 0 &&
276 nlen
+ 1 < rdlength
&& !strcmp(rdata_buffer
+ rdlength
- nlen
, name
))
277 host_len
= rdlength
- nlen
- 1;
280 cache_service(iface
, rdata_buffer
, host_len
, a
->ttl
);
282 dlen
= strlen(rdata_buffer
) + 1;
283 rdata
= (uint8_t*)rdata_buffer
;
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");
297 dlen
= sizeof(*dsd
) + strlen(&rdata_buffer
[sizeof(*dsd
)]) + 1;
298 rdata
= (uint8_t*)rdata_buffer
;
302 rdlength
= a
->rdlength
;
306 memcpy(rdata_buffer
, &rdata
[1], rdlength
-1);
307 rdata_buffer
[rdlength
] = rdata_buffer
[rdlength
+ 1] = '\0';
309 p
= &rdata_buffer
[*rdata
];
315 if (v
&& p
+ v
< &rdata_buffer
[rdlength
])
321 if (a
->rdlength
!= 4)
327 if (a
->rdlength
!= 16)
336 r
= cache_record_find(name
, a
->type
, port
, dlen
, rdata
);
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
;
346 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
354 r
= calloc_a(sizeof(*r
),
355 &name_buf
, strlen(name
) + 1,
359 r
->avl
.key
= r
->record
= strcpy(name_buf
, name
);
366 if (interface_ipv6(iface
))
367 memcpy(&r
->from
, from
, sizeof(struct sockaddr_in6
));
369 memcpy(&r
->from
, from
, sizeof(struct sockaddr_in
));
373 r
->txt
= memcpy(txt_ptr
, rdata_buffer
, tlen
);
376 r
->rdata
= memcpy(rdata_ptr
, rdata
, dlen
);
378 if (avl_insert(&records
, &r
->avl
))
381 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
385 cache_dump_records(struct blob_buf
*buf
, const char *name
, int array
,
386 const char **hostname
)
388 struct cache_record
*r
, *last
, *next
;
390 char buffer
[INET6_ADDRSTRLEN
];
393 last
= avl_last_element(&records
, last
, avl
);
394 for (r
= avl_find_element(&records
, name
, r
, avl
); r
; r
= next
) {
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
);
407 next
= avl_next_element(r
, avl
);
408 if (strcmp(r
->record
, next
->record
) != 0)
413 blobmsg_close_array(buf
, c
);
417 for (r
= avl_find_element(&records
, name
, r
, avl
); r
; r
= next
) {
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
);
430 next
= avl_next_element(r
, avl
);
431 if (strcmp(r
->record
, next
->record
) != 0)
436 blobmsg_close_array(buf
, c
);
440 for (r
= avl_find_element(&records
, name
, r
, avl
); r
; r
= next
) {
443 if (r
->txt
&& strlen(r
->txt
)) {
445 c
= blobmsg_open_array(buf
, "txt");
449 blobmsg_add_string(buf
, "txt", txt
);
450 txt
= &txt
[strlen(txt
) + 1];
453 blobmsg_close_array(buf
, c
);
459 blobmsg_add_string(buf
, "host", (char *)r
->rdata
+ sizeof(struct dns_srv_data
));
461 *hostname
= (char *)r
->rdata
+ sizeof(struct dns_srv_data
);
464 blobmsg_add_u32(buf
, "port", r
->port
);
471 next
= avl_next_element(r
, avl
);
472 if (strcmp(r
->record
, next
->record
) != 0)
478 cache_dump_recursive(struct blob_buf
*b
, const char *name
, uint16_t type
, struct interface
*iface
)
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
))
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
;
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
);
500 if ((txt
= r
->txt
) && strlen(txt
)) {
501 l
= blobmsg_open_array(b
, "data");
503 blobmsg_add_string(b
, NULL
, txt
);
504 txt
= &txt
[strlen(txt
) + 1];
506 blobmsg_close_array(b
, l
);
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]);
521 blobmsg_add_string(b
, "target", (const char*)r
->rdata
);
525 if ((r
->rdlength
== 4) && inet_ntop(AF_INET
, r
->rdata
, buf
, sizeof(buf
)))
526 blobmsg_add_string(b
, "target", buf
);
530 if ((r
->rdlength
== 16) && inet_ntop(AF_INET6
, r
->rdata
, buf
, sizeof(buf
)))
531 blobmsg_add_string(b
, "target", buf
);
535 blobmsg_close_table(b
, k
);
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
);
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
);