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>
42 static struct uloop_timeout cache_gc
;
43 struct avl_tree records
, entries
, types
, hosts
;
44 static struct blob_buf b
;
47 cache_record_free(struct cache_record
*r
, int rem
)
49 DBG(2, "%s %s\n", dns_type_string(r
->type
), r
->record
);
51 avl_delete(&records
, &r
->avl
);
62 cache_entry_free(struct cache_entry
*s
)
64 DBG(2, "%s\n", s
->entry
);
65 avl_delete(&entries
, &s
->avl
);
74 cache_is_expired(time_t t
, uint32_t ttl
)
76 if (time(NULL
) - t
>= ttl
)
83 cache_gc_timer(struct uloop_timeout
*timeout
)
85 struct cache_record
*r
, *p
;
86 struct cache_entry
*s
, *t
;
88 avl_for_each_element_safe(&records
, r
, avl
, p
)
89 if (cache_is_expired(r
->time
, r
->ttl
))
90 cache_record_free(r
, 1);
92 avl_for_each_element_safe(&entries
, s
, avl
, t
) {
95 if (cache_is_expired(s
->time
, s
->ttl
))
99 uloop_timeout_set(timeout
, 10000);
103 cache_load_services(void)
105 struct blob_attr
*cur
;
108 blob_buf_init(&b
, 0);
110 if (!blobmsg_add_json_from_file(&b
, "/lib/mdns/service-types"))
113 blob_for_each_attr(cur
, b
.head
, rem
) {
114 struct cache_type
*t
= malloc(sizeof(struct cache_type
));
118 t
->avl
.key
= t
->key
= strdup(blobmsg_name(cur
));
119 t
->val
= strdup(blobmsg_get_string(cur
));
120 avl_insert(&types
, &t
->avl
);
125 cache_lookup_name(const char *key
)
127 struct cache_type
*t
;
129 t
= avl_find_element(&types
, key
, t
, avl
);
139 avl_init(&entries
, avl_strcmp
, true, NULL
);
140 avl_init(&types
, avl_strcmp
, false, NULL
);
141 avl_init(&records
, avl_strcmp
, true, NULL
);
143 cache_gc
.cb
= cache_gc_timer
;
144 uloop_timeout_set(&cache_gc
, 10000);
145 cache_load_services();
150 void cache_cleanup(void)
152 struct cache_record
*r
, *p
;
153 struct cache_entry
*s
, *t
;
155 avl_for_each_element_safe(&records
, r
, avl
, p
)
156 cache_record_free(r
, 1);
158 avl_for_each_element_safe(&entries
, s
, avl
, t
)
165 struct cache_entry
*s
;
167 avl_for_each_element(&entries
, s
, avl
)
168 dns_send_question(&listener
, s
->entry
, TYPE_PTR
);
171 static struct cache_entry
*
172 cache_find_entry(char *entry
)
174 struct cache_entry
*s
;
176 avl_for_each_element(&entries
, s
, avl
)
177 if (!strcmp(s
->entry
, entry
))
182 static struct cache_entry
*
183 cache_entry(struct uloop_fd
*u
, char *entry
, int hlen
, int ttl
)
185 struct cache_entry
*s
;
188 s
= cache_find_entry(entry
);
192 s
= malloc(sizeof(struct cache_entry
));
193 memset(s
, 0, sizeof(struct cache_entry
));
194 s
->avl
.key
= s
->entry
= strdup(entry
);
195 s
->time
= time(NULL
);
199 s
->host
= strndup(s
->entry
, hlen
);
200 type
= strstr(s
->entry
, "._");
205 avl_insert(&entries
, &s
->avl
);
208 dns_send_question(u
, entry
, TYPE_PTR
);
213 static struct cache_record
*
214 cache_record_find(char *record
, int type
, int port
, int rdlength
, uint8_t *rdata
)
216 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
221 while (l
&& !avl_is_last(&records
, &l
->avl
) && !strcmp(l
->record
, record
)) {
222 struct cache_record
*r
= l
;
224 l
= avl_next_element(l
, avl
);
228 if (r
->type
== TYPE_TXT
|| (r
->type
== TYPE_SRV
))
234 if (r
->rdlength
!= rdlength
)
237 if (!!r
->rdata
!= !!rdata
)
240 if (!r
->rdata
|| !rdata
|| memcmp(r
->rdata
, rdata
, rdlength
))
250 cache_host_is_known(char *record
)
252 struct cache_record
*l
= avl_find_element(&records
, record
, l
, avl
);
257 while (l
&& !avl_is_last(&records
, &l
->avl
) && !strcmp(l
->record
, record
)) {
258 struct cache_record
*r
= l
;
260 l
= avl_next_element(l
, avl
);
261 if ((r
->type
!= TYPE_A
) && (r
->type
!= TYPE_AAAA
))
270 cache_answer(struct uloop_fd
*u
, uint8_t *base
, int blen
, char *name
, struct dns_answer
*a
, uint8_t *rdata
)
272 struct dns_srv_data
*dsd
= (struct dns_srv_data
*) rdata
;
273 struct cache_record
*r
;
274 int port
= 0, dlen
= 0, tlen
= 0, nlen
, rdlength
;
277 if (!(a
->class & CLASS_IN
))
287 if (dn_expand(base
, base
+ blen
, rdata
, rdata_buffer
, MAX_DATA_LEN
) < 0) {
288 perror("process_answer/dn_expand");
292 DBG(1, "A -> %s %s %s\n", dns_type_string(a
->type
), name
, rdata_buffer
);
294 rdlength
= strlen(rdata_buffer
);
296 if (!strcmp(C_DNS_SD
, name
)) {
297 cache_entry(u
, rdata_buffer
, 0, a
->ttl
);
301 if ((rdlength
< nlen
) && (rdlength
- nlen
- 1 > 0))
304 cache_entry(u
, rdata_buffer
, rdlength
- nlen
- 1, a
->ttl
);
311 port
= be16_to_cpu(dsd
->port
);
315 rdlength
= a
->rdlength
;
319 memcpy(rdata_buffer
, &rdata
[1], rdlength
);
320 rdata_buffer
[rdlength
] = rdata_buffer
[rdlength
+ 1] = '\0';
322 p
= &rdata_buffer
[*rdata
];
334 cache_entry(u
, name
, strlen(name
), a
->ttl
);
335 if (a
->rdlength
!= 4)
341 cache_entry(u
, name
, strlen(name
), a
->ttl
);
342 if (a
->rdlength
!= 16)
351 r
= cache_record_find(name
, a
->type
, port
, dlen
, rdata
);
354 cache_record_free(r
, 1);
355 DBG(1, "D -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
358 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);
366 r
= malloc(sizeof(struct cache_record
));
367 memset(r
, 0, sizeof(struct cache_record
));
368 r
->avl
.key
= r
->record
= strdup(name
);
373 r
->time
= time(NULL
);
376 r
->txt
= malloc(tlen
);
378 memcpy(r
->txt
, rdata_buffer
, tlen
);
382 r
->rdata
= malloc(dlen
);
384 cache_record_free(r
, 0);
387 memcpy(r
->rdata
, rdata
, dlen
);
390 if (avl_insert(&records
, &r
->avl
))
391 cache_record_free(r
, 0);
393 DBG(1, "A -> %s %s ttl:%d\n", dns_type_string(r
->type
), r
->record
, r
->ttl
);