/* * mDNS registration handler. This file is part of Shairport. * Copyright (c) Paul Lietar 2013 * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "mdns.h" #include "common.h" #include #include #include #include #include #include #include #include "tinysvcmdns.h" static struct mdnsd *svr = NULL; static int mdns_tinysvcmdns_register(char *apname, int port) { struct ifaddrs *ifalist; struct ifaddrs *ifa; svr = mdnsd_start(); if (svr == NULL) { warn("tinysvcmdns: mdnsd_start() failed"); return -1; } // Thanks to Paul Lietar for this // room for name + .local + NULL char hostname[100 + 6]; gethostname(hostname, 99); // according to POSIX, this may be truncated without a final NULL ! hostname[99] = 0; // will not work if the hostname doesn't end in .local char *hostend = hostname + strlen(hostname); if ((strlen(hostname) < strlen(".local")) || (strcmp(hostend - 6, ".local") != 0)) { strcat(hostname, ".local"); } if (getifaddrs(&ifalist) < 0) { warn("tinysvcmdns: getifaddrs() failed"); return -1; } ifa = ifalist; // Look for an ipv4/ipv6 non-loopback interface to use as the main one. for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next) { // only check for the named interface, if specified if ((config.interface == NULL) || (strcmp(config.interface, ifa->ifa_name) == 0)) { if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { uint32_t main_ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; mdnsd_set_hostname(svr, hostname, main_ip); // TTL should be 120 seconds break; } else if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; mdnsd_set_hostname_v6(svr, hostname, addr); // TTL should be 120 seconds break; } } } if (ifa == NULL) { warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found"); return -1; } // Skip the first one, it was already added by set_hostname for (ifa = ifa->ifa_next; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_flags & IFF_LOOPBACK) // Skip loop-back interfaces continue; // only check for the named interface, if specified if ((config.interface == NULL) || (strcmp(config.interface, ifa->ifa_name) == 0)) { switch (ifa->ifa_addr->sa_family) { case AF_INET: { // ipv4 uint32_t ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; struct rr_entry *a_e = rr_create_a(create_nlabel(hostname), ip); // TTL should be 120 seconds mdnsd_add_rr(svr, a_e); } break; case AF_INET6: { // ipv6 struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; struct rr_entry *aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // TTL should be 120 seconds mdnsd_add_rr(svr, aaaa_e); } break; } } } freeifaddrs(ifa); char *txtwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL}; #ifdef CONFIG_METADATA char *txtwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL}; #endif char **txt; #ifdef CONFIG_METADATA if (config.metadata_enabled) txt = txtwithmetadata; else #endif txt = txtwithoutmetadata; if (config.regtype == NULL) die("tinysvcmdns: regtype is null"); char *extendedregtype = malloc(strlen(config.regtype) + strlen(".local") + 1); if (extendedregtype == NULL) die("tinysvcmdns: could not allocated memory to request a Zeroconf service"); strcpy(extendedregtype, config.regtype); strcat(extendedregtype, ".local"); struct mdns_service *svc = mdnsd_register_svc(svr, apname, extendedregtype, port, NULL, (const char **)txt); // TTL should be 75 minutes, i.e. 4500 seconds mdns_service_destroy(svc); free(extendedregtype); return 0; } static void mdns_tinysvcmdns_unregister(void) { if (svr) { mdnsd_stop(svr); svr = NULL; } } mdns_backend mdns_tinysvcmdns = {.name = "tinysvcmdns", .mdns_register = mdns_tinysvcmdns_register, .mdns_unregister = mdns_tinysvcmdns_unregister, .mdns_dacp_monitor_start = NULL, .mdns_dacp_monitor_set_id = NULL, .mdns_dacp_monitor_stop = NULL};