/src/open62541/deps/mdnsd/libmdnsd/mdnsd.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "mdnsd_config.h" |
2 | | #include "mdnsd.h" |
3 | | #include <string.h> |
4 | | #include <stdlib.h> |
5 | | #include <errno.h> |
6 | | |
7 | 212k | #define SPRIME 108 /* Size of query/publish hashes */ |
8 | 1.52M | #define LPRIME 1009 /* Size of cache hash */ |
9 | | |
10 | 1.49k | #define GC 86400 /* Brute force garbage cleanup |
11 | | * frequency, rarely needed (daily |
12 | | * default) */ |
13 | | |
14 | | #ifdef _MSC_VER |
15 | | #include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */ |
16 | | |
17 | | int gettimeofday(struct timeval * tp, struct timezone * tzp) |
18 | | { |
19 | | // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's |
20 | | static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); |
21 | | |
22 | | SYSTEMTIME system_time; |
23 | | FILETIME file_time; |
24 | | uint64_t time; |
25 | | |
26 | | GetSystemTime( &system_time ); |
27 | | SystemTimeToFileTime( &system_time, &file_time ); |
28 | | time = ((uint64_t)file_time.dwLowDateTime ) ; |
29 | | time += ((uint64_t)file_time.dwHighDateTime) << 32; |
30 | | |
31 | | tp->tv_sec = (long) ((time - EPOCH) / 10000000L); |
32 | | tp->tv_usec = (long) (system_time.wMilliseconds * 1000); |
33 | | return 0; |
34 | | } |
35 | | #else |
36 | | #include <sys/time.h> |
37 | | #endif |
38 | | |
39 | | #if defined(__MINGW32__) |
40 | | static char *my_strdup(const char *s) { |
41 | | char *p = (char *)MDNSD_malloc(strlen(s) + 1); |
42 | | if(p) { strcpy(p, s); } |
43 | | return p; |
44 | | } |
45 | | #define STRDUP my_strdup |
46 | | #elif defined(_WIN32) |
47 | | #define STRDUP _strdup |
48 | | #else |
49 | 10.7k | #define STRDUP strdup |
50 | | #endif |
51 | | |
52 | | #ifndef _WIN32 |
53 | | # include <netdb.h> |
54 | | #endif |
55 | | |
56 | | /** |
57 | | * Messy, but it's the best/simplest balance I can find at the moment |
58 | | * |
59 | | * Some internal data types, and a few hashes: querys, answers, cached, |
60 | | * and records (published, unique and shared). Each type has different |
61 | | * semantics for processing, both for timeouts, incoming, and outgoing |
62 | | * I/O. They inter-relate too, like records affect the querys they are |
63 | | * relevant to. Nice things about MDNS: we only publish once (and then |
64 | | * ask asked), and only query once, then just expire records we've got |
65 | | * cached |
66 | | */ |
67 | | |
68 | | struct query { |
69 | | char *name; |
70 | | int type; |
71 | | unsigned long int nexttry; |
72 | | int tries; |
73 | | int (*answer)(mdns_answer_t *, void *); |
74 | | void *arg; |
75 | | struct query *next, *list; |
76 | | }; |
77 | | |
78 | | struct unicast { |
79 | | int id; |
80 | | struct sockaddr *to; |
81 | | unsigned short int port; |
82 | | mdns_record_t *r; |
83 | | struct unicast *next; |
84 | | }; |
85 | | |
86 | | struct cached { |
87 | | struct mdns_answer rr; |
88 | | struct query *q; |
89 | | struct cached *next; |
90 | | }; |
91 | | |
92 | | struct mdns_record { |
93 | | struct mdns_answer rr; |
94 | | char unique; /* # of checks performed to ensure */ |
95 | | int tries; |
96 | | void (*conflict)(char *, int, void *); |
97 | | void *arg; |
98 | | struct timeval last_sent; |
99 | | struct mdns_record *next, *list; |
100 | | }; |
101 | | |
102 | | struct mdns_daemon { |
103 | | char shutdown; |
104 | | unsigned long int expireall, checkqlist; |
105 | | struct timeval now, sleep, pause, probe, publish; |
106 | | int clazz, frame; |
107 | | struct cached *cache[LPRIME]; |
108 | | struct mdns_record *published[SPRIME], *probing, *a_now, *a_pause, *a_publish; |
109 | | struct unicast *uanswers; |
110 | | struct query *queries[SPRIME], *qlist; |
111 | | mdnsd_record_received_callback received_callback; |
112 | | void *received_callback_data; |
113 | | }; |
114 | | |
115 | | static int _namehash(const char *s) |
116 | 30.1k | { |
117 | 30.1k | const unsigned char *name = (const unsigned char *)s; |
118 | 30.1k | unsigned long h = 0; |
119 | | |
120 | 244k | while (*name) { /* do some fancy bitwanking on the string */ |
121 | 214k | unsigned long int g; |
122 | 214k | h = (h << 4) + (unsigned long int)(*name++); |
123 | 214k | if ((g = (h & 0xF0000000UL)) != 0) |
124 | 153k | h ^= (g >> 24); |
125 | 214k | h &= ~g; |
126 | 214k | } |
127 | | |
128 | 30.1k | return (int)h; |
129 | 30.1k | } |
130 | | |
131 | | /* Basic linked list and hash primitives */ |
132 | | static struct query *_q_next(mdns_daemon_t *d, struct query *q, const char *host, int type) |
133 | 5.51k | { |
134 | 5.51k | if (q == 0) |
135 | 5.51k | q = d->queries[_namehash(host) % SPRIME]; |
136 | 0 | else |
137 | 0 | q = q->next; |
138 | | |
139 | 5.51k | for (; q != 0; q = q->next) { |
140 | 0 | if (q->type == type && strcmp(q->name, host) == 0) |
141 | 0 | return q; |
142 | 0 | } |
143 | | |
144 | 5.51k | return 0; |
145 | 5.51k | } |
146 | | |
147 | | static struct cached *_c_next(mdns_daemon_t *d, struct cached *c,const char *host, int type) |
148 | 7.89k | { |
149 | 7.89k | if (c == 0) |
150 | 5.90k | c = d->cache[_namehash(host) % LPRIME]; |
151 | 1.98k | else |
152 | 1.98k | c = c->next; |
153 | | |
154 | 12.6k | for (; c != 0; c = c->next) { |
155 | 9.12k | if ((type == c->rr.type || type == 255) && strcmp(c->rr.name, host) == 0) |
156 | 4.34k | return c; |
157 | 9.12k | } |
158 | | |
159 | 3.54k | return 0; |
160 | 7.89k | } |
161 | | |
162 | | static mdns_record_t *_r_next(mdns_daemon_t *d, mdns_record_t *r, const char *host, int type) |
163 | 7.62k | { |
164 | 7.62k | if (r == 0) |
165 | 7.62k | r = d->published[_namehash(host) % SPRIME]; |
166 | 0 | else |
167 | 0 | r = r->next; |
168 | | |
169 | 7.62k | for (; r != 0; r = r->next) { |
170 | 0 | if (type == r->rr.type && strcmp(r->rr.name, host) == 0) |
171 | 0 | return r; |
172 | 0 | } |
173 | | |
174 | 7.62k | return 0; |
175 | 7.62k | } |
176 | | |
177 | | static int _rr_len(mdns_answer_t *rr) |
178 | 0 | { |
179 | 0 | int len = 12; /* name is always compressed (dup of earlier), plus normal stuff */ |
180 | |
|
181 | 0 | if (rr->rdata) |
182 | 0 | len += rr->rdlen; |
183 | 0 | if (rr->rdname) |
184 | 0 | len += (int)strlen(rr->rdname); /* worst case */ |
185 | 0 | if (rr->ip.s_addr) |
186 | 0 | len += 4; |
187 | 0 | if (rr->type == QTYPE_PTR) |
188 | 0 | len += 6; /* srv record stuff */ |
189 | |
|
190 | 0 | return len; |
191 | 0 | } |
192 | | |
193 | | /* Compares new rdata with known a, painfully */ |
194 | | static int _a_match(struct resource *r, mdns_answer_t *a) |
195 | 3.90k | { |
196 | 3.90k | if (!a->name) |
197 | 0 | return 0; |
198 | 3.90k | if (strcmp(r->name, a->name) != 0 || r->type != a->type) |
199 | 344 | return 0; |
200 | | |
201 | 3.56k | if (r->type == QTYPE_SRV && !strcmp(r->known.srv.name, a->rdname) && a->srv.port == r->known.srv.port && |
202 | 3.56k | a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority) |
203 | 75 | return 1; |
204 | | |
205 | 3.49k | if ((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp(a->rdname, r->known.ns.name)) |
206 | 525 | return 1; |
207 | | |
208 | 2.96k | if (r->rdlength == a->rdlen && r->rdlength == 0) |
209 | 1.64k | return 1; |
210 | | |
211 | 1.32k | if ((r->rdlength == a->rdlen) && !memcmp(r->rdata, a->rdata, r->rdlength)) |
212 | 117 | return 1; |
213 | | |
214 | 1.20k | return 0; |
215 | 1.32k | } |
216 | | |
217 | | /* Compare time values easily */ |
218 | | static int _tvdiff(struct timeval old_time, struct timeval new_time) |
219 | 0 | { |
220 | 0 | int udiff = 0; |
221 | |
|
222 | 0 | if (old_time.tv_sec != new_time.tv_sec) |
223 | 0 | udiff = (int)((new_time.tv_sec - old_time.tv_sec) * 1000000); |
224 | |
|
225 | 0 | return (int)((new_time.tv_usec - old_time.tv_usec) + udiff); |
226 | 0 | } |
227 | | |
228 | 0 | static void _r_remove_list(mdns_record_t **list, mdns_record_t *r) { |
229 | 0 | if (*list == r) { |
230 | 0 | *list = r->list; |
231 | 0 | } else { |
232 | 0 | mdns_record_t *tmp = *list; |
233 | 0 | while (tmp) { |
234 | 0 | if (tmp->list == r) { |
235 | 0 | tmp->list = r->list; |
236 | 0 | break; |
237 | 0 | } |
238 | 0 | if (tmp == tmp->list) |
239 | 0 | break; |
240 | 0 | tmp = tmp->list; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | 0 | static void _r_remove_lists(mdns_daemon_t *d, mdns_record_t *r, mdns_record_t **skip) { |
246 | 0 | if (d->probing && &d->probing != skip) { |
247 | 0 | _r_remove_list(&d->probing, r); |
248 | 0 | } |
249 | 0 | if (d->a_now && &d->a_now != skip) { |
250 | 0 | _r_remove_list(&d->a_now, r); |
251 | 0 | } |
252 | 0 | if (d->a_pause && &d->a_pause != skip) { |
253 | 0 | _r_remove_list(&d->a_pause, r); |
254 | 0 | } |
255 | 0 | if (d->a_publish && &d->a_publish != skip) { |
256 | 0 | _r_remove_list(&d->a_publish, r); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | /* Make sure not already on the list, then insert */ |
261 | | static void _r_push(mdns_record_t **list, mdns_record_t *r) |
262 | 1.80k | { |
263 | 1.80k | mdns_record_t *cur; |
264 | | |
265 | 3.90k | for (cur = *list; cur != 0; cur = cur->list) { |
266 | 2.10k | if (cur == r) |
267 | 0 | return; |
268 | 2.10k | } |
269 | | |
270 | 1.80k | r->list = *list; |
271 | 1.80k | *list = r; |
272 | 1.80k | } |
273 | | |
274 | | /* Force any r out right away, if valid */ |
275 | | static void _r_publish(mdns_daemon_t *d, mdns_record_t *r) |
276 | 1.80k | { |
277 | 1.80k | if (r->unique && r->unique < 5) |
278 | 600 | return; /* Probing already */ |
279 | | |
280 | 1.20k | r->tries = 0; |
281 | 1.20k | d->publish.tv_sec = d->now.tv_sec; |
282 | 1.20k | d->publish.tv_usec = d->now.tv_usec; |
283 | 1.20k | _r_push(&d->a_publish, r); |
284 | 1.20k | } |
285 | | |
286 | | /* send r out asap */ |
287 | | static void _r_send(mdns_daemon_t *d, mdns_record_t *r) |
288 | 600 | { |
289 | | /* Being published, make sure that happens soon */ |
290 | 600 | if (r->tries < 4) { |
291 | 600 | d->publish.tv_sec = d->now.tv_sec; |
292 | 600 | d->publish.tv_usec = d->now.tv_usec; |
293 | 600 | return; |
294 | 600 | } |
295 | | |
296 | | /* Known unique ones can be sent asap */ |
297 | 0 | if (r->unique) { |
298 | | |
299 | | // check if r already in other lists. If yes, remove it from there |
300 | 0 | _r_remove_lists(d,r, &d->a_now); |
301 | 0 | _r_push(&d->a_now, r); |
302 | 0 | return; |
303 | 0 | } |
304 | | |
305 | | /* Set d->pause.tv_usec to random 20-120 msec */ |
306 | 0 | d->pause.tv_sec = d->now.tv_sec; |
307 | 0 | d->pause.tv_usec = d->now.tv_usec + (d->now.tv_usec % 100) + 20; |
308 | 0 | _r_push(&d->a_pause, r); |
309 | 0 | } |
310 | | |
311 | | /* Create generic unicast response struct */ |
312 | | static void _u_push(mdns_daemon_t *d, mdns_record_t *r, int id, struct sockaddr *to, unsigned short int port) |
313 | 0 | { |
314 | 0 | struct unicast *u; |
315 | |
|
316 | 0 | u = (struct unicast *)MDNSD_calloc(1, sizeof(struct unicast)); |
317 | 0 | u->r = r; |
318 | 0 | u->id = id; |
319 | 0 | u->to = to; |
320 | 0 | u->port = port; |
321 | 0 | u->next = d->uanswers; |
322 | 0 | d->uanswers = u; |
323 | 0 | } |
324 | | |
325 | | static void _q_reset(mdns_daemon_t *d, struct query *q) |
326 | 300 | { |
327 | 300 | struct cached *cur = 0; |
328 | | |
329 | 300 | q->nexttry = 0; |
330 | 300 | q->tries = 0; |
331 | | |
332 | 300 | while ((cur = _c_next(d, cur, q->name, q->type))) { |
333 | 0 | if (q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry) |
334 | 0 | q->nexttry = cur->rr.ttl - 7; |
335 | 0 | } |
336 | | |
337 | 300 | if (q->nexttry != 0 && q->nexttry < d->checkqlist) |
338 | 0 | d->checkqlist = q->nexttry; |
339 | 300 | } |
340 | | |
341 | | /* No more query, update all it's cached entries, remove from lists */ |
342 | | static void _q_done(mdns_daemon_t *d, struct query *q) |
343 | 0 | { |
344 | 0 | struct cached *c = 0; |
345 | 0 | struct query *cur; |
346 | 0 | int i = _namehash(q->name) % LPRIME; |
347 | |
|
348 | 0 | while ((c = _c_next(d, c, q->name, q->type))) |
349 | 0 | c->q = 0; |
350 | |
|
351 | 0 | if (d->qlist == q) { |
352 | 0 | d->qlist = q->list; |
353 | 0 | } else { |
354 | 0 | for (cur = d->qlist; cur->list != q; cur = cur->list) |
355 | 0 | ; |
356 | 0 | cur->list = q->list; |
357 | 0 | } |
358 | |
|
359 | 0 | if (d->queries[i] == q) { |
360 | 0 | d->queries[i] = q->next; |
361 | 0 | } else { |
362 | 0 | for (cur = d->queries[i]; cur->next != q; cur = cur->next) |
363 | 0 | ; |
364 | 0 | cur->next = q->next; |
365 | 0 | } |
366 | |
|
367 | 0 | MDNSD_free(q->name); |
368 | 0 | MDNSD_free(q); |
369 | 0 | } |
370 | | |
371 | | /* buh-bye, remove from hash and free */ |
372 | | static void _r_done(mdns_daemon_t *d, mdns_record_t *r) |
373 | 600 | { |
374 | 600 | mdns_record_t *cur = 0; |
375 | 600 | int i = _namehash(r->rr.name) % SPRIME; |
376 | | |
377 | 600 | if (d->published[i] == r) |
378 | 300 | d->published[i] = r->next; |
379 | 300 | else { |
380 | 300 | for (cur = d->published[i]; cur && cur->next != r; cur = cur->next) ; |
381 | 300 | if (cur) |
382 | 300 | cur->next = r->next; |
383 | 300 | } |
384 | 600 | MDNSD_free(r->rr.name); |
385 | 600 | MDNSD_free(r->rr.rdata); |
386 | 600 | MDNSD_free(r->rr.rdname); |
387 | 600 | MDNSD_free(r); |
388 | 600 | } |
389 | | |
390 | | /* Call the answer function with this cached entry */ |
391 | | static void _q_answer(mdns_daemon_t *d, struct cached *c) |
392 | 0 | { |
393 | 0 | if (c->rr.ttl <= (unsigned long int)d->now.tv_sec) |
394 | 0 | c->rr.ttl = 0; |
395 | 0 | if (c->q->answer(&c->rr, c->q->arg) == -1) |
396 | 0 | _q_done(d, c->q); |
397 | 0 | } |
398 | | |
399 | | static void _conflict(mdns_daemon_t *d, mdns_record_t *r) |
400 | 0 | { |
401 | 0 | r->conflict(r->rr.name, r->rr.type, r->arg); |
402 | 0 | mdnsd_done(d, r); |
403 | 0 | } |
404 | | |
405 | | /* Expire any old entries in this list */ |
406 | | static void _c_expire(mdns_daemon_t *d, struct cached **list) |
407 | 3.32k | { |
408 | 3.32k | struct cached *next, *cur = *list, *last = 0; |
409 | | |
410 | 77.6k | while (cur != 0) { |
411 | 74.2k | next = cur->next; |
412 | 74.2k | if ((unsigned long int)d->now.tv_sec >= cur->rr.ttl) { |
413 | 2.79k | if (last) |
414 | 678 | last->next = next; |
415 | | |
416 | | /* Update list pointer if the first one expired */ |
417 | 2.79k | if (*list == cur) |
418 | 2.12k | *list = next; |
419 | | |
420 | 2.79k | if (cur->q) |
421 | 0 | _q_answer(d, cur); |
422 | | |
423 | 2.79k | MDNSD_free(cur->rr.name); |
424 | 2.79k | MDNSD_free(cur->rr.rdata); |
425 | 2.79k | MDNSD_free(cur->rr.rdname); |
426 | 2.79k | MDNSD_free(cur); |
427 | 71.4k | } else { |
428 | 71.4k | last = cur; |
429 | 71.4k | } |
430 | 74.2k | cur = next; |
431 | 74.2k | } |
432 | 3.32k | } |
433 | | |
434 | | /* Brute force expire any old cached records */ |
435 | | static void _gc(mdns_daemon_t *d) |
436 | 0 | { |
437 | 0 | int i; |
438 | |
|
439 | 0 | for (i = 0; i < LPRIME; i++) { |
440 | 0 | if (d->cache[i]) |
441 | 0 | _c_expire(d, &d->cache[i]); |
442 | 0 | } |
443 | |
|
444 | 0 | d->expireall = (unsigned long int)(d->now.tv_sec + GC); |
445 | 0 | } |
446 | | |
447 | | static int _cache(mdns_daemon_t *d, struct resource *r) |
448 | 7.19k | { |
449 | 7.19k | struct cached *c = 0; |
450 | 7.19k | int i = _namehash(r->name) % LPRIME; |
451 | | |
452 | | /* Cache flush for unique entries */ |
453 | 7.19k | if (r->clazz == 32768 + d->clazz) { |
454 | 1.40k | while ((c = _c_next(d, c, r->name, r->type))) |
455 | 438 | c->rr.ttl = 0; |
456 | 966 | _c_expire(d, &d->cache[i]); |
457 | 966 | } |
458 | | |
459 | | /* Process deletes */ |
460 | 7.19k | if (r->ttl == 0) { |
461 | 5.88k | while ((c = _c_next(d, c, r->name, r->type))) { |
462 | 3.90k | if (_a_match(r, &c->rr)) { |
463 | 2.36k | c->rr.ttl = 0; |
464 | 2.36k | _c_expire(d, &d->cache[i]); |
465 | 2.36k | c = NULL; |
466 | 2.36k | } |
467 | 3.90k | } |
468 | | |
469 | 1.97k | return 0; |
470 | 1.97k | } |
471 | | |
472 | | /* |
473 | | * XXX: The c->rr.ttl is a hack for now, BAD SPEC, start |
474 | | * retrying just after half-waypoint, then expire |
475 | | */ |
476 | 5.21k | c = (struct cached *)MDNSD_calloc(1, sizeof(struct cached)); |
477 | 5.21k | c->rr.name = STRDUP(r->name); |
478 | 5.21k | c->rr.type = r->type; |
479 | 5.21k | c->rr.ttl = (unsigned int)((unsigned long)d->now.tv_sec + (r->ttl / 2) + 8); |
480 | 5.21k | c->rr.rdlen = r->rdlength; |
481 | 5.21k | if (r->rdlength && !r->rdata) { |
482 | | //MDNSD_LOG_ERROR("rdlength is %d but rdata is NULL for domain name %s, type: %d, ttl: %ld", r->rdlength, r->name, r->type, r->ttl); |
483 | 0 | MDNSD_free(c->rr.name); |
484 | 0 | MDNSD_free(c); |
485 | 0 | return 1; |
486 | 0 | } |
487 | 5.21k | if (r->rdlength) { |
488 | 656 | c->rr.rdata = (unsigned char *)MDNSD_malloc(r->rdlength); |
489 | 656 | memcpy(c->rr.rdata, r->rdata, r->rdlength); |
490 | 4.55k | } else { |
491 | 4.55k | c->rr.rdata = NULL; |
492 | 4.55k | } |
493 | | |
494 | 5.21k | switch (r->type) { |
495 | 74 | case QTYPE_AAAA: |
496 | 74 | c->rr.ip6 = r->known.aaaa.ip6; |
497 | 74 | break; |
498 | | |
499 | 233 | case QTYPE_A: |
500 | 233 | c->rr.ip = r->known.a.ip; |
501 | 233 | break; |
502 | | |
503 | 291 | case QTYPE_NS: |
504 | 977 | case QTYPE_CNAME: |
505 | 1.34k | case QTYPE_PTR: |
506 | 1.34k | c->rr.rdname = STRDUP(r->known.ns.name); |
507 | 1.34k | break; |
508 | | |
509 | 1.17k | case QTYPE_SRV: |
510 | 1.17k | c->rr.rdname = STRDUP(r->known.srv.name); |
511 | 1.17k | c->rr.srv.port = r->known.srv.port; |
512 | 1.17k | c->rr.srv.weight = r->known.srv.weight; |
513 | 1.17k | c->rr.srv.priority = r->known.srv.priority; |
514 | 1.17k | break; |
515 | 5.21k | } |
516 | | |
517 | 5.21k | c->next = d->cache[i]; |
518 | 5.21k | d->cache[i] = c; |
519 | | |
520 | 5.21k | if ((c->q = _q_next(d, 0, r->name, r->type))) |
521 | 0 | _q_answer(d, c); |
522 | | |
523 | 5.21k | return 0; |
524 | 5.21k | } |
525 | | |
526 | | /* Copy the data bits only */ |
527 | | static void _a_copy(struct message *m, mdns_answer_t *a) |
528 | 0 | { |
529 | 0 | if (a->rdata) { |
530 | 0 | message_rdata_raw(m, a->rdata, a->rdlen); |
531 | 0 | return; |
532 | 0 | } |
533 | | |
534 | 0 | if (a->ip.s_addr) |
535 | 0 | message_rdata_long(m, a->ip); |
536 | 0 | if (a->type == QTYPE_SRV) |
537 | 0 | message_rdata_srv(m, a->srv.priority, a->srv.weight, a->srv.port, a->rdname); |
538 | 0 | else if (a->rdname) |
539 | 0 | message_rdata_name(m, a->rdname); |
540 | 0 | } |
541 | | |
542 | | /* Copy a published record into an outgoing message */ |
543 | | static int _r_out(mdns_daemon_t *d, struct message *m, mdns_record_t **list) |
544 | 0 | { |
545 | 0 | mdns_record_t *r; |
546 | 0 | int ret = 0; |
547 | |
|
548 | 0 | while ((r = *list) != 0 && message_packet_len(m) + _rr_len(&r->rr) < d->frame) { |
549 | 0 | if (r != r->list) |
550 | 0 | *list = r->list; |
551 | 0 | else |
552 | 0 | *list = NULL; |
553 | 0 | ret++; |
554 | |
|
555 | 0 | if (r->unique) |
556 | 0 | message_an(m, r->rr.name, r->rr.type, (unsigned short int)(d->clazz + 32768), r->rr.ttl); |
557 | 0 | else |
558 | 0 | message_an(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz, r->rr.ttl); |
559 | 0 | r->last_sent = d->now; |
560 | |
|
561 | 0 | _a_copy(m, &r->rr); |
562 | 0 | if (r->rr.ttl == 0) { |
563 | | |
564 | | // also remove from other lists, because record may be in multiple lists at the same time |
565 | 0 | _r_remove_lists(d, r, list); |
566 | |
|
567 | 0 | _r_done(d, r); |
568 | |
|
569 | 0 | } |
570 | 0 | } |
571 | |
|
572 | 0 | return ret; |
573 | 0 | } |
574 | | |
575 | | |
576 | | mdns_daemon_t *mdnsd_new(int clazz, int frame) |
577 | 1.49k | { |
578 | 1.49k | mdns_daemon_t *d; |
579 | | |
580 | 1.49k | d = (mdns_daemon_t *)MDNSD_calloc(1, sizeof(struct mdns_daemon)); |
581 | 1.49k | gettimeofday(&d->now, 0); |
582 | 1.49k | d->expireall = (unsigned long int)(d->now.tv_sec + GC); |
583 | 1.49k | d->clazz = clazz; |
584 | 1.49k | d->frame = frame; |
585 | 1.49k | d->received_callback = NULL; |
586 | | |
587 | 1.49k | return d; |
588 | 1.49k | } |
589 | | |
590 | | /* Shutting down, zero out ttl and push out all records */ |
591 | | void mdnsd_shutdown(mdns_daemon_t *d) |
592 | 300 | { |
593 | 300 | int i; |
594 | 300 | mdns_record_t *cur, *next; |
595 | | |
596 | 300 | d->a_now = 0; |
597 | 32.7k | for (i = 0; i < SPRIME; i++) { |
598 | 33.6k | for (cur = d->published[i]; cur != 0;) { |
599 | 1.20k | next = cur->next; |
600 | 1.20k | cur->rr.ttl = 0; |
601 | 1.20k | cur->list = d->a_now; |
602 | 1.20k | d->a_now = cur; |
603 | 1.20k | cur = next; |
604 | 1.20k | } |
605 | 32.4k | } |
606 | | |
607 | 300 | d->shutdown = 1; |
608 | 300 | } |
609 | | |
610 | | void mdnsd_flush(mdns_daemon_t *d) |
611 | 0 | { |
612 | 0 | (void)d; |
613 | | /* - Set all querys to 0 tries |
614 | | * - Free whole cache |
615 | | * - Set all mdns_record_t *to probing |
616 | | * - Reset all answer lists |
617 | | */ |
618 | 0 | } |
619 | | |
620 | | void mdnsd_free(mdns_daemon_t *d) |
621 | 1.49k | { |
622 | 1.49k | size_t i; |
623 | 1.50M | for (i = 0; i< LPRIME; i++) { |
624 | 1.50M | struct cached* cur = d->cache[i]; |
625 | 1.50M | while (cur) { |
626 | 2.41k | struct cached* next = cur->next; |
627 | 2.41k | MDNSD_free(cur->rr.name); |
628 | 2.41k | MDNSD_free(cur->rr.rdata); |
629 | 2.41k | MDNSD_free(cur->rr.rdname); |
630 | 2.41k | MDNSD_free(cur); |
631 | 2.41k | cur = next; |
632 | 2.41k | } |
633 | 1.50M | } |
634 | | |
635 | 162k | for (i = 0; i< SPRIME; i++) { |
636 | 161k | struct mdns_record* cur = d->published[i]; |
637 | 161k | struct query* curq = NULL; |
638 | 162k | while (cur) { |
639 | 1.20k | struct mdns_record* next = cur->next; |
640 | 1.20k | MDNSD_free(cur->rr.name); |
641 | 1.20k | MDNSD_free(cur->rr.rdata); |
642 | 1.20k | MDNSD_free(cur->rr.rdname); |
643 | 1.20k | MDNSD_free(cur); |
644 | 1.20k | cur = next; |
645 | 1.20k | } |
646 | | |
647 | | |
648 | 161k | curq = d->queries[i]; |
649 | 161k | while (curq) { |
650 | 300 | struct query* next = curq->next; |
651 | 300 | MDNSD_free(curq->name); |
652 | 300 | MDNSD_free(curq); |
653 | 300 | curq = next; |
654 | 300 | } |
655 | | |
656 | 161k | } |
657 | | |
658 | 1.49k | { |
659 | 1.49k | struct unicast *u = d->uanswers; |
660 | 1.49k | while (u) { |
661 | 0 | struct unicast *next = u->next; |
662 | 0 | MDNSD_free(u); |
663 | 0 | u=next; |
664 | 0 | } |
665 | 1.49k | } |
666 | | |
667 | 1.49k | MDNSD_free(d); |
668 | 1.49k | } |
669 | | |
670 | | |
671 | 300 | void mdnsd_register_receive_callback(mdns_daemon_t *d, mdnsd_record_received_callback cb, void* data) { |
672 | 300 | d->received_callback = cb; |
673 | 300 | d->received_callback_data = data; |
674 | 300 | } |
675 | | |
676 | | int mdnsd_in(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int port) |
677 | 1.19k | { |
678 | 1.19k | int i; |
679 | 1.19k | mdns_record_t *r = 0; |
680 | | |
681 | 1.19k | if (d->shutdown) |
682 | 0 | return 1; |
683 | | |
684 | 1.19k | gettimeofday(&d->now, 0); |
685 | | |
686 | 1.19k | if (m->header.qr == 0) { |
687 | | /* Process each query */ |
688 | 1.17k | for (i = 0; i < m->qdcount; i++) { |
689 | 1.09k | mdns_record_t *r_start, *r_next = NULL; |
690 | 1.09k | bool hasConflict = false; |
691 | 1.09k | if (m->qd[i].clazz != d->clazz || (r = _r_next(d, 0, m->qd[i].name, m->qd[i].type)) == 0) |
692 | 1.09k | continue; |
693 | 0 | r_start = r; |
694 | | |
695 | | |
696 | | /* Check all of our potential answers */ |
697 | 0 | for (; r != 0; r = r_next) { |
698 | |
|
699 | 0 | MDNSD_LOG_TRACE("Got Query: Name: %s, Type: %d", r->rr.name, r->rr.type); |
700 | | |
701 | | // do this here, because _conflict deletes r and thus next is not valid anymore |
702 | 0 | r_next = _r_next(d, r, m->qd[i].name, m->qd[i].type); |
703 | | /* probing state, check for conflicts */ |
704 | 0 | if (r->unique && r->unique < 5) { |
705 | | /* Check all to-be answers against our own */ |
706 | 0 | int j; |
707 | 0 | for (j = 0; j < m->nscount; j++) { |
708 | 0 | if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name)) |
709 | 0 | continue; |
710 | | |
711 | | /* This answer isn't ours, conflict! */ |
712 | 0 | if (!_a_match(&m->an[j], &r->rr)) { |
713 | 0 | _conflict(d, r); |
714 | 0 | hasConflict = true; |
715 | 0 | break; |
716 | 0 | } |
717 | 0 | } |
718 | 0 | continue; |
719 | 0 | } |
720 | | |
721 | | /* Check the known answers for this question */ |
722 | 0 | { |
723 | 0 | int j; |
724 | 0 | for (j = 0; j < m->ancount; j++) { |
725 | 0 | if (m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name, m->an[j].name)) |
726 | 0 | continue; |
727 | | |
728 | 0 | if (d->received_callback) { |
729 | 0 | d->received_callback(&m->an[j], d->received_callback_data); |
730 | 0 | } |
731 | | |
732 | | /* Do they already have this answer? */ |
733 | 0 | if (_a_match(&m->an[j], &r->rr)) |
734 | 0 | break; |
735 | 0 | } |
736 | 0 | if (j == m->ancount) |
737 | 0 | _r_send(d, r); |
738 | 0 | } |
739 | |
|
740 | 0 | } |
741 | | |
742 | | /* Send the matching unicast reply */ |
743 | 0 | if (!hasConflict && port != 5353) |
744 | 0 | _u_push(d, r_start, m->id, ip, port); |
745 | 0 | } |
746 | | |
747 | 71 | return 0; |
748 | 71 | } |
749 | | |
750 | | /* Process each answer, check for a conflict, and cache */ |
751 | 8.31k | for (i = 0; i < m->ancount; i++) { |
752 | 7.19k | if (m->an[i].name == NULL) { |
753 | 0 | MDNSD_LOG_ERROR("Got answer with NULL name at %p. Type: %d, TTL: %ld\n", (void*)&m->an[i], m->an[i].type, m->an[i].ttl); |
754 | 0 | return 3; |
755 | 0 | } |
756 | | |
757 | 7.19k | MDNSD_LOG_TRACE("Got Answer: Name: %s, Type: %d", m->an[i].name, m->an[i].type); |
758 | 7.19k | if ((r = _r_next(d, 0, m->an[i].name, m->an[i].type)) != 0 && |
759 | 7.19k | r->unique && _a_match(&m->an[i], &r->rr) == 0) |
760 | 0 | _conflict(d, r); |
761 | | |
762 | 7.19k | if (d->received_callback) { |
763 | 0 | d->received_callback(&m->an[i], d->received_callback_data); |
764 | 0 | } |
765 | 7.19k | if (_cache(d, &m->an[i]) != 0) |
766 | 0 | return 2; |
767 | 7.19k | } |
768 | 1.12k | return 0; |
769 | 1.12k | } |
770 | | |
771 | | int mdnsd_out(mdns_daemon_t *d, struct message *m, struct sockaddr *ip, unsigned short int *port) |
772 | 0 | { |
773 | 0 | mdns_record_t *r; |
774 | 0 | int ret = 0; |
775 | |
|
776 | 0 | gettimeofday(&d->now, 0); |
777 | 0 | memset(m, 0, sizeof(struct message)); |
778 | | |
779 | | /* Defaults, multicast */ |
780 | 0 | *port = htons(5353); |
781 | 0 | if(ip->sa_family == AF_INET6) { |
782 | 0 | struct sockaddr_in6 addr6; |
783 | |
|
784 | 0 | memset(&addr6, 0, sizeof(addr6)); |
785 | 0 | addr6.sin6_family = AF_INET6; |
786 | 0 | addr6.sin6_port = *port; |
787 | 0 | inet_pton(AF_INET6, "ff02::fb", &(addr6.sin6_addr)); |
788 | 0 | memcpy(ip, &addr6, sizeof(addr6)); |
789 | 0 | } else { |
790 | 0 | struct sockaddr_in addr; |
791 | |
|
792 | 0 | memset(&addr, 0, sizeof(addr)); |
793 | 0 | addr.sin_family = AF_INET; |
794 | 0 | addr.sin_port = *port; |
795 | 0 | inet_pton(AF_INET, "224.0.0.251", &(addr.sin_addr)); |
796 | 0 | memcpy(ip, &addr, sizeof(addr)); |
797 | 0 | } |
798 | 0 | m->header.qr = 1; |
799 | 0 | m->header.aa = 1; |
800 | | |
801 | | /* Send out individual unicast answers */ |
802 | 0 | if (d->uanswers) { |
803 | 0 | struct unicast *u = d->uanswers; |
804 | |
|
805 | 0 | MDNSD_LOG_TRACE("Send Unicast Answer: Name: %s, Type: %d", u->r->rr.name, u->r->rr.type); |
806 | |
|
807 | 0 | d->uanswers = u->next; |
808 | 0 | *port = u->port; |
809 | 0 | ip = u->to; |
810 | 0 | m->id = (unsigned short int)u->id; |
811 | 0 | message_qd(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz); |
812 | 0 | message_an(m, u->r->rr.name, u->r->rr.type, (unsigned short int)d->clazz, u->r->rr.ttl); |
813 | 0 | u->r->last_sent = d->now; |
814 | 0 | _a_copy(m, &u->r->rr); |
815 | 0 | MDNSD_free(u); |
816 | |
|
817 | 0 | return 1; |
818 | 0 | } |
819 | | |
820 | | // printf("OUT: probing %X now %X pause %X publish %X\n",d->probing,d->a_now,d->a_pause,d->a_publish); |
821 | | |
822 | | /* Accumulate any immediate responses */ |
823 | 0 | if (d->a_now) |
824 | 0 | ret += _r_out(d, m, &d->a_now); |
825 | | |
826 | | /* Check if it's time to send the publish retries (unlink if done) */ |
827 | 0 | if (d->a_publish && _tvdiff(d->now, d->publish) <= 0) { |
828 | |
|
829 | 0 | mdns_record_t *next, *cur = d->a_publish, *last = NULL; |
830 | |
|
831 | 0 | while (cur && message_packet_len(m) + _rr_len(&cur->rr) < d->frame) { |
832 | |
|
833 | 0 | if (cur->rr.type == QTYPE_PTR) { |
834 | 0 | MDNSD_LOG_TRACE("Send Publish PTR: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s", |
835 | 0 | cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname); |
836 | 0 | } else if (cur->rr.type == QTYPE_SRV) { |
837 | 0 | MDNSD_LOG_TRACE("Send Publish SRV: Name: %s, rdlen: %d, rdata: %.*s, rdname: %s, port: %d, prio: %d, weight: %d", |
838 | 0 | cur->rr.name,cur->rr.rdlen, cur->rr.rdlen, cur->rr.rdata, cur->rr.rdname == NULL ? "" : cur->rr.rdname, |
839 | 0 | cur->rr.srv.port, cur->rr.srv.priority, cur->rr.srv.weight); |
840 | 0 | } else { |
841 | 0 | MDNSD_LOG_TRACE("Send Publish: Name: %s, Type: %d", cur->rr.name, cur->rr.type); |
842 | 0 | } |
843 | 0 | next = cur->list; |
844 | 0 | ret++; |
845 | 0 | cur->tries++; |
846 | |
|
847 | 0 | if (cur->unique) |
848 | 0 | message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)(d->clazz + 32768), cur->rr.ttl); |
849 | 0 | else |
850 | 0 | message_an(m, cur->rr.name, cur->rr.type, (unsigned short int)d->clazz, cur->rr.ttl); |
851 | 0 | _a_copy(m, &cur->rr); |
852 | 0 | cur->last_sent = d->now; |
853 | |
|
854 | 0 | if (cur->rr.ttl != 0 && cur->tries < 4) { |
855 | 0 | last = cur; |
856 | 0 | cur = next; |
857 | 0 | continue; |
858 | 0 | } |
859 | | |
860 | 0 | if (d->a_publish == cur) |
861 | 0 | d->a_publish = next; |
862 | 0 | if (last) |
863 | 0 | last->list = next; |
864 | 0 | if (cur->rr.ttl == 0) |
865 | 0 | _r_done(d, cur); |
866 | 0 | cur = next; |
867 | 0 | } |
868 | |
|
869 | 0 | if (d->a_publish) { |
870 | 0 | d->publish.tv_sec = d->now.tv_sec + 2; |
871 | 0 | d->publish.tv_usec = d->now.tv_usec; |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | /* If we're in shutdown, we're done */ |
876 | 0 | if (d->shutdown) |
877 | 0 | return ret; |
878 | | |
879 | | /* Check if a_pause is ready */ |
880 | 0 | if (d->a_pause && _tvdiff(d->now, d->pause) <= 0) |
881 | 0 | ret += _r_out(d, m, &d->a_pause); |
882 | | |
883 | | /* Now process questions */ |
884 | 0 | if (ret > 0) |
885 | 0 | return ret; |
886 | | |
887 | 0 | m->header.qr = 0; |
888 | 0 | m->header.aa = 0; |
889 | |
|
890 | 0 | if (d->probing && _tvdiff(d->now, d->probe) <= 0) { |
891 | 0 | mdns_record_t *last = 0; |
892 | | |
893 | | /* Scan probe list to ask questions and process published */ |
894 | 0 | for (r = d->probing; r != 0;) { |
895 | | /* Done probing, publish */ |
896 | 0 | if (r->unique == 4) { |
897 | 0 | mdns_record_t *next = r->list; |
898 | |
|
899 | 0 | if (d->probing == r) |
900 | 0 | d->probing = r->list; |
901 | 0 | else |
902 | 0 | last->list = r->list; |
903 | |
|
904 | 0 | r->list = 0; |
905 | 0 | r->unique = 5; |
906 | 0 | _r_publish(d, r); |
907 | 0 | r = next; |
908 | 0 | continue; |
909 | 0 | } |
910 | | |
911 | 0 | MDNSD_LOG_TRACE("Send Probing: Name: %s, Type: %d", r->rr.name, r->rr.type); |
912 | |
|
913 | 0 | message_qd(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz); |
914 | 0 | r->last_sent = d->now; |
915 | 0 | last = r; |
916 | 0 | r = r->list; |
917 | 0 | } |
918 | | |
919 | | /* Scan probe list again to append our to-be answers */ |
920 | 0 | for (r = d->probing; r != 0; r = r->list) { |
921 | 0 | r->unique++; |
922 | |
|
923 | 0 | MDNSD_LOG_TRACE("Send Answer in Probe: Name: %s, Type: %d", r->rr.name, r->rr.type); |
924 | 0 | message_ns(m, r->rr.name, r->rr.type, (unsigned short int)d->clazz, r->rr.ttl); |
925 | 0 | _a_copy(m, &r->rr); |
926 | 0 | r->last_sent = d->now; |
927 | 0 | ret++; |
928 | 0 | } |
929 | | |
930 | | /* Process probes again in the future */ |
931 | 0 | if (ret) { |
932 | 0 | d->probe.tv_sec = d->now.tv_sec; |
933 | 0 | d->probe.tv_usec = d->now.tv_usec + 250000; |
934 | 0 | return ret; |
935 | 0 | } |
936 | 0 | } |
937 | | |
938 | | /* Process qlist for retries or expirations */ |
939 | 0 | if (d->checkqlist && (unsigned long int)d->now.tv_sec >= d->checkqlist) { |
940 | 0 | struct query *q; |
941 | 0 | struct cached *c; |
942 | 0 | unsigned long int nextbest = 0; |
943 | | |
944 | | /* Ask questions first, track nextbest time */ |
945 | 0 | for (q = d->qlist; q != 0; q = q->list) { |
946 | 0 | if (q->nexttry > 0 && q->nexttry <= (unsigned long int)d->now.tv_sec && q->tries < 3) |
947 | 0 | message_qd(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz); |
948 | 0 | else if (q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest)) |
949 | 0 | nextbest = q->nexttry; |
950 | 0 | } |
951 | | |
952 | | /* Include known answers, update questions */ |
953 | 0 | for (q = d->qlist; q != 0; q = q->list) { |
954 | 0 | if (q->nexttry == 0 || q->nexttry > (unsigned long int)d->now.tv_sec) |
955 | 0 | continue; |
956 | | |
957 | | /* Done retrying, expire and reset */ |
958 | 0 | if (q->tries == 3) { |
959 | 0 | _c_expire(d, &d->cache[_namehash(q->name) % LPRIME]); |
960 | 0 | _q_reset(d, q); |
961 | 0 | continue; |
962 | 0 | } |
963 | | |
964 | 0 | ret++; |
965 | 0 | q->nexttry = (unsigned long int)(d->now.tv_sec + ++q->tries); |
966 | 0 | if (nextbest == 0 || q->nexttry < nextbest) |
967 | 0 | nextbest = q->nexttry; |
968 | | |
969 | | /* If room, add all known good entries */ |
970 | 0 | c = 0; |
971 | 0 | while ((c = _c_next(d, c, q->name, q->type)) != 0 && c->rr.ttl > (unsigned long int)d->now.tv_sec + 8 && |
972 | 0 | message_packet_len(m) + _rr_len(&c->rr) < d->frame) { |
973 | |
|
974 | 0 | MDNSD_LOG_TRACE("Add known answer: Name: %s, Type: %d", c->rr.name, c->rr.type); |
975 | 0 | message_an(m, q->name, (unsigned short int)q->type, (unsigned short int)d->clazz, c->rr.ttl - (unsigned long int)d->now.tv_sec); |
976 | 0 | _a_copy(m, &c->rr); |
977 | 0 | } |
978 | 0 | } |
979 | 0 | d->checkqlist = nextbest; |
980 | 0 | } |
981 | |
|
982 | 0 | if ((unsigned long int)d->now.tv_sec > d->expireall) |
983 | 0 | _gc(d); |
984 | |
|
985 | 0 | return ret; |
986 | 0 | } |
987 | | |
988 | | |
989 | | #define RET \ |
990 | 0 | while (d->sleep.tv_usec > 1000000) { \ |
991 | 0 | d->sleep.tv_sec++; \ |
992 | 0 | d->sleep.tv_usec -= 1000000; \ |
993 | 0 | } \ |
994 | 0 | return &d->sleep; |
995 | | |
996 | | struct timeval *mdnsd_sleep(mdns_daemon_t *d) |
997 | 0 | { |
998 | 0 | int usec, minExpire; |
999 | |
|
1000 | 0 | d->sleep.tv_sec = d->sleep.tv_usec = 0; |
1001 | | |
1002 | | /* First check for any immediate items to handle */ |
1003 | 0 | if (d->uanswers || d->a_now) |
1004 | 0 | return &d->sleep; |
1005 | | |
1006 | 0 | gettimeofday(&d->now, 0); |
1007 | | |
1008 | | /* Then check for paused answers or nearly expired records */ |
1009 | 0 | if (d->a_pause) { |
1010 | 0 | if ((usec = _tvdiff(d->now, d->pause)) > 0) |
1011 | 0 | d->sleep.tv_usec = usec; |
1012 | 0 | RET; |
1013 | 0 | } |
1014 | | |
1015 | | /* Now check for probe retries */ |
1016 | 0 | if (d->probing) { |
1017 | 0 | if ((usec = _tvdiff(d->now, d->probe)) > 0) |
1018 | 0 | d->sleep.tv_usec = usec; |
1019 | 0 | RET; |
1020 | 0 | } |
1021 | | |
1022 | | /* Now check for publish retries */ |
1023 | 0 | if (d->a_publish) { |
1024 | 0 | if ((usec = _tvdiff(d->now, d->publish)) > 0) |
1025 | 0 | d->sleep.tv_usec = usec; |
1026 | 0 | RET; |
1027 | 0 | } |
1028 | | |
1029 | | /* Also check for queries with known answer expiration/retry */ |
1030 | 0 | if (d->checkqlist) { |
1031 | 0 | int sec; |
1032 | 0 | if ((sec = (int)(d->checkqlist - (unsigned long int)d->now.tv_sec)) > 0) |
1033 | 0 | d->sleep.tv_sec = sec; |
1034 | 0 | RET; |
1035 | 0 | } |
1036 | | |
1037 | | /* Resend published records before TTL expires */ |
1038 | | // latest expire is garbage collection |
1039 | 0 | minExpire = (int)(d->expireall - (unsigned long int)d->now.tv_sec); |
1040 | 0 | if (minExpire < 0) |
1041 | 0 | return &d->sleep; |
1042 | | |
1043 | 0 | { |
1044 | 0 | size_t i; |
1045 | 0 | for (i=0; i<SPRIME; i++) { |
1046 | 0 | int expire; |
1047 | 0 | if (!d->published[i]) |
1048 | 0 | continue; |
1049 | 0 | expire = (int)((d->published[i]->last_sent.tv_sec + (long int)d->published[i]->rr.ttl) - d->now.tv_sec); |
1050 | 0 | if (expire < minExpire) |
1051 | 0 | d->a_pause = NULL; |
1052 | 0 | minExpire = expire < minExpire ? expire : minExpire; |
1053 | 0 | _r_push(&d->a_pause, d->published[i]); |
1054 | 0 | } |
1055 | 0 | } |
1056 | | // publish 2 seconds before expire. |
1057 | 0 | d->sleep.tv_sec = minExpire > 2 ? minExpire-2 : 0; |
1058 | 0 | d->pause.tv_sec = d->now.tv_sec + d->sleep.tv_sec; |
1059 | 0 | RET; |
1060 | 0 | } |
1061 | | |
1062 | | void mdnsd_query(mdns_daemon_t *d, const char *host, int type, int (*answer)(mdns_answer_t *a, void *arg), void *arg) |
1063 | 300 | { |
1064 | 300 | struct query *q; |
1065 | 300 | int i = _namehash(host) % SPRIME; |
1066 | | |
1067 | 300 | if (!(q = _q_next(d, 0, host, type))) { |
1068 | 300 | if (!answer) |
1069 | 0 | return; |
1070 | | |
1071 | 300 | q = (struct query *)MDNSD_calloc(1, sizeof(struct query)); |
1072 | 300 | q->name = STRDUP(host); |
1073 | 300 | q->type = type; |
1074 | 300 | q->next = d->queries[i]; |
1075 | 300 | q->list = d->qlist; |
1076 | 300 | d->qlist = d->queries[i] = q; |
1077 | | |
1078 | | /* Any cached entries should be associated */ |
1079 | 300 | { |
1080 | 300 | struct cached *cur = 0; |
1081 | 300 | while ((cur = _c_next(d, cur, q->name, q->type))) |
1082 | 0 | cur->q = q; |
1083 | 300 | } |
1084 | 300 | _q_reset(d, q); |
1085 | | |
1086 | | /* New question, immediately send out */ |
1087 | 300 | q->nexttry = d->checkqlist = (unsigned long int)d->now.tv_sec; |
1088 | 300 | } |
1089 | | |
1090 | | /* No answer means we don't care anymore */ |
1091 | 300 | if (!answer) { |
1092 | 0 | _q_done(d, q); |
1093 | 0 | return; |
1094 | 0 | } |
1095 | | |
1096 | 300 | q->answer = answer; |
1097 | 300 | q->arg = arg; |
1098 | 300 | } |
1099 | | |
1100 | | mdns_answer_t *mdnsd_list(mdns_daemon_t *d,const char *host, int type, mdns_answer_t *last) |
1101 | 0 | { |
1102 | 0 | return (mdns_answer_t *)_c_next(d, (struct cached *)last, host, type); |
1103 | 0 | } |
1104 | | |
1105 | 900 | mdns_record_t *mdnsd_record_next(const mdns_record_t* r) { |
1106 | 900 | return r ? r->next : NULL; |
1107 | 900 | } |
1108 | | |
1109 | 1.20k | const mdns_answer_t *mdnsd_record_data(const mdns_record_t* r) { |
1110 | 1.20k | return &r->rr; |
1111 | 1.20k | } |
1112 | | |
1113 | | mdns_record_t *mdnsd_shared(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl) |
1114 | 1.80k | { |
1115 | 1.80k | int i = _namehash(host) % SPRIME; |
1116 | 1.80k | mdns_record_t *r; |
1117 | | |
1118 | 1.80k | r = (struct mdns_record *)MDNSD_calloc(1, sizeof(struct mdns_record)); |
1119 | 1.80k | r->rr.name = STRDUP(host); |
1120 | 1.80k | r->rr.type = type; |
1121 | 1.80k | r->rr.ttl = ttl; |
1122 | 1.80k | r->next = d->published[i]; |
1123 | 1.80k | d->published[i] = r; |
1124 | | |
1125 | 1.80k | return r; |
1126 | 1.80k | } |
1127 | | |
1128 | | mdns_record_t *mdnsd_unique(mdns_daemon_t *d, const char *host, unsigned short int type, unsigned long int ttl, void (*conflict)(char *host, int type, void *arg), void *arg) |
1129 | 600 | { |
1130 | 600 | mdns_record_t *r; |
1131 | | |
1132 | 600 | r = mdnsd_shared(d, host, type, ttl); |
1133 | 600 | r->conflict = conflict; |
1134 | 600 | r->arg = arg; |
1135 | 600 | r->unique = 1; |
1136 | 600 | _r_push(&d->probing, r); |
1137 | 600 | d->probe.tv_sec = d->now.tv_sec; |
1138 | 600 | d->probe.tv_usec = d->now.tv_usec; |
1139 | | |
1140 | 600 | return r; |
1141 | 600 | } |
1142 | | |
1143 | 1.20k | mdns_record_t * mdnsd_get_published(const mdns_daemon_t *d, const char *host) { |
1144 | 1.20k | return d->published[_namehash(host) % SPRIME]; |
1145 | 1.20k | } |
1146 | | |
1147 | 0 | int mdnsd_has_query(const mdns_daemon_t *d, const char *host) { |
1148 | 0 | return d->queries[_namehash(host) % SPRIME]!=NULL; |
1149 | 0 | } |
1150 | | |
1151 | | void mdnsd_done(mdns_daemon_t *d, mdns_record_t *r) |
1152 | 1.20k | { |
1153 | 1.20k | mdns_record_t *cur; |
1154 | | |
1155 | 1.20k | if (r->unique && r->unique < 5) { |
1156 | | /* Probing yet, zap from that list first! */ |
1157 | 600 | if (d->probing == r) { |
1158 | 600 | d->probing = r->list; |
1159 | 600 | } else { |
1160 | 0 | for (cur = d->probing; cur->list != r; cur = cur->list) |
1161 | 0 | ; |
1162 | 0 | cur->list = r->list; |
1163 | 0 | } |
1164 | | |
1165 | 600 | _r_done(d, r); |
1166 | 600 | return; |
1167 | 600 | } |
1168 | | |
1169 | 600 | r->rr.ttl = 0; |
1170 | 600 | _r_send(d, r); |
1171 | 600 | } |
1172 | | |
1173 | | void mdnsd_set_raw(mdns_daemon_t *d, mdns_record_t *r, const char *data, unsigned short int len) |
1174 | 900 | { |
1175 | 900 | MDNSD_free(r->rr.rdata); |
1176 | 900 | r->rr.rdata = (unsigned char *)MDNSD_malloc(len); |
1177 | 900 | memcpy(r->rr.rdata, data, len); |
1178 | 900 | r->rr.rdlen = len; |
1179 | 900 | _r_publish(d, r); |
1180 | 900 | } |
1181 | | |
1182 | | void mdnsd_set_host(mdns_daemon_t *d, mdns_record_t *r, const char *name) |
1183 | 900 | { |
1184 | 900 | MDNSD_free(r->rr.rdname); |
1185 | 900 | r->rr.rdname = STRDUP(name); |
1186 | 900 | _r_publish(d, r); |
1187 | 900 | } |
1188 | | |
1189 | | void mdnsd_set_ip(mdns_daemon_t *d, mdns_record_t *r, struct in_addr ip) |
1190 | 0 | { |
1191 | 0 | r->rr.ip = ip; |
1192 | 0 | _r_publish(d, r); |
1193 | 0 | } |
1194 | | |
1195 | | void mdnsd_set_srv(mdns_daemon_t *d, mdns_record_t *r, unsigned short int priority, unsigned short int weight, unsigned short int port, char *name) |
1196 | 300 | { |
1197 | 300 | r->rr.srv.priority = priority; |
1198 | 300 | r->rr.srv.weight = weight; |
1199 | 300 | r->rr.srv.port = port; |
1200 | 300 | mdnsd_set_host(d, r, name); |
1201 | 300 | } |
1202 | | |
1203 | | #if MDNSD_LOGLEVEL <= 100 |
1204 | | #include <ctype.h> |
1205 | | static void dump_hex_pkg(char* buffer, int bufferLen) { |
1206 | | char ascii[17]; |
1207 | | memset(ascii,0,17); |
1208 | | for (int i = 0; i < bufferLen; i++) |
1209 | | { |
1210 | | if (i%16 == 0) |
1211 | | printf("%s\n%06x ", ascii, i); |
1212 | | if (isprint((int)(buffer[i]))) |
1213 | | ascii[i%16] = buffer[i]; |
1214 | | else |
1215 | | ascii[i%16] = '.'; |
1216 | | printf("%02X ", (unsigned char)buffer[i]); |
1217 | | } |
1218 | | printf("%s\n%06x ", ascii, bufferLen); |
1219 | | printf("\n"); |
1220 | | } |
1221 | | #endif |
1222 | | |
1223 | 0 | unsigned short int mdnsd_step(mdns_daemon_t *d, int mdns_socket, bool processIn, bool processOut, struct timeval *nextSleep) { |
1224 | |
|
1225 | 0 | struct message m; |
1226 | |
|
1227 | 0 | if (processIn) { |
1228 | 0 | int bsize; |
1229 | 0 | unsigned char buf[MAX_PACKET_LEN]; |
1230 | 0 | struct sockaddr_storage from; |
1231 | 0 | socklen_t ssize = sizeof(from); |
1232 | |
|
1233 | 0 | while ((bsize = (int)recvfrom(mdns_socket, (char*)buf, MAX_PACKET_LEN, 0, (struct sockaddr *)&from, &ssize)) > 0) { |
1234 | 0 | memset(&m, 0, sizeof(struct message)); |
1235 | | #if MDNSD_LOGLEVEL <= 100 |
1236 | | MDNSD_LOG_TRACE("Got Data:"); |
1237 | | dump_hex_pkg((char*)buf, bsize); |
1238 | | #endif |
1239 | | #ifdef MDNSD_DEBUG_DUMP_PKGS_FILE |
1240 | | mdnsd_debug_dumpCompleteChunk(d, (char*)buf, (size_t) bsize); |
1241 | | #endif |
1242 | 0 | if (!message_parse(&m, buf, (size_t)bsize)) |
1243 | 0 | continue; |
1244 | | |
1245 | 0 | unsigned short int fromPort; |
1246 | 0 | if (from.ss_family == AF_INET){ |
1247 | 0 | fromPort = ((struct sockaddr_in *)&from)->sin_port; |
1248 | 0 | } else { |
1249 | 0 | fromPort = ((struct sockaddr_in6 *)&from)->sin6_port; |
1250 | 0 | } |
1251 | 0 | if (mdnsd_in(d, &m,(struct sockaddr *)&from, fromPort) !=0) |
1252 | 0 | return 2; |
1253 | 0 | } |
1254 | | #ifdef _WIN32 |
1255 | | if (bsize < 0 && WSAGetLastError() != WSAEWOULDBLOCK) |
1256 | | #else |
1257 | 0 | if (bsize < 0 && errno != EAGAIN) |
1258 | 0 | #endif |
1259 | 0 | { |
1260 | 0 | return 1; |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | 0 | if (processOut) { |
1265 | 0 | struct sockaddr_storage addrStorage; |
1266 | 0 | struct sockaddr *to = (struct sockaddr*)&addrStorage; |
1267 | 0 | socklen_t addrLength = sizeof(struct sockaddr_storage); |
1268 | 0 | unsigned short int port; |
1269 | 0 | #ifdef __clang__ |
1270 | 0 | #pragma clang diagnostic push |
1271 | 0 | #pragma clang diagnostic ignored "-Wcast-align" |
1272 | 0 | #endif |
1273 | |
|
1274 | 0 | if(getsockname(mdns_socket, to, &addrLength)) |
1275 | 0 | return 2; |
1276 | 0 | while (mdnsd_out(d, &m, to, &port)) { |
1277 | 0 | #ifdef __clang__ |
1278 | 0 | #pragma clang diagnostic pop |
1279 | 0 | #endif |
1280 | 0 | int len = message_packet_len(&m); |
1281 | 0 | char* buf = (char*)message_packet(&m); |
1282 | | #if MDNSD_LOGLEVEL <= 100 |
1283 | | MDNSD_LOG_TRACE("Send Data:"); |
1284 | | dump_hex_pkg(buf, (int)len); |
1285 | | #endif |
1286 | |
|
1287 | | #ifdef MDNSD_DEBUG_DUMP_PKGS_FILE |
1288 | | mdnsd_debug_dumpCompleteChunk(d, buf, (size_t) len); |
1289 | | #endif |
1290 | 0 | if ((sendto(mdns_socket, buf, (unsigned int)len, 0, to, |
1291 | 0 | addrLength)) != len) { |
1292 | 0 | return 2; |
1293 | 0 | } |
1294 | 0 | } |
1295 | 0 | } |
1296 | | |
1297 | 0 | if (nextSleep) { |
1298 | 0 | struct timeval *tv = mdnsd_sleep(d); |
1299 | 0 | nextSleep->tv_sec = tv->tv_sec; |
1300 | 0 | nextSleep->tv_usec = tv->tv_usec; |
1301 | 0 | } |
1302 | |
|
1303 | 0 | return 0; |
1304 | 0 | } |