minor bugs.
-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
+/*
+ * 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;
}
+
+/*@}*/
-#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
+/*
+ * 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(¤t->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, ¤t->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);
}
+/*@}*/
-#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
-#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;
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);
}
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,
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);
}
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;
}
#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;
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;
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);
{
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;
}
#include <linux/rtnetlink.h>
#include "netwatch.h"
+#include "mdnsd.h"
#define NL_BUFSIZE 8192
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;
}
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);
}
}
}
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
-#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;
}
+
+/*@}*/
-#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
+/*
+ * 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);
+ }
+ }
+ }
}
+/*@}*/
-#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