425 lines
11 KiB
C
425 lines
11 KiB
C
/*
|
|
* tinysvcmdns - a tiny MDNS implementation for publishing services
|
|
* Copyright (C) 2011 Darell Tan
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*/
|
|
#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>
|
|
#include <in6addr.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "mdns.h"
|
|
#include "mdnsd.h"
|
|
|
|
#define HOSTFILE "/tmp/hosts"
|
|
|
|
#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;
|
|
}
|
|
|
|
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=/",
|
|
NULL
|
|
};
|
|
|
|
running = 0;
|
|
|
|
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;
|
|
}
|