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