]> git.karo-electronics.de Git - mdnsd.git/blob - mdnsd.c
Replaced int8_t by char for all strings
[mdnsd.git] / mdnsd.c
1 /*
2  * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
3  * Copyright (c) 2009 Simon Budig <simon@budig.org>
4  * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the copyright holders nor the names of
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
31  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * For additional information see http://www.ethernut.de/
35  */
36
37 /* This code is based on
38  * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
39  * http://dotlocal.org/mdnsd/
40  *
41  * Unfortunately this site is now longer alive. You can still find it at:
42  * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
43  *
44  * mdnsd - embeddable Multicast DNS Daemon
45  * =======================================
46  *
47  * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
48  * open source implementation of Multicast DNS (part of Zeroconf, also called
49  * Rendezvous by Apple) for developers. It supports both acting as a Query and
50  * a Responder, allowing any software to participate fully on the .localnetwork
51  * just by including a few files and calling a few functions.  All of the
52  * complexity of handling the Multicast DNS retransmit timing, duplicate
53  * suppression, probing, conflict detection, and other facets of the DNS
54  * protocol is hidden behind a very simple and very easy to use interface,
55  * described in the header file. The single small c source file has almost no
56  * dependencies, and is portable to almost any embedded platform.
57  * Multiple example applications and usages are included in the download,
58  * including a simple persistent query browser and a tool to advertise .local
59  * web sites.
60  *
61  * The code is licensed under both the GPL and BSD licenses, for use in any
62  * free software or commercial application. If there is a licensing need not
63  * covered by either of those, alternative licensing is available upon request.
64  *
65  */
66
67 /*!
68  * \file pro/mdnsd.c
69  * \brief Multicast DNS Deamon
70  *
71  * \verbatim
72  *
73  * $Id$
74  *
75  * \endverbatim
76  */
77
78 #include <string.h>
79 #include <ctype.h>
80 #include <stdlib.h>
81 #include <arpa/inet.h>
82 #include <stdio.h>
83 #include "mdnsd.h"
84
85 /*!
86  * \addtogroup xgMulticastDns
87  */
88 /*@{*/
89
90 #ifdef NUTDEBUG
91 ##define MDNSD_DEBUG
92 #endif
93
94 /*
95  * Messy, but it's the best/simplest balance I can find at the moment
96  *
97  * Some internal data types, and a few hashes:
98  * - querys
99  * - answers
100  * - cached
101  * - records (published, unique and shared)
102  *
103  * Each type has different semantics for processing, both for timeouts,
104  * incoming, and outgoing I/O.
105  *
106  * They inter-relate too, like records affect the querys they are relevant to.
107  *
108  * Nice things about MDNS: we only publish once (and then ask asked),
109  * and only query once, then just expire records we've got cached
110  */
111
112 /*!
113  * \brief Generates a hash code for a string.
114  *
115  * This function uses the ELF hashing algorithm as reprinted in
116  * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
117  *
118  * \param s     The string to hash
119  *
120  * \return      The calculated hash value
121  */
122
123 static int NameHash(const char *str)
124 {
125     const char *name = (const char *)str;
126     uint32_t hash = 0;
127     uint32_t g;
128
129     while (*name) {
130         /* do some fancy bitwanking on the string */
131         hash = (hash << 4) + (unsigned long)(tolower (*name++));
132         if ((g = (hash & 0xF0000000UL))!=0)
133             hash ^= (g >> 24);
134         hash &= ~g;
135     }
136
137     return (int)hash;
138 }
139
140 /*!
141  * \brief Get the next matching query in the hash list
142  *
143  * Basic hash and linked list primatives for Query hash.
144  * Iterate through the given query list and search for the given host.
145  *
146  * \param mdnsd     MDNS deamon data of the current mdnsd instace
147  * \param query     The query list head entry to start searching from or NULL
148  *                  if a new search shall be started
149  * \param host      host name to search for
150  * \param type      query type
151  *
152  * \return          The next matching query or NULL
153  */
154 static TQuery *QueryNext(TMdnsd *mdnsd, TQuery *query, char *host, int type)
155 {
156     if (query == NULL) {
157         query = mdnsd->queries[NameHash(host) % SPRIME];
158     } else {
159         query = query->next;
160     }
161
162     for( ; query != NULL; query = query->next) {
163         if (((query->type == QTYPE_ANY) || (query->type == type)) &&
164             (strcasecmp(query->name, host) == 0)) {
165             return query;
166         }
167     }
168
169     return NULL;
170 }
171
172 /*!
173  * \brief Get the next matching cache entry in the hash list
174  *
175  * Basic hash and linked list primatives for cache hash.
176  * Iterate through the given cache list and search for the given host.
177  *
178  * \param mdnsd     MDNS deamon data of the current mdnsd instace
179  * \param cached    The cache list head entry to start searching from or NULL
180  *                  if a new search shall be started
181  * \param host      host name to search for
182  * \param type      query type
183  *
184  * \return          The next matching cache entry or NULL
185  */
186 static TCached *CachedNext(TMdnsd *mdnsd, TCached *cached, char *host, int type)
187 {
188     if (cached == NULL) {
189         cached = mdnsd->cache[NameHash(host) % LPRIME];
190     } else {
191         cached = cached->next;
192     }
193
194     for( ; cached != NULL; cached = cached->next) {
195         if (((type == cached->rr.type) || (type == QTYPE_ANY)) &&
196             (strcasecmp(cached->rr.name, host) == 0)) {
197             return cached;
198         }
199     }
200     return NULL;
201 }
202
203 /*!
204  * \brief Get the next matching dns record in the published hash list
205  *
206  * Basic hash and linked list primatives for published dns record hash.
207  * Iterate through the given dns record list and search for the given host.
208  *
209  * \param mdnsd     MDNS deamon data of the current mdnsd instace
210  * \param cached    The dns record list head entry to start searching from or NULL
211  *                  if a new search shall be started
212  * \param host      host name to search for
213  * \param type      query type
214  *
215  * \return          The next matching dns record or NULL
216  */
217 static TMdnsdRecord *RecordNext(TMdnsd *mdnsd, TMdnsdRecord *record, char *host, int type)
218 {
219     if (record == NULL) {
220         record = mdnsd->published[NameHash(host) % SPRIME];
221     } else {
222         record = record->next;
223     }
224
225     for( ; record != NULL; record = record->next) {
226         if (((type == record->rr.type) || (type == QTYPE_ANY)) &&
227             (strcasecmp(record->rr.name, host) == 0)) {
228             return record;
229         }
230     }
231     return NULL;
232 }
233
234 /*!
235  * \brief Get the length of the given ressource record
236  *
237  * \param rr        Ressource record buffer to calculate the length for
238  *
239  * \return          calculated length of the ressource record
240  */
241 static int GetRessourceRecordLength(TMdnsdAnswer *rr)
242 {
243     int len;
244
245     /* Initialise the length: Name is always compressed (dup of earlier occurence)
246        plus further normal stuff.
247     */
248     len = 12;
249
250     if (rr->rdata)  len += rr->rdlen;
251     if (rr->rdname) len += strlen(rr->rdname); /* add worst case */
252     if (rr->ip.s_addr) len += 4;
253     if (rr->type == QTYPE_PTR) len += 6; /* Add srv record length */
254
255     return len;
256 }
257
258
259 /*!
260  * \brief Compare the ressource data with a given answer record
261  *
262  * This is a painfull compare, with lots of needed comparisions... computing intensive
263  *
264  * \param res       ressource data to compare with the given answer
265  * \param answer    Answer record to compare with
266  *
267  * \return          1 in case of a math or 0 if no match found
268  */
269 static int MatchAnswer(DNSRESOURCE *res, TMdnsdAnswer *answer)
270 {
271     /* Check if the name and ressource type is matching */
272     if ((strcasecmp(res->name, answer->name) != 0) ||
273         ((res->type != QTYPE_ANY) && (res->type != answer->type))) {
274         /* no match ... */
275         return 0;
276     }
277
278     /* If name matches, and ressource type is QTYPE_ANY we found a match */
279     if ((res->type == QTYPE_ANY) && (strcasecmp(res->name, answer->name) == 0)) {
280         return 1;
281     }
282
283     /* Special checks for SRV type ressource data */
284     if ((res->type == QTYPE_SRV) && (strcasecmp(res->known.srv.name, answer->rdname) == 0) &&
285         (answer->srv.port == res->known.srv.port) &&
286         (answer->srv.weight == res->known.srv.weight) &&
287         (answer->srv.priority == res->known.srv.priority)) {
288         return 1;
289     }
290
291     /* Check for PTR, NS or CNAME type ressource data */
292     if (((res->type == QTYPE_PTR) || (res->type == QTYPE_NS) || (res->type == QTYPE_CNAME)) &&
293         (strcasecmp(answer->rdname, res->known.ns.name) == 0)) {
294         return 1;
295     }
296
297
298     if ((res->rdlength == answer->rdlen) && (memcmp(res->rdata, answer->rdata, res->rdlength) == 0)) {
299         return 1;
300     }
301
302     return 0;
303 }
304
305 /*!
306  * \brief Calculate time elapsed between a and b
307  *
308  * Compares two timeval values
309  *
310  * \param a         older timeval value
311  * \param b         newer timeval value
312  *
313  * \return          Elapsed time in Âµs
314  */
315 static int TvDiff(struct timeval a, struct timeval b)
316 {
317     int udiff = 0;
318
319     if (a.tv_sec != b.tv_sec) {
320         udiff = (b.tv_sec - a.tv_sec) * 1000000;
321     }
322
323     return (b.tv_usec - a.tv_usec) + udiff;
324 }
325
326 /*!
327  * \brief create generic unicast response struct
328  *
329  * \param mdnsd     MDNS deamon data of the current mdnsd instace
330  * \param record    Record to push
331  * \param id        unicast record id
332  */
333 static void MdnsdPushUnicast(TMdnsd *mdnsd, TMdnsdRecord *record, int id, struct in_addr to, uint16_t port)
334 {
335     TUnicast *unicast;
336
337     unicast = (TUnicast *)malloc(sizeof(TUnicast));
338     memset(unicast, 0, sizeof(TUnicast));
339
340     unicast->record = record;
341     unicast->id = id;
342     unicast->to = to;
343     unicast->port = port;
344     unicast->next = mdnsd->uanswers;
345
346     mdnsd->uanswers = unicast;
347 }
348
349 /*!
350  * \brief Insert a record to the list if not yet inserted
351  *
352  * \param list      Linked record list head
353  * \param record    Record to insert
354  */
355 static void MdnsdPushRecord(TMdnsdRecord **list, TMdnsdRecord *record)
356 {
357     TMdnsdRecord *current;
358
359     for(current = *list; current != NULL; current = current->list) {
360         if(current == record) {
361             return;
362         }
363     }
364
365     record->list = *list;
366     *list = record;
367 }
368
369 /*!
370  * \brief Publish a record if valid
371  *
372  * \param mdnsd     MDNS deamon data of the current mdnsd instace
373  * \param record    Record to publish
374  */
375 static void MdnsdPublishRecord(TMdnsd *mdnsd, TMdnsdRecord *record)
376 {
377     if (record->unique && (record->unique < 5)) {
378         /* probing already */
379         return;
380     }
381
382     record->tries = 0;
383     mdnsd->publish.tv_sec = mdnsd->now.tv_sec;
384     mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
385
386     MdnsdPushRecord(&mdnsd->a_publish, record);
387 }
388
389 /*!
390  * \brief Send out a record as soon as possible
391  *
392  * \param mdnsd     MDNS deamon data of the current mdnsd instace
393  * \param record    Record to send
394  */
395 static void MdnsdSendRecord(TMdnsd *mdnsd, TMdnsdRecord *record)
396 {
397     if (record->tries < 4) {
398         /* The record has been published, speed up the things... */
399         mdnsd->publish.tv_sec = mdnsd->now.tv_sec;
400         mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
401         return;
402     }
403
404     if (record->unique) {
405         /* Unique records can be sent ASAP */
406         MdnsdPushRecord(&mdnsd->a_now, record);
407         return;
408     }
409 // TODO: better random, do not use modulo
410     /* set mdnsd->pause.tv_usec to random 20-120 msec */
411     mdnsd->pause.tv_sec = mdnsd->now.tv_sec;
412     mdnsd->pause.tv_usec = mdnsd->now.tv_usec + ((mdnsd->now.tv_usec % 101) + 20) * 1000;
413     if (mdnsd->pause.tv_usec >= 1000000) {
414         mdnsd->pause.tv_sec++;
415         mdnsd->pause.tv_usec -= 1000000;
416     }
417
418     /* And push the record... */
419     MdnsdPushRecord(&mdnsd->a_pause, record);
420 }
421
422 /*!
423  * \brief Clean up record
424  *
425  * Remove from hash and free allocated memory
426  *
427  * \param mdnsd     MDNS deamon data of the current mdnsd instace
428  * \param record    Record to clean up
429  */
430 static void MdnsdRecordDone(TMdnsd *mdnsd, TMdnsdRecord *record)
431 {
432     TMdnsdRecord *current = NULL;
433     int idx = NameHash(record->rr.name) % SPRIME;
434
435     if(mdnsd->published[idx] == record) {
436         mdnsd->published[idx] = record->next;
437     } else {
438         for (current = mdnsd->published[idx]; (current != NULL) && (current->next != record); current = current->next);
439
440         if (current) {
441             current->next = record->next;
442         }
443     }
444     free(record->rr.name);
445     free(record->rr.rdata);
446     free(record->rr.rdname);
447     free(record);
448 }
449
450 /*!
451  * \brief Reset query
452  *
453  * \param mdnsd     MDNS deamon data of the current mdnsd instace
454  * \param query     Query to reset
455  */
456 static void MdnsdQueryReset(TMdnsd *mdnsd, TQuery *query)
457 {
458     TCached *current = NULL;
459
460     query->nexttry = 0;
461     query->tries = 0;
462
463     while ((current = CachedNext(mdnsd, current, query->name, query->type))) {
464         if ((query->nexttry == 0 ) || (current->rr.ttl - 7 < query->nexttry)) {
465             query->nexttry = current->rr.ttl - 7;
466         }
467     }
468
469     if ((query->nexttry != 0) && (query->nexttry < mdnsd->checkqlist)) {
470         mdnsd->checkqlist = query->nexttry;
471     }
472 }
473
474 /*!
475  * \brief Clean up query
476  *
477  * Update all its cached entries and remove it from list, and free allocated memory
478  *
479  * \param mdnsd     MDNS deamon data of the current mdnsd instace
480  * \param query     Query to clean up
481  */
482 static void MdnsdQueryDone(TMdnsd *mdnsd, TQuery *query)
483 {
484     TCached *cached = NULL;
485     TQuery  *current;
486     int      idx;
487
488     idx = NameHash(query->name) % SPRIME;
489
490     while ((cached = CachedNext(mdnsd, cached, query->name, query->type))) {
491         cached->query = NULL;
492     }
493
494     if (mdnsd->qlist == query) {
495         mdnsd->qlist = query->list;
496     } else {
497         for (current = mdnsd->qlist; current->list != query; current = current->list);
498         current->list = query->list;
499     }
500
501     if (mdnsd->queries[idx] == query) {
502         mdnsd->queries[idx] = query->next;
503     } else {
504         for (current = mdnsd->queries[idx]; current->next != query; current = current->next);
505         current->next = query->next;
506     }
507
508     free(query->name);
509     free(query);
510 }
511
512 /*!
513  * \brief call the answer function with this cached entry
514  *
515  * \param mdnsd     MDNS deamon data of the current mdnsd instace
516  * \param cached    Cached record
517  */
518 static void MdnsdQueryAnswer(TMdnsd *mdnsd, TCached *cached)
519 {
520     if (cached->rr.ttl <= mdnsd->now.tv_sec) {
521         cached->rr.ttl = 0;
522     }
523
524     if (cached->query->answer(&cached->rr, cached->query->arg) == -1) {
525         MdnsdQueryDone(mdnsd, cached->query);
526     }
527 }
528
529 /*!
530  * \brief call the conflict function with this record
531  *
532  * \param mdnsd     MDNS deamon data of the current mdnsd instace
533  * \param record    Record to call conflict for
534  */
535 static void MdnsdCallConflict(TMdnsd *mdnsd, TMdnsdRecord *record)
536 {
537     record->conflict(record, record->rr.name, record->rr.type, record->arg);
538     MdnsdDone(mdnsd, record);
539 }
540
541 /*!
542  * \brief Expire any old entries in this hash list
543  *
544  * \param mdnsd     MDNS deamon data of the current mdnsd instace
545  * \param list      Cache hash list head
546  */
547 static void MdnsdCacheExpire(TMdnsd *mdnsd, TCached **list)
548 {
549     TCached *next;
550     TCached *current  = *list;
551     TCached *last = NULL;
552
553     while(current != NULL) {
554         next = current->next;
555
556         if(mdnsd->now.tv_sec >= current->rr.ttl) {
557             if (last) {
558                 last->next = next;
559             }
560
561             if (*list == current) {
562                 /* Update list pointer if the first one expired */
563                 *list = next;
564             }
565
566             if (current->query) {
567                 MdnsdQueryAnswer(mdnsd, current);
568             }
569
570             free(current->rr.name);
571             free(current->rr.rdata);
572             free(current->rr.rdname);
573             free(current);
574         } else {
575             last = current;
576         }
577         current = next;
578     }
579 }
580
581
582 /*!
583  * \brief Garbage collector: Expire any old cached records
584  *
585  * \param mdnsd     MDNS deamon data of the current mdnsd instace
586  */
587 static void MdnsdCacheGarbageCollect(TMdnsd *mdnsd)
588 {
589     int idx;
590
591     for(idx = 0; idx < LPRIME; idx++) {
592         if (mdnsd->cache[idx]) {
593             MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
594         }
595     }
596
597     mdnsd->expireall = mdnsd->now.tv_sec + GC;
598 }
599
600 /*!
601  * \brief Add a ressource to the cache
602  *
603  * \param mdnsd     MDNS deamon data of the current mdnsd instace
604  * \param res       ressource data to add
605  */
606 static void MdnsdCacheAddRessource(TMdnsd *mdnsd, DNSRESOURCE *res)
607 {
608     TCached *cached = NULL;
609
610     int idx;
611
612     idx = NameHash(res->name) % LPRIME;
613
614     if (res->class == 32768 + mdnsd->class) {
615         /* Flush the cache */
616         while ((cached = CachedNext(mdnsd, cached, res->name, res->type))) {
617             cached->rr.ttl = 0;
618         }
619         MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
620     }
621
622     if (res->ttl == 0) {
623         /* Process deletes */
624         while ((cached = CachedNext(mdnsd, cached, res->name, res->type))) {
625             if (MatchAnswer(res, &cached->rr)) {
626                 cached->rr.ttl = 0;
627             }
628         }
629         MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
630         return;
631     }
632
633     cached = (TCached *)malloc(sizeof(TCached));
634     memset(cached, 0, sizeof(TCached));
635
636     cached->rr.name = strdup(res->name);
637     cached->rr.type = res->type;
638     cached->rr.ttl = mdnsd->now.tv_sec + (res->ttl / 2) + 8; // XXX hack for now, BAD SPEC, start retrying just after half-waypoint, then expire
639     cached->rr.rdlen = res->rdlength;
640     cached->rr.rdata = (uint8_t *)malloc(res->rdlength);
641     memcpy(cached->rr.rdata, res->rdata, res->rdlength);
642
643     switch(res->type) {
644         case QTYPE_A:
645             cached->rr.ip.s_addr = res->known.a.ip;
646             break;
647         case QTYPE_NS:
648         case QTYPE_CNAME:
649         case QTYPE_PTR:
650             cached->rr.rdname = strdup(res->known.ns.name);
651             break;
652         case QTYPE_SRV:
653             cached->rr.rdname = strdup(res->known.srv.name);
654             cached->rr.srv.port = res->known.srv.port;
655             cached->rr.srv.weight = res->known.srv.weight;
656             cached->rr.srv.priority = res->known.srv.priority;
657             break;
658     }
659
660     cached->next = mdnsd->cache[idx];
661     mdnsd->cache[idx] = cached;
662
663     if((cached->query = QueryNext(mdnsd, 0, res->name, res->type))) {
664         MdnsdQueryAnswer(mdnsd, cached);
665     }
666 }
667
668 /*!
669  * \brief Copy an answer
670  *
671  * Copy the databits only
672  *
673  * \param msg       DNS message struct
674  * \param answer    Answer to get the data from
675  */
676 static void MdnsdCopyAnswer(DNSMESSAGE *msg, TMdnsdAnswer *answer)
677 {
678     if(answer->rdata) {
679         DnsMsgAdd_rdata_raw(msg, answer->rdata, answer->rdlen);
680         return;
681     }
682
683     if(answer->ip.s_addr) {
684         DnsMsgAdd_rdata_long(msg, answer->ip.s_addr);
685     }
686
687     if(answer->type == QTYPE_SRV) {
688         DnsMsgAdd_rdata_srv(msg, answer->srv.priority, answer->srv.weight, answer->srv.port, answer->rdname);
689     } else
690     if (answer->rdname) {
691         DnsMsgAdd_rdata_name(msg, answer->rdname);
692     }
693 }
694
695 /*!
696  * \brief Copy a published record into an outgoing message
697  *
698  * \param mdnsd     MDNS deamon data of the current mdnsd instace
699  * \param msg       DNS message struct
700  * \param list      List of publishing records
701  *
702  * \return          Number of send records
703  */
704 static int MdnsdRecordOut(TMdnsd *mdnsd, DNSMESSAGE *m, TMdnsdRecord **list)
705 {
706     TMdnsdRecord *record;
707     int ret = 0;
708
709     while (((record = *list) != NULL) &&
710            (DnsMsgLen(m) + GetRessourceRecordLength(&record->rr) < mdnsd->frame)) {
711
712         *list = record->list;
713         ret++;
714
715         if (record->unique) {
716             DnsMsgAdd_an(m, record->rr.name, record->rr.type, mdnsd->class + 32768, record->rr.ttl);
717         } else {
718             DnsMsgAdd_an(m, record->rr.name, record->rr.type, mdnsd->class, record->rr.ttl);
719         }
720         MdnsdCopyAnswer(m, &record->rr);
721
722         if(record->rr.ttl == 0) {
723             MdnsdRecordDone(mdnsd, record);
724         }
725     }
726     return ret;
727 }
728
729 /*!
730  * \brief Initialise the mdnsd deamon instance
731  *
732  * Create a new mdns daemon for the given class of names (usually 1) and maximum frame size
733  *
734  * \param class     Class of names
735  * \param frame     Maximum frame size
736  *
737  * \return          Newly allocated mdnsd struct instance
738  */
739 TMdnsd *MdnsdNew(int class, int frame)
740 {
741     TMdnsd *mdnsd;
742
743     mdnsd = (TMdnsd*)malloc(sizeof(TMdnsd));
744     memset(mdnsd, 0, sizeof(TMdnsd));
745
746     gettimeofday(&mdnsd->now, 0);
747     mdnsd->expireall = mdnsd->now.tv_sec + GC;
748
749     mdnsd->class = class;
750     mdnsd->frame = frame;
751
752     return mdnsd;
753 }
754
755 /*!
756  * \brief Shutdown and cleanup an MDNSD instance
757  *
758  * Gracefully shutdown the daemon, use mdnsd_out() to get the last packets
759  *
760  * \param mdnsd     MDNS deamon to shutdown
761  *
762  * \return          Newly allocated mdnsd struct instance
763  */
764 void MdnsdShutdown(TMdnsd *mdnsd)
765 {
766
767     int idx;
768     TMdnsdRecord *current;
769     TMdnsdRecord *next;
770
771     mdnsd->a_now = 0;
772
773     /* zero out ttl and push out all records */
774     for(idx = 0; idx < SPRIME; idx++) {
775         for(current = mdnsd->published[idx]; current != NULL; ) {
776             next = current->next;
777             current->rr.ttl = 0;
778             current->list = mdnsd->a_now;
779             mdnsd->a_now = current;
780             current = next;
781         }
782     }
783
784     mdnsd->shutdown = 1;
785 }
786
787
788 /*!
789  * \brief Flush all cached records (network/interface changed)
790  *
791  * \param mdnsd     MDNS deamon to flush
792  */
793 void MdnsdFlush(TMdnsd *UNUSED(mdnsd))
794 {
795     // TODO:
796     // set all querys to 0 tries
797     // free whole cache
798     // set all TMdnsdRecord *to probing
799     // reset all answer lists
800 }
801
802 /*!
803  * \brief Free given mdnsd (should have used mdnsd_shutdown() first!)
804  *
805  * \param mdnsd     MDNS deamon to free
806  */
807 void MdnsdFree(TMdnsd *mdnsd)
808 {
809     // TODO:
810     // loop through all hashes, free everything
811     // free answers if any
812     free(mdnsd);
813 }
814
815 #ifdef MDNSD_DEBUG
816 char *MdnsdDecodeType(uint16_t type)
817 {
818     switch (type) {
819         case QTYPE_A:     return "A";
820         case QTYPE_NS:    return "NS";
821         case QTYPE_CNAME: return "CNAME";
822         case QTYPE_PTR:   return "PTR";
823         case QTYPE_TXT:   return "TXT";
824         case QTYPE_SRV:   return "SRV";
825         default:          return "???";
826     }
827 }
828
829 void MdnsdDumpRessource (FILE *file, DNSRESOURCE *res)
830 {
831     fprintf (file, "%s \"%s\" = ", MdnsdDecodeType (res->type), res->name);
832
833     switch (res->type) {
834         case QTYPE_A:
835             fprintf (file, "%d.%d.%d.%d\n",
836                      (res->known.a.ip >> 24) & 0xff,
837                      (res->known.a.ip >> 16) & 0xff,
838                      (res->known.a.ip >> 8) & 0xff,
839                      (res->known.a.ip >> 0) & 0xff);
840             break;
841
842         case QTYPE_NS:
843             fprintf (file, "%s\n", res->known.ns.name);
844             break;
845
846         case QTYPE_CNAME:
847             fprintf (file, "%s\n", res->known.cname.name);
848             break;
849
850         case QTYPE_PTR:
851             fprintf (file, "%s\n", res->known.ptr.name);
852             break;
853
854         case QTYPE_SRV:
855             fprintf (file, "%s:%d\n", res->known.srv.name, res->known.srv.port);
856             break;
857
858         default:
859             fprintf (file, "???\n");
860     }
861 }
862
863 void mdnsd_dump (FILE *file, DNSMESSAGE *msg, char *type)
864 {
865     int idx;
866
867     fprintf (file, "==== %s message ====\n", type);
868
869     if ((msg->header.qr == 0) && (msg->qdcount > 0)) {
870         fprintf (file, "Questions:\n");
871         for (idx = 0; idx < msg->qdcount; idx++) {
872             fprintf (file, " %3d: %s \"%s\"?\n", idx,
873                      MdnsdDecodeType (msg->qd[idx].type), msg->qd[idx].name);
874         }
875     }
876
877     if (msg->ancount > 0) {
878         fprintf (file, "Answers:\n");
879         for (idx = 0; idx < msg->ancount; idx++) {
880             fprintf (file, " %3d: ", idx);
881             MdnsdDumpRessource (file, &msg->an[idx]);
882         }
883     }
884
885     if (msg->nscount > 0) {
886         fprintf (file, "Authority:\n");
887         for (idx = 0; idx < msg->nscount; idx++) {
888             fprintf (file, " %3d: ", idx);
889             MdnsdDumpRessource (file, &msg->ns[idx]);
890         }
891     }
892     if (msg->arcount > 0) {
893         fprintf (file, "Additional:\n");
894         for (idx = 0; idx < msg->arcount; idx++) {
895             fprintf (file, " %3d: ", idx);
896             MdnsdDumpRessource (file, &msg->ar[idx]);
897         }
898     }
899     fprintf (file, "\n");
900 }
901 #endif /* MDNSD_DEBUG */
902
903
904 /*******************************************************************************
905  *                            I/O functions                                    *
906  *******************************************************************************/
907
908 /*!
909  * \brief Process incomming messages from the host
910  *
911  * This function processes each query and sends out the matching unicast reply
912  * to each query. For each question, the potential answers are checked. Each
913  * answer is checked for potential conflicts. Each answer is processed and
914  * cached.
915  *
916  * \param mdnsd     MDNS deamon instance
917  * \param msg       incomming message
918  * \param ip        source IP
919  * \param port      source port
920  *
921  */
922
923 // TODO: SRV record: "reinhardt" is cut down to "einhardt", first character is dropped... Wrong index?
924
925 void MdnsdInput(TMdnsd *mdnsd, DNSMESSAGE *msg, struct in_addr ip, uint16_t port)
926 {
927     int qd_idx;
928     int an_idx;
929     TMdnsdRecord *record;
930     int have_match;
931     int may_conflict;
932
933     if(mdnsd->shutdown) return;
934
935     gettimeofday(&mdnsd->now,0);
936
937     if(msg->header.qr == 0) {
938         /* This message contains a query... Process the question and send out
939            our answer if needed
940          */
941
942         for(qd_idx = 0; qd_idx < msg->qdcount; qd_idx++) {
943             /* Process each query */
944             if ((msg->qd[qd_idx].class != mdnsd->class) ||
945                 ((record = RecordNext(mdnsd, 0, msg->qd[qd_idx].name, msg->qd[qd_idx].type)) == NULL)) {
946                 continue;
947             }
948
949             /* Send the matching unicast reply */
950             if (port != MDNS_PORT) {
951                 MdnsdPushUnicast(mdnsd, record, msg->id, ip, port);
952             }
953
954             for( ; record != NULL; record = RecordNext(mdnsd, record, msg->qd[qd_idx].name, msg->qd[qd_idx].type)) {
955                 /* Check all of our potential answers */
956                 have_match   = 0;
957                 may_conflict = 0;
958
959                 if (record->unique && record->unique < 5) {
960                     /* Probing state, check for conflicts */
961
962                     for(an_idx = 0; an_idx < msg->nscount; an_idx++) {
963                         /* Check all to-be answers against our own */
964                         if ((msg->an[an_idx].ttl == 0) || (msg->qd[qd_idx].type != msg->an[an_idx].type) ||
965                             (strcasecmp(msg->qd[qd_idx].name, msg->an[an_idx].name) != 0)) {
966                             continue;
967                         }
968
969                         if (!MatchAnswer(&msg->an[an_idx], &record->rr)) {
970                             /* Not matching answers may cause conflicts */
971                             may_conflict = 1;
972                         } else {
973                             have_match = 1;
974                         }
975                     }
976
977                     if (may_conflict && !have_match) {
978                         /* The answer isn't ours, we have a conflict */
979                         MdnsdCallConflict(mdnsd, record);
980                     }
981
982                     continue;
983                 }
984
985                 for(an_idx = 0; an_idx < msg->ancount; an_idx++) {
986                     /* Check the known answers for this question */
987                     if (((msg->qd[qd_idx].type != QTYPE_ANY) && (msg->qd[qd_idx].type != msg->an[an_idx].type)) ||
988                         (strcasecmp(msg->qd[qd_idx].name, msg->an[an_idx].name) != 0)) {
989                         continue;
990                     }
991
992                     if (MatchAnswer(&msg->an[an_idx], &record->rr)) {
993                         /* They already have this answer */
994                         break;
995                     }
996                 }
997
998                 if(an_idx == msg->ancount) {
999                     /* No matching answers found, send out our answer */
1000                     MdnsdSendRecord(mdnsd, record);
1001                 }
1002             }
1003         }
1004         return;
1005     }
1006
1007     for (an_idx = 0; an_idx < msg->ancount; an_idx++) {
1008         /* Process each answer, check for a conflict, and cache it */
1009         have_match = 0;
1010         may_conflict = 0;
1011
1012         record = NULL;
1013         while ((record = RecordNext(mdnsd, record, msg->an[an_idx].name, msg->an[an_idx].type)) != NULL) {
1014             if (record->unique) {
1015                 if (MatchAnswer(&msg->an[an_idx], &record->rr) == 0) {
1016                     may_conflict = 1;
1017                 } else {
1018                     have_match = 1;
1019                 }
1020             }
1021         }
1022
1023         if (may_conflict && !have_match) {
1024             while ((record = RecordNext(mdnsd, record, msg->an[an_idx].name, msg->an[an_idx].type)) != NULL) {
1025                 if ((record->unique && MatchAnswer(&msg->an[an_idx], &record->rr) == 0) && (msg->an[an_idx].ttl > 0)) {
1026                     MdnsdCallConflict(mdnsd, record);
1027                 }
1028             }
1029         }
1030
1031         MdnsdCacheAddRessource(mdnsd, &msg->an[an_idx]);
1032     }
1033 }
1034
1035 /*!
1036  * \brief Send outgoing messages to the host.
1037  *
1038  * \param mdnsd     MDNS deamon instance
1039  * \param msg       outgoing message
1040  * \param ip        destination IP
1041  * \param port      destination port
1042  *
1043  * \return          >0 if one was returned and m/ip/port set
1044  */
1045 int MdnsdOutput(TMdnsd *mdnsd, DNSMESSAGE *msg, struct in_addr *ip, uint16_t *port)
1046 {
1047     TMdnsdRecord *record;
1048     int ret = 0;
1049
1050     gettimeofday(&mdnsd->now,0);
1051     memset(msg, 0, sizeof(DNSMESSAGE));
1052
1053     /* Set multicast defaults */
1054     *port = htons(MDNS_PORT);
1055     (*ip).s_addr = inet_addr(MDNS_MULTICAST_IP);
1056     msg->header.qr = 1;
1057     msg->header.aa = 1;
1058
1059     if(mdnsd->uanswers) {
1060         /* Send out individual unicast answers */
1061         TUnicast *unicast = mdnsd->uanswers;
1062
1063         mdnsd->uanswers = unicast->next;
1064         *port = unicast->port;
1065         *ip = unicast->to;
1066         msg->id = unicast->id;
1067
1068         DnsMsgAdd_qd(msg, unicast->record->rr.name, unicast->record->rr.type, mdnsd->class);
1069         DnsMsgAdd_an(msg, unicast->record->rr.name, unicast->record->rr.type, mdnsd->class, unicast->record->rr.ttl);
1070
1071         MdnsdCopyAnswer(msg, &unicast->record->rr);
1072
1073         free(unicast);
1074         return 1;
1075     }
1076
1077     //printf("OUT: probing %p now %p pause %p publish %p\n",mdnsd->probing,mdnsd->a_now,mdnsd->a_pause,mdnsd->a_publish);
1078
1079     /* Accumulate any immediate responses */
1080     if (mdnsd->a_now) {
1081         ret += MdnsdRecordOut(mdnsd, msg, &mdnsd->a_now);
1082     }
1083
1084     if (mdnsd->a_publish && (TvDiff(mdnsd->now,mdnsd->publish) <= 0)) {
1085         /* Check to see if it's time to send the publish retries (and unlink if done) */
1086         TMdnsdRecord *next;
1087         TMdnsdRecord *current;
1088         TMdnsdRecord *last = NULL;
1089
1090         current = mdnsd->a_publish;
1091         while(current && (DnsMsgLen(msg) + GetRessourceRecordLength(&current->rr) < mdnsd->frame)) {
1092             next = current->list;
1093             ret++;
1094             current->tries++;
1095
1096             if (current->unique) {
1097                 DnsMsgAdd_an(msg, current->rr.name, current->rr.type, mdnsd->class + 32768, current->rr.ttl);
1098             } else {
1099                 DnsMsgAdd_an(msg, current->rr.name, current->rr.type, mdnsd->class, current->rr.ttl);
1100             }
1101             MdnsdCopyAnswer(msg, &current->rr);
1102
1103             if ((current->rr.ttl != 0) && (current->tries < 4)) {
1104                 last = current;
1105                 current = next;
1106                 continue;
1107             }
1108
1109             if (mdnsd->a_publish == current) {
1110                 mdnsd->a_publish = next;
1111             }
1112
1113             if (last) {
1114                 last->list = next;
1115             }
1116
1117             if (current->rr.ttl == 0) {
1118                 MdnsdRecordDone(mdnsd, current);
1119             }
1120             current = next;
1121         }
1122
1123         if (mdnsd->a_publish) {
1124             mdnsd->publish.tv_sec = mdnsd->now.tv_sec + 2;
1125             mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
1126         }
1127     }
1128
1129     /* If we're in shutdown state, we're done */
1130     if (mdnsd->shutdown) {
1131         return ret;
1132     }
1133
1134     /* Check if a_pause is ready */
1135     if (mdnsd->a_pause && (TvDiff(mdnsd->now, mdnsd->pause) <= 0)) {
1136         ret += MdnsdRecordOut(mdnsd, msg, &mdnsd->a_pause);
1137     }
1138
1139     /* Now process questions */
1140     if (ret) {
1141         return ret;
1142     }
1143
1144     msg->header.qr = 0;
1145     msg->header.aa = 0;
1146
1147     if (mdnsd->probing && (TvDiff(mdnsd->now, mdnsd->probe) <= 0)) {
1148         TMdnsdRecord *last = NULL;
1149
1150         for (record = mdnsd->probing; record != NULL; ) {
1151             /* Scan probe list to ask questions and process published */
1152             if (record->unique == 4) {
1153                 /* Done probing, publish now */
1154                 TMdnsdRecord *next = record->list;
1155
1156                 if (mdnsd->probing == record) {
1157                     mdnsd->probing = record->list;
1158                 } else {
1159                     last->list = record->list;
1160                 }
1161
1162                 record->list = NULL;
1163                 record->unique = 5;
1164
1165                 MdnsdPublishRecord(mdnsd, record);
1166
1167                 record = next;
1168                 continue;
1169             }
1170             DnsMsgAdd_qd(msg, record->rr.name, QTYPE_ANY, mdnsd->class);
1171             last = record;
1172             record = record->list;
1173         }
1174
1175         for (record = mdnsd->probing; record != NULL; last = record, record = record->list) {
1176             /* Scan probe list again to append our to-be answers */
1177             record->unique++;
1178             DnsMsgAdd_ns(msg, record->rr.name, record->rr.type, mdnsd->class, record->rr.ttl);
1179             MdnsdCopyAnswer(msg, &record->rr);
1180             ret++;
1181         }
1182
1183         if (ret) {
1184             /* Set timeout to process probes again */
1185             mdnsd->probe.tv_sec = mdnsd->now.tv_sec;
1186             mdnsd->probe.tv_usec = mdnsd->now.tv_usec + 250000;
1187             return ret;
1188         }
1189     }
1190
1191     if (mdnsd->checkqlist && (mdnsd->now.tv_sec >= mdnsd->checkqlist)) {
1192         /* Process qlist for retries or expirations */
1193         TQuery *query;
1194         TCached *cached;
1195         uint32_t nextbest = 0;
1196
1197         /* Ask questions first, track nextbest time */
1198         for(query = mdnsd->qlist; query != NULL; query = query->list) {
1199             if ((query->nexttry > 0) && (query->nexttry <= mdnsd->now.tv_sec) && (query->tries < 3)) {
1200                 DnsMsgAdd_qd(msg, query->name, query->type,mdnsd->class);
1201             } else
1202             if ((query->nexttry > 0) && ((nextbest == 0) || (query->nexttry < nextbest))) {
1203                 nextbest = query->nexttry;
1204             }
1205         }
1206
1207         /* Include known answers, update questions */
1208         for (query = mdnsd->qlist; query != NULL; query = query->list) {
1209             if ((query->nexttry == 0) || (query->nexttry > mdnsd->now.tv_sec)) {
1210                 continue;
1211             }
1212
1213             if (query->tries == 3) {
1214                 /* Done retrying, expire and reset */
1215                 MdnsdCacheExpire(mdnsd, &mdnsd->cache[NameHash(query->name) % LPRIME]);
1216                 MdnsdQueryReset(mdnsd, query);
1217                 continue;
1218             }
1219
1220             ret++;
1221             query->nexttry = mdnsd->now.tv_sec + ++query->tries;
1222
1223             if ((nextbest == 0) || (query->nexttry < nextbest)) {
1224                 nextbest = query->nexttry;
1225             }
1226
1227             /* If room, add all known good entries */
1228             cached = NULL;
1229             while (((cached = CachedNext(mdnsd, cached, query->name, query->type)) != NULL) &&
1230                    (cached->rr.ttl > mdnsd->now.tv_sec + 8) && (DnsMsgLen(msg) + GetRessourceRecordLength(&cached->rr) < mdnsd->frame)) {
1231                 DnsMsgAdd_an(msg, query->name, query->type, mdnsd->class, cached->rr.ttl - mdnsd->now.tv_sec);
1232                 MdnsdCopyAnswer(msg, &cached->rr);
1233             }
1234         }
1235
1236         mdnsd->checkqlist = nextbest;
1237     }
1238
1239     if (mdnsd->now.tv_sec > mdnsd->expireall) {
1240         MdnsdCacheGarbageCollect(mdnsd);
1241     }
1242
1243     return ret;
1244 }
1245
1246
1247 /*!
1248  * \brief Send outgoing messages to the host.
1249  *
1250  * This function returns the max wait-time until MdnsdOutput() needs to be
1251  * called again
1252  *
1253  * \param mdnsd     MDNS deamon instance
1254  *
1255  * \return          Maximum time after which MdnsdOutput needs to be called again
1256  */
1257 struct timeval *MdnsdGetMaxSleepTime(TMdnsd *mdnsd)
1258 {
1259     int sec, usec;
1260     mdnsd->sleep.tv_sec = mdnsd->sleep.tv_usec = 0;
1261
1262     /* first check for any immediate items to handle */
1263     if(mdnsd->uanswers || mdnsd->a_now) {
1264         return &mdnsd->sleep;
1265     }
1266
1267     gettimeofday(&mdnsd->now,0);
1268
1269     if(mdnsd->a_pause) {
1270         /* Check for paused answers */
1271         if ((usec = TvDiff(mdnsd->now,mdnsd->pause)) > 0) {
1272             mdnsd->sleep.tv_usec = usec;
1273         }
1274         goto out;
1275     }
1276
1277     if(mdnsd->probing) {
1278         /* Check for probe retries */
1279         if ((usec = TvDiff(mdnsd->now,mdnsd->probe)) > 0) {
1280             mdnsd->sleep.tv_usec = usec;
1281         }
1282         goto out;
1283     }
1284
1285     if(mdnsd->a_publish) {
1286         /* Check for publish retries */
1287         if ((usec = TvDiff(mdnsd->now,mdnsd->publish)) > 0) {
1288             mdnsd->sleep.tv_usec = usec;
1289         }
1290         goto out;
1291     }
1292
1293     if(mdnsd->checkqlist) {
1294         /* Also check for queries with known answer expiration/retry */
1295         if ((sec = mdnsd->checkqlist - mdnsd->now.tv_sec) > 0) {
1296             mdnsd->sleep.tv_sec = sec;
1297         }
1298         goto out;
1299     }
1300
1301     /* Last resort, next gc expiration */
1302     if ((sec = mdnsd->expireall - mdnsd->now.tv_sec) > 0) {
1303         mdnsd->sleep.tv_sec = sec;
1304     }
1305
1306 out:
1307     /* Fix up seconds ... */
1308     while (mdnsd->sleep.tv_usec > 1000000) {
1309         mdnsd->sleep.tv_sec++;
1310         mdnsd->sleep.tv_usec -= 1000000;
1311     }
1312     return &mdnsd->sleep;
1313 }
1314
1315 /*******************************************************************************
1316  *                       Query and answer functions                            *
1317  *******************************************************************************/
1318
1319 /*!
1320  * \brief Register a new query
1321  *
1322  * Answer(record, arg) is called whenever one is found/changes/expires
1323  * (immediate or anytime after, mdnsda valid until ->ttl==0)
1324  * Either answer returns -1, or another MdnsdQuery with a NULL answer will
1325  * remove/unregister this query
1326  * This function returns the max wait-time until MdnsdOutput() needs to be
1327  * called again
1328  *
1329  * \param mdnsd     MDNS deamon instance
1330  * \param host      Hostname
1331  * \param type      Query type
1332  * \param answer    Callback to the answer function, which shall be called if an
1333  *                  answer was found / changes / expires...
1334  *
1335  * \return          Maximum time after which MdnsdOutput needs to be called again
1336  */
1337 void MdnsdQuery(TMdnsd *mdnsd, char *host, int type, int (*answer)(TMdnsdAnswer *answer, void *arg), void *arg)
1338 {
1339     TQuery  *query;
1340     TCached *current = NULL;
1341     int      idx;
1342
1343     idx = NameHash(host) % SPRIME;
1344     if ((query = QueryNext(mdnsd, 0, host,type)) == NULL) {
1345         if (answer == NULL) {
1346             return;
1347         }
1348
1349         query = (TQuery *)malloc(sizeof(TQuery));
1350         memset(query, 0, sizeof(TQuery));
1351
1352         query->name = strdup(host);
1353         query->type = type;
1354         query->next = mdnsd->queries[idx];
1355         query->list = mdnsd->qlist;
1356         mdnsd->qlist = mdnsd->queries[idx] = query;
1357
1358         while ((current = CachedNext(mdnsd, current, query->name, query->type))) {
1359             /* Any cached entries should be associated */
1360             current->query = query;
1361         }
1362
1363         MdnsdQueryReset(mdnsd, query);
1364
1365         /* New questin, immediately send out */
1366         query->nexttry = mdnsd->checkqlist = mdnsd->now.tv_sec;
1367     }
1368
1369     if (answer == NULL) {
1370         /* no answer means we don't care anymore */
1371         MdnsdQueryDone(mdnsd, query);
1372         return;
1373     }
1374
1375     query->answer = answer;
1376     query->arg = arg;
1377 }
1378
1379 /*!
1380  * \brief Get the next answer from the cache
1381  *
1382  * Returns the first (if last == NULL) or next answer after last from the cache
1383  *
1384  * \param mdnsd     MDNS deamon instance
1385  * \param host      Hostname
1386  * \param type      Query type
1387  * \param last      Last answer
1388  *
1389  * \return          next cached answer
1390  */
1391 TMdnsdAnswer *MdnsdListCachedAnswers(TMdnsd *mdnsd, char *host, int type, TMdnsdAnswer *last)
1392 {
1393     return (TMdnsdAnswer *)CachedNext(mdnsd, (TCached *)last, host, type);
1394 }
1395
1396 /*******************************************************************************
1397  *                        Publishing functions                                 *
1398  *******************************************************************************/
1399
1400 /*!
1401  * \brief Create a new shared record
1402  *
1403  * Returns the newly allocated record struct
1404  *
1405  * \param mdnsd     MDNS deamon instance
1406  * \param host      Hostname
1407  * \param type      Query type
1408  * \param ttl       Time to live value
1409  *
1410  * \return          newly allocated share record
1411  */
1412 TMdnsdRecord *MdnsdAllocShared(TMdnsd *mdnsd, char *host, int type, uint32_t ttl)
1413 {
1414     int idx;
1415     TMdnsdRecord *record;
1416
1417     idx = NameHash(host) % SPRIME;
1418
1419     record = (TMdnsdRecord *)malloc(sizeof(TMdnsdRecord));
1420     memset(record, 0, sizeof(TMdnsdRecord));
1421
1422     record->rr.name = strdup(host);
1423     record->rr.type = type;
1424     record->rr.ttl = ttl;
1425     record->next = mdnsd->published[idx];
1426
1427     mdnsd->published[idx] = record;
1428
1429     return record;
1430 }
1431
1432
1433 /*!
1434  * \brief Create a new unique record
1435  *
1436  * Create a new unique record (try MdnsdListCachedAnswers first to make sure
1437  * it's not used)
1438  *
1439  * The conflict callback will be called at any point when one is detected and
1440  * is unable to recover.
1441  *
1442  * After the first data is set by MdnsdSet*(), any future changes effectively
1443  * expire the old one and attempt to create a new unique record.
1444  *
1445  * \param mdnsd     MDNS deamon instance
1446  * \param host      Hostname
1447  * \param type      Query type
1448  * \param ttl       Time to live value
1449  * \param conflict  Callback function called in case of a conflict
1450  * \param arg       Argument passed to the conflict callback
1451  *
1452  * \return          newly allocated share record
1453  */
1454 TMdnsdRecord *MdnsdAllocUnique(TMdnsd *mdnsd, char *host, int type, uint32_t ttl, void (*conflict)(TMdnsdRecord *record, char *host, int type, void *arg), void *arg)
1455 {
1456     TMdnsdRecord *record;
1457     record = MdnsdAllocShared(mdnsd, host, type, ttl);
1458     record->conflict = conflict;
1459     record->arg = arg;
1460     record->unique = 1;
1461     MdnsdPushRecord(&mdnsd->probing, record);
1462     mdnsd->probe.tv_sec = mdnsd->now.tv_sec;
1463     mdnsd->probe.tv_usec = mdnsd->now.tv_usec;
1464     return record;
1465 }
1466
1467
1468 /*!
1469  * \brief Remove record from the list and clean up
1470  *
1471  * \param mdnsd     MDNS deamon instance
1472  * \param record    The record which shall be de-listed
1473  *
1474  * \return          newly allocated share record
1475  */
1476 void MdnsdDone(TMdnsd *mdnsd, TMdnsdRecord *record)
1477 {
1478     TMdnsdRecord *cur;
1479     if(record->unique && record->unique < 5)
1480     { // probing yet, zap from that list first!
1481         if(mdnsd->probing == record) {
1482             mdnsd->probing = record->list;
1483         } else {
1484             for (cur=mdnsd->probing; cur->list != record; cur = cur->list);
1485             cur->list = record->list;
1486         }
1487         MdnsdRecordDone(mdnsd, record);
1488         return;
1489     }
1490     record->rr.ttl = 0;
1491     MdnsdSendRecord(mdnsd, record);
1492 }
1493
1494
1495 /*!
1496  * \brief Set/update raw data of the record and call publish
1497  *
1498  * \param mdnsd     MDNS deamon instance
1499  * \param record    The record which shall be de-listed
1500  * \param data      Raw record data
1501  * \param len       Datalength
1502  */
1503 void MdnsdSetRaw(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *data, int len)
1504 {
1505     free(record->rr.rdata);
1506     record->rr.rdata = (uint8_t *)malloc(len);
1507     memcpy(record->rr.rdata,data,len);
1508     record->rr.rdlen = len;
1509     MdnsdPublishRecord(mdnsd, record);
1510 }
1511
1512
1513 /*!
1514  * \brief Set/update record host entry and call publish
1515  *
1516  * \param mdnsd     MDNS deamon instance
1517  * \param record    The record which shall be de-listed
1518  * \param name      Hostname
1519  */
1520 void MdnsdSetHost(TMdnsd *mdnsd, TMdnsdRecord *record, char *name)
1521 {
1522     free(record->rr.rdname);
1523     record->rr.rdname = strdup(name);
1524     MdnsdPublishRecord(mdnsd, record);
1525 }
1526
1527
1528 /*!
1529  * \brief Set/update IP address entry and call publish
1530  *
1531  * \param mdnsd     MDNS deamon instance
1532  * \param record    The record which shall be de-listed
1533  * \param ip        IP address
1534  */
1535 void MdnsdSetIp(TMdnsd *mdnsd, TMdnsdRecord *record, struct in_addr ip)
1536 {
1537     record->rr.ip = ip;
1538     MdnsdPublishRecord(mdnsd, record);
1539 }
1540
1541
1542 /*!
1543  * \brief Set/update service info and call publish
1544  *
1545  * \param mdnsd     MDNS deamon instance
1546  * \param record    The record which shall be de-listed
1547  * \param priority  Priority of the target host: lower value means more preferred.
1548  * \param weight    Relative weight for records with the same priority.
1549  * \param port      TCP / UDP port number of the service
1550  * \param name      The canonical hostname of the machine providing the service.
1551  */
1552 void MdnsdSetSrv(TMdnsd *mdnsd, TMdnsdRecord *record, int priority, int weight, uint16_t port, char *name)
1553 {
1554     record->rr.srv.priority = priority;
1555     record->rr.srv.weight = weight;
1556     record->rr.srv.port = port;
1557     MdnsdSetHost(mdnsd, record, name);
1558 }
1559
1560 /*@}*/