/* * 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 #include #include #include #include #include #include #ifdef _WIN32 #include #include #include #else #include #include #include #endif #include "mdns.h" #include "mdnsd.h" #define HOSTFILE "/tmp/hosts" #if 0 #include #include 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; }