1 /* 2 * mDNS registration handler. This file is part of Shairport. 3 * Copyright (c) Paul Lietar 2013 4 * Amendments and updates copyright (c) Mike Brady 2014 -- 2019 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be 16 * included in all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 * OTHER DEALINGS IN THE SOFTWARE. 26 */ 27 28 #include "common.h" 29 #include "mdns.h" 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 int mdns_pid = 0; 37 38 /* 39 * Do a fork followed by a execvp, handling execvp errors correctly. 40 * Return the pid of the new process upon success, or -1 if something failed. 41 * Check errno for error details. 42 */ 43 static int fork_execvp(const char *file, char *const argv[]) { 44 int execpipe[2]; 45 int response = -1; 46 if (pipe(execpipe) >= 0) { 47 48 if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) { 49 close(execpipe[0]); 50 close(execpipe[1]); 51 } else { 52 53 int pid = fork(); 54 if (pid < 0) { 55 close(execpipe[0]); 56 close(execpipe[1]); 57 } else if (pid == 0) { // Child 58 close(execpipe[0]); // Close the read end 59 execvp(file, argv); 60 // If we reach this point then execve has failed. 61 // Write erno's value into the pipe and exit. 62 if (write(execpipe[1], &errno, sizeof(errno)) != sizeof(errno)) 63 debug(1, 64 "Execve has failed and there was a further error writing an error message, duh."); 65 die("mdns_external: execve has failed."); 66 } else { // Parent 67 close(execpipe[1]); // Close the write end 68 69 int childErrno; 70 // Block until child closes the pipe or sends errno. 71 if (read(execpipe[0], &childErrno, sizeof(childErrno)) == 72 sizeof(childErrno)) { // We received errno 73 errno = childErrno; resolve_callback(AvahiServiceResolver * r,AVAHI_GCC_UNUSED AvahiIfIndex interface,AVAHI_GCC_UNUSED AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * address,uint16_t port,AvahiStringList * txt,AvahiLookupResultFlags flags,void * userdata)74 } else { 75 response = pid; 76 } 77 } 78 } 79 } 80 return response; 81 } 82 83 static int mdns_external_avahi_register(char *apname, __attribute__((unused)) int port) { 84 char mdns_port[6]; 85 snprintf(mdns_port, sizeof(mdns_port), "%d", config.port); 86 87 char *argvwithoutmetadata[] = { 88 NULL, apname, config.regtype, mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL}; 89 #ifdef CONFIG_METADATA 90 char *argvwithmetadata[] = {NULL, apname, config.regtype, mdns_port, MDNS_RECORD_WITH_METADATA, 91 NULL}; 92 #endif 93 char **argv; 94 95 #ifdef CONFIG_METADATA 96 if (config.metadata_enabled) 97 argv = argvwithmetadata; 98 else 99 #endif 100 argv = argvwithoutmetadata; 101 102 argv[0] = "avahi-publish-service"; 103 int pid = fork_execvp(argv[0], argv); 104 if (pid >= 0) { 105 mdns_pid = pid; 106 return 0; 107 } else 108 warn("Calling %s failed !", argv[0]); 109 110 argv[0] = "mDNSPublish"; 111 pid = fork_execvp(argv[0], argv); 112 if (pid >= 0) { 113 mdns_pid = pid; 114 return 0; 115 } else 116 warn("Calling %s failed !", argv[0]); 117 118 // If we reach here, both execvp calls failed. 119 return -1; 120 } 121 122 static int mdns_external_dns_sd_register(char *apname, __attribute__((unused)) int port) { browse_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)123 char mdns_port[6]; 124 snprintf(mdns_port, sizeof(mdns_port), "%d", config.port); 125 126 char *argvwithoutmetadata[] = { 127 NULL, apname, config.regtype, mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL}; 128 129 #ifdef CONFIG_METADATA 130 char *argvwithmetadata[] = {NULL, apname, config.regtype, mdns_port, MDNS_RECORD_WITH_METADATA, 131 NULL}; 132 #endif 133 134 char **argv; 135 #ifdef CONFIG_METADATA 136 if (config.metadata_enabled) 137 argv = argvwithmetadata; 138 else 139 #endif 140 141 argv = argvwithoutmetadata; 142 143 int pid = fork_execvp(argv[0], argv); 144 if (pid >= 0) { 145 mdns_pid = pid; 146 return 0; 147 } else 148 warn("Calling %s failed !", argv[0]); 149 150 return -1; 151 } 152 153 static void kill_mdns_child(void) { 154 if (mdns_pid) 155 kill(mdns_pid, SIGTERM); 156 mdns_pid = 0; 157 } 158 159 mdns_backend mdns_external_avahi = {.name = "external-avahi", 160 .mdns_register = mdns_external_avahi_register, 161 .mdns_unregister = kill_mdns_child, 162 .mdns_dacp_monitor_start = NULL, 163 .mdns_dacp_monitor_set_id = NULL, 164 .mdns_dacp_monitor_stop = NULL}; 165 166 mdns_backend mdns_external_dns_sd = {.name = "external-dns-sd", 167 .mdns_register = mdns_external_dns_sd_register, 168 .mdns_unregister = kill_mdns_child, 169 .mdns_dacp_monitor_start = NULL, 170 .mdns_dacp_monitor_set_id = NULL, 171 .mdns_dacp_monitor_stop = NULL}; 172