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