1 /*
2 * Copyright (c) 2010-2014 Christiano F. Haesbaert <haesbaert@haesbaert.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/utsname.h>
20 #include <sys/wait.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23
24 #include <err.h>
25 #include <event.h>
26 #include <pwd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <signal.h>
31 #include <unistd.h>
32
33 #include "version.h"
34 #include "mdnsd.h"
35 #include "mdns.h"
36 #include "log.h"
37 #include "control.h"
38
39 __dead void usage(void);
40 __dead void display_version(void);
41 void mdnsd_sig_handler(int, short, void *);
42 void conf_init_ifaces(int, char *[]);
43 void mdnsd_shutdown(void);
44 int mdns_sock(void);
45 void fetchmyname(char [MAXHOSTNAMELEN]);
46 void fetchhinfo(struct hinfo *);
47
48 ctl_conns_t ctl_conns;
49
50 struct mdnsd_conf *conf = NULL;
51 #ifdef __OpenBSD__
52 extern char *malloc_options;
53 #endif
54
55 __dead void
usage(void)56 usage(void)
57 {
58 extern char *__progname;
59
60 fprintf(stderr, "usage: %s [-dw] ifname [ifnames...]\n",
61 __progname);
62 fprintf(stderr, "usage: %s -v\n", __progname);
63 exit(1);
64 }
65
66 __dead void
display_version(void)67 display_version(void)
68 {
69 printf("OpenMdns Daemon %s\n", MDNS_VERSION);
70 printf("Copyright (C) 2010-2014 Christiano F. Haesbaert\n");
71
72 exit(0);
73 }
74
75 void
conf_init_ifaces(int argc,char * argv[])76 conf_init_ifaces(int argc, char *argv[])
77 {
78 int found = 0;
79 int i;
80 struct kif *k;
81 struct iface *iface;
82
83 for (i = 0; i < argc; i++) {
84 k = kif_findname(argv[i]);
85 if (k == NULL) {
86 log_warnx("Unknown interface %s", argv[i]);
87 continue;
88 }
89
90 iface = if_new(k);
91 if (iface == NULL)
92 continue;
93 found++;
94 LIST_INSERT_HEAD(&conf->iface_list, iface, entry);
95 }
96
97 if (!found)
98 fatal("Couldn't find any interface");
99
100 LIST_FOREACH(iface, &conf->iface_list, entry)
101 log_debug("using iface %s index %u", iface->name, iface->ifindex);
102 }
103
104 /* ARGSUSED */
105 void
mdnsd_sig_handler(int sig,short event,void * arg)106 mdnsd_sig_handler(int sig, short event, void *arg)
107 {
108 /*
109 * signal handler rules don't apply, libevent decouples for us
110 */
111
112 switch (sig) {
113 case SIGTERM:
114 case SIGINT:
115 mdnsd_shutdown();
116 break; /* NOTREACHED */
117 case SIGHUP:
118 log_debug("got SIGHUP");
119 /* reconfigure */
120 /* ... */
121 break;
122 default:
123 fatalx("unexpected signal");
124 /* NOTREACHED */
125 }
126 }
127
128 void
mdnsd_shutdown(void)129 mdnsd_shutdown(void)
130 {
131 struct iface *iface;
132 struct pge *pge;
133
134 /*
135 * Send goodbye RR for all published records.
136 */
137 while ((pge = TAILQ_FIRST(&pge_queue)) != NULL)
138 pge_kill(pge);
139
140 while ((iface = LIST_FIRST(&conf->iface_list)) != NULL) {
141 LIST_REMOVE(iface, entry);
142 free(iface);
143 }
144
145 kev_cleanup();
146 kif_cleanup();
147 control_cleanup();
148 free(conf);
149
150 log_info("terminating");
151 exit(0);
152 }
153
154
155 int
mdns_sock(void)156 mdns_sock(void)
157 {
158 int sock;
159 struct sockaddr_in addr;
160
161 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
162 fatal("socket");
163
164 addr.sin_family = AF_INET;
165 addr.sin_port = htons(MDNS_PORT);
166 addr.sin_addr.s_addr = INADDR_ANY;
167
168 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1)
169 fatal("bind");
170
171 if (if_set_opt(sock) == -1)
172 fatal("if_set_opt");
173
174 if (if_set_mcast_ttl(sock, MDNS_TTL) == -1)
175 fatal("if_set_mcast_ttl");
176
177 if (if_set_mcast_loop(sock) == -1)
178 fatal("if_set_mcast_loop");
179
180 /* if (if_set_tos(sock, IPTOS_PREC_INTERNETCONTROL) == -1) */
181 /* fatal("if_set_tos"); */
182
183 if_set_recvbuf(sock);
184
185 log_debug("mdns sock bound to %s:%u", inet_ntoa(addr.sin_addr),
186 ntohs(addr.sin_port));
187
188 return (sock);
189 }
190
191 void
fetchmyname(char myname[MAXHOSTNAMELEN])192 fetchmyname(char myname[MAXHOSTNAMELEN])
193 {
194 char *end;
195
196 if (gethostname(myname, MAXHOSTNAMELEN) == -1)
197 fatal("gethostname");
198 end = strchr(myname, '.');
199 if (end != NULL)
200 *end = '\0'; /* use short hostnames */
201 if (strlcat(myname, ".local", MAXHOSTNAMELEN) >= MAXHOSTNAMELEN)
202 errx(1, "hostname too long %s", myname);
203 }
204
205 void
fetchhinfo(struct hinfo * hi)206 fetchhinfo(struct hinfo *hi)
207 {
208 struct utsname utsname;
209
210 if (uname(&utsname) == -1)
211 fatal("uname");
212 bzero(hi, sizeof(*hi));
213 strlcpy(hi->cpu, utsname.machine, sizeof(hi->cpu));
214 snprintf(hi->os, sizeof(hi->os), "%s %s", utsname.sysname,
215 utsname.release);
216 }
217
218 int
main(int argc,char * argv[])219 main(int argc, char *argv[])
220 {
221 int ch;
222 int debug, no_workstation;
223 struct passwd *pw;
224 struct iface *iface;
225 struct event ev_sigint, ev_sigterm, ev_sighup;
226
227 debug = no_workstation = 0;
228 /*
229 * XXX Carefull not to call anything that would malloc prior to setting
230 * malloc_options, malloc will disregard malloc_options after the first
231 * call.
232 */
233 while ((ch = getopt(argc, argv, "dvw")) != -1) {
234 switch (ch) {
235 case 'd':
236 debug = 1;
237 #ifdef __OpenBSD__
238 malloc_options = "AFGJPX";
239 #endif
240 break;
241 case 'v':
242 display_version();
243 break; /* NOTREACHED */
244 case 'w':
245 no_workstation = 1;
246 break;
247 default:
248 usage();
249 /* NOTREACHED */
250 }
251 }
252
253 log_init(1); /* log to stderr until daemonized */
254
255 argc -= optind;
256 argv += optind;
257
258 if (!argc)
259 usage();
260
261 /* check for root privileges */
262 if (geteuid())
263 errx(1, "need root privileges");
264
265 /* check for mdnsd user */
266 if ((pw = getpwnam(MDNSD_USER)) == NULL)
267 fatal("getpwnam, make sure you have user and group _mdnsd");
268
269 log_init(debug);
270
271 if (!debug)
272 daemon(1, 0);
273
274 log_info("startup");
275
276 /* init control before chroot */
277 if (control_init() == -1)
278 fatalx("control socket setup failed");
279
280 /* chroot */
281 if (chroot(pw->pw_dir) == -1)
282 fatal("chroot");
283 if (chdir("/") == -1)
284 fatal("chdir(\"/\")");
285
286 /* show who we are */
287 setproctitle("mdnsd");
288
289 /* drop privileges */
290 if (setgroups(1, &pw->pw_gid) ||
291 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
292 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
293 fatal("error droping privileges");
294
295 /* init libevent */
296 event_init();
297
298 /* setup signals */
299 signal_set(&ev_sigint, SIGINT, mdnsd_sig_handler, NULL);
300 signal_set(&ev_sigterm, SIGTERM, mdnsd_sig_handler, NULL);
301 signal_set(&ev_sighup, SIGHUP, mdnsd_sig_handler, NULL);
302 signal_add(&ev_sigint, NULL);
303 signal_add(&ev_sigterm, NULL);
304 signal_add(&ev_sighup, NULL);
305 signal(SIGPIPE, SIG_IGN);
306
307 /* fetch all kernel interfaces */
308 if (kif_init() != 0)
309 fatal("Can't get kernel interfaces");
310
311 /* init conf */
312 if ((conf = calloc(1, sizeof(*conf))) == NULL)
313 fatal("calloc");
314 fetchmyname(conf->myname);
315 fetchhinfo(&conf->hi);
316 LIST_INIT(&conf->iface_list);
317 conf->no_workstation = no_workstation;
318
319 /* init RR cache */
320 cache_init();
321
322 /* init publish queues */
323 pg_init();
324
325 /* init interfaces and names */
326 conf_init_ifaces(argc, argv);
327
328 /* Create primary pge */
329 pge_initprimary();
330
331 /* init some packet internals */
332 packet_init();
333
334 /* init querier */
335 query_init();
336
337 /* listen to kernel interface events */
338 kev_init();
339
340 /* create mdns socket */
341 conf->mdns_sock = mdns_sock();
342
343 /* setup mdns events */
344 event_set(&conf->ev_mdns, conf->mdns_sock, EV_READ|EV_PERSIST,
345 recv_packet, NULL);
346 event_add(&conf->ev_mdns, NULL);
347
348 /* start interfaces */
349 LIST_FOREACH(iface, &conf->iface_list, entry) {
350 /* XXX yep it seems wrong indeed */
351 iface->fd = conf->mdns_sock;
352 if (if_fsm(iface, IF_EVT_UP))
353 log_warnx("error starting interface %s", iface->name);
354 }
355
356 /* listen on mdns control socket */
357 TAILQ_INIT(&ctl_conns);
358 control_listen();
359
360 /* parent mainloop */
361 event_dispatch();
362
363 return (0);
364 }
365
366 void
imsg_event_add(struct imsgev * iev)367 imsg_event_add(struct imsgev *iev)
368 {
369 if (iev->handler == NULL) {
370 imsg_flush(&iev->ibuf);
371 return;
372 }
373
374 iev->events = EV_READ;
375 if (iev->ibuf.w.queued)
376 iev->events |= EV_WRITE;
377
378 event_del(&iev->ev);
379 event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
380 event_add(&iev->ev, NULL);
381 }
382
383 int
mdnsd_imsg_compose_ctl(struct ctl_conn * c,u_int16_t type,void * data,u_int16_t datalen)384 mdnsd_imsg_compose_ctl(struct ctl_conn *c, u_int16_t type,
385 void *data, u_int16_t datalen)
386 {
387 return (imsg_compose_event(&c->iev, type, 0, 0, -1, data, datalen));
388 }
389
390 int
imsg_compose_event(struct imsgev * iev,u_int16_t type,u_int32_t peerid,pid_t pid,int fd,void * data,u_int16_t datalen)391 imsg_compose_event(struct imsgev *iev, u_int16_t type,
392 u_int32_t peerid, pid_t pid, int fd, void *data, u_int16_t datalen)
393 {
394 int ret;
395
396 if ((ret = imsg_compose(&iev->ibuf, type, peerid,
397 pid, fd, data, datalen)) != -1)
398 imsg_event_add(iev);
399 return (ret);
400 }
401
402 int
peersuser(int fd)403 peersuser(int fd)
404 {
405 uid_t euid;
406
407 if (getpeereid(fd, &euid, NULL) == -1)
408 fatal("getpeereid");
409 return (euid == 0);
410 }
411