Compare commits

...

28 Commits

Author SHA1 Message Date
df
56cfa698a4 Release 2.0-2. 2020-10-16 15:33:35 +01:00
df
3b8db036a1 Fix making a release. 2020-10-16 15:32:18 +01:00
df
59368bb111 Fix error installing service-control file. 2020-10-16 15:11:49 +01:00
df
c6b9730299 Fix service-control always shows not running. 2020-10-16 15:10:50 +01:00
df
e436becbbd Stop and (re-)start the service correctly around installation. 2020-10-16 15:06:03 +01:00
df
302d113377 Fix losing static address mappings appended to hosts file 2020-10-16 15:03:13 +01:00
df
a9dcc648ae Add service configuration in case the service-control package is installed. 2020-08-02 20:36:10 +01:00
df
75af91d0cc Don't exit, but retry, if mdns_start() fails. 2020-08-02 00:23:28 +01:00
df
ec6463e6c4 Add release-tagging function. 2020-07-28 16:42:57 +01:00
df
ba051c353a Create new release 2020-07-28 00:52:44 +01:00
df
46a74b3c06 Added service definition file for service-control. 2020-07-28 00:41:25 +01:00
df
7223f118a7 Added service file and package generation 2020-07-28 00:28:33 +01:00
df
cfdf09a1dc Merge branch 'HummyNew' of https://git.hpkg.tv/df/zeroconf into HummyNew 2020-07-27 15:39:37 +01:00
df
d7c62ad55c Add signal handling: SIGUSR1 - dump cache; SIGHUP - restart server 2020-07-27 15:18:17 +01:00
df
964c2dd28d Document current version
Add description of HummyNew version and de-emphasise DT's health warning, now mostly obsolete.
2020-07-27 01:03:59 +00:00
df
0aa116dbce Support responding to unicast queries: initially just legacy queries and multicast responses. 2020-07-26 21:02:43 +01:00
df
2ce1bb8ae4 Move DNS TTL definitions to header; add TTL for answers to legacy unicast queries.
fe
2020-07-26 20:54:31 +01:00
df
17ef47d6f2 Add response to service meta-query (RFC6763 Section 10), following https://github.com/espressif/esp-idf/pull/340/files 2020-07-26 14:57:23 +01:00
df
3c1f038fbc Service registered must have FQDN as hostname 2020-07-26 14:56:29 +01:00
df
de3042f808 PTR shouldn't normally have the shorter host TTL. 2020-07-25 23:59:33 +01:00
df
3a24c51c56 Correct IP address type in API; clean up scopes, loops. 2020-07-25 19:44:53 +01:00
df
90d70d13b1 Add working binary 2020-07-24 10:34:31 +01:00
df
aa472217c5 Recognise local host with .local suffix. 2020-07-24 10:17:56 +01:00
df
e42e88d61f Improve Makefile for CC/CPP options 2020-07-24 10:16:53 +01:00
df
c9a1e4cf0d Reinstate accidentally disabled reply function and remove %m formats. 2020-07-24 10:13:16 +01:00
df
8f15f5873b Merge IP-TTL-255-patch, fixing #13 Set multicast IP TTL to 255 in case a receiver demands an unrouted packet; minor non-functional tweaks. 2020-07-23 16:21:18 +01:00
df
ed03a6098d Initial working responder version reabsorbing TizenRT changes.
Consequently:
- fixes #9 Arbitrary memory read while parsing malicious mDNS queries (mdns.c);
- fixes #10 Denial of Service vulnerability (infinite loop) while parsing malicious mDNS queries (mdns.c);
- fixes #11 Heap-based buffer overread (off-by-one) (mdns.c);
- fixes #12 Heap-based buffer overread while parsing mDNS RR section (off-by-one) (mdns.c).
2020-07-23 12:02:06 +01:00
df
8b2f04c2ea Tweak 2020-07-19 19:38:23 +00:00
13 changed files with 3272 additions and 494 deletions

View File

@ -2,9 +2,16 @@
# Makefile for tinysvcmdns
#
MV ?= mv
CPPFLAGS += -DPTHREAD_CREATE_DETACHED_SUPPORTED
#CPPFLAGS += -DNDEBUG
CPPFLAGS += $(CPPFLAGS_EXTRA)
CFLAGS += -Wall -pedantic -std=gnu99
#CFLAGS += -g
CFLAGS += -O2 -DNDEBUG
CFLAGS += -g
CFLAGS += -O2
CFLAGS += $(CFLAGS_EXTRA)
LDLIBS = -lpthread
ifneq ($(CROSS_COMPILE),)
@ -13,26 +20,49 @@ ifneq ($(CROSS_COMPILE),)
AR := $(CROSS_COMPILE)$(AR)
endif
BIN=testmdnsd
BIN=testmdnsd mdnsd
LIBTINYSVCMDNS_OBJS = mdns.o mdnsd.o
.PHONY: all clean
.PHONY: all clean opkg release opkg-service opkg-init
all: $(BIN) libtinysvcmdns.a
clean:
-$(RM) *.o
-$(RM) *.bin
-$(RM) mdns
-$(RM) $(LIBTINYSVCMDNS_OBJS)
-$(RM) $(BIN)
-$(RM) libtinysvcmdns.a
mdns: mdns.o
mdns.o: mdns.h
mdnsd: mdns.o mdnsd.o
mdnsd.o: mdns.h mdnsd.h
testmdnsd.o: mdnsd.h
testmdnsd: testmdnsd.o libtinysvcmdns.a
libtinysvcmdns.a: $(patsubst %, libtinysvcmdns.a(%), $(LIBTINYSVCMDNS_OBJS))
mdnsd: testmdnsd
strip -o $@ $<
opkg-init: $(wildcard etc/init.d/*)
install -d opkg/etc/init.d
install -p $? opkg/etc/init.d/
opkg-service: $(wildcard etc/modservice.d/*)
install -d opkg/etc/modservice.d
install -m 644 -p $? opkg/etc/modservice.d/
opkg: mdnsd $(wildcard opkg/CONTROL/*) opkg-init opkg-service
install -d opkg/sbin
install -p $< opkg/sbin/
$(foreach opk,$(wildcard *.opk),$(MV) $(opk) $(opk).old; )
opkg-pack opkg
release: opkg
tagname=$$(echo zeroconf*.opk | sed -n 'p;q') && \
tagname="$${tagname%_*.*}" && \
test -n "$${tagname}" && \
git tag -f -a -m "Release $${tagname}" "$${tagname}"

View File

@ -1,5 +1,5 @@
NOTICE
=======
NOTICE FROM THE ORIGINAL AUTHOR
-------------------------------
This project is un-maintained, and has been since 2013.
@ -27,26 +27,25 @@ Decoding of MDNS packets is only done to retrieve the questions and answer RRs.
The purpose for decoding answer RRs is to make sure the service PTR is not
sent out if it is already included in the answer RRs.
It also only utilizes multicast packets, so no "QU" queries are accepted.
There is no name collision detection, so this means no queries are generated
before publishing the services. However compliant responders will avoid using
our names, since the implementation will respond to queries that match our
name.
TODO
-----
* better, more stable & complete API
* name collision detection
Unicast queries are accepted but only legacy queries (non-mDNS source port) get a unicast response (think `dig`).
FILES
------
* mdns.c - provides data structures, parsing & encoding of MDNS packets
* mdns.h - interface to the above
* mdnsd.c - implements the server socket, communication and thread
* mdnsd.h - interface to the above
* testmdnsd.c - an example that creates an instance until terminated
HISTORY
-------
Darell Tan is the original author. His version, to which the Notice above refers, can be found at https://bitbucket.org/geekman/tinysvcmdns, and in a branch of this repository. Several forked versions can be found across the Internet. The version at https://github.com/philippe44/TinySVCmDNS is adapted for more platforms and addresses some of the issues in the original version. Meanwhile, the code was adopted as part of Samsung's TizenRT [IOT](https://www.briansolis.com/tag/internet-of-shit/) framework, but without maintaining the change history between that version and the original. The TizenRT version implemented most of the RFC 6762/6763 features missing from the original version, and also fixed the known vulnerabilities mentioned in the Notice.
In 2012 the original version was [adapted as a package](https://hummy.tv/forum/threads/announcing-dns-name-on-upnp-for-webif.1640/) for the [Custom Firmware](https://hummy.tv/forum/forums/hd-hdr-fox-t2-customised-firmware.28/) on the Humax HD/R-Fox T2 TV set-top boxes. This fork (let's call it HummyPkg) differed mostly in the `testmdnsd.c` program which was specialised for the package. However it did not track the changes in the original version. The branch named HummyPkg does so.
The HummyPkg version enabled other LAN devices to find the tinysvcmdns host, but only if they were able to resolve names by querying the mDNS service; this excluded other Humax CF boxes as they rely on a `hosts` file for name resolution. It also had to be restarted if the LAN IP address of the tinysvcmdns host changed, as would happen at startup using a WiFi dongle.
By reabsorbing the changes from the TizenRT version into the HummyPkg version and making some further enhancements, it has been possible to create a version named HummyNew that addresses those issues. This comprises a highly modified `testmdnsd.c` that can parse and write `hosts` files together with an almost fully featured mDNS implementation.
LICENSE
--------

32
etc/init.d/S80mdnsd Executable file
View File

@ -0,0 +1,32 @@
#!/bin/sh
case "$1" in
start)
# Wait for network to come up...
(
while [ ! -f /tmp/if-up ]; do
echo "mdnsd: waiting for network..."
sleep 1
done
interface="`/sbin/route | /bin/sed -n '$ {
s/.* //
p
}'`"
echo " MDNS interface: [$interface]"
[ -z "$interface" ] && interface=eth0
/bin/ip route add 224.0.0.0/4 dev $interface
/mod/sbin/mdnsd &
) &
;;
stop)
killall -q mdnsd
;;
*)
exit 1
;;
esac
exit 0

3
etc/modservice.d/mdnsd Normal file
View File

@ -0,0 +1,3 @@
key=/mod/sbin/mdnsd
rc=80mdnsd
proc=$key

598
mdns.c

File diff suppressed because it is too large Load Diff

49
mdns.h
View File

@ -32,6 +32,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <winsock.h>
@ -39,18 +40,53 @@
#include <arpa/inet.h>
#endif
/* see RFC 6762 Section 10 */
#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
#define DEFAULT_TTL 4500
/* see RFC 6762 Section 5.4 */
#define DEFAULT_TTL_LEGACY 10
#ifndef MDNS_DEBUG_PRINTF
#define MDNS_DEBUG_PRINTF 0
#endif
#if MDNS_DEBUG_PRINTF==1
#define MDNS_RR_DEBUG 0
#define MDNS_MEMORY_DEBUG 0
#endif
#if MDNS_DEBUG_PRINTF==1 && MDNS_MEMORY_DEBUG==1
void *mdns_malloc(const char *func_name, int line, unsigned int size);
void mdns_free(const char *func_name, int line, void *ptr);
char *mdns_strdup(const char *func_name, int line, const char *str);
void mdns_show_meminfo(void);
#define MDNS_MALLOC(size) mdns_malloc(__FUNCTION__, __LINE__, size)
#define MDNS_FREE(ptr) mdns_free(__FUNCTION__, __LINE__, ptr)
#define MDNS_STRDUP(str) mdns_strdup(__FUNCTION__, __LINE__, str)
#else
#define MDNS_MALLOC(size) malloc(size)
#define MDNS_FREE(ptr) free(ptr)
#define MDNS_STRDUP(size) strdup(size)
#endif
#define MALLOC_ZERO_STRUCT(x, type) \
x = malloc(sizeof(struct type)); \
memset(x, 0, sizeof(struct type));
x = MDNS_MALLOC(sizeof(struct type)); \
memset(x, 0, sizeof(struct type))
#define DECL_MALLOC_ZERO_STRUCT(x, type) \
struct type * MALLOC_ZERO_STRUCT(x, type)
#ifndef NDEBUG
#define DECL_ZERO_STRUCT(_x, _type) \
struct _type _x; \
memset(&(_x), 0, sizeof(struct _type))
#if MDNS_DEBUG_PRINTF==1
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...) ((void) 0)
#endif
#define MDNS_RR_DEBUG_PRINTF(...) printf(__VA_ARGS__)
struct rr_data_srv {
@ -118,6 +154,7 @@ struct rr_entry {
struct rr_data_a A;
struct rr_data_aaaa AAAA;
} data;
time_t update_time;
};
struct rr_list {
@ -162,8 +199,9 @@ struct mdns_pkt {
struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len);
void mdns_init_query(struct mdns_pkt *pkt, uint16_t id);
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id);
size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len);
size_t mdns_encode_pkt(struct mdns_pkt *encoded_pkt, uint8_t *pkt_buf, size_t pkt_len);
void mdns_pkt_destroy(struct mdns_pkt *p);
void rr_group_destroy(struct rr_group *group);
@ -171,17 +209,20 @@ struct rr_group *rr_group_find(struct rr_group *g, uint8_t *name);
struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type);
struct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry);
void rr_group_add(struct rr_group **group, struct rr_entry *rr);
void rr_group_del(struct rr_group **group, struct rr_entry *rr);
int rr_list_count(struct rr_list *rr);
int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr);
struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr);
void rr_list_destroy(struct rr_list *rr, char destroy_items);
struct rr_entry *qn_create(uint8_t *name, enum rr_type type, int unicast_query);
struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr);
struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target);
struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr);
struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr);
struct rr_entry *rr_create(uint8_t *name, enum rr_type type);
struct rr_entry *rr_duplicate(struct rr_entry *rr_src);
void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type);
void rr_add_txt(struct rr_entry *rr_txt, const char *txt);

2361
mdnsd.c

File diff suppressed because it is too large Load Diff

113
mdnsd.h
View File

@ -25,6 +25,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* APIs for Multicast DNS */
#ifndef __MDNSD_H__
#define __MDNSD_H__
@ -34,12 +35,76 @@
struct mdnsd;
struct mdns_service;
// starts a MDNS responder instance
// returns NULL if unsuccessful
struct mdnsd *mdnsd_start();
#define MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT 10
#define MAX_SERVICE_DISCOVERY_TIME_MS (60 * 1000)
// stops the given MDNS responder instance
void mdnsd_stop(struct mdnsd *s);
#define MDNS_HOSTNAME_RESOLVER_TIMEOUT_MSEC (3 * 1000)
#define MDNS_HOSTNAME_RESOLVER_MAX_TRY_COUNT 5
#define MDNS_HOSTNAME_RESOLVER_WAIT_TIME_MSEC 250
#define MDNS_SERVICE_DISCOVERY_MAX_TRY_COUNT 5
#define MDNS_SERVICE_DISCOVERY_WAIT_TIME_MSEC 250
/* Structure of MDNS service information */
struct mdns_service_info {
char *type;
char *instance_name;
char *hostname;
unsigned int ipaddr; /* ipv4 */
unsigned int port;
};
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
#ifndef MDNS_NO_RESPONDER_SUPPORT
/**
* starts the MDNS daemon.
*
* [in] desired_hostname the desired host name as string type; if same name is in the network, an alternative name will be set as hostname.
* [in] netif_name network interface name as string type
* On success, 0 is returned. On failure, a negative value is returned.
*
*/
int mdnsd_start(const char *desired_hostname, const char *netif_name);
/**
* mdnsd_stop() stops the MDNS daemon.
*
* On success, 0 is returned. On failure, a negative value is returned.
*
*/
int mdnsd_stop(void);
/**
* mdnsd_get_hostname() gets the current host name as MDNS type.
*
* [out] hostname_result 32-bytes string buffer for the host name result
* On success, 0 is returned. On failure, a negative value is returned.
*
*/
int mdnsd_get_hostname(char *hostname_result);
/**
* mdnsd_register_service() register a service to expose through mDNS-SD.
*
* [in] instance instance name to expose
* [in] type type of service. e.g. _http._tcp.local
* [in] port port to which the service is reachable
* [in] hostname if NULL, use the hostname configured when starting the daemon,
* or use this parameter otherwise
* [in] txt text records to add to the service announcement. Can be NULL.
* On success, 0 is returned. On failure, a negative errno value is returned.
*
*/
int mdnsd_register_service(const char *instance, const char *type,
uint16_t port, const char *hostname, const char *txt[]);
// sets the hostname for the given MDNS responder instance
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip);
@ -48,11 +113,41 @@ void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip);
void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr);
// registers a service with the MDNS responder instance
struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
const char *type, uint16_t port, const char *hostname, const char *txt[]);
//struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
// const char *type, uint16_t port, const char *hostname, const char *txt[]);
// destroys the mdns_service struct returned by mdnsd_register_svc()
void mdns_service_destroy(struct mdns_service *srv);
//void mdns_service_destroy(struct mdns_service *srv);
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
/**
* mdnsd_resolve_hostname() gets ip address with the given hostname.
*
* [in] hostname host name as string type
* [out] ipaddr the pointer of ip address result
* On success, 0 is returned. On failure, a negative value is returned.
*
*/
int mdnsd_resolve_hostname(char *hostname, uint32_t *ipaddr);
/**
* mdnsd_discover_service() discovers services with the given service type string
*
* [in] service_type mdns service type string
* [in] discover_time_msec time in milliseconds for discovering service
* [out] service_list the array of service list
* [out] num_of_services number of services
* On success, 0 is returned. On failure, a negative value is returned.
*
*/
int mdnsd_discover_service(char *service_type, int discover_time_msec, struct mdns_service_info **service_list, int *num_of_services);
#endif/*!__MDNSD_H__*/
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /*!__MDNSD_H__ */

8
opkg/CONTROL/control Normal file
View File

@ -0,0 +1,8 @@
Package: zeroconf
Priority: optional
Section: net
Version: 2.0-2
Architecture: mipsel
Maintainer: af123@hummypkg.org.uk
Depends:
Description: MDNS server, enables name resolution for received .local names

8
opkg/CONTROL/postinst Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
for action in stop start; do
/mod/etc/init.d/S80mdnsd $action
done < /dev/null > /dev/null 2>&1
exit 0

6
opkg/CONTROL/prerm Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
/mod/etc/init.d/S80mdnsd stop
exit 0

BIN
opkg/sbin/mdnsd Executable file

Binary file not shown.

View File

@ -25,6 +25,14 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#ifdef _WIN32
#include <winsock2.h>
@ -36,62 +44,381 @@
#include <arpa/inet.h>
#endif
#include <stdio.h>
#include "mdns.h"
#include "mdnsd.h"
int main(int argc, char *argv[]) {
// create host entries
char *hostname = "some-random-host.local";
#define HOSTFILE "/tmp/hosts"
struct mdnsd *svr = mdnsd_start();
if (svr == NULL) {
printf("mdnsd_start() error\n");
#if 0
#include <sys/stat.h>
#include <fcntl.h>
void
background()
{
int fd;
switch ((int)fork())
{
case -1:
perror("fork");
exit(1);
case 0:
break;
default:
exit(0);
}
#ifdef TIOCNOTTY
if (ioctl(fileno(stdin), TIOCNOTTY) == -1)
perror("detach_console");
#endif
freopen("/dev/null", "r", stdin);
fd = open("/dev/null", O_WRONLY, 0666);
dup2(fd, fileno(stdout));
dup2(fd, fileno(stderr));
close(fd);
setsid();
}
#endif
static void freehostents( struct hostent * h) {
for (struct hostent *p = h; p->h_name; ++p) {
free(p->h_name);
free(p->h_addr_list);
}
free(h);
}
/*
* Add the mapping from hostname to in_addr_ip to the hosts block.
* The return value is the pointer to the next entry after the added entry.
* Case 1. The block is initially empty. Populate it with a loop like
* for (p = hosts,i=0;; p = add_host(p, hostname[i], addr[i],++i);
* Case 2. The block is in use. The added entry replaces the first one with
* h_length == 0.
*/
static struct hostent * add_host(struct hostent * hosts, const char * hostname, struct in_addr ip) {
struct hostent * p;
int newname = 1;
for (p=hosts;p->h_name;++p) {
if (p->h_length == 0 ||
0 == (newname = strcasecmp(p->h_name, hostname)))
break;
}
if (p->h_name) {
if (0 != newname) {
/* reusing an entry with a new name */
free(p->h_name);
p->h_name = strdup(hostname);
}
} else {
/* new entry */
p->h_name = strdup(hostname);
(p+1)->h_name = 0;
}
p->h_aliases = 0;
p->h_addrtype = AF_INET;
p->h_length = sizeof(in_addr_t);
p->h_addr_list = (char **) realloc(p->h_addr_list, sizeof(struct in_addr)+2*sizeof(struct in_addr*));
p->h_addr_list[0] = (char *)(p->h_addr_list + 2);
*(struct in_addr *)(p->h_addr_list[0]) = ip;
p->h_addr_list[1] = NULL;
return ++p;
}
struct hostent * realloc_hosts(struct hostent * p, size_t n) {
struct hostent * new = (struct hostent *) realloc(p,n*sizeof(struct hostent));
if (!new) {
fprintf(stderr, "Failed to allocate hostent: %s\n", strerror(errno));
}
return new;
}
struct hostent * read_hosts(unsigned int extra) {
FILE * hosts = fopen( HOSTFILE, "r");
if (!hosts) {
fprintf(stderr, "Failed to open hosts file: %s\n", strerror(errno));
return NULL;
}
size_t nhost = extra;
while (0 <= fscanf(hosts, "%*[^\n]") && !ferror(hosts)) {
if (0 <= fscanf(hosts,"%*c"))
++nhost;
}
if (ferror(hosts) || -1 == fseek(hosts, 0L, SEEK_SET)) {
fprintf(stderr, "Failed to read hosts file: %s\n", strerror(errno));
fclose(hosts);
return NULL;
}
struct hostent * hostents = (struct hostent *) realloc_hosts(NULL,++nhost);
if (!hostents) {
fclose(hosts);
return NULL;
} else {
memset(hostents,0,nhost*sizeof(struct hostent));
}
char ip[256];
char name[256];
char fmt[32];
/* a format for [the beginning of] a /etc/hosts line */
int n = 0;
if (snprintf( fmt, sizeof fmt - 1, "%%%zus %%%zus%%n%n", sizeof ip - 1, sizeof name - 1, &n) != n) {
fprintf(stderr, "Format length %d inadequate\n", n);
}
/* and for further hostnames */
char * nfmt = strchr(fmt,' ')+1;
char * line = NULL;
size_t linesz = 0;
size_t nleft = nhost;
hostents->h_name = 0;
for (struct hostent * p = hostents;;) {
int ret = getline( &line, &linesz, hosts);
int pos = 0;
if (ret == -1) {
if (!feof(hosts)) {
fprintf(stderr, "Failure reading hosts file: %s\n", strerror(errno));
freehostents(hostents);
hostents = NULL;
}
break;
}
/* skip initial space and any comment line */
ret = sscanf( line, " %n", &pos );
if (0 <= ret) {
int n = 0;
ret = sscanf( line+pos, "#%*[^\n]%n", &n);
if (n > 0) continue;
}
if (0 <= ret) {
int n = 0;
ret = sscanf( line+pos, fmt, ip, name, &n);
// printf( "Read: %.255s\t%.32s\t%d\n", name, ip, ret);
pos += n;
}
if (ret == 2) {
struct in_addr it;
it.s_addr = inet_addr(ip);
do {
char * dot = strrchr(name, '.');
if (dot && 0 == strcasecmp(dot, ".local")) {
struct hostent * pp = add_host(p, name, it);
/* flag as expendable */
p->h_length = 0;
p = pp;
} else {
/* try to push down non-local names */
struct hostent * pp = add_host(hostents, name, it);
/* not pushed down -> appended */
if (pp > p) p = pp;
}
/* running out of space? try for more */
if (--nleft < extra + 1) {
size_t n = 2*nhost;
struct hostent * new = (struct hostent *) realloc_hosts(hostents,n);
if (!new) {
free(line);
fclose(hosts);
free(hostents);
return NULL;
}
/* zero the new space and adjust counts and pointers */
memset(new+nhost,0,(n-nhost)*sizeof(struct hostent));
nleft += n - nhost;
nhost = n;
p += new - hostents;
hostents = new;
}
/* allow for ip name [name ...] */
int n = 0;
ret = sscanf( line+pos, " %n", &n);
pos += n;
/* skip comment */
if (0 <= ret) {
n = 0;
ret = sscanf( line+pos, "#%*[^\n]%n", &n);
if (n > 0) break;
}
if (0 <= ret) {
n = 0;
ret = sscanf( line+pos, nfmt, name, &n);
pos += n;
}
} while (ret == 1);
p->h_name = 0;
} else if (ret >= 0) {
fprintf(stderr, "Unexpected format in hosts file; line was\n%s\n", line);
}
}
free(line);
fclose(hosts);
return hostents;
}
static int write_hosts(const struct hostent * hostents) {
int ret = -1;
FILE * hosts = fopen( HOSTFILE, "w");
if (!hosts) {
fprintf(stderr, "Failed to open hosts file: %s\n", strerror(errno));
return ret;
}
for (const struct hostent * p = hostents;p->h_name;++p) {
/* printf( "writing %.255s\n", p->h_name); */
if (p->h_length > 0) {
for (struct in_addr ** pa = (struct in_addr **)(p->h_addr_list); *pa; ++pa) {
ret = fprintf( hosts, "%s\t%s\n", inet_ntoa(**pa), p->h_name);
}
}
}
fclose(hosts);
return ret;
}
#if 0
static void dump_host(const struct hostent * hostent) {
printf( "Host: %.255s\t%.32s\t%d\n", hostent->h_name,
inet_ntoa(*(struct in_addr *)(hostent->h_addr_list[0])),
hostent->h_length);
}
static void dump_hosts(const struct hostent * hostents) {
for (const struct hostent * p = hostents;p->h_name;++p) {
dump_host(p);
}
}
#endif
static void handler_stop(int sig) {
(void)sig;
mdnsd_stop();
#ifdef WIN32
winsock_close();
#endif
exit(0);
}
volatile static int running;
static void handler_restart(int sig) {
/* a dummy handler so that sleep() will end early */
(void)sig;
if (running != 0)
running = 2;
}
int
main(int argc, char **argv)
{
char hostname[0x100], ifname[0x120], fullname[0x108];
struct in_addr saddr;
if (gethostname(hostname, sizeof(hostname) - 1) == -1)
{
fprintf(stderr, "Couldn't retrieve humax hostname.\n");
return 1;
}
printf("mdnsd_start OK. press ENTER to add hostname & service\n");
getchar();
mdnsd_set_hostname(svr, hostname, inet_addr("192.168.0.29"));
struct rr_entry *a2_e = NULL;
a2_e = rr_create_a(create_nlabel(hostname), inet_addr("192.168.0.31"));
mdnsd_add_rr(svr, a2_e);
struct rr_entry *aaaa_e = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo* results;
getaddrinfo(
"fe80::e2f8:47ff:fe20:28e0",
NULL,
&hints,
&results);
struct sockaddr_in6* addr = (struct sockaddr_in6*)results->ai_addr;
struct in6_addr v6addr = addr->sin6_addr;
freeaddrinfo(results);
aaaa_e = rr_create_aaaa(create_nlabel(hostname), &v6addr);
mdnsd_add_rr(svr, aaaa_e);
int n = 0;
if (snprintf(fullname, sizeof fullname - 1, "%s.local%n", hostname, &n) != n ||
(n = 0, sprintf(ifname, "Humax Fox T2 (%s) Web Interface%n", hostname, &n) != n)) {
fprintf( stderr, "Format length %d inadeqate\n", n);
}
const char *txt[] = {
"path=/mywebsite",
"path=/",
NULL
};
struct mdns_service *svc = mdnsd_register_svc(svr, "My Website",
"_http._tcp.local", 8080, NULL, txt);
mdns_service_destroy(svc);
printf("added service and hostname. press ENTER to exit\n");
getchar();
running = 0;
mdnsd_stop(svr);
signal(SIGINT, handler_stop);
signal(SIGTERM, handler_stop);
#if defined(SIGPIPE)
signal(SIGPIPE, SIG_IGN);
#endif
#if defined(SIGQUIT)
signal(SIGQUIT, handler_stop);
#endif
#if defined(SIGHUP)
signal(SIGHUP, handler_restart);
#endif
for (saddr.s_addr = 0;;sleep(61)) {
struct hostent *hp = gethostbyname(hostname);
char * ip_addr;
if (!hp || hp->h_length != sizeof(saddr.s_addr))
{
fprintf(stderr, "Couldn't retrieve humax IP address.\n");
return 1;
}
if (running != 1 || saddr.s_addr != *(in_addr_t *)(hp->h_addr_list[0])) {
if (running && (0 > mdnsd_stop())) {
fprintf(stderr, "mdnsd_stop() error\n");
return 1;
}
saddr.s_addr = *(in_addr_t *)(hp->h_addr_list[0]);
ip_addr = inet_ntoa(saddr);
printf("Hostname: %s (%s)\n", hostname, ip_addr);
if (mdnsd_start(fullname, ip_addr) < 0) {
fprintf(stderr, "mdnsd_start() error\n");
/* may just have no interface */
continue;
}
running = 1;
/* service must have FQDN */
if (0 <= mdnsd_register_service(
ifname, "_http._tcp.local", 80,
fullname, txt))
printf("Registered name.\n");
}
int numserv = 0;
struct mdns_service_info * svcinfo;
if (0 <= mdnsd_discover_service("_http._tcp.local", 5000, &svcinfo, &numserv)) {
/* allow an extra entry for own host */
struct hostent * hosts = read_hosts(numserv+1);
if (!hosts) {
fprintf(stderr,"null hosts\n");
return 1;
}
//dump_hosts(hosts);
/* recognise own .local hostname */
struct hostent * p = add_host(hosts, fullname, saddr);
for (int i = 0; i < numserv; ++i) {
struct in_addr it;
it.s_addr = svcinfo[i].ipaddr;
printf("Host %d: %s (%s)\n", i, svcinfo[i].hostname, inet_ntoa(it));
p = add_host(p, svcinfo[i].hostname, it);
}
/* p->h_name = 0; */
write_hosts(hosts);
freehostents(hosts);
}
}
if (running && (0 > mdnsd_stop())) {
fprintf(stderr,"mdnsd_stop() error\n");
}
return 0;
}