dbd258d2f073a3e79802815b9b6e6969e767bf8e
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 <asm/byteorder.h>
28 #include <arpa/nameser.h>
32 #include <libubox/usock.h>
33 #include <libubox/uloop.h>
34 #include <libubox/avl-cmp.h>
35 #include <libubox/blobmsg_json.h>
36 #include <libubox/kvlist.h>
42 #include "interface.h"
44 static struct uloop_timeout cache_gc
;
45 struct avl_tree entries
;
46 static AVL_TREE(records
, avl_strcmp
, true, NULL
);
49 cache_record_free(struct cache_record
*r
)
51 DBG(2, "%s %s\n", dns_type_string(r
->type
), r
->record
);
52 avl_delete(&records
, &r
->avl
);
57 cache_entry_free(struct cache_entry
*s
)
59 DBG(2, "%s\n", s
->entry
);
60 avl_delete(&entries
, &s
->avl
);
65 cache_is_expired(time_t t
, uint32_t ttl
)
67 if (time(NULL
) - t
>= ttl
)
74 cache_gc_timer(struct uloop_timeout
*timeout
)
76 struct cache_record
*r
, *p
;
77 struct cache_entry
*s
, *t
;
79 avl_for_each_element_safe(&records
, r
, avl
, p
)
80 if (cache_is_expired(r
->time
, r
->ttl
))
83 avl_for_each_element_safe(&entries
, s
, avl
, t
) {
86 if (cache_is_expired(s
->time
, s
->ttl
))
90 uloop_timeout_set(timeout
, 10000);
96 avl_init(&entries
, avl_strcmp
, true, NULL
);
98 cache_gc
.cb
= cache_gc_timer
;
99 uloop_timeout_set(&cache_gc
, 10000);
104 void cache_cleanup(void)
106 struct cache_record
*r
, *p
;
107 struct cache_entry
*s
, *t
;
109 avl_for_each_element_safe(&records
, r
, avl
, p
)
110 cache_record_free(r
);
112 avl_for_each_element_safe(&entries
, s
, avl
, t
)
119 struct interface
*iface
;
120 struct cache_entry
*s
;
122 vlist_for_each_element(&interfaces
, iface
, node
)
123 avl_for_each_element(&entries
, s
, avl
)
124 dns_send_question(iface
, s
->entry
, TYPE_PTR
);
127 static struct cache_entry
*
128 cache_entry(struct interface
*iface
, char *entry
, int hlen
, int ttl
)
130 struct cache_entry
*s
;
135 s
= avl_find_element(&entries
, entry
, s
, avl
);
139 s
= calloc_a(sizeof(*s
),
140 &entry_buf
, strlen(entry
) + 1,
141 &host_buf
, hlen
? hlen
+ 1 : 0);
143 s
->avl
.key
= s
->entry
= strcpy(entry_buf
, entry
);
144 s
->time
= time(NULL
);
148 s
->host
= strncpy(host_buf
, s
->entry
, hlen
);
150 type
= strstr(entry_buf
, "._");
155 avl_insert(&entries
, &s
->avl
);
158 dns_send_question(iface
, entry
, TYPE_PTR
);
163 static struct cache_record
*
164 cache_record_find(char *record
, int type
, int port
, int rdlength
, uint8_t *rdata
)
166 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
171 while (l
&& !avl_is_last(&records
, &l
->avl
) && !strcmp(l
->record
, record
)) {
172 struct cache_record
*r
= l
;
174 l
= avl_next_element(l
, avl
);
178 if (r
->type
== TYPE_TXT
|| (r
->type
== TYPE_SRV
))
184 if (r
->rdlength
!= rdlength
)
187 if (!!r
->rdata
!= !!rdata
)
190 if (!r
->rdata
|| !rdata
|| memcmp(r
->rdata
, rdata
, rdlength
))
200 cache_host_is_known(char *record
)
202 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
207 while (l
&& !avl_is_last(&records
, &l
->avl
) && !strcmp(l
->record
, record
)) {
208 struct cache_record
*r
= l
;
210 l
= avl_next_element(l
, avl
);
211 if ((r
->type
!= TYPE_A
) && (r
->type
!= TYPE_AAAA
))
220 cache_answer(struct interface
*iface
, uint8_t *base
, int blen
, char *name
, struct dns_answer
*a
, uint8_t *rdata
)
222 struct dns_srv_data
*dsd
= (struct dns_srv_data
*) rdata
;
223 struct cache_record
*r
;
224 int port
= 0, dlen
= 0, tlen
= 0, nlen
, rdlength
;
227 void *rdata_ptr
, *txt_ptr
;
229 static char *rdata_buffer
= (char *) mdns_buf
;
231 if (!(a
->class & CLASS_IN
))
241 if (dn_expand(base
, base
+ blen
, rdata
, rdata_buffer
, MAX_DATA_LEN
) < 0) {
242 perror("process_answer/dn_expand");
246 DBG(1, "A -> %s %s %s\n", dns_type_string(a
->type
), name
, rdata_buffer
);
248 rdlength
= strlen(rdata_buffer
);
250 if (strcmp(C_DNS_SD
, name
) != 0 &&
251 nlen
+ 1 < rdlength
&& !strcmp(rdata_buffer
+ rdlength
- nlen
, name
))
252 host_len
= rdlength
- nlen
- 1;
254 cache_entry(iface
, rdata_buffer
, host_len
, a
->ttl
);
261 port
= be16_to_cpu(dsd
->port
);
265 rdlength
= a
->rdlength
;
269 memcpy(rdata_buffer
, &rdata
[1], rdlength
);
270 rdata_buffer
[rdlength
] = rdata_buffer
[rdlength
+ 1] = '\0';
272 p
= &rdata_buffer
[*rdata
];
284 cache_entry(iface
, name
, strlen(name
), a
->ttl
);
285 if (a
->rdlength
!= 4)
291 cache_entry(iface
, name
, strlen(name
), a
->ttl
);
292 if (a
->rdlength
!= 16)
301 r
= cache_record_find(name
, a
->type
, port
, dlen
, rdata
);
304 cache_record_free(r
);
305 DBG(1, "D -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
308 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
316 r
= calloc_a(sizeof(*r
),
317 &name_buf
, strlen(name
) + 1,
321 r
->avl
.key
= r
->record
= strcpy(name_buf
, name
);
326 r
->time
= time(NULL
);
329 r
->txt
= memcpy(txt_ptr
, rdata_buffer
, tlen
);
332 r
->rdata
= memcpy(rdata_ptr
, rdata
, dlen
);
334 if (avl_insert(&records
, &r
->avl
))
337 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
341 cache_dump_records(struct blob_buf
*buf
, const char *name
)
343 struct cache_record
*r
, *q
= avl_find_element(&records
, name
, r
, avl
);
345 char buffer
[INET6_ADDRSTRLEN
];
354 if (r
->txt
&& strlen(r
->txt
)) {
357 blobmsg_add_string(buf
, "txt", txt
);
358 txt
= &txt
[strlen(txt
) + 1];
365 blobmsg_add_u32(buf
, "port", r
->port
);
369 if ((r
->rdlength
== 4) && inet_ntop(AF_INET
, r
->rdata
, buffer
, INET6_ADDRSTRLEN
))
370 blobmsg_add_string(buf
, "ipv4", buffer
);
374 if ((r
->rdlength
== 16) && inet_ntop(AF_INET6
, r
->rdata
, buffer
, INET6_ADDRSTRLEN
))
375 blobmsg_add_string(buf
, "ipv6", buffer
);
378 q
= avl_next_element(r
, avl
);
379 } while (q
&& !strcmp(r
->record
, q
->record
));