]> git.karo-electronics.de Git - mdnsd.git/commitdiff
Complete cleanup and rewrite. Added lots of documentation and fixed some
authorOle Reinhardt <ole.reinhardt@kernelconcepts.de>
Sun, 28 Apr 2013 23:48:55 +0000 (01:48 +0200)
committerOle Reinhardt <ole.reinhardt@kernelconcepts.de>
Sun, 28 Apr 2013 23:48:55 +0000 (01:48 +0200)
minor bugs.

13 files changed:
Makefile
dns_sd_txt.c
dns_sd_txt.h
mdnsd.c
mdnsd.h
mhttp.c
mquery.c
netwatch.c [changed mode: 0755->0644]
netwatch.h [changed mode: 0755->0644]
rfc1035.c
rfc1035.h
shash.c
shash.h

index 735aed2132a6d10519fae202c5bc39dce88a5f8f..07f3d94d1770027f367852586a4b57e1112ddd5a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
-all: mhttp mquery netwatch
+all: mhttp mquery
 
-mhttp: mhttp.c mdnsd.c 1035.c sdtxt.c xht.c
-       $(CC) -Wall -Wno-pointer-sign -g -o mhttp mhttp.c mdnsd.c 1035.c sdtxt.c xht.c netwatch.c
+mhttp: mhttp.c mdnsd.c rfc1035.c dns_sd_txt.c shash.c
+       $(CC) -D_GNU_SOURCE -Wall -Wextra -Wno-pointer-sign -g -o mhttp mhttp.c mdnsd.c rfc1035.c dns_sd_txt.c shash.c
 
-mquery: mquery.c mdnsd.c 1035.c
-       $(CC) -Wall -Wno-pointer-sign -g -o mquery mquery.c mdnsd.c 1035.c sdtxt.c xht.c
+mquery: mquery.c mdnsd.c rfc1035.c dns_sd_txt.c shash.c
+       $(CC) -D_GNU_SOURCE -Wall -Wextra -Wno-pointer-sign -g -o mquery mquery.c mdnsd.c rfc1035.c dns_sd_txt.c shash.c
 
 netwatch: netwatch.c
-       $(CC) -Wall -DNETWATCH_MAIN -g -o $@ $<
+       $(CC) -D_GNU_SOURCE -Wall -Wextra -DNETWATCH_MAIN -g -o $@ $<
 
 clean:
        rm -f mquery mhttp netwatch
index 8180d14af39e442ad60d4123baac2eb7643f8da0..79082665e1e28972b511e473894c70af1a78590e 100644 (file)
+/*
+ * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+
+/*!
+ * \file pro/dns_sd_txt.c
+ * \brief Helper functions to hadle the DNS-SD (Multicast DNS) TXT record format
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
 #include <stdlib.h>
 #include <string.h>
-#include "sdtxt.h"
+#include "dns_sd_txt.h"
+#include "mdnsd.h"
+#include "shash.h"
 
-// the universe is bound in equal parts by arrogance and altruism, any attempt to alter this would be suicide
+/*!
+ * \addtogroup xgMulticastDns
+ */
+/*@{*/
 
-int _sd2txt_len(const char *key, char *val)
+
+/*!
+ * \brief Calculate the length of one key=value pair
+ *
+ * \param key   key name
+ * \param val   hashed value string
+ */
+static int DnsSd2TxtLen(const char *key, char *val)
 {
     int ret = strlen(key);
-    if(!*val) return ret;
+
+    if(*val == 0) {
+        return ret;
+    }
+
     ret += strlen(val);
     ret++;
     return ret;
 }
 
-void _sd2txt_count(xht h, const char *key, void *val, void *arg)
+
+/*!
+ * \brief Count the length of hashedd key=value pairs
+ *
+ * Callback routine called by SHashForEach() in DnsSd2Txt()
+ *
+ * \param hash  The hash table
+ * \param key   key name
+ * \param val   hashed value string
+ * \param arg   pointer to the count variable
+ */
+static void DnsSd2TxtCount_CB(SHASH UNUSED(hash), const char *key, void *val, void *arg)
 {
     int *count = (int*)arg;
-    *count += _sd2txt_len(key,(char*)val) + 1;
+    *count += DnsSd2TxtLen(key, (char*)val) + 1;
 }
 
-void _sd2txt_write(xht h, const char *key, void *val, void *arg)
+
+/*!
+ * \brief Append hashed key=value pairs to the text buffer
+ *
+ * Callback routine called by SHashForEach() in DnsSd2Txt()
+ *
+ * \param hash  The hash table
+ * \param key   key name
+ * \param val   hashed value string
+ * \param arg   pointer to the text buffer, where we want to append the value of the hashed entry to
+ */
+static void DnsSd2TxtWrite_CB(SHASH UNUSED(hash), const char *key, void *val, void *arg)
 {
     unsigned char **txtp = (unsigned char **)arg;
     char *cval = (char*)val;
 
-    // copy in lengths, then strings
-    **txtp = _sd2txt_len(key,(char*)val);
+    /* Copy the length... */
+    **txtp = DnsSd2TxtLen(key, (char*)val);
     (*txtp)++;
-    memcpy(*txtp,key,strlen(key));
+
+    memcpy(*txtp, key, strlen(key));
     *txtp += strlen(key);
-    if(!*cval) return;
+
+    if(*cval == 0) {
+        return;
+    }
+
+    /* ...and then the strings. */
     **txtp = '=';
     (*txtp)++;
-    memcpy(*txtp,cval,strlen(cval));
+
+    memcpy(*txtp, cval, strlen(cval));
     *txtp += strlen(cval);
 }
 
-unsigned char *sd2txt(xht h, int *len)
+
+/*!
+ * \brief Returns a raw block of data that can be send with a SD TXT record and also
+          sets length.
+ *
+ * \param hash  The hash table from which the SD TXT record data shall be generated from
+ * \param len   Call by reference: Returns the length of the raw data record
+ *
+ * \return      The newly allocated and filled hash table
+ */
+unsigned char *DnsSd2Txt(SHASH hash, int *len)
 {
     unsigned char *buf, *raw;
     *len = 0;
 
-    xht_walk(h,_sd2txt_count,(void*)len);
-    if(!*len)
-    {
+    SHashForEach(hash, DnsSd2TxtCount_CB, (void*)len);
+    if(*len == 0) {
         *len = 1;
         buf = (unsigned char *)malloc(1);
         *buf = 0;
         return buf;
     }
-    raw = buf = (unsigned char *)malloc(*len);
-    xht_walk(h,_sd2txt_write,&buf);
+    buf = (unsigned char *)malloc(*len);
+    raw = buf;
+
+    SHashForEach(hash, DnsSd2TxtWrite_CB, &buf);
     return raw;
 }
 
-xht txt2sd(unsigned char *txt, int len)
+
+/*!
+ * \brief Returns a hashtable of strings of the raw SD TXT record rdata
+ *
+ * \param txt   The SD TXT record data
+ * \param len   Length of the data
+ *
+ * \return      The newly allocated and filled hash table
+ */
+SHASH DnsTxt2Sd(unsigned char *txt, int len)
 {
     char key[256], *val;
-    xht h = 0;
+    SHASH hash = NULL;
 
-    if(txt == 0 || len == 0 || *txt == 0) return 0;
-    h = xht_new(23);
+    if ((txt == 0) || (len == 0) || (*txt == 0)) {
+        return 0;
+    }
 
-    // loop through data breaking out each block, storing into hashtable
-    for(;*txt <= len && len > 0; len -= *txt, txt += *txt + 1)
-    {
-        if(*txt == 0) break;
-        memcpy(key,txt+1,*txt);
+    hash = SHashInit(23);
+
+    /* Loop through the data, break out each block and store it into the hash table */
+    for(; (*txt <= len) && (len > 0); len -= *txt, txt += *txt + 1) {
+        if(*txt == 0) {
+            break;
+        }
+        memcpy(key, txt+1, *txt);
         key[*txt] = 0;
-        if((val = strchr(key,'=')) != 0)
-        {
+
+        val = strchr(key, '=');
+        if (val != NULL) {
             *val = 0;
             val++;
         }
-        xht_store(h, key, strlen(key), val, val ? strlen(val) : 0);
+
+        SHashStore(hash, key, strlen(key), val, val ? strlen(val) : 0);
     }
-    return h;
+    return hash;
 }
+
+/*@}*/
index 3d519b9e312b3c92ef94d3c8a3895067def14dbe..566a098e1c04a379d94de9f3f6c6f6f67407ceda 100644 (file)
@@ -1,11 +1,85 @@
-#ifndef sdtxt_h
-#define sdtxt_h
-#include "xht.h"
+#ifndef _DNS_SD_TXT_H_
+#define _DNS_SD_TXT_H_
 
-// returns hashtable of strings from the SD TXT record rdata
-xht txt2sd(unsigned char *txt, int len);
+/*
+ * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
 
-// returns a raw block that can be sent with a SD TXT record, sets length
-unsigned char *sd2txt(xht h, int *len);
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file include/pro/dns_sd_txt.h
+ * \brief Helper functions to handle the DNS-SD (Multicast DNS) TXT record format
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
+#include "shash.h"
+
+unsigned char *DnsSd2Txt(SHASH hash, int *len);
+SHASH DnsTxt2Sd(unsigned char *txt, int len);
 
 #endif
diff --git a/mdnsd.c b/mdnsd.c
index e640a6dac389eefee82307dd2702d992e65ca2a8..a04a98480414ead60309a2bd30b329396e1cc848 100644 (file)
--- a/mdnsd.c
+++ b/mdnsd.c
+/*
+ * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
+ * Copyright (c) 2009 Simon Budig <simon@budig.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file pro/mdnsd.c
+ * \brief Multicast DNS Deamon
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
 #include <string.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <arpa/inet.h>
-#include "mdnsd.h"
 #include <stdio.h>
+#include "mdnsd.h"
 
-// size of query/publish hashes
-#define SPRIME 108
-// size of cache hash
-#define LPRIME 1009
-// brute force garbage cleanup frequency, rarely needed (daily default)
-#define GC 86400
-
-/* messy, but it's the best/simplest balance I can find at the moment
-Some internal data types, and a few hashes: querys, answers, cached, and records (published, unique and shared)
-Each type has different semantics for processing, both for timeouts, incoming, and outgoing I/O
-They inter-relate too, like records affect the querys they are relevant to
-Nice things about MDNS: we only publish once (and then ask asked), and only query once, then just expire records we've got cached
-*/
-
-struct query
-{
-    char *name;
-    int type;
-    unsigned long int nexttry;
-    int tries;
-    int (*answer)(mdnsda, void *);
-    void *arg;
-    struct query *next, *list;
-};
-
-struct unicast
-{
-    int id;
-    unsigned long int to;
-    unsigned short int port;
-    mdnsdr r;
-    struct unicast *next;
-};
-
-struct cached
-{
-    struct mdnsda_struct rr;
-    struct query *q;
-    struct cached *next;
-};
-
-struct mdnsdr_struct
-{
-    struct mdnsda_struct rr;
-    char unique; // # of checks performed to ensure
-    int tries;
-    void (*conflict)(mdnsdr r, char *, int, void *);
-    void *arg;
-    struct mdnsdr_struct *next, *list;
-};
-
-struct mdnsd_struct
-{
-    char shutdown;
-    unsigned long int expireall, checkqlist;
-    struct timeval now, sleep, pause, probe, publish;
-    int class, frame;
-    struct cached *cache[LPRIME];
-    struct mdnsdr_struct *published[SPRIME], *probing, *a_now, *a_pause, *a_publish;
-    struct unicast *uanswers;
-    struct query *queries[SPRIME], *qlist;
-};
-
-int _namehash(const char *s)
-{
-    const unsigned char *name = (const unsigned char *)s;
-    unsigned long h = 0, g;
-
-    while (*name)
-    { /* do some fancy bitwanking on the string */
-        h = (h << 4) + (unsigned long)(tolower (*name++));
-        if ((g = (h & 0xF0000000UL))!=0)
-            h ^= (g >> 24);
-        h &= ~g;
-    }
-
-    return (int)h;
-}
-
-// basic linked list and hash primitives
-struct query *_q_next(mdnsd d, struct query *q, char *host, int type)
-{
-    if(q == 0) q = d->queries[_namehash(host) % SPRIME];
-    else q = q->next;
-    for(;q != 0; q = q->next)
-        if((q->type == QTYPE_ANY || q->type == type) && strcasecmp(q->name, host) == 0)
-            return q;
-    return 0;
+/*!
+ * \addtogroup xgMulticastDns
+ */
+/*@{*/
+
+#ifdef NUTDEBUG
+##define MDNSD_DEBUG
+#endif
+
+/*
+ * Messy, but it's the best/simplest balance I can find at the moment
+ *
+ * Some internal data types, and a few hashes:
+ * - querys
+ * - answers
+ * - cached
+ * - records (published, unique and shared)
+ *
+ * Each type has different semantics for processing, both for timeouts,
+ * incoming, and outgoing I/O.
+ *
+ * They inter-relate too, like records affect the querys they are relevant to.
+ *
+ * Nice things about MDNS: we only publish once (and then ask asked),
+ * and only query once, then just expire records we've got cached
+ */
+
+/*!
+ * \brief Generates a hash code for a string.
+ *
+ * This function uses the ELF hashing algorithm as reprinted in
+ * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
+ *
+ * \param s     The string to hash
+ *
+ * \return      The calculated hash value
+ */
+
+static int NameHash(const int8_t *str)
+{
+    const uint8_t *name = (const uint8_t *)str;
+    uint32_t hash = 0;
+    uint32_t g;
+
+    while (*name) {
+        /* do some fancy bitwanking on the string */
+        hash = (hash << 4) + (unsigned long)(tolower (*name++));
+        if ((g = (hash & 0xF0000000UL))!=0)
+            hash ^= (g >> 24);
+        hash &= ~g;
+    }
+
+    return (int)hash;
 }
-struct cached *_c_next(mdnsd d, struct cached *c, char *host, int type)
+
+/*!
+ * \brief Get the next matching query in the hash list
+ *
+ * Basic hash and linked list primatives for Query hash.
+ * Iterate through the given query list and search for the given host.
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param query     The query list head entry to start searching from or NULL
+ *                  if a new search shall be started
+ * \param host      host name to search for
+ * \param type      query type
+ *
+ * \return          The next matching query or NULL
+ */
+static TQuery *QueryNext(TMdnsd *mdnsd, TQuery *query, int8_t *host, int type)
 {
-    if(c == 0) c = d->cache[_namehash(host) % LPRIME];
-    else c = c->next;
-    for(;c != 0; c = c->next)
-        if((type == c->rr.type || type == QTYPE_ANY) && strcasecmp(c->rr.name, host) == 0)
-            return c;
-    return 0;
+    if (query == NULL) {
+        query = mdnsd->queries[NameHash(host) % SPRIME];
+    } else {
+        query = query->next;
+    }
+
+    for( ; query != NULL; query = query->next) {
+        if (((query->type == QTYPE_ANY) || (query->type == type)) &&
+            (strcasecmp(query->name, host) == 0)) {
+            return query;
+        }
+    }
+
+    return NULL;
 }
-mdnsdr _r_next(mdnsd d, mdnsdr r, char *host, int type)
+
+/*!
+ * \brief Get the next matching cache entry in the hash list
+ *
+ * Basic hash and linked list primatives for cache hash.
+ * Iterate through the given cache list and search for the given host.
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param cached    The cache list head entry to start searching from or NULL
+ *                  if a new search shall be started
+ * \param host      host name to search for
+ * \param type      query type
+ *
+ * \return          The next matching cache entry or NULL
+ */
+static TCached *CachedNext(TMdnsd *mdnsd, TCached *cached, uint8_t *host, int type)
 {
-    if(r == 0) r = d->published[_namehash(host) % SPRIME];
-    else r = r->next;
-    for(;r != 0; r = r->next)
-        if((type == r->rr.type || type == QTYPE_ANY) && strcasecmp(r->rr.name, host) == 0)
-            return r;
-    return 0;
+    if (cached == NULL) {
+        cached = mdnsd->cache[NameHash(host) % LPRIME];
+    } else {
+        cached = cached->next;
+    }
+
+    for( ; cached != NULL; cached = cached->next) {
+        if (((type == cached->rr.type) || (type == QTYPE_ANY)) &&
+            (strcasecmp(cached->rr.name, host) == 0)) {
+            return cached;
+        }
+    }
+    return NULL;
 }
 
-int _rr_len(mdnsda rr)
+/*!
+ * \brief Get the next matching dns record in the published hash list
+ *
+ * Basic hash and linked list primatives for published dns record hash.
+ * Iterate through the given dns record list and search for the given host.
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param cached    The dns record list head entry to start searching from or NULL
+ *                  if a new search shall be started
+ * \param host      host name to search for
+ * \param type      query type
+ *
+ * \return          The next matching dns record or NULL
+ */
+static TMdnsdRecord *RecordNext(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *host, int type)
 {
-    int len = 12; // name is always compressed (dup of earlier), plus normal stuff
-    if(rr->rdata) len += rr->rdlen;
-    if(rr->rdname) len += strlen(rr->rdname); // worst case
-    if(rr->ip) len += 4;
-    if(rr->type == QTYPE_PTR) len += 6; // srv record stuff
+    if (record == NULL) {
+        record = mdnsd->published[NameHash(host) % SPRIME];
+    } else {
+        record = record->next;
+    }
+
+    for( ; record != NULL; record = record->next) {
+        if (((type == record->rr.type) || (type == QTYPE_ANY)) &&
+            (strcasecmp(record->rr.name, host) == 0)) {
+            return record;
+        }
+    }
+    return NULL;
+}
+
+/*!
+ * \brief Get the length of the given ressource record
+ *
+ * \param rr        Ressource record buffer to calculate the length for
+ *
+ * \return          calculated length of the ressource record
+ */
+static int GetRessourceRecordLength(TMdnsdAnswer *rr)
+{
+    int len;
+
+    /* Initialise the length: Name is always compressed (dup of earlier occurence)
+       plus further normal stuff.
+    */
+    len = 12;
+
+    if (rr->rdata)  len += rr->rdlen;
+    if (rr->rdname) len += strlen(rr->rdname); /* add worst case */
+    if (rr->ip.s_addr) len += 4;
+    if (rr->type == QTYPE_PTR) len += 6; /* Add srv record length */
+
     return len;
 }
 
-int _a_match(struct resource *r, mdnsda a)
-{ // compares new rdata with known a, painfully
-    if(strcasecmp(r->name,a->name) || (r->type != QTYPE_ANY && r->type != a->type)) return 0;
-    if(!strcasecmp(r->name,a->name) && r->type == QTYPE_ANY) return 1;
-    if(r->type == QTYPE_SRV && !strcasecmp(r->known.srv.name,a->rdname) && a->srv.port == r->known.srv.port && a->srv.weight == r->known.srv.weight && a->srv.priority == r->known.srv.priority) return 1;
-    if((r->type == QTYPE_PTR || r->type == QTYPE_NS || r->type == QTYPE_CNAME) && !strcasecmp(a->rdname,r->known.ns.name)) return 1;
-    if(r->rdlength == a->rdlen && !memcmp(r->rdata,a->rdata,r->rdlength)) return 1;
+
+/*!
+ * \brief Compare the ressource data with a given answer record
+ *
+ * This is a painfull compare, with lots of needed comparisions... computing intensive
+ *
+ * \param res       ressource data to compare with the given answer
+ * \param answer    Answer record to compare with
+ *
+ * \return          1 in case of a math or 0 if no match found
+ */
+static int MatchAnswer(DNSRESOURCE *res, TMdnsdAnswer *answer)
+{
+    /* Check if the name and ressource type is matching */
+    if ((strcasecmp(res->name, answer->name) != 0) ||
+        ((res->type != QTYPE_ANY) && (res->type != answer->type))) {
+        /* no match ... */
+        return 0;
+    }
+
+    /* If name matches, and ressource type is QTYPE_ANY we found a match */
+    if ((res->type == QTYPE_ANY) && (strcasecmp(res->name, answer->name) == 0)) {
+        return 1;
+    }
+
+    /* Special checks for SRV type ressource data */
+    if ((res->type == QTYPE_SRV) && (strcasecmp(res->known.srv.name, answer->rdname) == 0) &&
+        (answer->srv.port == res->known.srv.port) &&
+        (answer->srv.weight == res->known.srv.weight) &&
+        (answer->srv.priority == res->known.srv.priority)) {
+        return 1;
+    }
+
+    /* Check for PTR, NS or CNAME type ressource data */
+    if (((res->type == QTYPE_PTR) || (res->type == QTYPE_NS) || (res->type == QTYPE_CNAME)) &&
+        (strcasecmp(answer->rdname, res->known.ns.name) == 0)) {
+        return 1;
+    }
+
+
+    if ((res->rdlength == answer->rdlen) && (memcmp(res->rdata, answer->rdata, res->rdlength) == 0)) {
+        return 1;
+    }
+
     return 0;
 }
 
-// compare time values easily
-int _tvdiff(struct timeval old, struct timeval new)
+/*!
+ * \brief Calculate time elapsed between a and b
+ *
+ * Compares two timeval values
+ *
+ * \param a         older timeval value
+ * \param b         newer timeval value
+ *
+ * \return          Elapsed time in Âµs
+ */
+static int TvDiff(struct timeval a, struct timeval b)
 {
     int udiff = 0;
-    if(old.tv_sec != new.tv_sec) udiff = (new.tv_sec - old.tv_sec) * 1000000;
-    return (new.tv_usec - old.tv_usec) + udiff;
+
+    if (a.tv_sec != b.tv_sec) {
+        udiff = (b.tv_sec - a.tv_sec) * 1000000;
+    }
+
+    return (b.tv_usec - a.tv_usec) + udiff;
 }
 
-// make sure not already on the list, then insert
-void _r_push(mdnsdr *list, mdnsdr r)
+/*!
+ * \brief create generic unicast response struct
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param record    Record to push
+ * \param id        unicast record id
+ */
+static void MdnsdPushUnicast(TMdnsd *mdnsd, TMdnsdRecord *record, int id, struct in_addr to, uint16_t port)
 {
-    mdnsdr cur;
-    for(cur = *list; cur != 0; cur = cur->list)
-        if(cur == r) return;
-    r->list = *list;
-    *list = r;
+    TUnicast *unicast;
+
+    unicast = (TUnicast *)malloc(sizeof(TUnicast));
+    memset(unicast, 0, sizeof(TUnicast));
+
+    unicast->record = record;
+    unicast->id = id;
+    unicast->to = to;
+    unicast->port = port;
+    unicast->next = mdnsd->uanswers;
+
+    mdnsd->uanswers = unicast;
 }
 
-// set this r to probing, set next probe time
-void _r_probe(mdnsd d, mdnsdr r)
+/*!
+ * \brief Insert a record to the list if not yet inserted
+ *
+ * \param list      Linked record list head
+ * \param record    Record to insert
+ */
+static void MdnsdPushRecord(TMdnsdRecord **list, TMdnsdRecord *record)
 {
+    TMdnsdRecord *current;
+
+    for(current = *list; current != NULL; current = current->list) {
+        if(current == record) {
+            return;
+        }
+    }
+
+    record->list = *list;
+    *list = record;
 }
 
-// force any r out right away, if valid
-void _r_publish(mdnsd d, mdnsdr r)
+/*!
+ * \brief Publish a record if valid
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param record    Record to publish
+ */
+static void MdnsdPublishRecord(TMdnsd *mdnsd, TMdnsdRecord *record)
 {
-    if(r->unique && r->unique < 5) return; // probing already
-    r->tries = 0;
-    d->publish.tv_sec = d->now.tv_sec; d->publish.tv_usec = d->now.tv_usec;
-    _r_push(&d->a_publish,r);
+    if (record->unique && (record->unique < 5)) {
+        /* probing already */
+        return;
+    }
+
+    record->tries = 0;
+    mdnsd->publish.tv_sec = mdnsd->now.tv_sec;
+    mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
+
+    MdnsdPushRecord(&mdnsd->a_publish, record);
 }
 
-// send r out asap
-void _r_send(mdnsd d, mdnsdr r)
+/*!
+ * \brief Send out a record as soon as possible
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param record    Record to send
+ */
+static void MdnsdSendRecord(TMdnsd *mdnsd, TMdnsdRecord *record)
 {
-    if(r->tries < 4)
-    { // being published, make sure that happens soon
-        d->publish.tv_sec = d->now.tv_sec; d->publish.tv_usec = d->now.tv_usec;
+    if (record->tries < 4) {
+        /* The record has been published, speed up the things... */
+        mdnsd->publish.tv_sec = mdnsd->now.tv_sec;
+        mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
         return;
     }
-    if(r->unique)
-    { // known unique ones can be sent asap
-        _r_push(&d->a_now,r);
+
+    if (record->unique) {
+        /* Unique records can be sent ASAP */
+        MdnsdPushRecord(&mdnsd->a_now, record);
         return;
     }
-    // set d->pause.tv_usec to random 20-120 msec
-    d->pause.tv_sec = d->now.tv_sec;
-    d->pause.tv_usec = d->now.tv_usec + ((d->now.tv_usec % 101) + 20) * 1000;
-    if (d->pause.tv_usec >= 1000000)
-    {
-        d->pause.tv_sec++;
-        d->pause.tv_usec -= 1000000;
+// TODO: better random, do not use modulo
+    /* set mdnsd->pause.tv_usec to random 20-120 msec */
+    mdnsd->pause.tv_sec = mdnsd->now.tv_sec;
+    mdnsd->pause.tv_usec = mdnsd->now.tv_usec + ((mdnsd->now.tv_usec % 101) + 20) * 1000;
+    if (mdnsd->pause.tv_usec >= 1000000) {
+        mdnsd->pause.tv_sec++;
+        mdnsd->pause.tv_usec -= 1000000;
     }
-    _r_push(&d->a_pause,r);
+
+    /* And push the record... */
+    MdnsdPushRecord(&mdnsd->a_pause, record);
 }
-
-// create generic unicast response struct
-void _u_push(mdnsd d, mdnsdr r, int id, unsigned long int to, unsigned short int port)
+
+/*!
+ * \brief Clean up record
+ *
+ * Remove from hash and free allocated memory
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param record    Record to clean up
+ */
+static void MdnsdRecordDone(TMdnsd *mdnsd, TMdnsdRecord *record)
 {
-    struct unicast *u;
-    u = (struct unicast *)malloc(sizeof(struct unicast));
-    bzero(u,sizeof(struct unicast));
-    u->r = r;
-    u->id = id;
-    u->to = to;
-    u->port = port;
-    u->next = d->uanswers;
-    d->uanswers = u;
+    TMdnsdRecord *current = NULL;
+    int idx = NameHash(record->rr.name) % SPRIME;
+
+    if(mdnsd->published[idx] == record) {
+        mdnsd->published[idx] = record->next;
+    } else {
+        for (current = mdnsd->published[idx]; (current != NULL) && (current->next != record); current = current->next);
+
+        if (current) {
+            current->next = record->next;
+        }
+    }
+    free(record->rr.name);
+    free(record->rr.rdata);
+    free(record->rr.rdname);
+    free(record);
 }
 
-void _q_reset(mdnsd d, struct query *q)
+/*!
+ * \brief Reset query
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param query     Query to reset
+ */
+static void MdnsdQueryReset(TMdnsd *mdnsd, TQuery *query)
 {
-    struct cached *cur = 0;
-    q->nexttry = 0;
-    q->tries = 0;
-    while((cur = _c_next(d,cur,q->name,q->type)))
-        if(q->nexttry == 0 || cur->rr.ttl - 7 < q->nexttry) q->nexttry = cur->rr.ttl - 7;
-    if(q->nexttry != 0 && q->nexttry < d->checkqlist) d->checkqlist = q->nexttry;
-}
+    TCached *current = NULL;
+
+    query->nexttry = 0;
+    query->tries = 0;
 
-void _q_done(mdnsd d, struct query *q)
-{ // no more query, update all it's cached entries, remove from lists
-    struct cached *c = 0;
-    struct query *cur;
-    int i = _namehash(q->name) % SPRIME;
-    while((c = _c_next(d,c,q->name,q->type))) c->q = 0;
-    if(d->qlist == q) d->qlist = q->list;
-    else {
-        for(cur=d->qlist;cur->list != q;cur = cur->list);
-        cur->list = q->list;
+    while ((current = CachedNext(mdnsd, current, query->name, query->type))) {
+        if ((query->nexttry == 0 ) || (current->rr.ttl - 7 < query->nexttry)) {
+            query->nexttry = current->rr.ttl - 7;
+        }
     }
-    if(d->queries[i] == q) d->queries[i] = q->next;
-    else {
-        for(cur=d->queries[i];cur->next != q;cur = cur->next);
-        cur->next = q->next;
+
+    if ((query->nexttry != 0) && (query->nexttry < mdnsd->checkqlist)) {
+        mdnsd->checkqlist = query->nexttry;
     }
-    free(q->name);
-    free(q);
 }
 
-void _r_done(mdnsd d, mdnsdr r)
-{ // buh-bye, remove from hash and free
-    mdnsdr cur = 0;
-    int i = _namehash(r->rr.name) % SPRIME;
-    if(d->published[i] == r) d->published[i] = r->next;
-    else {
-        for(cur=d->published[i];cur && cur->next != r;cur = cur->next);
-        if(cur) cur->next = r->next;
+/*!
+ * \brief Clean up query
+ *
+ * Update all its cached entries and remove it from list, and free allocated memory
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param query     Query to clean up
+ */
+static void MdnsdQueryDone(TMdnsd *mdnsd, TQuery *query)
+{
+    TCached *cached = NULL;
+    TQuery  *current;
+    int      idx;
+
+    idx = NameHash(query->name) % SPRIME;
+
+    while ((cached = CachedNext(mdnsd, cached, query->name, query->type))) {
+        cached->query = NULL;
     }
-    free(r->rr.name);
-    free(r->rr.rdata);
-    free(r->rr.rdname);
-    free(r);
+
+    if (mdnsd->qlist == query) {
+        mdnsd->qlist = query->list;
+    } else {
+        for (current = mdnsd->qlist; current->list != query; current = current->list);
+        current->list = query->list;
+    }
+
+    if (mdnsd->queries[idx] == query) {
+        mdnsd->queries[idx] = query->next;
+    } else {
+        for (current = mdnsd->queries[idx]; current->next != query; current = current->next);
+        current->next = query->next;
+    }
+
+    free(query->name);
+    free(query);
 }
 
-void _q_answer(mdnsd d, struct cached *c)
-{ // call the answer function with this cached entry
-    if(c->rr.ttl <= d->now.tv_sec) c->rr.ttl = 0;
-    if(c->q->answer(&c->rr,c->q->arg) == -1) _q_done(d, c->q);
+/*!
+ * \brief call the answer function with this cached entry
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param cached    Cached record
+ */
+static void MdnsdQueryAnswer(TMdnsd *mdnsd, TCached *cached)
+{
+    if (cached->rr.ttl <= mdnsd->now.tv_sec) {
+        cached->rr.ttl = 0;
+    }
+
+    if (cached->query->answer(&cached->rr, cached->query->arg) == -1) {
+        MdnsdQueryDone(mdnsd, cached->query);
+    }
 }
 
-void _conflict(mdnsd d, mdnsdr r)
+/*!
+ * \brief call the conflict function with this record
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param record    Record to call conflict for
+ */
+static void MdnsdCallConflict(TMdnsd *mdnsd, TMdnsdRecord *record)
 {
-    r->conflict(r, r->rr.name,r->rr.type,r->arg);
-    mdnsd_done(d,r);
+    record->conflict(record, record->rr.name, record->rr.type, record->arg);
+    MdnsdDone(mdnsd, record);
 }
 
-void _c_expire(mdnsd d, struct cached **list)
-{ // expire any old entries in this list
-    struct cached *next, *cur = *list, *last = 0;
-    while(cur != 0)
-    {
-        next = cur->next;
-        if(d->now.tv_sec >= cur->rr.ttl)
-        {
-            if(last) last->next = next;
-            if(*list == cur) *list = next; // update list pointer if the first one expired
-            if(cur->q) _q_answer(d,cur);
-            free(cur->rr.name);
-            free(cur->rr.rdata);
-            free(cur->rr.rdname);
-            free(cur);
-        }else{
-            last = cur;
+/*!
+ * \brief Expire any old entries in this hash list
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param list      Cache hash list head
+ */
+static void MdnsdCacheExpire(TMdnsd *mdnsd, TCached **list)
+{
+    TCached *next;
+    TCached *current  = *list;
+    TCached *last = NULL;
+
+    while(current != NULL) {
+        next = current->next;
+
+        if(mdnsd->now.tv_sec >= current->rr.ttl) {
+            if (last) {
+                last->next = next;
+            }
+
+            if (*list == current) {
+                /* Update list pointer if the first one expired */
+                *list = next;
+            }
+
+            if (current->query) {
+                MdnsdQueryAnswer(mdnsd, current);
+            }
+
+            free(current->rr.name);
+            free(current->rr.rdata);
+            free(current->rr.rdname);
+            free(current);
+        } else {
+            last = current;
         }
-        cur = next;
+        current = next;
     }
 }
 
-// brute force expire any old cached records
-void _gc(mdnsd d)
+
+/*!
+ * \brief Garbage collector: Expire any old cached records
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ */
+static void MdnsdCacheGarbageCollect(TMdnsd *mdnsd)
 {
-    int i;
-    for(i=0;i<LPRIME;i++)
-        if(d->cache[i]) _c_expire(d,&d->cache[i]);
-    d->expireall = d->now.tv_sec + GC;
+    int idx;
+
+    for(idx = 0; idx < LPRIME; idx++) {
+        if (mdnsd->cache[idx]) {
+            MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
+        }
+    }
+
+    mdnsd->expireall = mdnsd->now.tv_sec + GC;
 }
 
-void _cache(mdnsd d, struct resource *r)
-{
-    struct cached *c = 0;
-    int i = _namehash(r->name) % LPRIME;
+/*!
+ * \brief Add a ressource to the cache
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param res       ressource data to add
+ */
+static void MdnsdCacheAddRessource(TMdnsd *mdnsd, DNSRESOURCE *res)
+{
+    TCached *cached = NULL;
+
+    int idx;
+
+    idx = NameHash(res->name) % LPRIME;
 
-    if(r->class == 32768 + d->class)
-    { // cache flush
-        while((c = _c_next(d,c,r->name,r->type))) c->rr.ttl = 0;
-        _c_expire(d,&d->cache[i]);
+    if (res->class == 32768 + mdnsd->class) {
+        /* Flush the cache */
+        while ((cached = CachedNext(mdnsd, cached, res->name, res->type))) {
+            cached->rr.ttl = 0;
+        }
+        MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
     }
 
-    if(r->ttl == 0)
-    { // process deletes
-        while((c = _c_next(d,c,r->name,r->type)))
-            if(_a_match(r,&c->rr))
-            {
-                c->rr.ttl = 0;
+    if (res->ttl == 0) {
+        /* Process deletes */
+        while ((cached = CachedNext(mdnsd, cached, res->name, res->type))) {
+            if (MatchAnswer(res, &cached->rr)) {
+                cached->rr.ttl = 0;
             }
-        _c_expire(d,&d->cache[i]);
+        }
+        MdnsdCacheExpire(mdnsd, &mdnsd->cache[idx]);
         return;
     }
 
-    c = (struct cached *)malloc(sizeof(struct cached));
-    bzero(c,sizeof(struct cached));
-    c->rr.name = strdup(r->name);
-    c->rr.type = r->type;
-    c->rr.ttl = d->now.tv_sec + (r->ttl / 2) + 8; // XXX hack for now, BAD SPEC, start retrying just after half-waypoint, then expire
-    c->rr.rdlen = r->rdlength;
-    c->rr.rdata = (unsigned char *)malloc(r->rdlength);
-    memcpy(c->rr.rdata,r->rdata,r->rdlength);
-    switch(r->type)
-    {
-    case QTYPE_A:
-        c->rr.ip = r->known.a.ip;
-        break;
-    case QTYPE_NS:
-    case QTYPE_CNAME:
-    case QTYPE_PTR:
-        c->rr.rdname = strdup(r->known.ns.name);
-        break;
-    case QTYPE_SRV:
-        c->rr.rdname = strdup(r->known.srv.name);
-        c->rr.srv.port = r->known.srv.port;
-        c->rr.srv.weight = r->known.srv.weight;
-        c->rr.srv.priority = r->known.srv.priority;
-        break;
-    }
-    c->next = d->cache[i];
-    d->cache[i] = c;
-    if((c->q = _q_next(d, 0, r->name, r->type)))
-        _q_answer(d,c);
-}
-
-void _a_copy(struct message *m, mdnsda a)
-{ // copy the data bits only
-    if(a->rdata) { message_rdata_raw(m, a->rdata, a->rdlen); return; }
-    if(a->ip) message_rdata_long(m, a->ip);
-    if(a->type == QTYPE_SRV) message_rdata_srv(m, a->srv.priority, a->srv.weight, a->srv.port, a->rdname);
-    else if(a->rdname) message_rdata_name(m, a->rdname);
-}
-
-int _r_out(mdnsd d, struct message *m, mdnsdr *list)
-{ // copy a published record into an outgoing message
-    mdnsdr r;
+    cached = (TCached *)malloc(sizeof(TCached));
+    memset(cached, 0, sizeof(TCached));
+
+    cached->rr.name = strdup(res->name);
+    cached->rr.type = res->type;
+    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
+    cached->rr.rdlen = res->rdlength;
+    cached->rr.rdata = (uint8_t *)malloc(res->rdlength);
+    memcpy(cached->rr.rdata, res->rdata, res->rdlength);
+
+    switch(res->type) {
+        case QTYPE_A:
+            cached->rr.ip.s_addr = res->known.a.ip;
+            break;
+        case QTYPE_NS:
+        case QTYPE_CNAME:
+        case QTYPE_PTR:
+            cached->rr.rdname = strdup(res->known.ns.name);
+            break;
+        case QTYPE_SRV:
+            cached->rr.rdname = strdup(res->known.srv.name);
+            cached->rr.srv.port = res->known.srv.port;
+            cached->rr.srv.weight = res->known.srv.weight;
+            cached->rr.srv.priority = res->known.srv.priority;
+            break;
+    }
+
+    cached->next = mdnsd->cache[idx];
+    mdnsd->cache[idx] = cached;
+
+    if((cached->query = QueryNext(mdnsd, 0, res->name, res->type))) {
+        MdnsdQueryAnswer(mdnsd, cached);
+    }
+}
+
+/*!
+ * \brief Copy an answer
+ *
+ * Copy the databits only
+ *
+ * \param msg       DNS message struct
+ * \param answer    Answer to get the data from
+ */
+static void MdnsdCopyAnswer(DNSMESSAGE *msg, TMdnsdAnswer *answer)
+{
+    if(answer->rdata) {
+        DnsMsgAdd_rdata_raw(msg, answer->rdata, answer->rdlen);
+        return;
+    }
+
+    if(answer->ip.s_addr) {
+        DnsMsgAdd_rdata_long(msg, answer->ip.s_addr);
+    }
+
+    if(answer->type == QTYPE_SRV) {
+        DnsMsgAdd_rdata_srv(msg, answer->srv.priority, answer->srv.weight, answer->srv.port, answer->rdname);
+    } else
+    if (answer->rdname) {
+        DnsMsgAdd_rdata_name(msg, answer->rdname);
+    }
+}
+
+/*!
+ * \brief Copy a published record into an outgoing message
+ *
+ * \param mdnsd     MDNS deamon data of the current mdnsd instace
+ * \param msg       DNS message struct
+ * \param list      List of publishing records
+ *
+ * \return          Number of send records
+ */
+static int MdnsdRecordOut(TMdnsd *mdnsd, DNSMESSAGE *m, TMdnsdRecord **list)
+{
+    TMdnsdRecord *record;
     int ret = 0;
-    while((r = *list) != 0 && message_packet_len(m) + _rr_len(&r->rr) < d->frame)
-    {
-        *list = r->list;
+
+    while (((record = *list) != NULL) &&
+           (DnsMsgLen(m) + GetRessourceRecordLength(&record->rr) < mdnsd->frame)) {
+
+        *list = record->list;
         ret++;
-        if(r->unique)
-            message_an(m, r->rr.name, r->rr.type, d->class + 32768, r->rr.ttl);
-        else
-            message_an(m, r->rr.name, r->rr.type, d->class, r->rr.ttl);
-        _a_copy(m, &r->rr);
-        if(r->rr.ttl == 0) _r_done(d,r);
+
+        if (record->unique) {
+            DnsMsgAdd_an(m, record->rr.name, record->rr.type, mdnsd->class + 32768, record->rr.ttl);
+        } else {
+            DnsMsgAdd_an(m, record->rr.name, record->rr.type, mdnsd->class, record->rr.ttl);
+        }
+        MdnsdCopyAnswer(m, &record->rr);
+
+        if(record->rr.ttl == 0) {
+            MdnsdRecordDone(mdnsd, record);
+        }
     }
     return ret;
 }
 
-
-mdnsd mdnsd_new(int class, int frame)
+/*!
+ * \brief Initialise the mdnsd deamon instance
+ *
+ * Create a new mdns daemon for the given class of names (usually 1) and maximum frame size
+ *
+ * \param class     Class of names
+ * \param frame     Maximum frame size
+ *
+ * \return          Newly allocated mdnsd struct instance
+ */
+TMdnsd *MdnsdNew(int class, int frame)
 {
-    mdnsd d;
-    d = (mdnsd)malloc(sizeof(struct mdnsd_struct));
-    bzero(d,sizeof(struct mdnsd_struct));
-    gettimeofday(&d->now,0);
-    d->expireall = d->now.tv_sec + GC;
-    d->class = class;
-    d->frame = frame;
-    return d;
+    TMdnsd *mdnsd;
+
+    mdnsd = (TMdnsd*)malloc(sizeof(TMdnsd));
+    memset(mdnsd, 0, sizeof(TMdnsd));
+
+    gettimeofday(&mdnsd->now, 0);
+    mdnsd->expireall = mdnsd->now.tv_sec + GC;
+
+    mdnsd->class = class;
+    mdnsd->frame = frame;
+
+    return mdnsd;
 }
 
-void mdnsd_shutdown(mdnsd d)
-{ // shutting down, zero out ttl and push out all records
-    int i;
-    mdnsdr cur,next;
-    d->a_now = 0;
-    for(i=0;i<SPRIME;i++)
-        for(cur = d->published[i]; cur != 0;)
-        {
-            next = cur->next;
-            cur->rr.ttl = 0;
-            cur->list = d->a_now;
-            d->a_now = cur;
-            cur = next;
+/*!
+ * \brief Shutdown and cleanup an MDNSD instance
+ *
+ * Gracefully shutdown the daemon, use mdnsd_out() to get the last packets
+ *
+ * \param mdnsd     MDNS deamon to shutdown
+ *
+ * \return          Newly allocated mdnsd struct instance
+ */
+void MdnsdShutdown(TMdnsd *mdnsd)
+{
+
+    int idx;
+    TMdnsdRecord *current;
+    TMdnsdRecord *next;
+
+    mdnsd->a_now = 0;
+
+    /* zero out ttl and push out all records */
+    for(idx = 0; idx < SPRIME; idx++) {
+        for(current = mdnsd->published[idx]; current != NULL; ) {
+            next = current->next;
+            current->rr.ttl = 0;
+            current->list = mdnsd->a_now;
+            mdnsd->a_now = current;
+            current = next;
         }
-    d->shutdown = 1;
+    }
+
+    mdnsd->shutdown = 1;
 }
 
-void mdnsd_flush(mdnsd d)
+
+/*!
+ * \brief Flush all cached records (network/interface changed)
+ *
+ * \param mdnsd     MDNS deamon to flush
+ */
+void MdnsdFlush(TMdnsd *UNUSED(mdnsd))
 {
+    // TODO:
     // set all querys to 0 tries
     // free whole cache
-    // set all mdnsdr to probing
+    // set all TMdnsdRecord *to probing
     // reset all answer lists
 }
 
-void mdnsd_free(mdnsd d)
+/*!
+ * \brief Free given mdnsd (should have used mdnsd_shutdown() first!)
+ *
+ * \param mdnsd     MDNS deamon to free
+ */
+void MdnsdFree(TMdnsd *mdnsd)
 {
+    // TODO:
     // loop through all hashes, free everything
     // free answers if any
-    free(d);
-}
-
-char *decode_type (unsigned short type)
-{
-  switch (type) {
-    case QTYPE_A:     return "A";
-    case QTYPE_NS:    return "NS";
-    case QTYPE_CNAME: return "CNAME";
-    case QTYPE_PTR:   return "PTR";
-    case QTYPE_TXT:   return "TXT";
-    case QTYPE_SRV:   return "SRV";
-    default:   return "???";
-  }
-}
-
-void mdnsd_res_dump (FILE *file, struct resource *res)
-{
-  fprintf (file, "%s \"%s\" = ",
-           decode_type (res->type), res->name);
-  switch (res->type)
-    {
-      case QTYPE_A:
-        fprintf (file, "%ld.%ld.%ld.%ld\n",
-                 (res->known.a.ip >> 24) & 0xff,
-                 (res->known.a.ip >> 16) & 0xff,
-                 (res->known.a.ip >> 8) & 0xff,
-                 (res->known.a.ip >> 0) & 0xff);
-        break;
-      case QTYPE_NS:
-        fprintf (file, "%s\n", res->known.ns.name);
-        break;
-      case QTYPE_CNAME:
-        fprintf (file, "%s\n", res->known.cname.name);
-        break;
-      case QTYPE_PTR:
-        fprintf (file, "%s\n", res->known.ptr.name);
-        break;
-      case QTYPE_SRV:
-        fprintf (file, "%s:%d\n", res->known.srv.name, res->known.srv.port);
-        break;
-      default:
-        fprintf (file, "???\n");
-    }
-}
-
-void mdnsd_dump (FILE *file, struct message *m, char *type)
-{
-  int i;
-  fprintf (file, "==== %s message ====\n", type);
-  if (m->header.qr == 0 && m->qdcount > 0)
-    {
-      fprintf (file, "Questions:\n");
-      for (i = 0; i < m->qdcount; i++)
-        fprintf (file, " %3d: %s \"%s\"?\n", i,
-                 decode_type (m->qd[i].type), m->qd[i].name);
-    }
-  if (m->ancount > 0)
-    {
-      fprintf (file, "Answers:\n");
-      for (i = 0; i < m->ancount; i++)
-        {
-          fprintf (file, " %3d: ", i);
-          mdnsd_res_dump (file, &m->an[i]);
-        }
-    }
-  if (m->nscount > 0)
-    {
-      fprintf (file, "Authority:\n");
-      for (i = 0; i < m->nscount; i++)
-        {
-          fprintf (file, " %3d: ", i);
-          mdnsd_res_dump (file, &m->ns[i]);
-        }
-    }
-  if (m->arcount > 0)
-    {
-      fprintf (file, "Additional:\n");
-      for (i = 0; i < m->arcount; i++)
-        {
-          fprintf (file, " %3d: ", i);
-          mdnsd_res_dump (file, &m->ar[i]);
-        }
-    }
-  fprintf (file, "\n");
-}
-
-void mdnsd_in(mdnsd d, struct message *m, unsigned long int ip, unsigned short int port)
-{
-    int i, j;
-    mdnsdr r;
+    free(mdnsd);
+}
+
+#ifdef MDNSD_DEBUG
+uint8_t *MdnsdDecodeType(uint16_t type)
+{
+    switch (type) {
+        case QTYPE_A:     return "A";
+        case QTYPE_NS:    return "NS";
+        case QTYPE_CNAME: return "CNAME";
+        case QTYPE_PTR:   return "PTR";
+        case QTYPE_TXT:   return "TXT";
+        case QTYPE_SRV:   return "SRV";
+        default:          return "???";
+    }
+}
+
+void MdnsdDumpRessource (FILE *file, DNSRESOURCE *res)
+{
+    fprintf (file, "%s \"%s\" = ", MdnsdDecodeType (res->type), res->name);
+
+    switch (res->type) {
+        case QTYPE_A:
+            fprintf (file, "%d.%d.%d.%d\n",
+                     (res->known.a.ip >> 24) & 0xff,
+                     (res->known.a.ip >> 16) & 0xff,
+                     (res->known.a.ip >> 8) & 0xff,
+                     (res->known.a.ip >> 0) & 0xff);
+            break;
+
+        case QTYPE_NS:
+            fprintf (file, "%s\n", res->known.ns.name);
+            break;
+
+        case QTYPE_CNAME:
+            fprintf (file, "%s\n", res->known.cname.name);
+            break;
+
+        case QTYPE_PTR:
+            fprintf (file, "%s\n", res->known.ptr.name);
+            break;
+
+        case QTYPE_SRV:
+            fprintf (file, "%s:%d\n", res->known.srv.name, res->known.srv.port);
+            break;
+
+        default:
+            fprintf (file, "???\n");
+    }
+}
+
+void mdnsd_dump (FILE *file, DNSMESSAGE *msg, uint8_t *type)
+{
+    int idx;
+
+    fprintf (file, "==== %s message ====\n", type);
+
+    if ((msg->header.qr == 0) && (msg->qdcount > 0)) {
+        fprintf (file, "Questions:\n");
+        for (idx = 0; idx < msg->qdcount; idx++) {
+            fprintf (file, " %3d: %s \"%s\"?\n", idx,
+                     MdnsdDecodeType (msg->qd[idx].type), msg->qd[idx].name);
+        }
+    }
+
+    if (msg->ancount > 0) {
+        fprintf (file, "Answers:\n");
+        for (idx = 0; idx < msg->ancount; idx++) {
+            fprintf (file, " %3d: ", idx);
+            MdnsdDumpRessource (file, &msg->an[idx]);
+        }
+    }
+
+    if (msg->nscount > 0) {
+        fprintf (file, "Authority:\n");
+        for (idx = 0; idx < msg->nscount; idx++) {
+            fprintf (file, " %3d: ", idx);
+            MdnsdDumpRessource (file, &msg->ns[idx]);
+        }
+    }
+    if (msg->arcount > 0) {
+        fprintf (file, "Additional:\n");
+        for (idx = 0; idx < msg->arcount; idx++) {
+            fprintf (file, " %3d: ", idx);
+            MdnsdDumpRessource (file, &msg->ar[idx]);
+        }
+    }
+    fprintf (file, "\n");
+}
+#endif /* MDNSD_DEBUG */
+
+
+/*******************************************************************************
+ *                            I/O functions                                    *
+ *******************************************************************************/
+
+/*!
+ * \brief Process incomming messages from the host
+ *
+ * This function processes each query and sends out the matching unicast reply
+ * to each query. For each question, the potential answers are checked. Each
+ * answer is checked for potential conflicts. Each answer is processed and
+ * cached.
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param msg       incomming message
+ * \param ip        source IP
+ * \param port      source port
+ *
+ */
+
+// TODO: SRV record: "reinhardt" is cut down to "einhardt", first character is dropped... Wrong index?
+
+void MdnsdInput(TMdnsd *mdnsd, DNSMESSAGE *msg, struct in_addr ip, uint16_t port)
+{
+    int qd_idx;
+    int an_idx;
+    TMdnsdRecord *record;
     int have_match;
     int may_conflict;
 
-    if(d->shutdown) return;
+    if(mdnsd->shutdown) return;
+
+    gettimeofday(&mdnsd->now,0);
+
+    if(msg->header.qr == 0) {
+        /* This message contains a query... Process the question and send out
+           our answer if needed
+         */
 
-    gettimeofday(&d->now,0);
+        for(qd_idx = 0; qd_idx < msg->qdcount; qd_idx++) {
+            /* Process each query */
+            if ((msg->qd[qd_idx].class != mdnsd->class) ||
+                ((record = RecordNext(mdnsd, 0, msg->qd[qd_idx].name, msg->qd[qd_idx].type)) == NULL)) {
+                continue;
+            }
+
+            /* Send the matching unicast reply */
+            if (port != MDNS_PORT) {
+                MdnsdPushUnicast(mdnsd, record, msg->id, ip, port);
+            }
 
-    if(m->header.qr == 0)
-    {
-        for(i=0;i<m->qdcount;i++)
-        { // process each query
-            if(m->qd[i].class != d->class || (r = _r_next(d,0,m->qd[i].name,m->qd[i].type)) == 0) continue;
+            for( ; record != NULL; record = RecordNext(mdnsd, record, msg->qd[qd_idx].name, msg->qd[qd_idx].type)) {
+                /* Check all of our potential answers */
+                have_match   = 0;
+                may_conflict = 0;
 
-            // send the matching unicast reply
-            if(port != 5353) _u_push(d,r,m->id,ip,port);
+                if (record->unique && record->unique < 5) {
+                    /* Probing state, check for conflicts */
 
-            for(;r != 0; r = _r_next(d,r,m->qd[i].name,m->qd[i].type))
-            { // check all of our potential answers
-                int have_match = 0;
-                int may_conflict = 0;
+                    for(an_idx = 0; an_idx < msg->nscount; an_idx++) {
+                        /* Check all to-be answers against our own */
+                        if ((msg->an[an_idx].ttl == 0) || (msg->qd[qd_idx].type != msg->an[an_idx].type) ||
+                            (strcasecmp(msg->qd[qd_idx].name, msg->an[an_idx].name) != 0)) {
+                            continue;
+                        }
 
-                if(r->unique && r->unique < 5)
-                { // probing state, check for conflicts
-                    for(j=0;j<m->nscount;j++)
-                    { // check all to-be answers against our own
-                        if(m->an[j].ttl == 0 || m->qd[i].type != m->an[j].type || strcasecmp(m->qd[i].name,m->an[j].name)) continue;
-                        if(!_a_match(&m->an[j],&r->rr))
+                        if (!MatchAnswer(&msg->an[an_idx], &record->rr)) {
+                            /* Not matching answers may cause conflicts */
                             may_conflict = 1;
-                        else
+                        } else {
                             have_match = 1;
+                        }
                     }
-                    if (may_conflict && !have_match)
-                       _conflict(d,r); // this answer isn't ours, conflict!
+
+                    if (may_conflict && !have_match) {
+                        /* The answer isn't ours, we have a conflict */
+                        MdnsdCallConflict(mdnsd, record);
+                    }
+
                     continue;
                 }
-                for(j=0;j<m->ancount;j++)
-                { // check the known answers for this question
-                    if((m->qd[i].type != QTYPE_ANY && m->qd[i].type != m->an[j].type) || strcasecmp(m->qd[i].name,m->an[j].name)) continue;
-                    if(_a_match(&m->an[j],&r->rr)) break; // they already have this answer
+
+                for(an_idx = 0; an_idx < msg->ancount; an_idx++) {
+                    /* Check the known answers for this question */
+                    if (((msg->qd[qd_idx].type != QTYPE_ANY) && (msg->qd[qd_idx].type != msg->an[an_idx].type)) ||
+                        (strcasecmp(msg->qd[qd_idx].name, msg->an[an_idx].name) != 0)) {
+                        continue;
+                    }
+
+                    if (MatchAnswer(&msg->an[an_idx], &record->rr)) {
+                        /* They already have this answer */
+                        break;
+                    }
+                }
+
+                if(an_idx == msg->ancount) {
+                    /* No matching answers found, send out our answer */
+                    MdnsdSendRecord(mdnsd, record);
                 }
-                if(j == m->ancount) _r_send(d,r);
             }
         }
         return;
     }
 
-    for(i=0;i<m->ancount;i++)
-    { // process each answer, check for a conflict, and cache
+    for (an_idx = 0; an_idx < msg->ancount; an_idx++) {
+        /* Process each answer, check for a conflict, and cache it */
         have_match = 0;
         may_conflict = 0;
 
-        r = 0;
-        while ((r = _r_next(d,r,m->an[i].name,m->an[i].type)) != 0)
-          {
-            if (r->unique)
-              {
-                if (_a_match(&m->an[i],&r->rr) == 0)
+        record = NULL;
+        while ((record = RecordNext(mdnsd, record, msg->an[an_idx].name, msg->an[an_idx].type)) != NULL) {
+            if (record->unique) {
+                if (MatchAnswer(&msg->an[an_idx], &record->rr) == 0) {
                     may_conflict = 1;
-                else
+                } else {
                     have_match = 1;
-              }
-          }
-        if (may_conflict && !have_match)
-          {
-            while ((r = _r_next(d,r,m->an[i].name,m->an[i].type)) != 0)
-              {
-                if (r->unique && _a_match(&m->an[i],&r->rr) == 0 && m->an[i].ttl > 0)
-                  _conflict(d, r);
-              }
-          }
-        _cache(d,&m->an[i]);
+                }
+            }
+        }
+
+        if (may_conflict && !have_match) {
+            while ((record = RecordNext(mdnsd, record, msg->an[an_idx].name, msg->an[an_idx].type)) != NULL) {
+                if ((record->unique && MatchAnswer(&msg->an[an_idx], &record->rr) == 0) && (msg->an[an_idx].ttl > 0)) {
+                    MdnsdCallConflict(mdnsd, record);
+                }
+            }
+        }
+
+        MdnsdCacheAddRessource(mdnsd, &msg->an[an_idx]);
     }
 }
 
-int mdnsd_out(mdnsd d, struct message *m, unsigned long int *ip, unsigned short int *port)
+/*!
+ * \brief Send outgoing messages to the host.
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param msg       outgoing message
+ * \param ip        destination IP
+ * \param port      destination port
+ *
+ * \return          >0 if one was returned and m/ip/port set
+ */
+int MdnsdOutput(TMdnsd *mdnsd, DNSMESSAGE *msg, struct in_addr *ip, uint16_t *port)
 {
-    mdnsdr r;
+    TMdnsdRecord *record;
     int ret = 0;
 
-    gettimeofday(&d->now,0);
-    bzero(m,sizeof(struct message));
-
-    // defaults, multicast
-    *port = htons(5353);
-    *ip = inet_addr("224.0.0.251");
-    m->header.qr = 1;
-    m->header.aa = 1;
-
-    if(d->uanswers)
-    { // send out individual unicast answers
-        struct unicast *u = d->uanswers;
-        d->uanswers = u->next;
-        *port = u->port;
-        *ip = u->to;
-        m->id = u->id;
-        message_qd(m, u->r->rr.name, u->r->rr.type, d->class);
-        message_an(m, u->r->rr.name, u->r->rr.type, d->class, u->r->rr.ttl);
-        _a_copy(m, &u->r->rr);
-        free(u);
+    gettimeofday(&mdnsd->now,0);
+    memset(msg, 0, sizeof(DNSMESSAGE));
+
+    /* Set multicast defaults */
+    *port = htons(MDNS_PORT);
+    (*ip).s_addr = inet_addr(MDNS_MULTICAST_IP);
+    msg->header.qr = 1;
+    msg->header.aa = 1;
+
+    if(mdnsd->uanswers) {
+        /* Send out individual unicast answers */
+        TUnicast *unicast = mdnsd->uanswers;
+
+        mdnsd->uanswers = unicast->next;
+        *port = unicast->port;
+        *ip = unicast->to;
+        msg->id = unicast->id;
+
+        DnsMsgAdd_qd(msg, unicast->record->rr.name, unicast->record->rr.type, mdnsd->class);
+        DnsMsgAdd_an(msg, unicast->record->rr.name, unicast->record->rr.type, mdnsd->class, unicast->record->rr.ttl);
+
+        MdnsdCopyAnswer(msg, &unicast->record->rr);
+
+        free(unicast);
         return 1;
     }
 
-//printf("OUT: probing %X now %X pause %X publish %X\n",d->probing,d->a_now,d->a_pause,d->a_publish);
-
-    // accumulate any immediate responses
-    if(d->a_now) ret += _r_out(d, m, &d->a_now);
-
-    if(d->a_publish && _tvdiff(d->now,d->publish) <= 0)
-    { // check to see if it's time to send the publish retries (and unlink if done)
-        mdnsdr next, cur = d->a_publish, last = 0;
-        while(cur && message_packet_len(m) + _rr_len(&cur->rr) < d->frame)
-        {
-            next = cur->list;
-            ret++; cur->tries++;
-            if(cur->unique)
-                message_an(m, cur->rr.name, cur->rr.type, d->class + 32768, cur->rr.ttl);
-            else
-                message_an(m, cur->rr.name, cur->rr.type, d->class, cur->rr.ttl);
-            _a_copy(m, &cur->rr);
-            if(cur->rr.ttl != 0 && cur->tries < 4)
-            {
-                last = cur;
-                cur = next;
+    //printf("OUT: probing %p now %p pause %p publish %p\n",mdnsd->probing,mdnsd->a_now,mdnsd->a_pause,mdnsd->a_publish);
+
+    /* Accumulate any immediate responses */
+    if (mdnsd->a_now) {
+        ret += MdnsdRecordOut(mdnsd, msg, &mdnsd->a_now);
+    }
+
+    if (mdnsd->a_publish && (TvDiff(mdnsd->now,mdnsd->publish) <= 0)) {
+        /* Check to see if it's time to send the publish retries (and unlink if done) */
+        TMdnsdRecord *next;
+        TMdnsdRecord *current;
+        TMdnsdRecord *last = NULL;
+
+        current = mdnsd->a_publish;
+        while(current && (DnsMsgLen(msg) + GetRessourceRecordLength(&current->rr) < mdnsd->frame)) {
+            next = current->list;
+            ret++;
+            current->tries++;
+
+            if (current->unique) {
+                DnsMsgAdd_an(msg, current->rr.name, current->rr.type, mdnsd->class + 32768, current->rr.ttl);
+            } else {
+                DnsMsgAdd_an(msg, current->rr.name, current->rr.type, mdnsd->class, current->rr.ttl);
+            }
+            MdnsdCopyAnswer(msg, &current->rr);
+
+            if ((current->rr.ttl != 0) && (current->tries < 4)) {
+                last = current;
+                current = next;
                 continue;
             }
-            if(d->a_publish == cur) d->a_publish = next;
-            if(last) last->list = next;
-            if(cur->rr.ttl == 0) _r_done(d,cur);
-            cur = next;
-        }
-        if(d->a_publish)
-        {
-            d->publish.tv_sec = d->now.tv_sec + 2;
-            d->publish.tv_usec = d->now.tv_usec;
-        }
-    }
-
-    // if we're in shutdown, we're done
-    if(d->shutdown) return ret;
-
-    // check if a_pause is ready
-    if(d->a_pause && _tvdiff(d->now, d->pause) <= 0) ret += _r_out(d, m, &d->a_pause);
-
-    // now process questions
-    if(ret) return ret;
-    m->header.qr = 0;
-    m->header.aa = 0;
-
-    if(d->probing && _tvdiff(d->now,d->probe) <= 0)
-    {
-        mdnsdr last = 0;
-        for(r = d->probing; r != 0;)
-        { // scan probe list to ask questions and process published
-            if(r->unique == 4)
-            { // done probing, publish
-                mdnsdr next = r->list;
-                if(d->probing == r)
-                    d->probing = r->list;
-                else
-                    last->list = r->list;
-                r->list = 0;
-                r->unique = 5;
-                _r_publish(d,r);
-                r = next;
+
+            if (mdnsd->a_publish == current) {
+                mdnsd->a_publish = next;
+            }
+
+            if (last) {
+                last->list = next;
+            }
+
+            if (current->rr.ttl == 0) {
+                MdnsdRecordDone(mdnsd, current);
+            }
+            current = next;
+        }
+
+        if (mdnsd->a_publish) {
+            mdnsd->publish.tv_sec = mdnsd->now.tv_sec + 2;
+            mdnsd->publish.tv_usec = mdnsd->now.tv_usec;
+        }
+    }
+
+    /* If we're in shutdown state, we're done */
+    if (mdnsd->shutdown) {
+        return ret;
+    }
+
+    /* Check if a_pause is ready */
+    if (mdnsd->a_pause && (TvDiff(mdnsd->now, mdnsd->pause) <= 0)) {
+        ret += MdnsdRecordOut(mdnsd, msg, &mdnsd->a_pause);
+    }
+
+    /* Now process questions */
+    if (ret) {
+        return ret;
+    }
+
+    msg->header.qr = 0;
+    msg->header.aa = 0;
+
+    if (mdnsd->probing && (TvDiff(mdnsd->now, mdnsd->probe) <= 0)) {
+        TMdnsdRecord *last = NULL;
+
+        for (record = mdnsd->probing; record != NULL; ) {
+            /* Scan probe list to ask questions and process published */
+            if (record->unique == 4) {
+                /* Done probing, publish now */
+                TMdnsdRecord *next = record->list;
+
+                if (mdnsd->probing == record) {
+                    mdnsd->probing = record->list;
+                } else {
+                    last->list = record->list;
+                }
+
+                record->list = NULL;
+                record->unique = 5;
+
+                MdnsdPublishRecord(mdnsd, record);
+
+                record = next;
                 continue;
             }
-            message_qd(m, r->rr.name, QTYPE_ANY, d->class);
-            last = r;
-            r = r->list;
-        }
-        for(r = d->probing; r != 0; last = r, r = r->list)
-        { // scan probe list again to append our to-be answers
-            r->unique++;
-            message_ns(m, r->rr.name, r->rr.type, d->class, r->rr.ttl);
-            _a_copy(m, &r->rr);
+            DnsMsgAdd_qd(msg, record->rr.name, QTYPE_ANY, mdnsd->class);
+            last = record;
+            record = record->list;
+        }
+
+        for (record = mdnsd->probing; record != NULL; last = record, record = record->list) {
+            /* Scan probe list again to append our to-be answers */
+            record->unique++;
+            DnsMsgAdd_ns(msg, record->rr.name, record->rr.type, mdnsd->class, record->rr.ttl);
+            MdnsdCopyAnswer(msg, &record->rr);
             ret++;
         }
-        if(ret)
-        { // process probes again in the future
-            d->probe.tv_sec = d->now.tv_sec;
-            d->probe.tv_usec = d->now.tv_usec + 250000;
+
+        if (ret) {
+            /* Set timeout to process probes again */
+            mdnsd->probe.tv_sec = mdnsd->now.tv_sec;
+            mdnsd->probe.tv_usec = mdnsd->now.tv_usec + 250000;
             return ret;
         }
     }
 
-    if(d->checkqlist && d->now.tv_sec >= d->checkqlist)
-    { // process qlist for retries or expirations
-        struct query *q;
-        struct cached *c;
-        unsigned long int nextbest = 0;
-
-        // ask questions first, track nextbest time
-        for(q = d->qlist; q != 0; q = q->list)
-            if(q->nexttry > 0 && q->nexttry <= d->now.tv_sec && q->tries < 3)
-                message_qd(m,q->name,q->type,d->class);
-            else if(q->nexttry > 0 && (nextbest == 0 || q->nexttry < nextbest))
-                nextbest = q->nexttry;
-
-        // include known answers, update questions
-        for(q = d->qlist; q != 0; q = q->list)
-        {
-            if(q->nexttry == 0 || q->nexttry > d->now.tv_sec) continue;
-            if(q->tries == 3)
-            { // done retrying, expire and reset
-                _c_expire(d,&d->cache[_namehash(q->name) % LPRIME]);
-                _q_reset(d,q);
+    if (mdnsd->checkqlist && (mdnsd->now.tv_sec >= mdnsd->checkqlist)) {
+        /* Process qlist for retries or expirations */
+        TQuery *query;
+        TCached *cached;
+        uint32_t nextbest = 0;
+
+        /* Ask questions first, track nextbest time */
+        for(query = mdnsd->qlist; query != NULL; query = query->list) {
+            if ((query->nexttry > 0) && (query->nexttry <= mdnsd->now.tv_sec) && (query->tries < 3)) {
+                DnsMsgAdd_qd(msg, query->name, query->type,mdnsd->class);
+            } else
+            if ((query->nexttry > 0) && ((nextbest == 0) || (query->nexttry < nextbest))) {
+                nextbest = query->nexttry;
+            }
+        }
+
+        /* Include known answers, update questions */
+        for (query = mdnsd->qlist; query != NULL; query = query->list) {
+            if ((query->nexttry == 0) || (query->nexttry > mdnsd->now.tv_sec)) {
+                continue;
+            }
+
+            if (query->tries == 3) {
+                /* Done retrying, expire and reset */
+                MdnsdCacheExpire(mdnsd, &mdnsd->cache[NameHash(query->name) % LPRIME]);
+                MdnsdQueryReset(mdnsd, query);
                 continue;
             }
+
             ret++;
-            q->nexttry = d->now.tv_sec + ++q->tries;
-            if(nextbest == 0 || q->nexttry < nextbest)
-                nextbest = q->nexttry;
-            // if room, add all known good entries
-            c = 0;
-            while((c = _c_next(d,c,q->name,q->type)) != 0 && c->rr.ttl > d->now.tv_sec + 8 && message_packet_len(m) + _rr_len(&c->rr) < d->frame)
-            {
-                message_an(m,q->name,q->type,d->class,c->rr.ttl - d->now.tv_sec);
-                _a_copy(m,&c->rr);
+            query->nexttry = mdnsd->now.tv_sec + ++query->tries;
+
+            if ((nextbest == 0) || (query->nexttry < nextbest)) {
+                nextbest = query->nexttry;
+            }
+
+            /* If room, add all known good entries */
+            cached = NULL;
+            while (((cached = CachedNext(mdnsd, cached, query->name, query->type)) != NULL) &&
+                   (cached->rr.ttl > mdnsd->now.tv_sec + 8) && (DnsMsgLen(msg) + GetRessourceRecordLength(&cached->rr) < mdnsd->frame)) {
+                DnsMsgAdd_an(msg, query->name, query->type, mdnsd->class, cached->rr.ttl - mdnsd->now.tv_sec);
+                MdnsdCopyAnswer(msg, &cached->rr);
             }
         }
-        d->checkqlist = nextbest;
+
+        mdnsd->checkqlist = nextbest;
     }
 
-    if(d->now.tv_sec > d->expireall)
-        _gc(d);
+    if (mdnsd->now.tv_sec > mdnsd->expireall) {
+        MdnsdCacheGarbageCollect(mdnsd);
+    }
 
     return ret;
 }
 
-struct timeval *mdnsd_sleep(mdnsd d)
+
+/*!
+ * \brief Send outgoing messages to the host.
+ *
+ * This function returns the max wait-time until MdnsdOutput() needs to be
+ * called again
+ *
+ * \param mdnsd     MDNS deamon instance
+ *
+ * \return          Maximum time after which MdnsdOutput needs to be called again
+ */
+struct timeval *MdnsdGetMaxSleepTime(TMdnsd *mdnsd)
 {
     int sec, usec;
-    d->sleep.tv_sec = d->sleep.tv_usec = 0;
-    #define RET while(d->sleep.tv_usec > 1000000) {d->sleep.tv_sec++;d->sleep.tv_usec -= 1000000;} return &d->sleep;
+    mdnsd->sleep.tv_sec = mdnsd->sleep.tv_usec = 0;
 
-    // first check for any immediate items to handle
-    if(d->uanswers || d->a_now) return &d->sleep;
+    /* first check for any immediate items to handle */
+    if(mdnsd->uanswers || mdnsd->a_now) {
+        return &mdnsd->sleep;
+    }
 
-    gettimeofday(&d->now,0);
+    gettimeofday(&mdnsd->now,0);
 
-    if(d->a_pause)
-    { // then check for paused answers
-        if((usec = _tvdiff(d->now,d->pause)) > 0) d->sleep.tv_usec = usec;
-        RET;
+    if(mdnsd->a_pause) {
+        /* Check for paused answers */
+        if ((usec = TvDiff(mdnsd->now,mdnsd->pause)) > 0) {
+            mdnsd->sleep.tv_usec = usec;
+        }
+        goto out;
     }
 
-    if(d->probing)
-    { // now check for probe retries
-        if((usec = _tvdiff(d->now,d->probe)) > 0) d->sleep.tv_usec = usec;
-        RET;
+    if(mdnsd->probing) {
+        /* Check for probe retries */
+        if ((usec = TvDiff(mdnsd->now,mdnsd->probe)) > 0) {
+            mdnsd->sleep.tv_usec = usec;
+        }
+        goto out;
     }
 
-    if(d->a_publish)
-    { // now check for publish retries
-        if((usec = _tvdiff(d->now,d->publish)) > 0) d->sleep.tv_usec = usec;
-        RET;
+    if(mdnsd->a_publish) {
+        /* Check for publish retries */
+        if ((usec = TvDiff(mdnsd->now,mdnsd->publish)) > 0) {
+            mdnsd->sleep.tv_usec = usec;
+        }
+        goto out;
     }
 
-    if(d->checkqlist)
-    { // also check for queries with known answer expiration/retry
-        if((sec = d->checkqlist - d->now.tv_sec) > 0) d->sleep.tv_sec = sec;
-        RET;
+    if(mdnsd->checkqlist) {
+        /* Also check for queries with known answer expiration/retry */
+        if ((sec = mdnsd->checkqlist - mdnsd->now.tv_sec) > 0) {
+            mdnsd->sleep.tv_sec = sec;
+        }
+        goto out;
+    }
+
+    /* Last resort, next gc expiration */
+    if ((sec = mdnsd->expireall - mdnsd->now.tv_sec) > 0) {
+        mdnsd->sleep.tv_sec = sec;
     }
 
-    // last resort, next gc expiration
-    if((sec = d->expireall - d->now.tv_sec) > 0) d->sleep.tv_sec = sec;
-    RET;
+out:
+    /* Fix up seconds ... */
+    while (mdnsd->sleep.tv_usec > 1000000) {
+        mdnsd->sleep.tv_sec++;
+        mdnsd->sleep.tv_usec -= 1000000;
+    }
+    return &mdnsd->sleep;
 }
 
-void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *arg), void *arg)
+/*******************************************************************************
+ *                       Query and answer functions                            *
+ *******************************************************************************/
+
+/*!
+ * \brief Register a new query
+ *
+ * Answer(record, arg) is called whenever one is found/changes/expires
+ * (immediate or anytime after, mdnsda valid until ->ttl==0)
+ * Either answer returns -1, or another MdnsdQuery with a NULL answer will
+ * remove/unregister this query
+ * This function returns the max wait-time until MdnsdOutput() needs to be
+ * called again
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param host      Hostname
+ * \param type      Query type
+ * \param answer    Callback to the answer function, which shall be called if an
+ *                  answer was found / changes / expires...
+ *
+ * \return          Maximum time after which MdnsdOutput needs to be called again
+ */
+void MdnsdQuery(TMdnsd *mdnsd, uint8_t *host, int type, int (*answer)(TMdnsdAnswer *answer, void *arg), void *arg)
 {
-    struct query *q;
-    struct cached *cur = 0;
-    int i = _namehash(host) % SPRIME;
-    if(!(q = _q_next(d,0,host,type)))
-    {
-        if(!answer) return;
-        q = (struct query *)malloc(sizeof(struct query));
-        bzero(q,sizeof(struct query));
-        q->name = strdup(host);
-        q->type = type;
-        q->next = d->queries[i];
-        q->list = d->qlist;
-        d->qlist = d->queries[i] = q;
-        while((cur = _c_next(d,cur,q->name,q->type)))
-            cur->q = q; // any cached entries should be associated
-        _q_reset(d,q);
-        q->nexttry = d->checkqlist = d->now.tv_sec; // new questin, immediately send out
+    TQuery  *query;
+    TCached *current = NULL;
+    int      idx;
+
+    idx = NameHash(host) % SPRIME;
+    if ((query = QueryNext(mdnsd, 0, host,type)) == NULL) {
+        if (answer == NULL) {
+            return;
+        }
+
+        query = (TQuery *)malloc(sizeof(TQuery));
+        memset(query, 0, sizeof(TQuery));
+
+        query->name = strdup(host);
+        query->type = type;
+        query->next = mdnsd->queries[idx];
+        query->list = mdnsd->qlist;
+        mdnsd->qlist = mdnsd->queries[idx] = query;
+
+        while ((current = CachedNext(mdnsd, current, query->name, query->type))) {
+            /* Any cached entries should be associated */
+            current->query = query;
+        }
+
+        MdnsdQueryReset(mdnsd, query);
+
+        /* New questin, immediately send out */
+        query->nexttry = mdnsd->checkqlist = mdnsd->now.tv_sec;
     }
-    if(!answer)
-    { // no answer means we don't care anymore
-        _q_done(d,q);
+
+    if (answer == NULL) {
+        /* no answer means we don't care anymore */
+        MdnsdQueryDone(mdnsd, query);
         return;
     }
-    q->answer = answer;
-    q->arg = arg;
+
+    query->answer = answer;
+    query->arg = arg;
 }
 
-mdnsda mdnsd_list(mdnsd d, char *host, int type, mdnsda last)
+/*!
+ * \brief Get the next answer from the cache
+ *
+ * Returns the first (if last == NULL) or next answer after last from the cache
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param host      Hostname
+ * \param type      Query type
+ * \param last      Last answer
+ *
+ * \return          next cached answer
+ */
+TMdnsdAnswer *MdnsdListCachedAnswers(TMdnsd *mdnsd, uint8_t *host, int type, TMdnsdAnswer *last)
 {
-    return (mdnsda)_c_next(d,(struct cached *)last,host,type);
+    return (TMdnsdAnswer *)CachedNext(mdnsd, (TCached *)last, host, type);
 }
 
-mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl)
+/*******************************************************************************
+ *                        Publishing functions                                 *
+ *******************************************************************************/
+
+/*!
+ * \brief Create a new shared record
+ *
+ * Returns the newly allocated record struct
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param host      Hostname
+ * \param type      Query type
+ * \param ttl       Time to live value
+ *
+ * \return          newly allocated share record
+ */
+TMdnsdRecord *MdnsdAllocShared(TMdnsd *mdnsd, uint8_t *host, int type, uint32_t ttl)
 {
-    int i = _namehash(host) % SPRIME;
-    mdnsdr r;
-    r = (mdnsdr)malloc(sizeof(struct mdnsdr_struct));
-    bzero(r,sizeof(struct mdnsdr_struct));
-    r->rr.name = strdup(host);
-    r->rr.type = type;
-    r->rr.ttl = ttl;
-    r->next = d->published[i];
-    d->published[i] = r;
-    return r;
+    int idx;
+    TMdnsdRecord *record;
+
+    idx = NameHash(host) % SPRIME;
+
+    record = (TMdnsdRecord *)malloc(sizeof(TMdnsdRecord));
+    memset(record, 0, sizeof(TMdnsdRecord));
+
+    record->rr.name = strdup(host);
+    record->rr.type = type;
+    record->rr.ttl = ttl;
+    record->next = mdnsd->published[idx];
+
+    mdnsd->published[idx] = record;
+
+    return record;
 }
 
-mdnsdr mdnsd_unique(mdnsd d, char *host, int type, long int ttl, void (*conflict)(mdnsdr r, char *host, int type, void *arg), void *arg)
+
+/*!
+ * \brief Create a new unique record
+ *
+ * Create a new unique record (try MdnsdListCachedAnswers first to make sure
+ * it's not used)
+ *
+ * The conflict callback will be called at any point when one is detected and
+ * is unable to recover.
+ *
+ * After the first data is set by MdnsdSet*(), any future changes effectively
+ * expire the old one and attempt to create a new unique record.
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param host      Hostname
+ * \param type      Query type
+ * \param ttl       Time to live value
+ * \param conflict  Callback function called in case of a conflict
+ * \param arg       Argument passed to the conflict callback
+ *
+ * \return          newly allocated share record
+ */
+TMdnsdRecord *MdnsdAllocUnique(TMdnsd *mdnsd, uint8_t *host, int type, uint32_t ttl, void (*conflict)(TMdnsdRecord *record, uint8_t *host, int type, void *arg), void *arg)
 {
-    mdnsdr r;
-    r = mdnsd_shared(d,host,type,ttl);
-    r->conflict = conflict;
-    r->arg = arg;
-    r->unique = 1;
-    _r_push(&d->probing,r);
-    d->probe.tv_sec = d->now.tv_sec;
-    d->probe.tv_usec = d->now.tv_usec;
-    return r;
+    TMdnsdRecord *record;
+    record = MdnsdAllocShared(mdnsd, host, type, ttl);
+    record->conflict = conflict;
+    record->arg = arg;
+    record->unique = 1;
+    MdnsdPushRecord(&mdnsd->probing, record);
+    mdnsd->probe.tv_sec = mdnsd->now.tv_sec;
+    mdnsd->probe.tv_usec = mdnsd->now.tv_usec;
+    return record;
 }
 
-void mdnsd_done(mdnsd d, mdnsdr r)
+
+/*!
+ * \brief Remove record from the list and clean up
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param record    The record which shall be de-listed
+ *
+ * \return          newly allocated share record
+ */
+void MdnsdDone(TMdnsd *mdnsd, TMdnsdRecord *record)
 {
-    mdnsdr cur;
-    if(r->unique && r->unique < 5)
+    TMdnsdRecord *cur;
+    if(record->unique && record->unique < 5)
     { // probing yet, zap from that list first!
-        if(d->probing == r) d->probing = r->list;
-        else {
-            for(cur=d->probing;cur->list != r;cur = cur->list);
-            cur->list = r->list;
+        if(mdnsd->probing == record) {
+            mdnsd->probing = record->list;
+        } else {
+            for (cur=mdnsd->probing; cur->list != record; cur = cur->list);
+            cur->list = record->list;
         }
-        _r_done(d,r);
+        MdnsdRecordDone(mdnsd, record);
         return;
     }
-    r->rr.ttl = 0;
-    _r_send(d,r);
+    record->rr.ttl = 0;
+    MdnsdSendRecord(mdnsd, record);
 }
 
-void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len)
+
+/*!
+ * \brief Set/update raw data of the record and call publish
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param record    The record which shall be de-listed
+ * \param data      Raw record data
+ * \param len       Datalength
+ */
+void MdnsdSetRaw(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *data, int len)
 {
-    free(r->rr.rdata);
-    r->rr.rdata = (unsigned char *)malloc(len);
-    memcpy(r->rr.rdata,data,len);
-    r->rr.rdlen = len;
-    _r_publish(d,r);
+    free(record->rr.rdata);
+    record->rr.rdata = (uint8_t *)malloc(len);
+    memcpy(record->rr.rdata,data,len);
+    record->rr.rdlen = len;
+    MdnsdPublishRecord(mdnsd, record);
 }
 
-void mdnsd_set_host(mdnsd d, mdnsdr r, char *name)
+
+/*!
+ * \brief Set/update record host entry and call publish
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param record    The record which shall be de-listed
+ * \param name      Hostname
+ */
+void MdnsdSetHost(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *name)
 {
-    free(r->rr.rdname);
-    r->rr.rdname = strdup(name);
-    _r_publish(d,r);
+    free(record->rr.rdname);
+    record->rr.rdname = strdup(name);
+    MdnsdPublishRecord(mdnsd, record);
 }
 
-void mdnsd_set_ip(mdnsd d, mdnsdr r, unsigned long int ip)
+
+/*!
+ * \brief Set/update IP address entry and call publish
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param record    The record which shall be de-listed
+ * \param ip        IP address
+ */
+void MdnsdSetIp(TMdnsd *mdnsd, TMdnsdRecord *record, struct in_addr ip)
 {
-    r->rr.ip = ip;
-    _r_publish(d,r);
+    record->rr.ip = ip;
+    MdnsdPublishRecord(mdnsd, record);
 }
 
-void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char *name)
+
+/*!
+ * \brief Set/update service info and call publish
+ *
+ * \param mdnsd     MDNS deamon instance
+ * \param record    The record which shall be de-listed
+ * \param priority  Priority of the target host: lower value means more preferred.
+ * \param weight    Relative weight for records with the same priority.
+ * \param port      TCP / UDP port number of the service
+ * \param name      The canonical hostname of the machine providing the service.
+ */
+void MdnsdSetSrv(TMdnsd *mdnsd, TMdnsdRecord *record, int priority, int weight, uint16_t port, uint8_t *name)
 {
-    r->rr.srv.priority = priority;
-    r->rr.srv.weight = weight;
-    r->rr.srv.port = port;
-    mdnsd_set_host(d,r,name);
+    record->rr.srv.priority = priority;
+    record->rr.srv.weight = weight;
+    record->rr.srv.port = port;
+    MdnsdSetHost(mdnsd, record, name);
 }
 
+/*@}*/
diff --git a/mdnsd.h b/mdnsd.h
index 636ebbfbebcc479a2b11f16c99ee084cb75d4689..c5cc7b4d7ba077eba4518933615eae2ed66f698b 100644 (file)
--- a/mdnsd.h
+++ b/mdnsd.h
-#ifndef mdnsd_h
-#define mdnsd_h
-#include "1035.h"
+#ifndef _MDNSD_H_
+#define _MDNSD_H_
+
+/*
+ * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
+ * Copyright (c) 2009 Simon Budig <simon@budig.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file include/pro/mdnsd.h
+ * \brief Multicast DNS Deamon
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
+#include "rfc1035.h"
 #include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+#define MDNS_PORT         5353
+#define MDNS_MULTICAST_IP "224.0.0.251"
+
+/* Size of query/publish hashes */
+#define SPRIME 108
+
+/* size of cache hash */
+#define LPRIME 1009
+
+/* brute force garbage cleanup frequency, rarely needed (daily default) */
+#define GC     86400
+
+
+/*!
+ * \brief MDNS answer data
+ */
+typedef struct _mdnsda_struct  TMdnsdAnswer;
+struct _mdnsda_struct
+{
+    uint8_t *name;
+    uint16_t type;
+    uint32_t ttl;
+    uint16_t rdlen;
+    uint8_t *rdata;
+    struct in_addr ip; // A
+    uint8_t *rdname;   // NS/CNAME/PTR/SRV
+    struct {
+        uint16_t priority;
+        uint16_t weight;
+        uint16_t port;
+    } srv; // SRV
+};
+
+
+/*!
+ * \brief MDNS record entry
+ */
+typedef struct _mdnsdr_struct  TMdnsdRecord;
+struct _mdnsdr_struct
+{
+    TMdnsdAnswer rr;
+    int8_t unique; // # of checks performed to ensure
+    int    tries;
+    void  (*conflict)(TMdnsdRecord *record, uint8_t *name, int type, void *arg);
+    void   *arg;
+    TMdnsdRecord *next;
+    TMdnsdRecord *list;
+};
+
+
+/*!
+ * \brief MDNS query struct
+ */
+typedef struct _query_struct   TQuery;
+struct _query_struct
+{
+    uint8_t *name;
+    int      type;
+    uint32_t nexttry;
+    int      tries;
+    int (*answer)(TMdnsdAnswer *answer, void *arg);
+    void    *arg;
+    TQuery  *next;
+    TQuery  *list;
+};
+
+
+/*!
+ * \brief Unicast record data
+ */
+typedef struct _unicast_struct TUnicast;
+struct _unicast_struct
+{
+    int       id;
+    struct in_addr to;
+    uint16_t  port;
+    TMdnsdRecord *record;
+    TUnicast *next;
+};
+
 
-typedef struct mdnsd_struct *mdnsd; // main daemon data
-typedef struct mdnsdr_struct *mdnsdr; // record entry
-// answer data
-typedef struct mdnsda_struct
+/*!
+ * \brief Cached record entry struct
+ */
+typedef struct _cached_struct  TCached;
+struct _cached_struct
 {
-    unsigned char *name;
-    unsigned short int type;
-    unsigned long int ttl;
-    unsigned short int rdlen;
-    unsigned char *rdata;
-    unsigned long int ip; // A
-    unsigned char *rdname; // NS/CNAME/PTR/SRV
-    struct { unsigned short int priority, weight, port; } srv; // SRV
-} *mdnsda;
-
-///////////
-// Global functions
-//
-// create a new mdns daemon for the given class of names (usually 1) and maximum frame size
-mdnsd mdnsd_new(int class, int frame);
-//
-// gracefully shutdown the daemon, use mdnsd_out() to get the last packets
-void mdnsd_shutdown(mdnsd d);
-//
-// flush all cached records (network/interface changed)
-void mdnsd_flush(mdnsd d);
-//
-// free given mdnsd (should have used mdnsd_shutdown() first!)
-void mdnsd_free(mdnsd d);
-//
-///////////
-
-///////////
-// I/O functions
-//
-// incoming message from host (to be cached/processed)
-void mdnsd_in(mdnsd d, struct message *m, unsigned long int ip, unsigned short int port);
-//
-// outgoing messge to be delivered to host, returns >0 if one was returned and m/ip/port set
-int mdnsd_out(mdnsd d, struct message *m, unsigned long int *ip, unsigned short int *port);
-//
-// returns the max wait-time until mdnsd_out() needs to be called again 
-struct timeval *mdnsd_sleep(mdnsd d);
-//
-////////////
-
-///////////
-// Q/A functions
-// 
-// register a new query
-//   answer(record, arg) is called whenever one is found/changes/expires (immediate or anytime after, mdnsda valid until ->ttl==0)
-//   either answer returns -1, or another mdnsd_query with a NULL answer will remove/unregister this query
-void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *arg), void *arg);
-//
-// returns the first (if last == NULL) or next answer after last from the cache
-//   mdnsda only valid until an I/O function is called
-mdnsda mdnsd_list(mdnsd d, char *host, int type, mdnsda last);
-//
-///////////
-
-///////////
-// Publishing functions
-//
-// create a new unique record (try mdnsda_list first to make sure it's not used)
-//   conflict(arg) called at any point when one is detected and unable to recover
-//   after the first data is set_*(), any future changes effectively expire the old one and attempt to create a new unique record
-mdnsdr mdnsd_unique(mdnsd d, char *host, int type, long int ttl, void (*conflict)(mdnsdr r, char *host, int type, void *arg), void *arg);
-// 
-// create a new shared record
-mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl);
-//
-// de-list the given record
-void mdnsd_done(mdnsd d, mdnsdr r);
-//
-// these all set/update the data for the given record, nothing is published until they are called
-void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len);
-void mdnsd_set_host(mdnsd d, mdnsdr r, char *name);
-void mdnsd_set_ip(mdnsd d, mdnsdr r, unsigned long int ip);
-void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char *name);
-//
-///////////
+    TMdnsdAnswer rr;
+    TQuery      *query;
+    TCached     *next;
+};
+
+
+/*!
+ * \brief Main MDNS deamon data
+ */
+typedef struct _mdnsd_struct   TMdnsd;
+struct _mdnsd_struct
+{
+    int8_t shutdown;
+    uint32_t expireall, checkqlist;
+    struct timeval now;
+    struct timeval sleep;
+    struct timeval pause;
+    struct timeval probe;
+    struct timeval publish;
+    int    class;
+    int    frame;
+    TCached      *cache[LPRIME];
+    TMdnsdRecord *published[SPRIME];
+    TMdnsdRecord *probing;
+    TMdnsdRecord *a_now;
+    TMdnsdRecord *a_pause;
+    TMdnsdRecord *a_publish;
+    TUnicast     *uanswers;
+    TQuery       *queries[SPRIME];
+    TQuery       *qlist;
+};
+
+/* Global functions */
+TMdnsd *MdnsdNew(int class, int frame);
+void MdnsdShutdown(TMdnsd *mdnsd);
+void MdnsdFlush(TMdnsd *mdnsd);
+void MdnsdFree(TMdnsd *mdnsd);
+
+/* I/O functions */
+void MdnsdInput(TMdnsd *mdnsd, DNSMESSAGE *m, struct in_addr ip, uint16_t port);
+int MdnsdOutput(TMdnsd *mdnsd, DNSMESSAGE *m, struct in_addr *ip, uint16_t *port);
+struct timeval *MdnsdGetMaxSleepTime(TMdnsd *mdnsd);
+
+/* Qery / Answer functions */
+void MdnsdQuery(TMdnsd *mdnsd, uint8_t *host, int type, int (*answer)(TMdnsdAnswer *a, void *arg), void *arg);
+TMdnsdAnswer *MdnsdListCachedAnswers(TMdnsd *mdnsd, uint8_t *host, int type, TMdnsdAnswer *last);
+
+/* Publishing functions */
+TMdnsdRecord *MdnsdAllocUnique(TMdnsd *mdnsd, uint8_t *host, int type, uint32_t ttl, void (*conflict)(TMdnsdRecord *record, uint8_t *host, int type, void *arg), void *arg);
+TMdnsdRecord *MdnsdAllocShared(TMdnsd *mdnsd, uint8_t *host, int type, uint32_t ttl);
+void MdnsdDone(TMdnsd *mdnsd, TMdnsdRecord *record);
 
+/* These all set/update the data for the given record, nothing is published until they are called */
+void MdnsdSetRaw(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *data, int len);
+void MdnsdSetHost(TMdnsd *mdnsd, TMdnsdRecord *record, uint8_t *name);
+void MdnsdSetIp(TMdnsd *mdnsd, TMdnsdRecord *record, struct in_addr ip);
+void MdnsdSetSrv(TMdnsd *mdnsd, TMdnsdRecord *record, int priority, int weight, uint16_t port, uint8_t *name);
 
 #endif
diff --git a/mhttp.c b/mhttp.c
index 4ed14516f8cb8e3a550c46ebc249e688d83c3ea2..3acb93773e9a4ff5130637f0d9a286eefbd9c2c7 100644 (file)
--- a/mhttp.c
+++ b/mhttp.c
@@ -1,5 +1,3 @@
-#define _GNU_SOURCE
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <signal.h>
 #include <poll.h>
 #include <sys/time.h>
+#include <sys/stat.h>
 
 #include "mdnsd.h"
-#include "sdtxt.h"
-#include "netwatch.h"
+#include "dns_sd_txt.h"
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
 
 #define HOSTNAMESIZE 64
 #define FIFO_PATH "/tmp/mdns-fifo"
 
-#define MAX_ANNOUNCE_IP 2
+#define MAX_ANNOUNCE_IP 1
 
 enum {
-   MDNSD_STARTUP,
-   MDNSD_PROBE,
-   MDNSD_ANNOUNCE,
-   MDNSD_RUN,
-   MDNSD_SHUTDOWN
+    MDNSD_STARTUP,
+    MDNSD_PROBE,
+    MDNSD_ANNOUNCE,
+    MDNSD_RUN,
+    MDNSD_SHUTDOWN
 };
 
 typedef struct _ipcam_ip_info
 {
-  char          *label;
-  char          *ip;
-  int            link_id;
-
-  /* service-discovery records */
-  mdnsdr         host_to_ip;
-  mdnsdr         ip_to_host;
-  long int       announce_ip;
+    char          *label;
+    char          *ip;
+    int            link_id;
+
+    /* service-discovery records */
+    TMdnsdRecord  *host_to_ip;
+    TMdnsdRecord  *ip_to_host;
+    struct in_addr announce_ip;
 } IpcamIPInfo;
 
 typedef struct _ipcam_service_info
 {
-  mdnsd          dnsd;
-  char           hostname[HOSTNAMESIZE];
-  char          *servicename;
+    TMdnsd        *mdnsd;
+    char           hostname[HOSTNAMESIZE];
+    char          *servicename;
 
-  int            port;
+    int            port;
 
-  IpcamIPInfo    ipinfos[MAX_ANNOUNCE_IP];
+    IpcamIPInfo    ipinfos[MAX_ANNOUNCE_IP];
 
-  xht            metadata;
+    SHASH          metadata;
 
-  /* service-discovery records */
-  mdnsdr         srv_to_host;
-  mdnsdr         txt_for_srv;
+    /* service-discovery records */
+    TMdnsdRecord  *srv_to_host;
+    TMdnsdRecord  *txt_for_srv;
 
-  mdnsdr         ptr_to_srv;
+    TMdnsdRecord  *ptr_to_srv;
 
-  int            state;
+    int            state;
 } IpcamServiceInfo;
 
 static IpcamServiceInfo ipcam_info;
@@ -74,88 +81,88 @@ void     request_ip_addresses (IpcamServiceInfo *info);
 char *
 increment_name (char *name)
 {
-  int   id = 1;
-  char *pos, *end = NULL;
-  char *ret = NULL;
+    int   id = 1;
+    char *pos, *end = NULL;
+    char *ret = NULL;
 
-  pos = strrchr (name, '-');
+    pos = strrchr (name, '-');
 
-  if (pos)
+    if (pos)
     {
-      id = strtol (pos + 1, &end, 10);
-      if (*end == '\0')
-        *pos = '\0';
-      else
-        id = 1;
+        id = strtol (pos + 1, &end, 10);
+        if (*end == '\0')
+            *pos = '\0';
+        else
+            id = 1;
     }
 
-  id += 1;
+    id += 1;
 
-  asprintf (&ret, "%s-%d", name, id);
+    asprintf (&ret, "%s-%d", name, id);
 
-  return ret;
+    return ret;
 }
 
 
 /* conflict handling */
 void
-handle_conflict (mdnsdr r, char *name, int type, void *arg)
+handle_conflict (TMdnsdRecord *r, uint8_t *name, int UNUSED(type), void *arg)
 {
-  IpcamServiceInfo *info = (IpcamServiceInfo *) arg;
-  char *newname;
-  int i;
+    IpcamServiceInfo *info = (IpcamServiceInfo *) arg;
+    char *newname;
+    int i;
 
-  for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+    for (i = 0; i < MAX_ANNOUNCE_IP; i++)
     {
-      if (r == info->ipinfos[i].ip_to_host)
+        if (r == info->ipinfos[i].ip_to_host)
         {
-          /* can't do anything about a reverse lookup conflict. Just stop
-           * announcing it. */
-          info->ipinfos[i].ip_to_host = NULL;
-          fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ipinfos[i].label);
-          return;
+            /* can't do anything about a reverse lookup conflict. Just stop
+             * announcing it. */
+            info->ipinfos[i].ip_to_host = NULL;
+            fprintf (stderr, "zeroconf reverse lookup conflict for %s!\n", info->ipinfos[i].label);
+            return;
         }
-      if (r == info->ipinfos[i].host_to_ip)
+        if (r == info->ipinfos[i].host_to_ip)
         {
-          info->ipinfos[i].host_to_ip = NULL;
-          info->ipinfos[i].announce_ip = 0;
+            info->ipinfos[i].host_to_ip = NULL;
+            info->ipinfos[i].announce_ip.s_addr = 0;
         }
     }
 
-  if (info->servicename == NULL)
+    if (info->servicename == NULL)
     {
-      newname = increment_name (info->hostname);
+        newname = increment_name (info->hostname);
     }
-  else
+    else
     {
-      newname = increment_name (info->servicename);
-      free (info->servicename);
+        newname = increment_name (info->servicename);
+        free (info->servicename);
     }
 
-  info->servicename = newname;
+    info->servicename = newname;
 
-  if (r == info->srv_to_host)
-    info->srv_to_host = NULL;
-  if (r == info->txt_for_srv)
-    info->txt_for_srv = NULL;
+    if (r == info->srv_to_host)
+        info->srv_to_host = NULL;
+    if (r == info->txt_for_srv)
+        info->txt_for_srv = NULL;
 
-  fprintf (stderr, "conflicting name \"%s\". trying %s\n",
-           name, info->servicename);
+    fprintf (stderr, "conflicting name \"%s\". trying %s\n",
+             name, info->servicename);
 
-  info->state = MDNSD_PROBE;
-  write (signal_pipe[1], " ", 1);
+    info->state = MDNSD_PROBE;
+    write (signal_pipe[1], " ", 1);
 }
 
 
 /* quit and updates */
 void sighandler (int sig)
 {
-  if (sig != SIGHUP)
+    if (sig != SIGHUP)
     {
-      ipcam_info.state = MDNSD_SHUTDOWN;
+        ipcam_info.state = MDNSD_SHUTDOWN;
     }
 
-  write (signal_pipe[1], " ", 1);
+    write (signal_pipe[1], " ", 1);
 }
 
 
@@ -163,192 +170,198 @@ void sighandler (int sig)
 int
 msock ()
 {
-  int s, flag = 1, ittl = 255;
-  struct sockaddr_in in;
-  struct ip_mreq mc;
-  char ttl = 255;
+    int s, flag = 1, ittl = 255;
+    struct sockaddr_in in;
+    struct ip_mreq mc;
+    char ttl = 255;
 
-  bzero (&in, sizeof (in));
-  in.sin_family = AF_INET;
-  in.sin_port = htons (5353);
-  in.sin_addr.s_addr = 0;
+    bzero (&in, sizeof (in));
+    in.sin_family = AF_INET;
+    in.sin_port = htons (MDNS_PORT);
+    in.sin_addr.s_addr = 0;
 
-  if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
-    return 0;
+    if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
+        return 0;
 
-  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
-  if (bind (s, (struct sockaddr*) &in, sizeof (in)))
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &flag, sizeof (flag));
+    if (bind (s, (struct sockaddr*) &in, sizeof (in)))
     {
-      close(s);
-      return 0;
+        close(s);
+        return 0;
     }
 
-  mc.imr_multiaddr.s_addr = inet_addr ("224.0.0.251");
-  mc.imr_interface.s_addr = htonl (INADDR_ANY);
-  setsockopt (s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc,   sizeof (mc));
-  setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL,  &ttl,  sizeof (ttl));
-  setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL,  &ittl, sizeof (ittl));
+    mc.imr_multiaddr.s_addr = inet_addr ("224.0.0.251");
+    mc.imr_interface.s_addr = htonl (INADDR_ANY);
+    setsockopt (s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mc,   sizeof (mc));
+    setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL,  &ttl,  sizeof (ttl));
+    setsockopt (s, IPPROTO_IP, IP_MULTICAST_TTL,  &ittl, sizeof (ittl));
 
-  flag =  fcntl (s, F_GETFL, 0);
-  flag |= O_NONBLOCK;
-  fcntl (s, F_SETFL, flag);
+    flag =  fcntl (s, F_GETFL, 0);
+    flag |= O_NONBLOCK;
+    fcntl (s, F_SETFL, flag);
 
-  return s;
+    return s;
 }
 
 void
 request_ip_addresses (IpcamServiceInfo *info)
 {
-  char revlookup[256], hostlocal[256];
-  int i;
-  long int ip;
-  int num_ips = 0;
+    char revlookup[256], hostlocal[256];
+    int i;
+    struct in_addr ip;
+    int num_ips = 0;
 
-  sprintf (hostlocal, "%s.local.",
-           info->servicename ? info->servicename : info->hostname);
+    sprintf (hostlocal, "%s.local.",
+             info->servicename ? info->servicename : info->hostname);
 
-  for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+    for (i = 0; i < MAX_ANNOUNCE_IP; i++)
     {
-      if (info->ipinfos[i].ip)
+printf("--> ip --> %s\n", info->ipinfos[i].ip);
+        if (info->ipinfos[i].ip)
         {
-          ip = inet_addr (info->ipinfos[i].ip);
+            ip.s_addr = inet_addr (info->ipinfos[i].ip);
 
-          if (ip != info->ipinfos[i].announce_ip)
+            if (ip.s_addr != info->ipinfos[i].announce_ip.s_addr)
             {
-              snprintf (revlookup, 256, "%ld.%ld.%ld.%ld.in-addr.arpa.",
-                        (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
-
-              if (!info->ipinfos[i].host_to_ip)
+                snprintf (revlookup, 256, "%d.%d.%d.%d.in-addr.arpa.",
+                          (ip.s_addr >> 24) & 0xff, (ip.s_addr >> 16) & 0xff, (ip.s_addr >> 8) & 0xff, (ip.s_addr >> 0) & 0xff);
+                // TODO: OR
+                printf("revlookup %d -> %s\n", i, revlookup);
+                if (!info->ipinfos[i].host_to_ip)
                 {
-                  info->ipinfos[i].host_to_ip  = mdnsd_unique (info->dnsd, hostlocal,
-                                                               QTYPE_A, 120, handle_conflict, info);
+                    info->ipinfos[i].host_to_ip  = MdnsdAllocUnique (info->mdnsd, hostlocal,
+                                                                 QTYPE_A, 120, handle_conflict, info);
                 }
-              mdnsd_set_raw (info->dnsd, info->ipinfos[i].host_to_ip, (unsigned char *) &ip, 4);
+                MdnsdSetRaw (info->mdnsd, info->ipinfos[i].host_to_ip, (uint8_t *) &ip, 4);
 
-              if (!info->ipinfos[i].ip_to_host)
+                if (!info->ipinfos[i].ip_to_host)
                 {
-                  info->ipinfos[i].ip_to_host  = mdnsd_unique (info->dnsd, revlookup,
-                                                               QTYPE_PTR, 120, handle_conflict, info);
+                    info->ipinfos[i].ip_to_host  = MdnsdAllocUnique (info->mdnsd, revlookup,
+                                                                 QTYPE_PTR, 120, handle_conflict, info);
                 }
-              mdnsd_set_host (info->dnsd, info->ipinfos[i].ip_to_host, hostlocal);
+                MdnsdSetHost (info->mdnsd, info->ipinfos[i].ip_to_host, hostlocal);
 
-              info->ipinfos[i].announce_ip = ip;
+                info->ipinfos[i].announce_ip = ip;
             }
 
-          num_ips++;
+            num_ips++;
         }
-      else
+        else
         {
-          if (info->ipinfos[i].host_to_ip)
-            mdnsd_done (ipcam_info.dnsd, info->ipinfos[i].host_to_ip);
-          if (info->ipinfos[i].ip_to_host)
-            mdnsd_done (ipcam_info.dnsd, info->ipinfos[i].ip_to_host);
-
-          info->ipinfos[i].host_to_ip = NULL;
-          info->ipinfos[i].ip_to_host = NULL;
-          info->ipinfos[i].announce_ip = 0;
+            if (info->ipinfos[i].host_to_ip)
+                MdnsdDone (ipcam_info.mdnsd, info->ipinfos[i].host_to_ip);
+            if (info->ipinfos[i].ip_to_host)
+                MdnsdDone (ipcam_info.mdnsd, info->ipinfos[i].ip_to_host);
+
+            info->ipinfos[i].host_to_ip = NULL;
+            info->ipinfos[i].ip_to_host = NULL;
+            info->ipinfos[i].announce_ip.s_addr = 0;
         }
     }
 
-  if (!num_ips)
-    info->state = MDNSD_STARTUP;
+    if (!num_ips) {
+        printf("----> Set state startup\n");
+        info->state = MDNSD_STARTUP;
+    }
 }
 
 void
 request_service (IpcamServiceInfo *info, int stage)
 {
-  unsigned char *packet, servlocal[256], hostlocal[256];
-  int i, len = 0;
-
-  sprintf (servlocal, "%s._http._tcp.local.",
-           info->servicename ? info->servicename : info->hostname);
-
-  /*
-   * Timeouts according to
-   *   http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
-   *
-   * As a general rule, the recommended TTL value for Multicast DNS
-   * resource records with a host name as the resource record's name
-   * (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
-   * rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
-   *
-   * The recommended TTL value for other Multicast DNS resource records
-   * is 75 minutes.
-   */
-
-  switch (stage)
+    uint8_t *packet, servlocal[256], hostlocal[256];
+    int i, len = 0;
+
+    sprintf (servlocal, "%s._http._tcp.local.",
+             info->servicename ? info->servicename : info->hostname);
+
+    /*
+     * Timeouts according to
+     *   http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt
+     *
+     * As a general rule, the recommended TTL value for Multicast DNS
+     * resource records with a host name as the resource record's name
+     * (e.g. A, AAAA, HINFO, etc.) or contained within the resource record's
+         * rdata (e.g. SRV, reverse mapping PTR record, etc.) is 120 seconds.
+             *
+     * The recommended TTL value for other Multicast DNS resource records
+     * is 75 minutes.
+     */
+
+    switch (stage)
     {
-      case 0:
-        request_ip_addresses (info);
+        case 0:
+            printf("---> request_service\n");
+            request_ip_addresses (info);
 
-        break;
+            break;
 
-      case 1:
-        sprintf (hostlocal, "%s.local.",
-                 info->servicename ? info->servicename : info->hostname);
+        case 1:
+            sprintf (hostlocal, "%s.local.",
+                     info->servicename ? info->servicename : info->hostname);
 
-        if (!info->srv_to_host)
-          {
-            info->srv_to_host = mdnsd_unique (info->dnsd, servlocal,
+            if (!info->srv_to_host)
+        {
+            info->srv_to_host = MdnsdAllocUnique (info->mdnsd, servlocal,
                                               QTYPE_SRV, 120, handle_conflict, info);
-          }
-        mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
-                       info->port, hostlocal);
+        }
+            MdnsdSetSrv (info->mdnsd, info->srv_to_host, 0, 0,
+                           info->port, hostlocal);
 
-        if (!info->txt_for_srv)
-          {
-            info->txt_for_srv = mdnsd_unique (info->dnsd, servlocal,
+            if (!info->txt_for_srv)
+        {
+            info->txt_for_srv = MdnsdAllocUnique (info->mdnsd, servlocal,
                                               QTYPE_TXT, 4500, handle_conflict, info);
-          }
-        packet = sd2txt (info->metadata, &len);
-        mdnsd_set_raw (info->dnsd, info->txt_for_srv, packet, len);
-        free(packet);
-        break;
-
-      case 2:
-        if (!info->ptr_to_srv)
-          {
-            info->ptr_to_srv  = mdnsd_shared (info->dnsd, "_http._tcp.local.",
+        }
+            packet = DnsSd2Txt (info->metadata, &len);
+            MdnsdSetRaw (info->mdnsd, info->txt_for_srv, packet, len);
+            free(packet);
+            break;
+
+        case 2:
+            if (!info->ptr_to_srv)
+        {
+            info->ptr_to_srv  = MdnsdAllocShared (info->mdnsd, "_http._tcp.local.",
                                               QTYPE_PTR, 4500);
-          }
-        mdnsd_set_host (info->dnsd, info->ptr_to_srv, servlocal);
+        }
+            MdnsdSetHost (info->mdnsd, info->ptr_to_srv, servlocal);
 
-        for (i = 0; i < MAX_ANNOUNCE_IP; i++)
-          {
+            for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+        {
             if (info->ipinfos[i].ip)
-              fprintf (stderr, "Announcing \"%s.local\" to %s:%d\n",
-                       info->servicename ? info->servicename : info->hostname,
-                       info->ipinfos[i].ip, info->port);
-          }
-        break;
-
-      default:
-        fprintf (stderr, "announce stage %d is invalid\n", stage);
-        break;
+                fprintf (stderr, "Announcing \"%s.local\" to %s:%d\n",
+                         info->servicename ? info->servicename : info->hostname,
+                         info->ipinfos[i].ip, info->port);
+        }
+            break;
+
+        default:
+            fprintf (stderr, "announce stage %d is invalid\n", stage);
+            break;
     }
 }
 
 void
 update_port_info (IpcamServiceInfo *info, int port)
 {
-  unsigned char hostlocal[256];
+    uint8_t hostlocal[256];
 
-  if (port == info->port)
-    return;
+    if (port == info->port)
+        return;
 
-  info->port = port;
+    info->port = port;
 
-  if (!info->srv_to_host)
-    return;
+    if (!info->srv_to_host)
+        return;
 
-  sprintf (hostlocal, "%s.local.",
-           info->servicename ? info->servicename : info->hostname);
+    sprintf (hostlocal, "%s.local.",
+             info->servicename ? info->servicename : info->hostname);
 
-  fprintf (stderr, "mhttp: updating port info to port %d\n", info->port);
-  mdnsd_set_srv (info->dnsd, info->srv_to_host, 0, 0,
-                 info->port, hostlocal);
+    fprintf (stderr, "mhttp: updating port info to port %d\n", info->port);
+    MdnsdSetSrv (info->mdnsd, info->srv_to_host, 0, 0,
+                   info->port, hostlocal);
 }
 
+#if 0
 void
 iface_change_callback (int   link_index,
                        char *label,
@@ -356,44 +369,45 @@ iface_change_callback (int   link_index,
                        int   add,
                        void *user_data)
 {
-  IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
-  int i;
+    IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
+    int i;
 
-  for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+    for (i = 0; i < MAX_ANNOUNCE_IP; i++)
     {
-      if (strcmp (info->ipinfos[i].label, label) != 0)
-        continue;
+        if (strcmp (info->ipinfos[i].label, label) != 0)
+            continue;
 
-      if (add && (!info->ipinfos[i].ip ||
-                  strcmp (info->ipinfos[i].ip, ipaddr) != 0 ||
-                  info->ipinfos[i].link_id != link_index))
+        if (add && (!info->ipinfos[i].ip ||
+                    strcmp (info->ipinfos[i].ip, ipaddr) != 0 ||
+                    info->ipinfos[i].link_id != link_index))
         {
-          if (info->ipinfos[i].ip)
-            free (info->ipinfos[i].ip);
-          info->ipinfos[i].ip = strdup (ipaddr);
-          info->ipinfos[i].link_id = link_index;
-          fprintf (stderr, "new ip address on %s: %s\n", label, ipaddr);
+            if (info->ipinfos[i].ip)
+                free (info->ipinfos[i].ip);
+            info->ipinfos[i].ip = strdup (ipaddr);
+            info->ipinfos[i].link_id = link_index;
+            fprintf (stderr, "new ip address on %s: %s\n", label, ipaddr);
         }
 
-      if (!add && info->ipinfos[i].ip)
+        if (!add && info->ipinfos[i].ip)
         {
-          fprintf (stderr, "lost ip address on %s\n", label);
-          free (info->ipinfos[i].ip);
-          info->ipinfos[i].ip = NULL;
-          info->ipinfos[i].link_id = -1;
+            fprintf (stderr, "lost ip address on %s\n", label);
+            free (info->ipinfos[i].ip);
+            info->ipinfos[i].ip = NULL;
+            info->ipinfos[i].link_id = -1;
         }
     }
 
-  if (add && info->state == MDNSD_STARTUP)
+    if (add && info->state == MDNSD_STARTUP)
     {
-      info->state = MDNSD_PROBE;
+        info->state = MDNSD_PROBE;
     }
-  else
+    else
     {
-      request_ip_addresses (info);
+        printf("---> iface_change_callback\n");
+        request_ip_addresses (info);
     }
 
-  write (signal_pipe[1], " ", 1);
+    write (signal_pipe[1], " ", 1);
 }
 
 
@@ -402,334 +416,360 @@ iface_link_callback (int   link_index,
                      int   running,
                      void *user_data)
 {
-  IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
-  int i;
-  int link_changed = 0;
+    IpcamServiceInfo *info = (IpcamServiceInfo *) user_data;
+    int i;
+    int link_changed = 0;
 
-  for (i = 0; i < MAX_ANNOUNCE_IP; i++)
-    if (link_index == info->ipinfos[i].link_id)
-      link_changed = 1;
+    for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+        if (link_index == info->ipinfos[i].link_id)
+        link_changed = 1;
 
-  if (!link_changed)
-    return;
+    if (!link_changed)
+        return;
 
-  info->state = running ? MDNSD_PROBE : MDNSD_STARTUP;
-  write (signal_pipe[1], " ", 1);
+    info->state = running ? MDNSD_PROBE : MDNSD_STARTUP;
+    write (signal_pipe[1], " ", 1);
 }
-
+#endif
 
 int main(int argc, char *argv[])
 {
-  struct message msg;
-  unsigned short int port;
-  struct timeval tv;
-  int bsize, ssize = sizeof(struct sockaddr_in);
-  unsigned char buf[MAX_PACKET_LEN];
-  struct sockaddr_in from, to;
-  int i, s;
-  int nlink;
-  unsigned long remote_ip;
-  char *value;
-  int polltime = 0;
-  int announce_stage = 0;
-  struct pollfd fds[4];
-
-  if(argc < 3)
+    DNSMESSAGE msg;
+    uint16_t port;
+    struct timeval tv;
+    int bsize, ssize = sizeof(struct sockaddr_in);
+    uint8_t buf[MAX_PACKET_LEN];
+    struct sockaddr_in from, to;
+    int i, s;
+//  int nlink;
+    struct in_addr remote_ip;
+    char *value;
+    int polltime = 0;
+    int announce_stage = 0;
+    struct pollfd fds[4];
+
+    if(argc < 4)
     {
-      fprintf (stderr, "usage: mhttp <label1> <label2> <port> <key1>=<value1> <key2>=<value2> ...\n");
-      fprintf (stderr, "   <label1>, <label2> are the labels of the network interface to be watched\n");
-      fprintf (stderr, "   <port> is the port number of the service to be advertized\n");
-      fprintf (stderr, "   <key>=<value> are the keys that get embedded into the TXT record.\n");
-      fprintf (stderr, "\n   The port later can be changed by writing \"port:8080\" to " FIFO_PATH ".\n");
-      return -1;
+        fprintf (stderr, "usage: mhttp <label> <ip> <port> <key1>=<value1> <key2>=<value2> ...\n");
+        fprintf (stderr, "   <label1> are the labels of the network interface to be watched\n");
+        fprintf (stderr, "   <port> is the port number of the service to be advertized\n");
+        fprintf (stderr, "   <key>=<value> are the keys that get embedded into the TXT record.\n");
+        fprintf (stderr, "\n   The port later can be changed by writing \"port:8080\" to " FIFO_PATH ".\n");
+        return -1;
     }
 
-  ipcam_info.dnsd = mdnsd_new (1, 1000);
-
-  ipcam_info.state = MDNSD_STARTUP;
+    ipcam_info.mdnsd = MdnsdNew (1, 1000);
 
-  gethostname (ipcam_info.hostname, HOSTNAMESIZE);
-  ipcam_info.hostname[HOSTNAMESIZE-1] = '\0';
-  if (strchr (ipcam_info.hostname, '.'))
-    strchr (ipcam_info.hostname, '.')[0] = '\0';
+    ipcam_info.state = MDNSD_STARTUP;
+    // TODO: OR
+    //  gethostname (ipcam_info.hostname, HOSTNAMESIZE);
+    strcpy(ipcam_info.hostname, "reinhardt");
+    ipcam_info.hostname[HOSTNAMESIZE-1] = '\0';
+    if (strchr (ipcam_info.hostname, '.'))
+        strchr (ipcam_info.hostname, '.')[0] = '\0';
 
-  ipcam_info.servicename = NULL;
+    ipcam_info.servicename = NULL;
 
-  for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+    for (i = 0; i < MAX_ANNOUNCE_IP; i++)
     {
-      ipcam_info.ipinfos[i].label       = argv[i+1];
-      ipcam_info.ipinfos[i].ip          = NULL;
-      ipcam_info.ipinfos[i].link_id     = -1;
-      ipcam_info.ipinfos[i].announce_ip = 0;
-      ipcam_info.ipinfos[i].host_to_ip  = NULL;
-      ipcam_info.ipinfos[i].ip_to_host  = NULL;
+        ipcam_info.ipinfos[i].label       = strdup("ethx");
+        ipcam_info.ipinfos[i].label[3] = '0' + i;
+
+        ipcam_info.ipinfos[i].ip          = strdup(argv[i+1]);
+        ipcam_info.ipinfos[i].link_id     = -1;
+        ipcam_info.ipinfos[i].announce_ip.s_addr = inet_addr(ipcam_info.ipinfos[i].ip);
+        ipcam_info.ipinfos[i].host_to_ip  = NULL;
+        ipcam_info.ipinfos[i].ip_to_host  = NULL;
     }
 
-  ipcam_info.port = atoi(argv[3]);
+    ipcam_info.port = atoi(argv[1 + MAX_ANNOUNCE_IP]);
 
-  ipcam_info.metadata = xht_new (11);
-  for (i = 4; i < argc; i++)
+    ipcam_info.metadata = SHashInit (11);
+    for (i = 2 + MAX_ANNOUNCE_IP; i < argc; i++)
     {
-      value = index (argv[i], '=');
-      if (value)
+        value = index (argv[i], '=');
+        if (value)
         {
-          value[0] = '\0';
-          value++;
-          xht_set (ipcam_info.metadata, argv[i], value);
+            value[0] = '\0';
+            value++;
+            SHashSet (ipcam_info.metadata, argv[i], value);
         }
     }
 
-  ipcam_info.ptr_to_srv     = NULL;
-  ipcam_info.srv_to_host    = NULL;
-  ipcam_info.txt_for_srv    = NULL;
+    ipcam_info.ptr_to_srv     = NULL;
+    ipcam_info.srv_to_host    = NULL;
+    ipcam_info.txt_for_srv    = NULL;
 
-  pipe (signal_pipe);
-  signal(SIGHUP,  sighandler);
-  signal(SIGINT,  sighandler);
-  signal(SIGQUIT, sighandler);
-  signal(SIGTERM, sighandler);
+    pipe (signal_pipe);
+    signal(SIGHUP,  sighandler);
+    signal(SIGINT,  sighandler);
+    signal(SIGQUIT, sighandler);
+    signal(SIGTERM, sighandler);
 
-  if ((s = msock()) == 0)
+    if ((s = msock()) == 0)
     {
-      fprintf (stderr, "can't create socket: %s\n", strerror(errno));
-      return -1;
+        fprintf (stderr, "can't create socket: %s\n", strerror(errno));
+        return -1;
     }
 
-  if ((nlink = netwatch_open ()) < 0)
+#if 0
+    if ((nlink = netwatch_open ()) < 0)
     {
-      fprintf (stderr, "can't connect to netlink: %s\n", strerror(errno));
-      return -1;
+         fprintf (stderr, "can't connect to netlink: %s\n", strerror(errno));
+         return -1;
     }
 
-  netwatch_register_callbacks (iface_change_callback,
-                               iface_link_callback,
-                               &ipcam_info);
-  netwatch_queue_inforequest (nlink);
+    netwatch_register_callbacks (iface_change_callback,
+                                 iface_link_callback,
+                                 &ipcam_info);
+    netwatch_queue_inforequest (nlink);
+#endif
 
+    // TODO: OR Set startup state
+    ipcam_info.state = MDNSD_STARTUP;
 
-  if (mkfifo (FIFO_PATH, S_IRWXU) < 0)
+
+    if (mkfifo (FIFO_PATH, S_IRWXU) < 0)
     {
-      if (errno != EEXIST)
+        if (errno != EEXIST)
         {
-          fprintf (stderr, "can't create named pipe: %s\n", strerror(errno));
-          return -1;
+            fprintf (stderr, "can't create named pipe: %s\n", strerror(errno));
+            return -1;
         }
     }
 
-  if ((fifo_fd = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0)
+    if ((fifo_fd = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0)
     {
-      fprintf (stderr, "can't open named pipe: %s\n", strerror(errno));
-      return -1;
+        fprintf (stderr, "can't open named pipe: %s\n", strerror(errno));
+        return -1;
     }
 
-  /* we need to open the fifo for writing as well (although we'll never
-   * use it for this) to avoid POLLHUP to happen when no client wants
-   * something from us. Ugh. */
+    /* we need to open the fifo for writing as well (although we'll never
+                                                     * use it for this) to avoid POLLHUP to happen when no client wants
+        * something from us. Ugh. */
 
-  if ((i = open (FIFO_PATH, O_WRONLY)) < 0)
+    if ((i = open (FIFO_PATH, O_WRONLY)) < 0)
     {
-      fprintf (stderr, "can't dummy-open write end of pipe: %s\n",
-               strerror(errno));
-      return -1;
+        fprintf (stderr, "can't dummy-open write end of pipe: %s\n",
+                 strerror(errno));
+        return -1;
     }
 
-  while(1)
+
+// TODO: OR
+    request_ip_addresses (&ipcam_info);
+    write (signal_pipe[1], " ", 1);
+ipcam_info.state = MDNSD_PROBE;
+//
+    while(1)
     {
-      fds[0].fd      = signal_pipe[0];
-      fds[0].events  = POLLIN;
-      fds[0].revents = 0;
-      fds[1].fd      = s;
-      fds[1].events  = POLLIN;
-      fds[1].revents = 0;
-      fds[2].fd      = nlink;
-      fds[2].events  = POLLIN;
-      fds[2].revents = 0;
-      fds[3].fd      = fifo_fd;
-      fds[3].events  = POLLIN;
-      fds[3].revents = 0;
-
-      poll (fds, 4, polltime);
-
-      /* only used when we wake-up from a signal */
-      if (fds[0].revents)
+        fds[0].fd      = signal_pipe[0];
+        fds[0].events  = POLLIN;
+        fds[0].revents = 0;
+
+        fds[1].fd      = s;
+        fds[1].events  = POLLIN;
+        fds[1].revents = 0;
+
+        fds[2].fd      = fifo_fd;
+        fds[2].events  = POLLIN;
+        fds[2].revents = 0;
+/*
+        fds[3].fd      = nlink;
+        fds[3].events  = POLLIN;
+        fds[3].revents = 0;
+*/
+        poll (fds, 4, polltime);
+
+        /* only used when we wake-up from a signal */
+        if (fds[0].revents)
         {
-          char hostname[HOSTNAMESIZE];
+            char hostname[HOSTNAMESIZE];
 
-          read (signal_pipe[0], buf, MAX_PACKET_LEN);
+            read (signal_pipe[0], buf, MAX_PACKET_LEN);
 
-          gethostname (hostname, HOSTNAMESIZE);
-          hostname[HOSTNAMESIZE-1] = '\0';
-          if (strchr (hostname, '.'))
-            strchr (hostname, '.')[0] = '\0';
-          if (strcmp (hostname, ipcam_info.hostname))
+            //TODO: OR
+            //          gethostname (hostname, HOSTNAMESIZE);
+            strcpy(hostname, "reinhardt");
+            hostname[HOSTNAMESIZE-1] = '\0';
+            if (strchr (hostname, '.'))
+                strchr (hostname, '.')[0] = '\0';
+            if (strcmp (hostname, ipcam_info.hostname))
             {
-              /* hostname changed */
-              strcpy (ipcam_info.hostname, hostname);
-              free (ipcam_info.servicename);
-              ipcam_info.servicename = NULL;
+                /* hostname changed */
+                strcpy (ipcam_info.hostname, hostname);
+                free (ipcam_info.servicename);
+                ipcam_info.servicename = NULL;
 
-              ipcam_info.state = MDNSD_PROBE;
+                ipcam_info.state = MDNSD_PROBE;
             }
         }
 
-      if (fds[2].revents)
+        if (fds[2].revents)
         {
-          netwatch_dispatch (nlink);
-        }
+            char message[1024];
+            int ret;
 
-      if (fds[3].revents)
-        {
-          char message[1024];
-          int ret;
+            ret = read (fifo_fd, message, 1023);
 
-          ret = read (fifo_fd, message, 1023);
-
-          if (ret > 0)
+            if (ret > 0)
             {
-              message[ret] = '\0';
+                message[ret] = '\0';
 
-              if (!strncmp ("port:", message, 5))
+                if (!strncmp ("port:", message, 5))
                 {
-                  int port = atoi (message + 5);
-                  if (port > 0 && port < 65536)
-                    update_port_info (&ipcam_info, port);
+                    int port = atoi (message + 5);
+                    if (port > 0 && port < 65536)
+                        update_port_info (&ipcam_info, port);
                 }
-              else
+                else
                 {
-                  fprintf (stderr, "mdnsd: got unknown fifo message: %s", message);
+                    fprintf (stderr, "mdnsd: got unknown fifo message: %s", message);
                 }
             }
-          else if (ret < 0)
+            else if (ret < 0)
             {
-              fprintf (stderr, "mdnsd: can't read from pipe: %s\n", strerror (errno));
+                fprintf (stderr, "mdnsd: can't read from pipe: %s\n", strerror (errno));
             }
         }
-
-      switch (ipcam_info.state)
+/*
+        if (fds[3].revents)
         {
-          case MDNSD_STARTUP:
-            /* we're waiting for a netwatch based statechange */
-            /* fprintf (stderr, "in STARTUP\n"); */
-            polltime = 5000;
-            break;
+            netwatch_dispatch (nlink);
+        }
+*/
+        // TODO: OR
+        printf("---> ipcam_info.state = %d\n", ipcam_info.state);
+        switch (ipcam_info.state)
+        {
+            case MDNSD_STARTUP:
+printf("--->Startup\n");
+                /* we're waiting for a netwatch based statechange */
+                /* fprintf (stderr, "in STARTUP\n"); */
+                polltime = 5000;
+                break;
+
+            case MDNSD_PROBE:
+printf("--->Probe\n");
+                /* fprintf (stderr, "in PROBE\n"); */
+                if (ipcam_info.ptr_to_srv)
+                    MdnsdDone (ipcam_info.mdnsd, ipcam_info.ptr_to_srv);
+                if (ipcam_info.srv_to_host)
+                    MdnsdDone (ipcam_info.mdnsd, ipcam_info.srv_to_host);
+                if (ipcam_info.txt_for_srv)
+                    MdnsdDone (ipcam_info.mdnsd, ipcam_info.txt_for_srv);
+
+                ipcam_info.ptr_to_srv     = NULL;
+                ipcam_info.srv_to_host    = NULL;
+                ipcam_info.txt_for_srv    = NULL;
+
+                for (i = 0; i < MAX_ANNOUNCE_IP; i++)
+                {
+                    if (ipcam_info.ipinfos[i].host_to_ip)
+                        MdnsdDone (ipcam_info.mdnsd, ipcam_info.ipinfos[i].host_to_ip);
+                    if (ipcam_info.ipinfos[i].ip_to_host)
+                        MdnsdDone (ipcam_info.mdnsd, ipcam_info.ipinfos[i].ip_to_host);
+                    ipcam_info.ipinfos[i].host_to_ip  = NULL;
+                    ipcam_info.ipinfos[i].ip_to_host  = NULL;
+                    ipcam_info.ipinfos[i].announce_ip.s_addr = 0;
+                }
 
-          case MDNSD_PROBE:
-            /* fprintf (stderr, "in PROBE\n"); */
-            if (ipcam_info.ptr_to_srv)
-              mdnsd_done (ipcam_info.dnsd, ipcam_info.ptr_to_srv);
-            if (ipcam_info.srv_to_host)
-              mdnsd_done (ipcam_info.dnsd, ipcam_info.srv_to_host);
-            if (ipcam_info.txt_for_srv)
-              mdnsd_done (ipcam_info.dnsd, ipcam_info.txt_for_srv);
+                ipcam_info.state = MDNSD_ANNOUNCE;
+                announce_stage = 0;
+                tv.tv_sec = 0;
+                tv.tv_usec = 0;
+                break;
 
-            ipcam_info.ptr_to_srv     = NULL;
-            ipcam_info.srv_to_host    = NULL;
-            ipcam_info.txt_for_srv    = NULL;
+            case MDNSD_ANNOUNCE:
+printf("--->Announce\n");
+                /* fprintf (stderr, "in ANNOUNCE\n"); */
+                if (announce_stage < 3)
+                {
+                    struct timeval cur_tv;
+                    long msecs;
+                    gettimeofday (&cur_tv, NULL);
+                    msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
+
+                    if (tv.tv_sec == 0 || msecs > 755)
+                    {
+                        request_service (&ipcam_info, announce_stage);
+                        announce_stage ++;
+                        tv = cur_tv;
+                        cur_tv = *MdnsdGetMaxSleepTime (ipcam_info.mdnsd);
+                        polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
+                        if (polltime >= 756)
+                            polltime = 756;
+                    }
+                    else
+                    {
+                        cur_tv = *MdnsdGetMaxSleepTime (ipcam_info.mdnsd);
+                        polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
+                        if (polltime >= 756 - msecs)
+                            polltime = 756 - msecs;
+                    }
+                }
+                    else
+                {
+                    tv = *MdnsdGetMaxSleepTime (ipcam_info.mdnsd);
+                    polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
 
-            for (i = 0; i < MAX_ANNOUNCE_IP; i++)
-              {
-                if (ipcam_info.ipinfos[i].host_to_ip)
-                  mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].host_to_ip);
-                if (ipcam_info.ipinfos[i].ip_to_host)
-                  mdnsd_done (ipcam_info.dnsd, ipcam_info.ipinfos[i].ip_to_host);
-                ipcam_info.ipinfos[i].host_to_ip  = NULL;
-                ipcam_info.ipinfos[i].ip_to_host  = NULL;
-                ipcam_info.ipinfos[i].announce_ip = 0;
-              }
-
-            ipcam_info.state = MDNSD_ANNOUNCE;
-            announce_stage = 0;
-            tv.tv_sec = 0;
-            tv.tv_usec = 0;
-            break;
+                    ipcam_info.state = MDNSD_RUN;
+                }
+                break;
 
-          case MDNSD_ANNOUNCE:
-            /* fprintf (stderr, "in ANNOUNCE\n"); */
-            if (announce_stage < 3)
-              {
-                struct timeval cur_tv;
-                long msecs;
-                gettimeofday (&cur_tv, NULL);
-                msecs = (cur_tv.tv_sec - tv.tv_sec) * 1000 + cur_tv.tv_usec / 1000 - tv.tv_usec / 1000;
-
-                if (tv.tv_sec == 0 || msecs > 755)
-                  {
-                    request_service (&ipcam_info, announce_stage);
-                    announce_stage ++;
-                    tv = cur_tv;
-                    cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
-                    polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
-                    if (polltime >= 756)
-                      polltime = 756;
-                  }
-                else
-                  {
-                    cur_tv = *mdnsd_sleep (ipcam_info.dnsd);
-                    polltime = cur_tv.tv_sec * 1000 + cur_tv.tv_usec / 1000;
-                    if (polltime >= 756 - msecs)
-                      polltime = 756 - msecs;
-                  }
-              }
-            else
-              {
-                tv = *mdnsd_sleep (ipcam_info.dnsd);
+            case MDNSD_RUN:
+printf("--->Run\n");
+                tv = *MdnsdGetMaxSleepTime (ipcam_info.mdnsd);
                 polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+                break;
 
-                ipcam_info.state = MDNSD_RUN;
-              }
-            break;
+            case MDNSD_SHUTDOWN:
+printf("--->Shutdown\n");
+                MdnsdShutdown (ipcam_info.mdnsd);
+                break;
 
-          case MDNSD_RUN:
-            tv = *mdnsd_sleep (ipcam_info.dnsd);
-            polltime = tv.tv_sec * 1000 + tv.tv_usec / 1000;
-            break;
-
-          case MDNSD_SHUTDOWN:
-            mdnsd_shutdown (ipcam_info.dnsd);
-            break;
-
-          default:
-            fprintf (stderr, "in default???\n");
-            break;
+            default:
+                fprintf (stderr, "in default???\n");
+                break;
         }
 
-      if (fds[1].revents)
+        if (fds[1].revents)
         {
-          while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0,
-                                    (struct sockaddr*) &from, &ssize)) > 0)
+            while ((bsize = recvfrom (s, buf, MAX_PACKET_LEN, 0,
+                                      (struct sockaddr*) &from, &ssize)) > 0)
             {
-              bzero (&msg, sizeof (struct message));
-              message_parse (&msg, buf);
-              mdnsd_in (ipcam_info.dnsd, &msg,
-                        (unsigned long int) from.sin_addr.s_addr,
-                        from.sin_port);
+                bzero (&msg, sizeof (DNSMESSAGE));
+                DnsParseMsg (&msg, buf);
+                MdnsdInput(ipcam_info.mdnsd, &msg,
+                          from.sin_addr,
+                          from.sin_port);
             }
-          if (bsize < 0 && errno != EAGAIN)
+            if (bsize < 0 && errno != EAGAIN)
             {
-              fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
+                fprintf (stderr, "can't read from socket: %s\n", strerror (errno));
             }
         }
 
-      while (mdnsd_out (ipcam_info.dnsd, &msg, &remote_ip, &port))
+        while (MdnsdOutput (ipcam_info.mdnsd, &msg, &remote_ip, &port))
         {
-          bzero (&to, sizeof (to));
-          to.sin_family = AF_INET;
-          to.sin_port = port;
-          to.sin_addr.s_addr = remote_ip;
-          if (sendto (s, message_packet (&msg), message_packet_len (&msg),
-                      0, (struct sockaddr *) &to,
-                      sizeof (struct sockaddr_in)) != message_packet_len (&msg))
+            bzero (&to, sizeof (to));
+            to.sin_family = AF_INET;
+            to.sin_port = port;
+            to.sin_addr.s_addr = remote_ip.s_addr;
+            if (sendto (s, DnsMsg2Pkt (&msg), DnsMsgLen(&msg),
+                        0, (struct sockaddr *) &to,
+                        sizeof (struct sockaddr_in)) != DnsMsgLen(&msg))
             {
-              fprintf (stderr, "can't write to socket: %s\n", strerror(errno));
+                fprintf (stderr, "can't write to socket: %s\n", strerror(errno));
             }
         }
 
-      if (ipcam_info.state == MDNSD_SHUTDOWN)
-        break;
+        if (ipcam_info.state == MDNSD_SHUTDOWN)
+            break;
     }
 
-  mdnsd_shutdown (ipcam_info.dnsd);
-  mdnsd_free (ipcam_info.dnsd);
-  return 0;
+    MdnsdShutdown (ipcam_info.mdnsd);
+    MdnsdFree (ipcam_info.mdnsd);
+    return 0;
 }
 
index 3672ce46c8a12356974be66a9512186da835c5d3..010ba23c9874d79b32c1aaf91bf80b2ffaf196a0 100644 (file)
--- a/mquery.c
+++ b/mquery.c
 #include <time.h>
 
 #include "mdnsd.h"
-#include "xht.h"
-#include "sdtxt.h"
+#include "shash.h"
+#include "rfc1035.h"
+#include "dns_sd_txt.h"
 
-void txt_printer (xht h, const char *key, void *val, void *arg)
+void txt_printer (SHASH UNUSED(hash), const char *key, void *val, void *UNUSED(arg))
 {
-  printf ("   %s=%s\n", key, (char *) val);
+    printf ("   %s=%s\n", key, (char *) val);
 }
 
 
 // print an answer
-int ans(mdnsda a, void *arg)
+int ans(TMdnsdAnswer *answer, void *UNUSED(arg))
 {
     int now;
-    xht h;
+    SHASH hash;
     struct in_addr ip_addr;
-    if(a->ttl == 0) now = 0;
-    else now = a->ttl - time(0);
-    switch(a->type)
+    if(answer->ttl == 0) now = 0;
+    else now = answer->ttl - time(0);
+    switch(answer->type)
     {
-    case QTYPE_A:
-        ip_addr.s_addr = a->ip;
-        printf("A %s for %d seconds to ip %s\n",a->name,now,inet_ntoa(ip_addr));
-        break;
-    case QTYPE_PTR:
-        printf("PTR %s for %d seconds to %s\n",a->name,now,a->rdname);
-        break;
-    case QTYPE_SRV:
-        printf("SRV %s for %d seconds to %s:%d\n",a->name,now,a->rdname,a->srv.port);
-        break;
-    case QTYPE_TXT:
-        printf("TXT %s for %d seconds:\n",a->name,now);
-        h = txt2sd (a->rdata, a->rdlen);
-        xht_walk (h, txt_printer, NULL);
-        xht_free (h);
-        break;
-    default:
-        printf("%d %s for %d seconds with %d data\n",a->type,a->name,now,a->rdlen);
+        case QTYPE_A:
+            ip_addr = answer->ip;
+            printf("A %s for %d seconds to ip %s\n",answer->name,now,inet_ntoa(ip_addr));
+            break;
+        case QTYPE_PTR:
+            printf("PTR %s for %d seconds to %s\n",answer->name,now,answer->rdname);
+            break;
+        case QTYPE_SRV:
+            printf("SRV %s for %d seconds to %s:%d\n",answer->name,now,answer->rdname,answer->srv.port);
+            break;
+        case QTYPE_TXT:
+            printf("TXT %s for %d seconds:\n",answer->name,now);
+            hash = DnsTxt2Sd (answer->rdata, answer->rdlen);
+            SHashForEach(hash, txt_printer, NULL);
+            SHashFree (hash);
+            break;
+        default:
+            printf("%d %s for %d seconds with %d data\n",answer->type,answer->name,now,answer->rdlen);
     }
 
     return 0;
@@ -63,7 +64,7 @@ int msock()
 
     bzero(&in, sizeof(in));
     in.sin_family = AF_INET;
-    in.sin_port = htons(5353);
+    in.sin_port = htons(MDNS_PORT);
     in.sin_addr.s_addr = 0;
 
     if((s = socket(AF_INET,SOCK_DGRAM,0)) < 0) return 0;
@@ -88,27 +89,27 @@ int msock()
 
 int main(int argc, char *argv[])
 {
-    mdnsd d;
-    struct message m;
-    unsigned long int ip;
-    unsigned short int port;
+    TMdnsd *d;
+    DNSMESSAGE m;
+    struct in_addr ip;
+    uint16_t port;
     struct timeval *tv;
     int bsize, ssize = sizeof(struct sockaddr_in);
-    unsigned char buf[MAX_PACKET_LEN];
+    uint8_t buf[MAX_PACKET_LEN];
     struct sockaddr_in from, to;
     fd_set fds;
     int s;
 
     if(argc != 3) { printf("usage: mquery 12 _http._tcp.local.\n"); return -1; }
 
-    d = mdnsd_new(1,1000);
+    d = MdnsdNew(1, 1000);
     if((s = msock()) == 0) { printf("can't create socket: %s\n",strerror(errno)); return 1; }
 
-    mdnsd_query(d,argv[2],atoi(argv[1]),ans,0);
+    MdnsdQuery(d,argv[2],atoi(argv[1]),ans,0);
 
     while(1)
     {
-        tv = mdnsd_sleep(d);
+        tv = MdnsdGetMaxSleepTime(d);
         FD_ZERO(&fds);
         FD_SET(s,&fds);
         select(s+1,&fds,0,0,tv);
@@ -117,24 +118,25 @@ int main(int argc, char *argv[])
         {
             while((bsize = recvfrom(s,buf,MAX_PACKET_LEN,0,(struct sockaddr*)&from,&ssize)) > 0)
             {
-                bzero(&m,sizeof(struct message));
-                message_parse(&m,buf);
-                mdnsd_in(d,&m,(unsigned long int)from.sin_addr.s_addr,from.sin_port);
+                bzero(&m,sizeof(DNSMESSAGE));
+                DnsParseMsg(&m,buf);
+                MdnsdInput(d, &m, from.sin_addr, from.sin_port);
             }
             if(bsize < 0 && errno != EAGAIN) { printf("can't read from socket %d: %s\n",errno,strerror(errno)); return 1; }
         }
-        while(mdnsd_out(d,&m,&ip,&port))
+
+        while(MdnsdOutput(d,&m,&ip,&port))
         {
             bzero(&to, sizeof(to));
             to.sin_family = AF_INET;
             to.sin_port = port;
-            to.sin_addr.s_addr = ip;
-            if(sendto(s,message_packet(&m),message_packet_len(&m),0,(struct sockaddr *)&to,sizeof(struct sockaddr_in)) != message_packet_len(&m))  { printf("can't write to socket: %s\n",strerror(errno)); return 1; }
+            to.sin_addr = ip;
+            if(sendto(s,DnsMsg2Pkt(&m),DnsMsgLen(&m),0,(struct sockaddr *)&to,sizeof(struct sockaddr_in)) != DnsMsgLen(&m))  { printf("can't write to socket: %s\n",strerror(errno)); return 1; }
         }
     }
 
-    mdnsd_shutdown(d);
-    mdnsd_free(d);
+    MdnsdShutdown(d);
+    MdnsdFree(d);
     return 0;
 }
 
old mode 100755 (executable)
new mode 100644 (file)
index 72320ed..910da48
@@ -11,6 +11,7 @@
 #include <linux/rtnetlink.h>
 
 #include "netwatch.h"
+#include "mdnsd.h"
 
 #define NL_BUFSIZE 8192
 
@@ -28,83 +29,83 @@ static void   netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg);
 int
 netwatch_open (void)
 {
-  int fd;
-  struct sockaddr_nl  local;
+    int fd;
+    struct sockaddr_nl  local;
 
-  /* Create Socket */
-  if ((fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
-    return -1;
+    /* Create Socket */
+    if ((fd = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
+        return -1;
 
-  memset (&local, 0, sizeof (local));
-  local.nl_family = AF_NETLINK;
-  local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
+    memset (&local, 0, sizeof (local));
+    local.nl_family = AF_NETLINK;
+    local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
 
-  if (bind (fd, (struct sockaddr*) &local, sizeof (local)) < 0)
-    return -1;
+    if (bind (fd, (struct sockaddr*) &local, sizeof (local)) < 0)
+        return -1;
 
-  if (fcntl (fd, F_SETFL, O_NONBLOCK))
-    return -1;
+    if (fcntl (fd, F_SETFL, O_NONBLOCK))
+        return -1;
 
-  return fd;
+    return fd;
 }
 
 int
 netwatch_queue_inforequest (int fd)
 {
-  char buf[NLMSG_LENGTH (sizeof (struct ifaddrmsg))];
-  struct nlmsghdr    *nl_msg;
+    char buf[NLMSG_LENGTH (sizeof (struct ifaddrmsg))];
+    struct nlmsghdr    *nl_msg;
 
-  bzero (buf, sizeof(buf));
-  nl_msg = (struct nlmsghdr *) buf;
+    bzero (buf, sizeof(buf));
+    nl_msg = (struct nlmsghdr *) buf;
 
-  /* For getting interface addresses */
-  nl_msg->nlmsg_len   = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
-  nl_msg->nlmsg_type  = RTM_GETADDR;
-  nl_msg->nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
-  nl_msg->nlmsg_pid   = getpid ();
+    /* For getting interface addresses */
+    nl_msg->nlmsg_len   = NLMSG_LENGTH (sizeof (struct ifaddrmsg));
+    nl_msg->nlmsg_type  = RTM_GETADDR;
+    nl_msg->nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+    nl_msg->nlmsg_pid   = getpid ();
 
-  return write (fd, nl_msg, nl_msg->nlmsg_len);
+    return write (fd, nl_msg, nl_msg->nlmsg_len);
 }
 
 
 int
 netwatch_dispatch (int fd)
 {
-  struct nlmsghdr    *nl_msg;
-  char buf[NL_BUFSIZE];
-  int len, count;
+    struct nlmsghdr    *nl_msg;
+    char buf[NL_BUFSIZE];
+    int len, count;
 
-  count = 0;
-  if ((len = recv (fd, buf, NL_BUFSIZE, 0)) < 0)
+    count = 0;
+    if ((len = recv (fd, buf, NL_BUFSIZE, 0)) < 0)
     {
-      return (errno == EAGAIN) ? 0 : -1;
+        return (errno == EAGAIN) ? 0 : -1;
     }
 
-  for (nl_msg = (struct nlmsghdr *) buf;
-       NLMSG_OK (nl_msg, len);
-       nl_msg = NLMSG_NEXT (nl_msg, len))
+    for (nl_msg = (struct nlmsghdr *) buf;
+         NLMSG_OK (nl_msg, (unsigned int) len);
+         nl_msg = NLMSG_NEXT (nl_msg, len))
     {
-      switch (nl_msg->nlmsg_type)
+        switch (nl_msg->nlmsg_type)
         {
-          case RTM_NEWADDR:
-          case RTM_DELADDR:
-            netwatch_handle_ifaddrmsg (nl_msg);
-            break;
-          case RTM_NEWLINK:
-          case RTM_DELLINK:
-            netwatch_handle_ifinfomsg (nl_msg);
-            break;
-          case NLMSG_DONE:
-            break;
-          default:
-            fprintf (stderr, "unhandled message (%d)\n",
-                     nl_msg->nlmsg_type);
-            break;
+            case RTM_NEWADDR:
+            case RTM_DELADDR:
+                netwatch_handle_ifaddrmsg (nl_msg);
+                break;
+            case RTM_NEWLINK:
+            case RTM_DELLINK:
+                netwatch_handle_ifinfomsg (nl_msg);
+                break;
+            case NLMSG_DONE:
+                break;
+            default:
+                fprintf (stderr, "unhandled message (%d)\n",
+                         nl_msg->nlmsg_type);
+                break;
         }
 
-      count++;
+        count++;
     }
-  return count;
+    return count;
 }
 
 
@@ -113,86 +114,86 @@ netwatch_register_callbacks (NW_IpChangeCallback    ip_cb,
                              NW_LinkChangeCallback  link_cb,
                              void                  *userdata)
 {
-  user_data = userdata;
-  on_ip_change = ip_cb;
-  on_link_change = link_cb;
+    user_data = userdata;
+    on_ip_change = ip_cb;
+    on_link_change = link_cb;
 }
 
 
 static void
 netwatch_handle_ifaddrmsg (struct nlmsghdr *nl_msg)
 {
-  struct ifaddrmsg *if_msg;
-  struct rtattr    *attrib;
-  int len;
+    struct ifaddrmsg *if_msg;
+    struct rtattr    *attrib;
+    int len;
 
-  char  address[100];
-  char *label = NULL;
+    char  address[100];
+    char *label = NULL;
 
-  if_msg = (struct ifaddrmsg *) NLMSG_DATA (nl_msg);
+    if_msg = (struct ifaddrmsg *) NLMSG_DATA (nl_msg);
 
-  if (if_msg->ifa_family != AF_INET)
-    return;
+    if (if_msg->ifa_family != AF_INET)
+        return;
 
-  address[0] = '\0';
+    address[0] = '\0';
 
-  len = IFA_PAYLOAD (nl_msg);
-  for (attrib = IFA_RTA (if_msg);
-       RTA_OK (attrib, len);
-       attrib = RTA_NEXT (attrib, len))
+    len = IFA_PAYLOAD (nl_msg);
+    for (attrib = IFA_RTA (if_msg);
+         RTA_OK (attrib, len);
+         attrib = RTA_NEXT (attrib, len))
     {
-      switch (attrib->rta_type)
+        switch (attrib->rta_type)
         {
-          case IFA_LOCAL:
-            inet_ntop (AF_INET, RTA_DATA (attrib), address, sizeof (address));
-            break;
+            case IFA_LOCAL:
+                inet_ntop (AF_INET, RTA_DATA (attrib), address, sizeof (address));
+                break;
 
-          case IFA_LABEL:
-            label = (char *) RTA_DATA (attrib);
-            break;
+            case IFA_LABEL:
+                label = (char *) RTA_DATA (attrib);
+                break;
 
-          default:
-            /* ignore all other attributes */
-            break;
+            default:
+                /* ignore all other attributes */
+                break;
         }
     }
 
-  /* if we got both a label and an IP address */
-  if (on_ip_change && label && address[0] &&
-      (nl_msg->nlmsg_type == RTM_NEWADDR || nl_msg->nlmsg_type == RTM_DELADDR))
+    /* if we got both a label and an IP address */
+    if (on_ip_change && label && address[0] &&
+        (nl_msg->nlmsg_type == RTM_NEWADDR || nl_msg->nlmsg_type == RTM_DELADDR))
     {
-      on_ip_change (if_msg->ifa_index, label, address,
-                    nl_msg->nlmsg_type == RTM_NEWADDR, user_data);
+        on_ip_change (if_msg->ifa_index, label, address,
+                      nl_msg->nlmsg_type == RTM_NEWADDR, user_data);
     }
 
-  return;
+    return;
 }
 
 
 static void
 netwatch_handle_ifinfomsg (struct nlmsghdr *nl_msg)
 {
-  static unsigned long have_state = 0;
-  static unsigned long linkstate = 0;
-  struct ifinfomsg *ifi;
+    static unsigned long have_state = 0;
+    static unsigned long linkstate = 0;
+    struct ifinfomsg *ifi;
 
-  ifi = (struct ifinfomsg *) NLMSG_DATA (nl_msg);
+    ifi = (struct ifinfomsg *) NLMSG_DATA (nl_msg);
 
-  if (on_link_change && ifi->ifi_index < sizeof (unsigned long) * 8)
+    if (on_link_change && ((unsigned int)ifi->ifi_index < sizeof (unsigned long) * 8))
     {
-      if (!((1 << ifi->ifi_index) & have_state) ||
-          ((ifi->ifi_flags & IFF_RUNNING) && !((1 << ifi->ifi_index) & linkstate)) ||
-          (!(ifi->ifi_flags & IFF_RUNNING) && ((1 << ifi->ifi_index) & linkstate)))
+        if (!((1 << ifi->ifi_index) & have_state) ||
+            ((ifi->ifi_flags & IFF_RUNNING) && !((1 << ifi->ifi_index) & linkstate)) ||
+            (!(ifi->ifi_flags & IFF_RUNNING) && ((1 << ifi->ifi_index) & linkstate)))
         {
-          have_state |= (1 << ifi->ifi_index);
-          if (ifi->ifi_flags & IFF_RUNNING)
-            linkstate |= (1 << ifi->ifi_index);
-          else
-            linkstate &= ~(1 << ifi->ifi_index);
-
-          on_link_change (ifi->ifi_index,
-                          ifi->ifi_flags & IFF_RUNNING ? 1 : 0,
-                          user_data);
+            have_state |= (1 << ifi->ifi_index);
+            if (ifi->ifi_flags & IFF_RUNNING)
+                linkstate |= (1 << ifi->ifi_index);
+            else
+                linkstate &= ~(1 << ifi->ifi_index);
+
+            on_link_change (ifi->ifi_index,
+                            ifi->ifi_flags & IFF_RUNNING ? 1 : 0,
+                            user_data);
         }
     }
 }
@@ -205,50 +206,50 @@ print_ip_change (int    link_index,
                  char  *label,
                  char  *ipaddr,
                  int    add,
-                 void  *user_data)
+                 void  *UNUSED(user_data))
 {
-  fprintf (stdout, "Link No. %d %s address %s (label %s)\n",
-           link_index, add ? "got" : "lost", ipaddr, label);
+    fprintf (stdout, "Link No. %d %s address %s (label %s)\n",
+             link_index, add ? "got" : "lost", ipaddr, label);
 }
 
 
 void print_link_change (int   link_index,
                         int   running,
-                        void *user_data)
+                        void *UNUSED(user_data))
 {
-  fprintf (stdout, "Link No. %d is %sconnected\n",
-           link_index, running ? "" : "no longer ");
+    fprintf (stdout, "Link No. %d is %sconnected\n",
+             link_index, running ? "" : "no longer ");
 }
 
 
 int
-main (int argc, char *argv[])
+main (int UNUSED(argc), char **UNUSED(argv))
 {
-  struct pollfd       pollfd;
-  int fd;
+    struct pollfd       pollfd;
+    int fd;
 
-  netwatch_register_callbacks (print_ip_change, print_link_change, NULL);
+    netwatch_register_callbacks (print_ip_change, print_link_change, NULL);
 
-  fd = netwatch_open ();
-  if (fd < 0)
+    fd = netwatch_open ();
+    if (fd < 0)
     {
-      perror ("Error opening netlink socket");
-      exit (1);
+        perror ("Error opening netlink socket");
+        exit (1);
     }
 
-  netwatch_queue_inforequest (fd);
+    netwatch_queue_inforequest (fd);
 
-  while (1)
+    while (1)
     {
-      pollfd.fd = fd;
-      pollfd.events = POLLIN;
-      pollfd.revents = 0;
+        pollfd.fd = fd;
+        pollfd.events = POLLIN;
+        pollfd.revents = 0;
 
-      if (poll (&pollfd, 1, 20000))
-        netwatch_dispatch (fd);
+        if (poll (&pollfd, 1, 20000))
+            netwatch_dispatch (fd);
     }
 
-  close (fd);
-  return 0;
+    close (fd);
+    return 0;
 }
 #endif
old mode 100755 (executable)
new mode 100644 (file)
index 0ed72a23178cb45918e9e0fe3e69a503dc5fe216..59e93d1551b6c9091851c7a9e72a4ad7369be152 100644 (file)
--- a/rfc1035.c
+++ b/rfc1035.c
-#include <string.h> 
-#include <stdio.h> 
-#include "1035.h"
-
-unsigned short int net2short(unsigned char **bufp)
+/*
+ * Copyright (C) 2003 Jeremie Miller <jer@jabber.org>
+ * Copyright (c) 2009 Simon Budig <simon@budig.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file pro/rfc1035.c
+ * \brief Standalone DNS parsing functions
+ *
+ * Implementation follows RF1035 [http://www.rfc-editor.org/rfc/rfc1035.txt] and
+ * includes decoding functions for svr rr's code type 33. See RFC2782
+ * [http://www.rfc-editor.org/rfc/rfc2782.txt]
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "rfc1035.h"
+
+/*!
+ * \addtogroup xgMulticastDns
+ */
+/*@{*/
+
+
+/*!
+ * \brief Conversion of the network byte order buffer content into uint16_t value.
+ *
+ * This functions increments the buffer pointer accordingly
+ *
+ * \param buf   buffer
+ *
+ * \return      Converted value
+ */
+uint16_t DnsNet2Short(uint8_t **buf)
 {
-    short int i;
-    i = **bufp;
-    i <<= 8;
-    i |= *(*bufp + 1);
-    *bufp += 2;
-    return i;
+    uint16_t val;
+    val  = (*((*buf) ++)) << 8;
+    val |=  *((*buf) ++);
+    return val;
 }
 
-unsigned long int net2long(unsigned char **bufp)
+
+/*!
+ * \brief Conversion of the network byte order buffer content into uint32_t value.
+ *
+ * This functions increments the buffer pointer accordingly
+ *
+ * \param buf   buffer
+ *
+ * \return      Converted value
+ */
+uint32_t DnsNet2Long(uint8_t **buf)
 {
-    long int l;
-    l = **bufp;
-    l <<= 8;
-    l |= *(*bufp + 1);
-    l <<= 8;
-    l |= *(*bufp + 2);
-    l <<= 8;
-    l |= *(*bufp + 3);
-    *bufp += 4;
-    return l;
+    uint32_t val;
+    val  = (*((*buf) ++)) << 24;
+    val |= (*((*buf) ++)) << 16;
+    val |= (*((*buf) ++)) <<  8;
+    val |=  *((*buf) ++);
+    return val;
 }
 
-void short2net(unsigned short int i, unsigned char **bufp)
+
+/*!
+ * \brief Conversion of uint16_t value into buffer content in network byte order
+ *
+ * This functions increments the buffer pointer accordingly
+ *
+ * \param val   Value
+ * \param buf   buffer
+ */
+void DnsShort2Net(uint16_t val, uint8_t **buf)
 {
-    *(*bufp + 1) = (unsigned char)i;
-    i >>= 8;
-    **bufp = (unsigned char)i;
-    *bufp += 2;    
+    *((*buf) ++) = (uint8_t) (val >> 8);
+    *((*buf) ++) = (uint8_t) val;
 }
 
-void long2net(unsigned long int l, unsigned char **bufp)
+/*!
+ * \brief Conversion of uint32_t value into buffer content in network byte order
+ *
+ * This functions increments the buffer pointer accordingly
+ *
+ * \param val   Value
+ * \param buf   buffer
+ */
+void DnsLong2Net(uint32_t val, uint8_t **buf)
 {
-    *(*bufp + 3) = (unsigned char)l;
-    l >>= 8;
-    *(*bufp + 2) = (unsigned char)l;
-    l >>= 8;
-    *(*bufp + 1) = (unsigned char)l;
-    l >>= 8;
-    **bufp = (unsigned char)l;
-    *bufp += 4;
+    *((*buf) ++) = (uint8_t) (val >> 24);
+    *((*buf) ++) = (uint8_t) (val >> 16);
+    *((*buf) ++) = (uint8_t) (val >> 8);
+    *((*buf) ++) = (uint8_t) val;
 }
 
-unsigned short int _ldecomp(unsigned char *ptr)
+
+/*!
+ * \brief "Decompress" a label by calculating the offset of the original label string
+ *
+ * This functions increments the buffer pointer accordingly
+ *
+ * \param ptr   Pointer to the compressed label
+ *
+ * \return      Offset of the original label string
+ */
+static uint16_t LabelDecompress(uint8_t *ptr)
 {
-    unsigned short int i;
-    i = 0xc0 ^ ptr[0];
-    i <<= 8;
-    i |= ptr[1];
-    if(i >= 4096) i = 4095;
-    return i;
+    uint16_t val;
+    val = 0xc0 ^ ptr[0];
+    val <<= 8;
+    val |= ptr[1];
+    if (val > PACKET_BUFFER_LEN - 1) {
+        val = PACKET_BUFFER_LEN - 1;
+    }
+    return val;
 }
 
-void _label(struct message *m, unsigned char **bufp, unsigned char **namep)
+
+/*!
+ * \brief Extracts the label from the buffer.
+ *
+ * In case the label is compressed, it will be decompressed.
+ * This functions increments the buffer pointer accordingly.
+ * If the label is not yet cached, it will be added to the cached labels
+ *
+ * \param msg   DNS message
+ * \param buf   Pointer to the package data buffer
+ * \param namep Pointer to the string buffer, where the label will be copied to
+ */
+static void ExtractLabel(DNSMESSAGE *msg, uint8_t **buf, uint8_t **namep)
 {
-    unsigned char *label, *name;
+    uint8_t *label;
+    uint8_t *name;
     int x;
 
-    // set namep to the end of the block
-    *namep = name = m->_packet + m->_len;
-
-    // loop storing label in the block
-    for(label = *bufp; *label != 0; name += *label + 1, label += *label + 1)
-    {
-        // skip past any compression pointers, kick out if end encountered (bad data prolly)
-        while(*label & 0xc0)
-            if(*(label = m->_buf + _ldecomp(label)) == 0) break;
+    /* Set the name pointer to the end of the data block */
+    name = msg->packet + msg->len;
+    *namep = name;
+
+    /* loop storing label in the block */
+    label = *buf;
+    while (*label != 0) {
+        /* Skip any compression pointer until end encountered */
+        while (*label & 0xc0) {
+            label = msg->buf + LabelDecompress(label);
+            if (*(label) == 0) {
+                break;
+            }
+        }
 
-        // make sure we're not over the limits
-        if((name + *label) - *namep > 255 || m->_len + ((name + *label) - *namep) > 4096) return;
+        /* Check the limits */
+        if (((name + *label) - *namep > 255) ||
+            (msg->len + ((name + *label) - *namep) > PACKET_BUFFER_LEN -1 )) {
+            return;
+        }
 
-        // copy chars for this label
-        memcpy(name,label+1,*label);
+        /* Copy label to the name buffer */
+        memcpy(name, label+1,*label);
         name[*label] = '.';
-    } 
 
-    // advance buffer
-    for(label = *bufp; *label != 0 && !(*label & 0xc0 && label++); label += *label + 1);
-    *bufp = label + 1;
+        name  += *label + 1;
+        label += *label + 1;
+    }
+
+    /* Advance in the buffer */
+    for (label = *buf; (*label != 0) && (!((*label & 0xc0) && label++)); label += *label + 1);
+    *buf = label + 1;
 
-    // terminate name and check for cache or cache it
+    /* Add a traling \0 and check if the name is yet cached */
     *name = '\0';
-    for(x = 0; x <= 19 && m->_labels[x]; x++)
-    {
-        if(strcmp(*namep,m->_labels[x])) continue;
-        *namep = m->_labels[x];
-        return;
+
+    for (x = 0; x < MAX_LABEL; x++) {
+        if (msg->labels[x]) {
+            if (strcmp(*namep, msg->labels[x]) != 0) {
+                continue;
+            }
+
+            *namep = msg->labels[x];
+            return;
+        }
+    }
+
+    /* The label was not yet cached, so cache it if there is still room */
+    if ((x < MAX_LABEL) && (msg->labels[x] == 0)) {
+        msg->labels[x] = *namep;
     }
-    // no cache, so cache it if room
-    if(x <= 19 && m->_labels[x] == 0)
-        m->_labels[x] = *namep;
-    m->_len += (name - *namep) + 1;
+    msg->len += (name - *namep) + 1;
 }
 
-// internal label matching
-int _lmatch(struct message *m, unsigned char *l1, unsigned char *l2)
+/*!
+ * \brief Check if labels are matching
+ *
+ * If labels are compressed, they will be decompressed first.
+ *
+ * \param msg       DNS message
+ * \param label1    Pointer to label1
+ * \param label1    Pointer to label2
+ *
+ * \return          1 in case they are matching, else 0
+ */
+static int MatchLabel(DNSMESSAGE *msg, uint8_t *label1, uint8_t *label2)
 {
     int len;
 
-    // always ensure we get called w/o a pointer
-    if(*l1 & 0xc0) return _lmatch(m, m->_buf + _ldecomp(l1),l2);
-    if(*l2 & 0xc0) return _lmatch(m, l1, m->_buf + _ldecomp(l2));
-    
-    // same already?
-    if(l1 == l2) return 1;
-    
-    // compare all label characters
-    if(*l1 != *l2) return 0;
-    for(len = 1; len <= *l1; len++) 
-        if(l1[len] != l2[len]) return 0;
-
-    // get new labels
-    l1 += *l1 + 1;
-    l2 += *l2 + 1;
-    
-    // at the end, all matched
-    if(*l1 == 0 && *l2 == 0) return 1;
-    
-    // try next labels
-    return _lmatch(m,l1,l2);
+    /* If we were calles with a pointer, call MatchLabel with dereferenced pointer again */
+    // TODO: Recursive
+    if (*label1 & 0xc0) {
+        return MatchLabel(msg, msg->buf + LabelDecompress(label1), label2);
+    }
+
+    if (*label2 & 0xc0) {
+        return MatchLabel(msg, label1, msg->buf + LabelDecompress(label2));
+    }
+
+    /* Return in case of a match */
+    if (label1 == label2) {
+        return 1;
+    }
+
+    /* Compare the label */
+    if (*label1 != *label2){
+        return 0;
+    }
+
+    for (len = 1; len <= *label1; len++) {
+        if (label1[len] != label2[len]) {
+            return 0;
+        }
+    }
+
+    /* Get the new labels */
+    label1 += *label1 + 1;
+    label2 += *label2 + 1;
+
+    /* Finally all labels should be matched */
+    if ((*label1 == 0) && (*label2 == 0)) {
+        return 1;
+    }
+
+    /* Recursivly call match with the next labels */
+    // TODO: Recursive
+    return MatchLabel(msg, label1, label2);
 }
 
-// nasty, convert host into label using compression
-int _host(struct message *m, unsigned char **bufp, unsigned char *name)
-{
-    unsigned char label[256], *l;
-    int len = 0, x = 1, y = 0, last = 0;
 
-    if(name == 0) return 0;
+/*!
+ * \brief Convert host name to label using compression
+ *
+ * \param msg       DNS message
+ * \param buf       Pointer to the buffer where the label shall be placed
+ * \param name      Hostname
+ *
+ * \return          Length of the label
+ */
+static int Host2Label(DNSMESSAGE *msg, uint8_t **buf, uint8_t *name)
+{
+    uint8_t  label[MAX_LABEL_SIZE];
+    uint8_t *l;
+    int len = 0;
+    int x = 1;
+    int y = 0;
+    int last = 0;
+
+    if (name == NULL) {
+        return 0;
+    }
 
-    // make our label
-    while(name[y])
-    {
-        if(name[y] == '.')
-        {
-            if(!name[y+1]) break;
+    /* Let's make the label */
+    while(name[y]) {
+        if (name[y] == '.') {
+            if (name[y + 1] == 0) {
+                break;
+            }
             label[last] = x - (last + 1);
             last = x;
-        }else{
+        } else {
             label[x] = name[y];
         }
-        if(x++ == 255) return 0;
+        if (x++ == MAX_LABEL_SIZE - 1) {
+            return 0;
+        }
         y++;
     }
+
     label[last] = x - (last + 1);
-    if(x == 1) x--; // special case, bad names, but handle correctly
+
+    if (x == 1) {
+        x--; // special case, bad names, but handle correctly
+    }
     len = x + 1;
-    label[x] = 0; // always terminate w/ a 0
-
-    // double-loop checking each label against all m->_labels for match
-    for(x = 0; label[x]; x += label[x] + 1)
-    {
-        for(y = 0; m->_labels[y]; y++)
-            if(_lmatch(m,label+x,m->_labels[y]))
-            {
-                // matching label, set up pointer
+
+    /* \0 terminate the label */
+    label[x] = 0;
+
+    /* Check each label against all msg->labels for a match */
+    for(x = 0; label[x]; x += label[x] + 1) {
+        for( y = 0; msg->labels[y]; y++) {
+            if (MatchLabel(msg, label+x, msg->labels[y])) {
+                /* If the label matches, create the pointer */
                 l = label + x;
-                short2net(m->_labels[y] - m->_packet, &l);
+                DnsShort2Net(msg->labels[y] - msg->packet, &l);
                 label[x] |= 0xc0;
                 len = x + 2;
                 break;
             }
-        if(label[x] & 0xc0) break;
+        }
+
+        if (label[x] & 0xc0) {
+            break;
+        }
     }
 
-    // copy into buffer, point there now
-    memcpy(*bufp,label,len);
-    l = *bufp;
-    *bufp += len;
+    /* Copy the label into the buffer and let the buffer pointer point to the label */
+    memcpy(*buf, label, len);
+    l = *buf;
+    *buf += len;
 
-    // for each new label, store it's location for future compression
-    for(x = 0; l[x]; x += l[x] + 1)
-    {
-        if(l[x] & 0xc0) break;
-        if(m->_label + 1 >= 19) break;
-        m->_labels[m->_label++] = l + x;
+    /* Save the label location of each new label for future compression */
+    for (x = 0; l[x]; x += l[x] + 1) {
+        if(l[x] & 0xc0) {
+            break;
+        }
+
+        if (msg->label + 1 >= MAX_LABEL - 1) {
+            break;
+        }
+
+        msg->labels[msg->label++] = l + x;
     }
 
     return len;
 }
 
-int _rrparse(struct message *m, struct resource *rr, int count, unsigned char **bufp)
+
+/*!
+ * \brief Parse a ressource record packet and fill the rr struct
+ *
+ * \param msg       DNS message
+ * \param rr        Pointer to ressource record struct where the informations shall be stored
+ * \param count     Number of ressource records in the package
+ * \param buf       Package buffer
+ *
+ * \return          0: success, -1 in case of an error
+ */
+static int ParseRR(DNSMESSAGE *msg, DNSRESOURCE *rr, int count, uint8_t **buf)
 {
     int i;
-    for(i=0; i < count; i++)
-    {
-        _label(m, bufp, &(rr[i].name));
-        rr[i].type = net2short(bufp);
-        rr[i].class = net2short(bufp);
-        rr[i].ttl = net2long(bufp);
-        rr[i].rdlength = net2short(bufp);
-
-        // if not going to overflow, make copy of source rdata
-        if(rr[i].rdlength + (*bufp - m->_buf) > MAX_PACKET_LEN || m->_len + rr[i].rdlength > MAX_PACKET_LEN) return 1;
-        rr[i].rdata = m->_packet + m->_len;
-        m->_len += rr[i].rdlength;
-        memcpy(rr[i].rdata,*bufp,rr[i].rdlength);
-
-        // parse commonly known ones
-        switch(rr[i].type)
-        {
-        case 1:
-            if(m->_len + 16 > MAX_PACKET_LEN) return 1;
-            rr[i].known.a.name = m->_packet + m->_len;
-            m->_len += 16;
-            sprintf(rr[i].known.a.name,"%d.%d.%d.%d",(*bufp)[0],(*bufp)[1],(*bufp)[2],(*bufp)[3]);
-            rr[i].known.a.ip = net2long(bufp);
-            break;
-        case 2:
-            _label(m, bufp, &(rr[i].known.ns.name));
-            break;
-        case 5:
-            _label(m, bufp, &(rr[i].known.cname.name));
-            break;
-        case 12:
-            _label(m, bufp, &(rr[i].known.ptr.name));
-            break;
-        case 33:
-            rr[i].known.srv.priority = net2short(bufp);
-            rr[i].known.srv.weight = net2short(bufp);
-            rr[i].known.srv.port = net2short(bufp);
-            _label(m, bufp, &(rr[i].known.srv.name));
-            break;            
-        default:
-            *bufp += rr[i].rdlength;
+
+    for (i=0; i < count; i++) {
+        /* Extract the name, type, class, tt and record data length from the
+           buffer. The buffer pointer is automatically incremented by each call
+           tp nut2xxx
+         */
+        ExtractLabel(msg, buf, &(rr[i].name));
+        rr[i].type     = DnsNet2Short(buf);
+        rr[i].class    = DnsNet2Short(buf);
+        rr[i].ttl      = DnsNet2Long(buf);
+        rr[i].rdlength = DnsNet2Short(buf);
+
+        /* Sanity checking the length */
+        if ((rr[i].rdlength + (*buf - msg->buf) > MAX_PACKET_LEN) ||
+           (msg->len + rr[i].rdlength > MAX_PACKET_LEN)) {
+            return -1;
+        }
+
+        /* Length was ok, make a copy of the data */
+        rr[i].rdata = msg->packet + msg->len;
+
+        msg->len += rr[i].rdlength;
+        memcpy(rr[i].rdata, *buf, rr[i].rdlength);
+
+        /* Parse known message types */
+        switch (rr[i].type) {
+            case RRTYPE_A:
+                if(msg->len + 16 > MAX_PACKET_LEN) {
+                    return -1;
+                }
+                rr[i].known.a.name = msg->packet + msg->len;
+                msg->len += 16;
+                sprintf(rr[i].known.a.name, "%d.%d.%d.%d", (*buf)[0], (*buf)[1], (*buf)[2], (*buf)[3]);
+                rr[i].known.a.ip = DnsNet2Long(buf);
+                break;
+
+            case RRTYPE_NS:
+                ExtractLabel(msg, buf, &(rr[i].known.ns.name));
+                break;
+
+            case RRTYPE_CNAME:
+                ExtractLabel(msg, buf, &(rr[i].known.cname.name));
+                break;
+
+            case RRTYPE_PTR:
+                ExtractLabel(msg, buf, &(rr[i].known.ptr.name));
+                break;
+
+            case RRTYPE_SRV:
+                rr[i].known.srv.priority = DnsNet2Short(buf);
+                rr[i].known.srv.weight   = DnsNet2Short(buf);
+                rr[i].known.srv.port     = DnsNet2Short(buf);
+                ExtractLabel(msg, buf, &(rr[i].known.srv.name));
+                break;
+
+            default:
+                *buf += rr[i].rdlength;
         }
     }
 
     return 0;
 }
 
-void message_parse(struct message *m, unsigned char *packet)
+
+/*!
+ * \brief Parse DNS packet into message format
+ *
+ * The packet must be at least MAX_PACKET_LEN bytes in size and must be allocated clean (zeroed)
+ *
+ * \param msg       DNS message
+ * \param packet    Package buffer
+ */
+void DnsParseMsg(DNSMESSAGE *msg, uint8_t *packet)
 {
-    unsigned char *buf;
+    uint8_t *buf;
     int i;
-    
-    if(packet == 0 || m == 0) return;
-
-    // keep all our mem in one (aligned) block for easy freeing
-    #define my(x,y,t) while(m->_len&7) m->_len++; (x) = (t *) (m->_packet + m->_len); m->_len += (sizeof (t) * (y));
-
-    // header stuff bit crap
-    m->_buf = buf = packet;
-    m->id = net2short(&buf);
-    if(buf[0] & 0x80) m->header.qr = 1;
-    m->header.opcode = (buf[0] & 0x78) >> 3;
-    if(buf[0] & 0x04) m->header.aa = 1;
-    if(buf[0] & 0x02) m->header.tc = 1;
-    if(buf[0] & 0x01) m->header.rd = 1;
-    if(buf[1] & 0x80) m->header.ra = 1;
-    m->header.z = (buf[1] & 0x70) >> 4;
-    m->header.rcode = buf[1] & 0x0F;
+
+    if((packet == 0) || (msg == 0)) {
+        return;
+    }
+
+    /* Bit decoding of the record header */
+    buf = packet;
+    msg->buf = buf;
+    msg->id = DnsNet2Short(&buf);
+// TODO: Ohne shiften auf die Bits prüfen, korrekte Bitwerte hinterlegen
+
+    if (buf[0] & 0x80) {
+        msg->header.qr = 1;
+    }
+
+    /* Shift opcode field 3 bits right for easy comparision */
+    msg->header.opcode = (buf[0] & 0x78) >> 3;
+
+    if (buf[0] & 0x01) {
+        msg->header.rd = 1;
+    }
+
+    if (buf[0] & 0x02) {
+        msg->header.tc = 1;
+    }
+
+    if (buf[0] & 0x04) {
+        msg->header.aa = 1;
+    }
+
+    if (buf[1] & 0x80) {
+        msg->header.ra = 1;
+    }
+
+    msg->header.z = (buf[1] & 0x70) >> 4;
+    msg->header.rcode = buf[1] & 0x0F;
     buf += 2;
-    m->qdcount = net2short(&buf);
-    if(m->_len + (sizeof(struct question) * m->qdcount) > MAX_PACKET_LEN - 8) { m->qdcount = 0; return; }
-    m->ancount = net2short(&buf);
-    if(m->_len + (sizeof(struct resource) * m->ancount) > MAX_PACKET_LEN - 8) { m->ancount = 0; return; }
-    m->nscount = net2short(&buf);
-    if(m->_len + (sizeof(struct resource) * m->nscount) > MAX_PACKET_LEN - 8) { m->nscount = 0; return; }
-    m->arcount = net2short(&buf);
-    if(m->_len + (sizeof(struct resource) * m->arcount) > MAX_PACKET_LEN - 8) { m->arcount = 0; return; }
-
-    // process questions
-    my(m->qd, m->qdcount, struct question);
-    for(i=0; i < m->qdcount; i++)
-    {
-        _label(m, &buf, &(m->qd[i].name));
-        m->qd[i].type = net2short(&buf);
-        m->qd[i].class = net2short(&buf);
-    }
-
-    // process rrs
-    my(m->an, m->ancount, struct resource);
-    my(m->ns, m->nscount, struct resource);
-    my(m->ar, m->arcount, struct resource);
-    if(_rrparse(m,m->an,m->ancount,&buf)) return;
-    if(_rrparse(m,m->ns,m->nscount,&buf)) return;
-    if(_rrparse(m,m->ar,m->arcount,&buf)) return;
+
+    msg->qdcount = DnsNet2Short(&buf);
+    if (msg->len + (sizeof(DNSQUESTION) * msg->qdcount) > MAX_PACKET_LEN - 8) {
+        msg->qdcount = 0;
+        return;
+    }
+
+    /* Get number of AN records */
+    msg->ancount = DnsNet2Short(&buf);
+    if (msg->len + (sizeof(DNSRESOURCE) * msg->ancount) > MAX_PACKET_LEN - 8) {
+        msg->ancount = 0;
+        return;
+    }
+
+    /* Get number of NS records */
+    msg->nscount = DnsNet2Short(&buf);
+    if (msg->len + (sizeof(DNSRESOURCE) * msg->nscount) > MAX_PACKET_LEN - 8) {
+        msg->nscount = 0;
+        return;
+    }
+
+    /* Get number of AR records */
+    msg->arcount = DnsNet2Short(&buf);
+    if (msg->len + (sizeof(DNSRESOURCE) * msg->arcount) > MAX_PACKET_LEN - 8) {
+        msg->arcount = 0;
+        return;
+    }
+
+    /* Process the questions */
+    while (msg->len & 0x07) msg->len++;
+    /* Process AN record */
+    msg->qd= (DNSQUESTION *) (msg->packet + msg->len);
+    msg->len += sizeof(DNSQUESTION) * msg->qdcount;
+
+    for(i = 0; i < msg->qdcount; i++) {
+        ExtractLabel(msg, &buf, &(msg->qd[i].name));
+        msg->qd[i].type  = DnsNet2Short(&buf);
+        msg->qd[i].class = DnsNet2Short(&buf);
+    }
+
+
+    /* Process Ressource records and keep us always at a 8 byte boundary */
+
+    /* Align... */
+    while (msg->len & 0x07) msg->len++;
+    /* Process AN record */
+    msg->an = (DNSRESOURCE *) (msg->packet + msg->len);
+    msg->len += sizeof(DNSRESOURCE) * msg->ancount;
+
+    /* Align... */
+    while (msg->len & 0x07) msg->len++;
+    /* Process NS record */
+    msg->ns = (DNSRESOURCE *) (msg->packet + msg->len);
+    msg->len += sizeof(DNSRESOURCE) * msg->nscount;
+
+    /* Align... */
+    while (msg->len & 0x07) msg->len++;
+    /* Process AR record */
+    msg->ar = (DNSRESOURCE *) (msg->packet + msg->len);
+    msg->len += sizeof(DNSRESOURCE) * msg->arcount;
+
+    /* Parse AN record */
+    if (ParseRR(msg, msg->an, msg->ancount, &buf)) {
+        /* Size limit checking failed */
+        return;
+    }
+
+    /* Parse NS record */
+    if (ParseRR(msg, msg->ns, msg->nscount, &buf)) {
+        /* Size limit checking failed */
+        return;
+    }
+
+    /* Parse AR record */
+    if (ParseRR(msg, msg->ar, msg->arcount, &buf)) {
+        /* Size limit checking failed */
+        return;
+    }
 }
 
-void message_qd(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class)
+
+/*!
+ * \brief Append a ressource record to the message
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ * \param type      Query type
+ * \param class     Query class
+ * \param ttl       TTL value
+ */
+static void DnsAppendRR(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl)
 {
-    m->qdcount++;
-    if(m->_buf == 0) m->_buf = m->_packet + 12; // initialization
-    _host(m, &(m->_buf), name);
-    short2net(type, &(m->_buf));
-    short2net(class, &(m->_buf));
+    if (msg->buf == 0) {
+        /* Initialise the buffer pointer */
+        msg->buf = msg->packet + 12;
+    }
+    Host2Label(msg, &(msg->buf), name);
+    DnsShort2Net(type, &(msg->buf));
+    DnsShort2Net(class, &(msg->buf));
+    DnsLong2Net(ttl, &(msg->buf));
 }
 
-void _rrappend(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl)
+
+/*!
+ * \brief Append a QD ressource record to the message
+ *
+ * Should be called first before calling DnsMsgAdd_an.
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ * \param type      Query type
+ * \param class     Query class
+ */
+void DnsMsgAdd_qd(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class)
 {
-    if(m->_buf == 0) m->_buf = m->_packet + 12; // initialization
-    _host(m, &(m->_buf), name);
-    short2net(type, &(m->_buf));
-    short2net(class, &(m->_buf));
-    long2net(ttl, &(m->_buf));
+    msg->qdcount++;
+    if (msg->buf == 0) {
+        /* Initialise the buffer pointer */
+        msg->buf = msg->packet + 12;
+    }
+
+    Host2Label(msg, &(msg->buf), name);
+    DnsShort2Net(type, &(msg->buf));
+    DnsShort2Net(class, &(msg->buf));
 }
 
-void message_an(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl)
+
+/*!
+ * \brief Append a AN ressource record to the message
+ *
+ * Should be called first before calling DnsMsgAdd_ns.
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ * \param type      Query type
+ * \param class     Query class
+ */
+void DnsMsgAdd_an(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl)
 {
-    m->ancount++;
-    _rrappend(m,name,type,class,ttl);
+    msg->ancount++;
+    DnsAppendRR(msg, name, type, class, ttl);
 }
 
-void message_ns(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl)
+
+/*!
+ * \brief Append a NS ressource record to the message
+ *
+ * Should be called first before calling DnsMsgAdd_ar.
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ * \param type      Query type
+ * \param class     Query class
+ */
+void DnsMsgAdd_ns(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl)
 {
-    m->nscount++;
-    _rrappend(m,name,type,class,ttl);
+    msg->nscount++;
+    DnsAppendRR(msg, name, type, class, ttl);
 }
 
-void message_ar(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl)
+
+/*!
+ * \brief Append a AR ressource record to the message
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ * \param type      Query type
+ * \param class     Query class
+ */
+void DnsMsgAdd_ar(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl)
 {
-    m->arcount++;
-    _rrappend(m,name,type,class,ttl);
+    msg->arcount++;
+    DnsAppendRR(msg, name, type, class, ttl);
 }
 
-void message_rdata_long(struct message *m, unsigned long int l)
+
+/*!
+ * \brief Append resource data types block: uint32_t value
+ *
+ * \param msg       DNS message
+ * \param val       value
+ */
+void DnsMsgAdd_rdata_long(DNSMESSAGE *msg, uint32_t val)
 {
-    short2net(4, &(m->_buf));
-    long2net(l, &(m->_buf));
+    DnsShort2Net(4, &(msg->buf));
+    DnsLong2Net(val, &(msg->buf));
 }
 
-void message_rdata_name(struct message *m, unsigned char *name)
+
+/*!
+ * \brief Append resource data types block: Hostname
+ *
+ * \param msg       DNS message
+ * \param name      Hostname
+ */
+void DnsMsgAdd_rdata_name(DNSMESSAGE *msg, uint8_t *name)
 {
-    unsigned char *mybuf = m->_buf;
-    m->_buf += 2;
-    short2net(_host(m, &(m->_buf), name),&mybuf); // hackish, but cute
+    uint8_t *tmp_buf = msg->buf;
+    msg->buf += 2;
+    DnsShort2Net(Host2Label(msg, &(msg->buf), name), &tmp_buf);
 }
 
-void message_rdata_srv(struct message *m, unsigned short int priority, unsigned short int weight, unsigned short int port, unsigned char *name)
+/*!
+ * \brief Append resource data types block: Service data
+ *
+ * \param msg       DNS message
+ * \param priority  Priority of the target host: lower value means more preferred.
+ * \param weight    Relative weight for records with the same priority.
+ * \param port      TCP / UDP port number of the service
+ * \param name      The canonical hostname of the machine providing the service.
+ */
+void DnsMsgAdd_rdata_srv(DNSMESSAGE *msg, uint16_t priority, uint16_t weight, uint16_t port, uint8_t *name)
 {
-    unsigned char *mybuf = m->_buf;
-    m->_buf += 2;
-    short2net(priority, &(m->_buf));
-    short2net(weight, &(m->_buf));
-    short2net(port, &(m->_buf));
-    short2net(_host(m, &(m->_buf), name) + 6, &mybuf);
+    uint8_t *tmp_buf = msg->buf;
+    msg->buf += 2;
+    DnsShort2Net(priority, &(msg->buf));
+    DnsShort2Net(weight, &(msg->buf));
+    DnsShort2Net(port, &(msg->buf));
+    DnsShort2Net(Host2Label(msg, &(msg->buf), name) + 6, &tmp_buf);
 }
 
-void message_rdata_raw(struct message *m, unsigned char *rdata, unsigned short int rdlength)
+
+/*!
+ * \brief Append resource data types block: Raw data
+ *
+ * \param msg       DNS message
+ * \param rdata     Pointer to the raw data buffer
+ * \param rdlength  Length of the raw data block
+ */
+void DnsMsgAdd_rdata_raw(DNSMESSAGE *msg, uint8_t *rdata, uint16_t rdlength)
 {
-    if((m->_buf - m->_packet) + rdlength > 4096) rdlength = 0;
-    short2net(rdlength, &(m->_buf));
-    memcpy(m->_buf,rdata,rdlength);
-    m->_buf += rdlength;
+    if ((msg->buf - msg->packet) + rdlength > MAX_PACKET_LEN) {
+        rdlength = 0;
+    }
+    DnsShort2Net(rdlength, &(msg->buf));
+    memcpy(msg->buf, rdata, rdlength);
+    msg->buf += rdlength;
 }
 
-unsigned char *message_packet(struct message *m)
+
+/*!
+ * \brief Generate the message packet to be send out.
+ *
+ * \return      Packet buffer
+ */
+uint8_t *DnsMsg2Pkt(DNSMESSAGE *msg)
 {
-    unsigned char c, *buf = m->_buf;
-    m->_buf = m->_packet;
-    short2net(m->id, &(m->_buf));
-    if(m->header.qr) m->_buf[0] |= 0x80;
-    if((c = m->header.opcode)) m->_buf[0] |= (c << 3);
-    if(m->header.aa) m->_buf[0] |= 0x04;
-    if(m->header.tc) m->_buf[0] |= 0x02;
-    if(m->header.rd) m->_buf[0] |= 0x01;
-    if(m->header.ra) m->_buf[1] |= 0x80;
-    if((c = m->header.z)) m->_buf[1] |= (c << 4);
-    if(m->header.rcode) m->_buf[1] |= m->header.rcode;
-    m->_buf += 2;
-    short2net(m->qdcount, &(m->_buf));
-    short2net(m->ancount, &(m->_buf));
-    short2net(m->nscount, &(m->_buf));
-    short2net(m->arcount, &(m->_buf));
-    m->_buf = buf; // restore, so packet_len works
-    return m->_packet;
+    uint8_t c, *buf = msg->buf;
+    msg->buf = msg->packet;
+    DnsShort2Net(msg->id, &(msg->buf));
+// TODO: Bits direkt ohne shiften
+    if (msg->header.qr) {
+        msg->buf[0] |= 0x80;
+    }
+
+    if ((c = msg->header.opcode)) {
+        msg->buf[0] |= (c << 3);
+    }
+    if (msg->header.aa) {
+        msg->buf[0] |= 0x04;
+    }
+
+    if (msg->header.tc) {
+        msg->buf[0] |= 0x02;
+    }
+
+    if (msg->header.rd) {
+        msg->buf[0] |= 0x01;
+    }
+
+    if (msg->header.ra) {
+        msg->buf[1] |= 0x80;
+    }
+
+    if ((c = msg->header.z)) {
+        msg->buf[1] |= (c << 4);
+    }
+
+    if (msg->header.rcode) {
+        msg->buf[1] |= msg->header.rcode;
+    }
+
+    msg->buf += 2;
+
+    DnsShort2Net(msg->qdcount, &(msg->buf));
+    DnsShort2Net(msg->ancount, &(msg->buf));
+    DnsShort2Net(msg->nscount, &(msg->buf));
+    DnsShort2Net(msg->arcount, &(msg->buf));
+
+    /* Restore the packet pointer. It is necessary for DnsMsgLen() */
+    msg->buf = buf;
+
+    /* Return the modified packet */
+    return msg->packet;
 }
 
-int message_packet_len(struct message *m)
+
+/*!
+ * \brief Calculate message packet length
+ *
+ * \return      Message packet length
+ */
+
+int DnsMsgLen(DNSMESSAGE *msg)
 {
-    if(m->_buf == 0) return 12;
-    return m->_buf - m->_packet;
+    if(msg->buf == 0) return 12;
+    return msg->buf - msg->packet;
 }
+
+/*@}*/
index 054980917aee9687dbea0a7f6903ccb94f4aeba6..8238cf4893b555746a954c06b86eed06884e36c2 100644 (file)
--- a/rfc1035.h
+++ b/rfc1035.h
-#ifndef _1035_h
-#define _1035_h
-
-// be familiar with rfc1035 if you want to know what all the variable names mean, but this hides most of the dirty work
-// all of this code depends on the buffer space a packet is in being 4096 and zero'd before the packet is copied in
-// also conveniently decodes srv rr's, type 33, see rfc2782
+#ifndef _RFC1035_H_
+#define _RFC1035_H_
+
+/*
+ * Copyright (C) 2003 Jer <jer@jabber.org>
+ * Copyright (c) 2009 Simon Budig <simon@budig.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file include/pro/rfc1035.h
+ * \brief Standalone DNS parsing functions
+ *
+ * Implementation follows RF1035 [http://www.rfc-editor.org/rfc/rfc1035.txt] and
+ * includes decoding functions for svr rr's code type 33. See RFC2782
+ * [http://www.rfc-editor.org/rfc/rfc2782.txt]*
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
+#include <stdint.h>
+
+/*!
+ * \addtogroup xgMulticastDns
+ */
+/*@{*/
+
+#define PACKET_BUFFER_LEN 4096
+#define MAX_PACKET_LEN    4000
+#define MAX_LABEL_SIZE    256
+#define MAX_LABEL         20
+
+#define QTYPE_A           1
+#define QTYPE_NS          2
+#define QTYPE_CNAME       5
+#define QTYPE_PTR         12
+#define QTYPE_TXT         16
+#define QTYPE_SRV         33
+#define QTYPE_ANY         255
+
+#define RRTYPE_A          QTYPE_A
+#define RRTYPE_NS         QTYPE_NS
+#define RRTYPE_CNAME      QTYPE_CNAME
+#define RRTYPE_PTR        QTYPE_PTR
+#define RRTYPE_TXT        QTYPE_TXT
+#define RRTYPE_SRV        QTYPE_SRV
+
+
+/*!
+ * \brief DNS question structure type.
+ *
+ * Contains the name, query type and class
+ */
+typedef struct
+{
+    uint8_t *name;
+    uint16_t type;
+    uint16_t class;
+} DNSQUESTION;
 
-// should be reasonably large, for udp
-#define MAX_PACKET_LEN 4000
 
-struct question
+/*!
+ * \brief DNS ressource record structure type.
+ */
+typedef struct
 {
-    unsigned char *name;
-    unsigned short int type, class;
-};
-
-#define QTYPE_A 1
-#define QTYPE_NS 2
-#define QTYPE_CNAME 5
-#define QTYPE_PTR 12
-#define QTYPE_TXT 16
-#define QTYPE_SRV 33
-#define QTYPE_ANY 255
-
-struct resource
-{
-    unsigned char *name;
-    unsigned short int type, class;
-    unsigned long int ttl;
-    unsigned short int rdlength;
-    unsigned char *rdata;
+    uint8_t *name;
+    uint16_t type;
+    uint16_t class;
+    uint32_t ttl;
+    uint16_t rdlength;
+    uint8_t *rdata;
     union {
-        struct { unsigned long int ip; char *name; } a;
-        struct { unsigned char *name; } ns;
-        struct { unsigned char *name; } cname;
-        struct { unsigned char *name; } ptr;
-        struct { unsigned short int priority, weight, port; unsigned char *name; } srv;
+        struct {
+            uint32_t ip;
+            uint8_t *name;
+        } a;
+        struct {
+            uint8_t *name;
+        } ns;
+        struct {
+            uint8_t *name;
+        } cname;
+        struct {
+            uint8_t *name;
+        } ptr;
+        struct {
+            uint16_t priority;
+            uint16_t weight;
+            uint16_t port;
+            uint8_t *name;
+        } srv;
     } known;
-};
-
-struct message
-{
-    // external data
-    unsigned short int id;
-    struct { unsigned short qr:1, opcode:4, aa:1, tc:1, rd:1, ra:1, z:3, rcode:4; } header;
-    unsigned short int qdcount, ancount, nscount, arcount;
-    struct question *qd;
-    struct resource *an, *ns, *ar;
-
-    // internal variables
-    unsigned char *_buf, *_labels[20];
-    int _len, _label;
-
-    // packet acts as padding, easier mem management
-    unsigned char _packet[MAX_PACKET_LEN];
-};
-
-// returns the next short/long off the buffer (and advances it)
-unsigned short int net2short(unsigned char **buf);
-unsigned long int net2long(unsigned char **buf);
-
-// copies the short/long into the buffer (and advances it)
-void short2net(unsigned short int i, unsigned char **buf);
-void long2net(unsigned long int l, unsigned char **buf);
-
-// parse packet into message, packet must be at least MAX_PACKET_LEN and message must be zero'd for safety
-void message_parse(struct message *m, unsigned char *packet);
-
-// create a message for sending out on the wire
-struct message *message_wire(void);
-
-// append a question to the wire message
-void message_qd(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class);
-
-// append a resource record to the message, all called in order!
-void message_an(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl);
-void message_ns(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl);
-void message_ar(struct message *m, unsigned char *name, unsigned short int type, unsigned short int class, unsigned long int ttl);
-
-// append various special types of resource data blocks
-void message_rdata_long(struct message *m, unsigned long int l);
-void message_rdata_name(struct message *m, unsigned char *name);
-void message_rdata_srv(struct message *m, unsigned short int priority, unsigned short int weight, unsigned short int port, unsigned char *name);
-void message_rdata_raw(struct message *m, unsigned char *rdata, unsigned short int rdlength);
-
-// return the wire format (and length) of the message, just free message when done
-unsigned char *message_packet(struct message *m);
-int message_packet_len(struct message *m);
+} DNSRESOURCE;
 
 
+/*!
+ * \brief DNS message structure type.
+ */
+typedef struct
+{
+    /* external data */
+    uint16_t id;
+    struct {
+        uint16_t qr:1;
+        uint16_t opcode:4;
+        uint16_t aa:1;
+        uint16_t tc:1;
+        uint16_t rd:1;
+        uint16_t ra:1;
+        uint16_t z:3;
+        uint16_t rcode:4;
+    } header;
+    uint16_t     qdcount;
+    uint16_t     ancount;
+    uint16_t     nscount;
+    uint16_t     arcount;
+    DNSQUESTION *qd;
+    DNSRESOURCE *an;
+    DNSRESOURCE *ns;
+    DNSRESOURCE *ar;
+
+    /* internal variables */
+    uint8_t* buf;
+    uint8_t* labels[MAX_LABEL];
+    int len;
+    int label;
+
+    /* Padding data */
+    uint8_t packet[MAX_PACKET_LEN];
+} DNSMESSAGE;
+
+/* Conversion of the network byte order buffer content into uint16_t or uint32_t values.
+ * Buffer pointer is incremented accordingly
+ */
+uint16_t DnsNet2Short(uint8_t **buf);
+uint32_t DnsNet2Long(uint8_t **buf);
+
+/* Copies a short or long value in host byte order to the buffer and convert it to network byte order */
+void DnsShort2Net(uint16_t val, uint8_t **buf);
+void DnsLong2net(uint32_t val, uint8_t **buf);
+
+/* Parsing function to parse a DNS packet into message format. Therfor the packet
+   must be at least MAX_PACKET_LEN bytes in size and must be allocated clean (zeroed)
+ */
+void DnsParseMsg(DNSMESSAGE *msg, uint8_t *packet);
+
+/* Create a message for sending out on the network. */
+DNSMESSAGE *DnsCreateMsg(void);
+
+/* Append a question to the message buffer */
+void DnsMsgAdd_qd(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class);
+
+/* Append a resource record to the message. Call these functions in the below order. */
+void DnsMsgAdd_an(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl);
+void DnsMsgAdd_ns(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl);
+void DnsMsgAdd_ar(DNSMESSAGE *msg, uint8_t *name, uint16_t type, uint16_t class, uint32_t ttl);
+
+/* Append resource data types blocks */
+void DnsMsgAdd_rdata_long(DNSMESSAGE *msg, uint32_t val);
+void DnsMsgAdd_rdata_name(DNSMESSAGE *msg, uint8_t *name);
+void DnsMsgAdd_rdata_srv(DNSMESSAGE *msg, uint16_t priority, uint16_t weight, uint16_t port, uint8_t *name);
+void DnsMsgAdd_rdata_raw(DNSMESSAGE *msg, uint8_t *rdata, uint16_t rdlength);
+
+/* Generate the message packet to be send out and return the length */
+uint8_t *DnsMsg2Pkt(DNSMESSAGE *msg);
+int DnsMsgLen(DNSMESSAGE *msg);
+
+
+/*@}*/
 #endif
diff --git a/shash.c b/shash.c
index b3dcf29fe9282586ba65506d044151463947b71f..740b0527d894731e2a93cced3384faf2727fc591 100644 (file)
--- a/shash.c
+++ b/shash.c
+/*
+ * Copyright (c) 1998-1999 Jeremie Miller <jer@jabber.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ *
+ */
+
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
+
+/*!
+ * \file gorp/hash/shash.c
+ * \brief Simple hastable implementation of a string ==> void* hashtable
+ *        Minimal and efficient
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
+
 #include <stdlib.h>
 #include <string.h>
-#include "xht.h"
+#include <inttypes.h>
+#include "shash.h"
+
+/*!
+ * \addtogroup xgSHash
+ */
+/*@{*/
 
-typedef struct xhn_struct
+typedef struct node_struct
 {
     char flag;
-    struct xhn_struct *next;
+    struct node_struct *next;
     char *key;
     void *val;
-} *xhn;
+} *SHNODE;
 
-struct xht_struct
+struct string_hash_table_struct
 {
     int prime;
-    xhn zen;
+    SHNODE zen;
 };
 
-/* Generates a hash code for a string.
- * This function uses the ELF hashing algorithm as reprinted in 
+/*!
+ * \brief Generates a hash code for a string.
+ *
+ * This function uses the ELF hashing algorithm as reprinted in
  * Andrew Binstock, "Hashing Rehashed," Dr. Dobb's Journal, April 1996.
+ *
+ * \param s     The string to hash
+ *
+ * \return      The calculated hash value
  */
-int _xhter(const char *s)
-{
-    /* ELF hash uses unsigned chars and unsigned arithmetic for portability */
-    const unsigned char *name = (const unsigned char *)s;
-    unsigned long h = 0, g;
-
-    while (*name)
-    { /* do some fancy bitwanking on the string */
-        h = (h << 4) + (unsigned long)(*name++);
-        if ((g = (h & 0xF0000000UL))!=0)
-            h ^= (g >> 24);
-        h &= ~g;
 
+static int32_t StrToHash(const char *str)
+{
+    /* ELF hash uses uint8_ts and unsigned arithmetic for portability */
+    const uint8_t *name = (const uint8_t *)str;
+    uint32_t hash = 0;
+    uint32_t g;
+
+    while (*name) {
+        /* do some fancy bit calculations on the string */
+        hash = (hash << 4) + (uint32_t)(*name++);
+        g = (hash & 0xF0000000UL);
+        if (g != 0) {
+            hash ^= (g >> 24);
+        }
+        hash &= ~g;
     }
 
-    return (int)h;
+    return (int32_t)hash;
 }
 
 
-xhn _xht_node_find(xhn n, const char *key)
+/*!
+ * \brief Initialise a new hash
+ *
+ * \param prime size of the hash (!!! You have to use a _prime_ number (e.g. 5, 7, 11, 13, ...) !!!)
+ *
+ * \return      pointer to the new hash
+ */
+SHASH SHashInit(int prime)
 {
-    for(;n != 0; n = n->next)
-        if(n->key != 0 && strcmp(key, n->key) == 0)
-            return n;
-    return 0;
+    SHASH xnew;
+
+    xnew = (SHASH)malloc(sizeof(struct string_hash_table_struct));
+    xnew->prime = prime;
+    xnew->zen = (SHNODE)malloc(sizeof(struct node_struct)*prime); /* array of SHNODE size of prime */
+    memset(xnew->zen, 0, sizeof(struct node_struct)*prime);
+    return xnew;
 }
 
+/*!
+ * \brief Search the node for the given key in the hash table
+ *
+ * \param node  starting node to search from
+ * \param key   The key to get
+ *
+ * \return      The node or NULL of node was not found
+ */
 
-xht xht_new(int prime)
+static SHNODE SHashFindNode(SHNODE node, const char *key)
 {
-    xht xnew;
-
-    xnew = (xht)malloc(sizeof(struct xht_struct));
-    xnew->prime = prime;
-    xnew->zen = (xhn)malloc(sizeof(struct xhn_struct)*prime); /* array of xhn size of prime */
-    bzero(xnew->zen,sizeof(struct xhn_struct)*prime);
-    return xnew;
+    for(; node != 0; node = node->next) {
+        if((node->key != 0) && (strcmp(key, node->key) == 0)) {
+            return node;
+        }
+    }
+    return 0;
 }
 
-/* does the set work, used by xht_set and xht_store */
-void _xht_set(xht h, char *key, void *val, char flag)
+
+/*!
+ * \brief Helper function: Add a new entry to the hash table
+ *
+ * \param hash  The hashtable to insert the new entry in
+ * \param key   The key, under which the value should be entered
+ * \param val   Void* pointer to be saved under 'key' in the hash table
+ * \param flag  If flag != 0 the node memory is freed first and managed by this function
+ */
+
+static void SHashDoSet(SHASH hash, char *key, void *val, char flag)
 {
-    int i;
-    xhn n;
+    int32_t i;
+    SHNODE node;
 
-    /* get our index for this key */
-    i = _xhter(key) % h->prime;
+    /* Get the hash index of the key */
+    i = StrToHash(key) % hash->prime;
 
-    /* check for existing key first, or find an empty one */
-    if((n = _xht_node_find(&h->zen[i], key)) == 0)
-        for(n = &h->zen[i]; n != 0; n = n->next)
-            if(n->val == 0)
+    /* Does the key just exists? If not, find an empty one */
+    node = SHashFindNode(&hash->zen[i], key);
+    if (node == 0) {
+        for(node = &hash->zen[i]; node != 0; node = node->next) {
+            if(node->val == 0) {
                 break;
+            }
+        }
+    }
 
-    /* if none, make a new one, link into this index */
-    if(n == 0)
-    {
-        n = (xhn)malloc(sizeof(struct xhn_struct));
-        n->next = h->zen[i].next;
-        h->zen[i].next = n;
+    /* If the key does not exists, create a new node and link into the hash */
+    if (node == 0) {
+        node = (SHNODE)malloc(sizeof(struct node_struct));
+        node->next = hash->zen[i].next;
+        hash->zen[i].next = node;
     }
 
-    /* when flag is set, we manage their mem and free em first */
-    if(n->flag)
-    {
-        free(n->key);
-        free(n->val);
+    /* If flag is set, the node memory will be freed first and then reused with the new key / value pair */
+    if (node->flag) {
+        free(node->key);
+        free(node->val);
     }
 
-    n->flag = flag;
-    n->key = key;
-    n->val = val;
+    node->flag = flag;
+    node->key = key;
+    node->val = val;
 }
 
-void xht_set(xht h, char *key, void *val)
+
+/*!
+ * \brief Add a new entry to the hash table
+ *
+ * The caller is responsible for the key storage. No copies are made.
+ * Do not free these valued before calling SHashFree()!!!
+ *
+ * If val is set to NULL, the entry is cleared and the memory is reused but
+ * never freed. The number of keys can only grow up to peak usage.
+ *
+ * \param hash  The hashtable to insert the new entry in
+ * \param key   The key, under which the value should be entered
+ * \param val   Void* pointer to be saved under 'key' in the hash table
+ */
+
+void SHashSet(SHASH hash, char *key, void *val)
 {
-    if(h == 0 || key == 0)
+    if ((hash == 0) || (key == 0)) {
         return;
-    _xht_set(h, key, val, 0);
+    }
+    SHashDoSet(hash, key, val, 0);
 }
 
-void xht_store(xht h, const char *key, int klen, void *val, int vlen)
+
+/*!
+ * \brief Add a new entry to the hash table
+ *
+ * Unlike SHashSet() where key and values are managed in the callers memory
+ * space, here they are copied into the hash table and freed when
+ * val is 0 or by calling SHashFree().
+ *
+ * If val is set to NULL, the entry is cleared and the memory is freed.
+ *
+ * \param hash  The hashtable to insert the new entry in
+ * \param key   The key, under which the value should be entered
+ * \param kley  Size of the key
+ * \param val   Void* pointer to be saved under 'key' in the hash table
+ * \param vlen  Size of the value
+ */
+
+void SHashStore(SHASH hash, const char *key, int key_len, void *val, int value_len)
 {
-    char *ckey, *cval;
+    char *ckey;
+    char *cval;
 
-    if(h == 0 || key == 0 || klen == 0)
+    if((hash == 0) || (key == 0) || (key_len == 0)) {
         return;
+    }
 
-    ckey = (char*)malloc(klen+1);
-    memcpy(ckey,key,klen);
-    ckey[klen] = '\0';
-    cval = (void*)malloc(vlen+1);
-    memcpy(cval,val,vlen);
-    cval[vlen] = '\0'; /* convenience, in case it was a string too */
-    _xht_set(h, ckey, cval, 1);
-}
+    ckey = (char*)malloc(key_len+1);
+    memcpy(ckey, key, key_len);
+    ckey[key_len] = '\0';
+
+    /* Assume the value is a string too and store it with a trailing
+       zero for safety reasons
+     */
+    cval = (void*)malloc(value_len+1);
+    memcpy(cval, val, value_len);
+    cval[value_len] = '\0';
 
+    SHashDoSet(hash, ckey, cval, 1);
+}
 
-void *xht_get(xht h, const char *key)
+/*!
+ * \brief Retrive a value associated to key from the hash table
+ *
+ * Return the value or NULL of the key was not found.
+ *
+ * \param hash  The hast table to get the value from
+ * \param key   Key to get the value for
+ * \return      pointer to the value, NULL if no key was not found
+ */
+void *SHashGet(SHASH hash, const char *key)
 {
-    xhn n;
+    SHNODE node;
 
-    if(h == 0 || key == 0 || (n = _xht_node_find(&h->zen[_xhter(key) % h->prime], key)) == 0)
+    if ((hash == 0) || (key == 0)) {
         return 0;
+    }
 
-    return n->val;
+    node = SHashFindNode(&hash->zen[StrToHash(key) % hash->prime], key);
+    if (node == NULL) {
+        return 0;
+    }
+
+    return node->val;
 }
 
+/*!
+ * \brief Free the hash table and all entries
+ *
+ * \param hash  The hast table to free
+ */
 
-void xht_free(xht h)
+void SHashFree(SHASH hash)
 {
-    xhn n, f;
+    SHNODE node;
+    SHNODE temp;
     int i;
 
-    if(h == 0) return;
+    if(hash == 0) {
+        return;
+    }
 
-    for(i = 0; i < h->prime; i++)
-        for(n = (&h->zen[i])->next; n != 0;)
-        {
-            f = n->next;
-            if(n->flag)
-            {
-                free(n->key);
-                free(n->val);
+    for (i = 0; i < hash->prime; i++) {
+        for (node = (&hash->zen[i])->next; node != 0; ) {
+            temp = node->next;
+            if (node->flag) {
+                free(node->key);
+                free(node->val);
             }
-            free(n);
-            n = f;
+            free(node);
+            node = temp;
         }
+    }
 
-    free(h->zen);
-    free(h);
+    free(hash->zen);
+    free(hash);
 }
 
-void xht_walk(xht h, xht_walker w, void *arg)
+/*!
+ * \brief Interate over the hash table and call a callback for each key
+ *        that has a value set.
+ *
+ *
+ * \param hash      The hash table to iterate though
+ * \param cb        SHASH_CB callback function
+ * \param arg       An optional user pointer to pass to the callback function
+ */
+void SHashForEach(SHASH hash, SHASH_CB cb, void *arg)
 {
     int i;
-    xhn n;
+    SHNODE node;
 
-    if(h == 0 || w == 0)
+    if ((hash == 0) || (cb == 0)) {
         return;
+    }
 
-    for(i = 0; i < h->prime; i++)
-        for(n = &h->zen[i]; n != 0; n = n->next)
-            if(n->key != 0 && n->val != 0)
-                (*w)(h, n->key, n->val, arg);
+    for(i = 0; i < hash->prime; i++) {
+        for(node = &hash->zen[i]; node != 0; node = node->next) {
+            if(node->key != 0 && node->val != 0) {
+                (*cb)(hash, node->key, node->val, arg);
+            }
+        }
+    }
 }
 
+/*@}*/
diff --git a/shash.h b/shash.h
index 1af42ea514c22e0f48c2b346b32540d039405538..829a8b0411c3832564d2ec1f7457ee94aac55758 100644 (file)
--- a/shash.h
+++ b/shash.h
-#ifndef xht_h
-#define xht_h
+#ifndef _SHASH_H_
+#define _SHASH_H_
 
-// simple string->void* hashtable, very static and bare minimal, but efficient
+/*
+ * Copyright (c) 1998-1999 Jeremie Miller <jer@jabber.org>
+ * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ *
+ */
 
-typedef struct xht_struct *xht;
+/* This code is based on
+ * Based on BSD licensed mdnsd implementation by Jer <jer@jabber.org>
+ * http://dotlocal.org/mdnsd/
+ *
+ * Unfortunately this site is now longer alive. You can still find it at:
+ * http://web.archive.org/web/20080705131510/http://dotlocal.org/mdnsd/
+ *
+ * mdnsd - embeddable Multicast DNS Daemon
+ * =======================================
+ *
+ * "mdnsd" is a very lightweight, simple, portable, and easy to integrate
+ * open source implementation of Multicast DNS (part of Zeroconf, also called
+ * Rendezvous by Apple) for developers. It supports both acting as a Query and
+ * a Responder, allowing any software to participate fully on the .localnetwork
+ * just by including a few files and calling a few functions.  All of the
+ * complexity of handling the Multicast DNS retransmit timing, duplicate
+ * suppression, probing, conflict detection, and other facets of the DNS
+ * protocol is hidden behind a very simple and very easy to use interface,
+ * described in the header file. The single small c source file has almost no
+ * dependencies, and is portable to almost any embedded platform.
+ * Multiple example applications and usages are included in the download,
+ * including a simple persistent query browser and a tool to advertise .local
+ * web sites.
+ *
+ * The code is licensed under both the GPL and BSD licenses, for use in any
+ * free software or commercial application. If there is a licensing need not
+ * covered by either of those, alternative licensing is available upon request.
+ *
+ */
 
-// must pass a prime#
-xht xht_new(int prime);
+/*!
+ * \file include/gorp/shash.h
+ * \brief Simple hastable implementation of a string ==> void* hashtable
+ *        Minimal and efficient
+ *
+ * \verbatim
+ *
+ * $Id$
+ *
+ * \endverbatim
+ */
 
-// caller responsible for key storage, no copies made (don't free it b4 xht_free()!)
-// set val to NULL to clear an entry, memory is reused but never free'd (# of keys only grows to peak usage)
-void xht_set(xht h, char *key, void *val);
 
-// ooh! unlike set where key/val is in caller's mem, here they are copied into xht and free'd when val is 0 or xht_free()
-void xht_store(xht h, const char *key, int klen, void *val, int vlen);
+/*!
+ * \addtogroup xgSHash
+ */
+/*@{*/
 
-// returns value of val if found, or NULL
-void *xht_get(xht h, const char *key);
 
-// free the hashtable and all entries
-void xht_free(xht h);
+/* Simple hastable implementation of a string ==> void* hashtable */
+typedef struct string_hash_table_struct *SHASH;
 
-// pass a function that is called for every key that has a value set
-typedef void (*xht_walker)(xht h, const char *key, void *val, void *arg);
-void xht_walk(xht h, xht_walker w, void *arg);
+/* Hash creation and cleanup functions */
+SHASH  SHashInit(int prime);
+void SHashFree(SHASH hash);
+
+/* Hash manipulation and query functions */
+void SHashSet(SHASH hash, char *key, void *val);
+void SHashStore(SHASH hash, const char *key, int key_len, void *val, int value_len);
+void *SHashGet(SHASH hash, const char *key);
+
+/* Routine to iterate over the hash and call a callback for each entry */
+typedef void (*SHASH_CB)(SHASH hash, const char *key, void *val, void *arg);
+void SHashForEach(SHASH hash, SHASH_CB cb, void *arg);
+
+/*@}*/
 
 #endif