]> git.karo-electronics.de Git - mdnsd.git/blob - mdnsd.c
fix warnings and a crasher in mdnsd.c
[mdnsd.git] / mdnsd.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <arpa/inet.h>
4 #include "mdnsd.h"
5
6 // size of query/publish hashes
7 #define SPRIME 108
8 // size of cache hash
9 #define LPRIME 1009
10 // brute force garbage cleanup frequency, rarely needed (daily default)
11 #define GC 86400
12
13 /* messy, but it's the best/simplest balance I can find at the moment
14 Some internal data types, and a few hashes: querys, answers, cached, and records (published, unique and shared)
15 Each type has different semantics for processing, both for timeouts, incoming, and outgoing I/O
16 They inter-relate too, like records affect the querys they are relevant to
17 Nice things about MDNS: we only publish once (and then ask asked), and only query once, then just expire records we've got cached
18 */
19
20 struct query
21 {
22     char *name;
23     int type;
24     unsigned long int nexttry;
25     int tries;
26     int (*answer)(mdnsda, void *);
27     void *arg;
28     struct query *next, *list;
29 };
30
31 struct unicast
32 {
33     int id;
34     unsigned long int to;
35     unsigned short int port;
36     mdnsdr r;
37     struct unicast *next;
38 };
39
40 struct cached
41 {
42     struct mdnsda_struct rr;
43     struct query *q;
44     struct cached *next;
45 };
46
47 struct mdnsdr_struct
48 {
49     struct mdnsda_struct rr;
50     char unique; // # of checks performed to ensure
51     int tries;
52     void (*conflict)(char *, int, void *);
53     void *arg;
54     struct mdnsdr_struct *next, *list;
55 };
56
57 struct mdnsd_struct
58 {
59     char shutdown;
60     unsigned long int expireall, checkqlist;
61     struct timeval now, sleep, pause, probe, publish;
62     int class, frame;
63     struct cached *cache[LPRIME];
64     struct mdnsdr_struct *published[SPRIME], *probing, *a_now, *a_pause, *a_publish;
65     struct unicast *uanswers;
66     struct query *queries[SPRIME], *qlist;
67 };
68
69 int _namehash(const char *s)
70 {
71     const unsigned char *name = (const unsigned char *)s;
72     unsigned long h = 0, g;
73
74     while (*name)
75     { /* do some fancy bitwanking on the string */
76         h = (h << 4) + (unsigned long)(*name++);
77         if ((g = (h & 0xF0000000UL))!=0)
78             h ^= (g >> 24);
79         h &= ~g;
80     }
81
82     return (int)h;
83 }
84
85 // basic linked list and hash primitives
86 struct query *_q_next(mdnsd d, struct query *q, char *host, int type)
87 {
88     if(q == 0) q = d->queries[_namehash(host) % SPRIME];
89     else q = q->next;
90     for(;q != 0; q = q->next)
91         if(q->type == type && strcmp(q->name, host) == 0)
92             return q;
93     return 0;
94 }
95 struct cached *_c_next(mdnsd d, struct cached *c, char *host, int type)
96 {
97     if(c == 0) c = d->cache[_namehash(host) % LPRIME];
98     else c = c->next;
99     for(;c != 0; c = c->next)
100         if((type == c->rr.type || type == 255) && strcmp(c->rr.name, host) == 0)
101             return c;
102     return 0;
103 }
104 mdnsdr _r_next(mdnsd d, mdnsdr r, char *host, int type)
105 {
106     if(r == 0) r = d->published[_namehash(host) % SPRIME];
107     else r = r->next;
108     for(;r != 0; r = r->next)
109         if(type == r->rr.type && strcmp(r->rr.name, host) == 0)
110             return r;
111     return 0;
112 }
113
114 int _rr_len(mdnsda rr)
115 {
116     int len = 12; // name is always compressed (dup of earlier), plus normal stuff
117     if(rr->rdata) len += rr->rdlen;
118     if(rr->rdname) len += strlen(rr->rdname); // worst case
119     if(rr->ip) len += 4;
120     if(rr->type == QTYPE_PTR) len += 6; // srv record stuff
121     return len;
122 }
123
124 int _a_match(struct resource *r, mdnsda a)
125 { // compares new rdata with known a, painfully
126     if(strcmp(r->name,a->name) || r->type != a->type) return 0;
127     if(r->type == QTYPE_SRV && !strcmp(r->known.srv.name,a->rdname) && a->srv.port == r->known.srv.port && a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority) return 1;
128     if((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcmp(a->rdname,r->known.ns.name)) return 1;
129     if(r->rdlength == a->rdlen && !memcmp(r->rdata,a->rdata,r->rdlength)) return 1;
130     return 0;
131 }
132
133 // compare time values easily
134 int _tvdiff(struct timeval old, struct timeval new)
135 {
136     int udiff = 0;
137     if(old.tv_sec != new.tv_sec) udiff = (new.tv_sec - old.tv_sec) * 1000000;
138     return (new.tv_usec - old.tv_usec) + udiff;
139 }
140
141 // make sure not already on the list, then insert
142 void _r_push(mdnsdr *list, mdnsdr r)
143 {
144     mdnsdr cur;
145     for(cur = *list; cur != 0; cur = cur->list)
146         if(cur == r) return;
147     r->list = *list;
148     *list = r;
149 }
150
151 // set this r to probing, set next probe time
152 void _r_probe(mdnsd d, mdnsdr r)
153 {
154 }
155
156 // force any r out right away, if valid
157 void _r_publish(mdnsd d, mdnsdr r)
158 {
159     if(r->unique && r->unique < 5) return; // probing already
160     r->tries = 0;
161     d->publish.tv_sec = d->now.tv_sec; d->publish.tv_usec = d->now.tv_usec;
162     _r_push(&d->a_publish,r);
163 }
164
165 // send r out asap
166 void _r_send(mdnsd d, mdnsdr r)
167 {
168     if(r->tries < 4)
169     { // being published, make sure that happens soon
170         d->publish.tv_sec = d->now.tv_sec; d->publish.tv_usec = d->now.tv_usec;
171         return;
172     }
173     if(r->unique)
174     { // known unique ones can be sent asap
175         _r_push(&d->a_now,r);
176         return;
177     }
178     // set d->pause.tv_usec to random 20-120 msec
179     d->pause.tv_sec = d->now.tv_sec;
180     d->pause.tv_usec = d->now.tv_usec + (d->now.tv_usec % 100) + 20;
181     _r_push(&d->a_pause,r);
182 }
183
184 // create generic unicast response struct
185 void _u_push(mdnsd d, mdnsdr r, int id, unsigned long int to, unsigned short int port)
186 {
187     struct unicast *u;
188     u = (struct unicast *)malloc(sizeof(struct unicast));
189     bzero(u,sizeof(struct unicast));
190     u->r = r;
191     u->id = id;
192     u->to = to;
193     u->port = port;
194     u->next = d->uanswers;
195     d->uanswers = u;
196 }
197
198 void _q_reset(mdnsd d, struct query *q)
199 {
200     struct cached *cur = 0;
201     q->nexttry = 0;
202     q->tries = 0;
203     while((cur = _c_next(d,cur,q->name,q->type)))
204         if(q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry) q->nexttry = cur->rr.ttl - 7;
205     if(q->nexttry != 0 && q->nexttry < d->checkqlist) d->checkqlist = q->nexttry;
206 }
207
208 void _q_done(mdnsd d, struct query *q)
209 { // no more query, update all it's cached entries, remove from lists
210     struct cached *c = 0;
211     struct query *cur;
212     int i = _namehash(q->name) % LPRIME;
213     while((c = _c_next(d,c,q->name,q->type))) c->q = 0;
214     if(d->qlist == q) d->qlist = q->list;
215     else {
216         for(cur=d->qlist;cur->list != q;cur = cur->list);
217         cur->list = q->list;
218     }
219     if(d->queries[i] == q) d->queries[i] = q->next;
220     else {
221         for(cur=d->queries[i];cur->next != q;cur = cur->next);
222         cur->next = q->next;
223     }
224     free(q->name);
225     free(q);
226 }
227
228 void _r_done(mdnsd d, mdnsdr r)
229 { // buh-bye, remove from hash and free
230     mdnsdr cur = 0;
231     int i = _namehash(r->rr.name) % SPRIME;
232     if(d->published[i] == r) d->published[i] = r->next;
233     else {
234         for(cur=d->published[i];cur && cur->next != r;cur = cur->next);
235         if(cur) cur->next = r->next;
236     }
237     free(r->rr.name);
238     free(r->rr.rdata);
239     free(r->rr.rdname);
240     free(r);
241 }
242
243 void _q_answer(mdnsd d, struct cached *c)
244 { // call the answer function with this cached entry
245     if(c->rr.ttl <= d->now.tv_sec) c->rr.ttl = 0;
246     if(c->q->answer(&c->rr,c->q->arg) == -1) _q_done(d, c->q);
247 }
248
249 void _conflict(mdnsd d, mdnsdr r)
250 {
251     r->conflict(r->rr.name,r->rr.type,r->arg);
252     mdnsd_done(d,r);
253 }
254
255 void _c_expire(mdnsd d, struct cached **list)
256 { // expire any old entries in this list
257     struct cached *next, *cur = *list, *last = 0;
258     while(cur != 0)
259     {
260         next = cur->next;
261         if(d->now.tv_sec >= cur->rr.ttl)
262         {
263             if(last) last->next = next;
264             if(*list == cur) *list = next; // update list pointer if the first one expired
265             if(cur->q) _q_answer(d,cur);
266             free(cur->rr.name);
267             free(cur->rr.rdata);
268             free(cur->rr.rdname);
269             free(cur);
270         }else{
271             last = cur;
272         }
273         cur = next;
274     }
275 }
276
277 // brute force expire any old cached records
278 void _gc(mdnsd d)
279 {
280     int i;
281     for(i=0;i<LPRIME;i++)
282         if(d->cache[i]) _c_expire(d,&d->cache[i]);
283     d->expireall = d->now.tv_sec + GC;
284 }
285
286 void _cache(mdnsd d, struct resource *r)
287 {
288     struct cached *c = 0;
289     int i = _namehash(r->name) % LPRIME;
290
291     if(r->class == 32768 + d->class)
292     { // cache flush
293         while((c = _c_next(d,c,r->name,r->type))) c->rr.ttl = 0;
294         _c_expire(d,&d->cache[i]);
295     }
296
297     if(r->ttl == 0)
298     { // process deletes
299         while((c = _c_next(d,c,r->name,r->type)))
300             if(_a_match(r,&c->rr))
301             {
302                 c->rr.ttl = 0;
303             }
304         _c_expire(d,&d->cache[i]);
305         return;
306     }
307
308     c = (struct cached *)malloc(sizeof(struct cached));
309     bzero(c,sizeof(struct cached));
310     c->rr.name = strdup(r->name);
311     c->rr.type = r->type;
312     c->rr.ttl = d->now.tv_sec + (r->ttl / 2) + 8; // XXX hack for now, BAD SPEC, start retrying just after half-waypoint, then expire
313     c->rr.rdlen = r->rdlength;
314     c->rr.rdata = (unsigned char *)malloc(r->rdlength);
315     memcpy(c->rr.rdata,r->rdata,r->rdlength);
316     switch(r->type)
317     {
318     case QTYPE_A:
319         c->rr.ip = r->known.a.ip;
320         break;
321     case QTYPE_NS:
322     case QTYPE_CNAME:
323     case QTYPE_PTR:
324         c->rr.rdname = strdup(r->known.ns.name);
325         break;
326     case QTYPE_SRV:
327         c->rr.rdname = strdup(r->known.srv.name);
328         c->rr.srv.port = r->known.srv.port;
329         c->rr.srv.weight = r->known.srv.weight;
330         c->rr.srv.priority = r->known.srv.priority;
331         break;
332     }
333     c->next = d->cache[i];
334     d->cache[i] = c;
335     if((c->q = _q_next(d, 0, r->name, r->type)))
336         _q_answer(d,c);
337 }
338
339 void _a_copy(struct message *m, mdnsda a)
340 { // copy the data bits only
341     if(a->rdata) { message_rdata_raw(m, a->rdata, a->rdlen); return; }
342     if(a->ip) message_rdata_long(m, a->ip);
343     if(a->type == QTYPE_SRV) message_rdata_srv(m, a->srv.priority, a->srv.weight, a->srv.port, a->rdname);
344     else if(a->rdname) message_rdata_name(m, a->rdname);
345 }
346
347 int _r_out(mdnsd d, struct message *m, mdnsdr *list)
348 { // copy a published record into an outgoing message
349     mdnsdr r;
350     int ret = 0;
351     while((r = *list) != 0 && message_packet_len(m) + _rr_len(&r->rr) < d->frame)
352     {
353         *list = r->list;
354         ret++;
355         if(r->unique)
356             message_an(m, r->rr.name, r->rr.type, d->class + 32768, r->rr.ttl);
357         else
358             message_an(m, r->rr.name, r->rr.type, d->class, r->rr.ttl);
359         _a_copy(m, &r->rr);
360         if(r->rr.ttl == 0) _r_done(d,r);
361     }
362     return ret;
363 }
364
365
366 mdnsd mdnsd_new(int class, int frame)
367 {
368     mdnsd d;
369     d = (mdnsd)malloc(sizeof(struct mdnsd_struct));
370     bzero(d,sizeof(struct mdnsd_struct));
371     gettimeofday(&d->now,0);
372     d->expireall = d->now.tv_sec + GC;
373     d->class = class;
374     d->frame = frame;
375     return d;
376 }
377
378 void mdnsd_shutdown(mdnsd d)
379 { // shutting down, zero out ttl and push out all records
380     int i;
381     mdnsdr cur,next;
382     d->a_now = 0;
383     for(i=0;i<SPRIME;i++)
384         for(cur = d->published[i]; cur != 0;)
385         {
386             next = cur->next;
387             cur->rr.ttl = 0;
388             cur->list = d->a_now;
389             d->a_now = cur;
390             cur = next;
391         }
392     d->shutdown = 1;
393 }
394
395 void mdnsd_flush(mdnsd d)
396 {
397     // set all querys to 0 tries
398     // free whole cache
399     // set all mdnsdr to probing
400     // reset all answer lists
401 }
402
403 void mdnsd_free(mdnsd d)
404 {
405     // loop through all hashes, free everything
406     // free answers if any
407     free(d);
408 }
409
410 void mdnsd_in(mdnsd d, struct message *m, unsigned long int ip, unsigned short int port)
411 {
412     int i, j;
413     mdnsdr r = 0;
414
415     if(d->shutdown) return;
416
417     gettimeofday(&d->now,0);
418
419     if(m->header.qr == 0)
420     {
421         for(i=0;i<m->qdcount;i++)
422         { // process each query
423             if(m->qd[i].class != d->class || (r = _r_next(d,0,m->qd[i].name,m->qd[i].type)) == 0) continue;
424
425             // send the matching unicast reply
426             if(port != 5353) _u_push(d,r,m->id,ip,port);
427
428             for(;r != 0; r = _r_next(d,r,m->qd[i].name,m->qd[i].type))
429             { // check all of our potential answers
430                 if(r->unique && r->unique < 5)
431                 { // probing state, check for conflicts
432                     for(j=0;j<m->nscount;j++)
433                     { // check all to-be answers against our own
434                         if(m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name,m->an[j].name)) continue;
435                         if(!_a_match(&m->an[j],&r->rr)) _conflict(d,r); // this answer isn't ours, conflict!
436                     }
437                     continue;
438                 }
439                 for(j=0;j<m->ancount;j++)
440                 { // check the known answers for this question
441                     if(m->qd[i].type != m->an[j].type || strcmp(m->qd[i].name,m->an[j].name)) continue;
442                     if(_a_match(&m->an[j],&r->rr)) break; // they already have this answer
443                 }
444                 if(j == m->ancount) _r_send(d,r);
445             }
446         }
447         return;
448     }
449
450     for(i=0;i<m->ancount;i++)
451     { // process each answer, check for a conflict, and cache
452         if((r = _r_next(d,0,m->an[i].name,m->an[i].type)) != 0 && r->unique && _a_match(&m->an[i],&r->rr) == 0) _conflict(d,r);
453         _cache(d,&m->an[i]);
454     }
455 }
456
457 int mdnsd_out(mdnsd d, struct message *m, unsigned long int *ip, unsigned short int *port)
458 {
459     mdnsdr r;
460     int ret = 0;
461
462     gettimeofday(&d->now,0);
463     bzero(m,sizeof(struct message));
464
465     // defaults, multicast
466     *port = htons(5353);
467     *ip = inet_addr("224.0.0.251");
468     m->header.qr = 1;
469     m->header.aa = 1;
470
471     if(d->uanswers)
472     { // send out individual unicast answers
473         struct unicast *u = d->uanswers;
474         d->uanswers = u->next;
475         *port = u->port;
476         *ip = u->to;
477         m->id = u->id;
478         message_qd(m, u->r->rr.name, u->r->rr.type, d->class);
479         message_an(m, u->r->rr.name, u->r->rr.type, d->class, u->r->rr.ttl);
480         _a_copy(m, &u->r->rr);
481         free(u);
482         return 1;
483     }
484
485 //printf("OUT: probing %X now %X pause %X publish %X\n",d->probing,d->a_now,d->a_pause,d->a_publish);
486
487     // accumulate any immediate responses
488     if(d->a_now) ret += _r_out(d, m, &d->a_now);
489
490     if(d->a_publish && _tvdiff(d->now,d->publish) <= 0)
491     { // check to see if it's time to send the publish retries (and unlink if done)
492         mdnsdr next, cur = d->a_publish, last = 0;
493         while(cur && message_packet_len(m) + _rr_len(&cur->rr) < d->frame)
494         {
495             next = cur->list;
496             ret++; cur->tries++;
497             if(cur->unique)
498                 message_an(m, cur->rr.name, cur->rr.type, d->class + 32768, cur->rr.ttl);
499             else
500                 message_an(m, cur->rr.name, cur->rr.type, d->class, cur->rr.ttl);
501             _a_copy(m, &cur->rr);
502             if(cur->rr.ttl != 0 && cur->tries < 4)
503             {
504                 last = cur;
505                 cur = next;
506                 continue;
507             }
508             if(d->a_publish == cur) d->a_publish = next;
509             if(last) last->list = next;
510             if(cur->rr.ttl == 0) _r_done(d,cur);
511             cur = next;
512         }
513         if(d->a_publish)
514         {
515             d->publish.tv_sec = d->now.tv_sec + 2;
516             d->publish.tv_usec = d->now.tv_usec;
517         }
518     }
519
520     // if we're in shutdown, we're done
521     if(d->shutdown) return ret;
522
523     // check if a_pause is ready
524     if(d->a_pause && _tvdiff(d->now, d->pause) <= 0) ret += _r_out(d, m, &d->a_pause);
525
526     // now process questions
527     if(ret) return ret;
528     m->header.qr = 0;
529     m->header.aa = 0;
530
531     if(d->probing && _tvdiff(d->now,d->probe) <= 0)
532     {
533         mdnsdr last = 0;
534         for(r = d->probing; r != 0;)
535         { // scan probe list to ask questions and process published
536             if(r->unique == 4)
537             { // done probing, publish
538                 mdnsdr next = r->list;
539                 if(d->probing == r)
540                     d->probing = r->list;
541                 else
542                     last->list = r->list;
543                 r->list = 0;
544                 r->unique = 5;
545                 _r_publish(d,r);
546                 r = next;
547                 continue;
548             }
549             message_qd(m, r->rr.name, r->rr.type, d->class);
550             last = r;
551             r = r->list;
552         }
553         for(r = d->probing; r != 0; last = r, r = r->list)
554         { // scan probe list again to append our to-be answers
555             r->unique++;
556             message_ns(m, r->rr.name, r->rr.type, d->class, r->rr.ttl);
557             _a_copy(m, &r->rr);
558             ret++;
559         }
560         if(ret)
561         { // process probes again in the future
562             d->probe.tv_sec = d->now.tv_sec;
563             d->probe.tv_usec = d->now.tv_usec + 250000;
564             return ret;
565         }
566     }
567
568     if(d->checkqlist && d->now.tv_sec >= d->checkqlist)
569     { // process qlist for retries or expirations
570         struct query *q;
571         struct cached *c;
572         unsigned long int nextbest = 0;
573
574         // ask questions first, track nextbest time
575         for(q = d->qlist; q != 0; q = q->list)
576             if(q->nexttry > 0 && q->nexttry <= d->now.tv_sec && q->tries < 3)
577                 message_qd(m,q->name,q->type,d->class);
578             else if(q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest))
579                 nextbest = q->nexttry;
580
581         // include known answers, update questions
582         for(q = d->qlist; q != 0; q = q->list)
583         {
584             if(q->nexttry == 0 || q->nexttry > d->now.tv_sec) continue;
585             if(q->tries == 3)
586             { // done retrying, expire and reset
587                 _c_expire(d,&d->cache[_namehash(q->name) % LPRIME]);
588                 _q_reset(d,q);
589                 continue;
590             }
591             ret++;
592             q->nexttry = d->now.tv_sec + ++q->tries;
593             if(nextbest == 0 || q->nexttry < nextbest)
594                 nextbest = q->nexttry;
595             // if room, add all known good entries
596             c = 0;
597             while((c = _c_next(d,c,q->name,q->type)) != 0 && c->rr.ttl > d->now.tv_sec + 8 && message_packet_len(m) + _rr_len(&c->rr) < d->frame)
598             {
599                 message_an(m,q->name,q->type,d->class,c->rr.ttl - d->now.tv_sec);
600                 _a_copy(m,&c->rr);
601             }
602         }
603         d->checkqlist = nextbest;
604     }
605
606     if(d->now.tv_sec > d->expireall)
607         _gc(d);
608
609     return ret;
610 }
611
612 struct timeval *mdnsd_sleep(mdnsd d)
613 {
614     int sec, usec;
615     d->sleep.tv_sec = d->sleep.tv_usec = 0;
616     #define RET while(d->sleep.tv_usec > 1000000) {d->sleep.tv_sec++;d->sleep.tv_usec -= 1000000;} return &d->sleep;
617
618     // first check for any immediate items to handle
619     if(d->uanswers || d->a_now) return &d->sleep;
620
621     gettimeofday(&d->now,0);
622
623     if(d->a_pause)
624     { // then check for paused answers
625         if((usec = _tvdiff(d->now,d->pause)) > 0) d->sleep.tv_usec = usec;
626         RET;
627     }
628
629     if(d->probing)
630     { // now check for probe retries
631         if((usec = _tvdiff(d->now,d->probe)) > 0) d->sleep.tv_usec = usec;
632         RET;
633     }
634
635     if(d->a_publish)
636     { // now check for publish retries
637         if((usec = _tvdiff(d->now,d->publish)) > 0) d->sleep.tv_usec = usec;
638         RET;
639     }
640
641     if(d->checkqlist)
642     { // also check for queries with known answer expiration/retry
643         if((sec = d->checkqlist - d->now.tv_sec) > 0) d->sleep.tv_sec = sec;
644         RET;
645     }
646
647     // last resort, next gc expiration
648     if((sec = d->expireall - d->now.tv_sec) > 0) d->sleep.tv_sec = sec;
649     RET;
650 }
651
652 void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *arg), void *arg)
653 {
654     struct query *q;
655     struct cached *cur = 0;
656     int i = _namehash(host) % SPRIME;
657     if(!(q = _q_next(d,0,host,type)))
658     {
659         if(!answer) return;
660         q = (struct query *)malloc(sizeof(struct query));
661         bzero(q,sizeof(struct query));
662         q->name = strdup(host);
663         q->type = type;
664         q->next = d->queries[i];
665         q->list = d->qlist;
666         d->qlist = d->queries[i] = q;
667         while((cur = _c_next(d,cur,q->name,q->type)))
668             cur->q = q; // any cached entries should be associated
669         _q_reset(d,q);
670         q->nexttry = d->checkqlist = d->now.tv_sec; // new questin, immediately send out
671     }
672     if(!answer)
673     { // no answer means we don't care anymore
674         _q_done(d,q);
675         return;
676     }
677     q->answer = answer;
678     q->arg = arg;
679 }
680
681 mdnsda mdnsd_list(mdnsd d, char *host, int type, mdnsda last)
682 {
683     return (mdnsda)_c_next(d,(struct cached *)last,host,type);
684 }
685
686 mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl)
687 {
688     int i = _namehash(host) % SPRIME;
689     mdnsdr r;
690     r = (mdnsdr)malloc(sizeof(struct mdnsdr_struct));
691     bzero(r,sizeof(struct mdnsdr_struct));
692     r->rr.name = strdup(host);
693     r->rr.type = type;
694     r->rr.ttl = ttl;
695     r->next = d->published[i];
696     d->published[i] = r;
697     return r;
698 }
699
700 mdnsdr mdnsd_unique(mdnsd d, char *host, int type, long int ttl, void (*conflict)(char *host, int type, void *arg), void *arg)
701 {
702     mdnsdr r;
703     r = mdnsd_shared(d,host,type,ttl);
704     r->conflict = conflict;
705     r->arg = arg;
706     r->unique = 1;
707     _r_push(&d->probing,r);
708     d->probe.tv_sec = d->now.tv_sec;
709     d->probe.tv_usec = d->now.tv_usec;
710     return r;
711 }
712
713 void mdnsd_done(mdnsd d, mdnsdr r)
714 {
715     mdnsdr cur;
716     if(r->unique && r->unique < 5)
717     { // probing yet, zap from that list first!
718         if(d->probing == r) d->probing = r->list;
719         else {
720             for(cur=d->probing;cur->list != r;cur = cur->list);
721             cur->list = r->list;
722         }
723         _r_done(d,r);
724         return;
725     }
726     r->rr.ttl = 0;
727     _r_send(d,r);
728 }
729
730 void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len)
731 {
732     free(r->rr.rdata);
733     r->rr.rdata = (unsigned char *)malloc(len);
734     memcpy(r->rr.rdata,data,len);
735     r->rr.rdlen = len;
736     _r_publish(d,r);
737 }
738
739 void mdnsd_set_host(mdnsd d, mdnsdr r, char *name)
740 {
741     free(r->rr.rdname);
742     r->rr.rdname = strdup(name);
743     _r_publish(d,r);
744 }
745
746 void mdnsd_set_ip(mdnsd d, mdnsdr r, unsigned long int ip)
747 {
748     r->rr.ip = ip;
749     _r_publish(d,r);
750 }
751
752 void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char *name)
753 {
754     r->rr.srv.priority = priority;
755     r->rr.srv.weight = weight;
756     r->rr.srv.port = port;
757     mdnsd_set_host(d,r,name);
758 }
759