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