xref: /openbsd/lib/libsndio/aucat.c (revision 73471bf0)
1 /*	$OpenBSD: aucat.c,v 1.79 2021/11/07 20:51:47 ratchov Exp $	*/
2 /*
3  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/un.h>
22 
23 #include <netinet/in.h>
24 #include <netinet/tcp.h>
25 #include <netdb.h>
26 
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <limits.h>
30 #include <poll.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "aucat.h"
37 #include "debug.h"
38 
39 
40 /*
41  * read a message, return 0 if not completed
42  */
43 int
44 _aucat_rmsg(struct aucat *hdl, int *eof)
45 {
46 	ssize_t n;
47 	unsigned char *data;
48 
49 	if (hdl->rstate != RSTATE_MSG) {
50 		DPRINTF("_aucat_rmsg: bad state\n");
51 		abort();
52 	}
53 	while (hdl->rtodo > 0) {
54 		data = (unsigned char *)&hdl->rmsg;
55 		data += sizeof(struct amsg) - hdl->rtodo;
56 		while ((n = read(hdl->fd, data, hdl->rtodo)) == -1) {
57 			if (errno == EINTR)
58 				continue;
59 			if (errno != EAGAIN) {
60 				*eof = 1;
61 				DPERROR("_aucat_rmsg: read");
62 			}
63 			return 0;
64 		}
65 		if (n == 0) {
66 			DPRINTF("_aucat_rmsg: eof\n");
67 			*eof = 1;
68 			return 0;
69 		}
70 		hdl->rtodo -= n;
71 	}
72 	if (ntohl(hdl->rmsg.cmd) == AMSG_DATA) {
73 		hdl->rtodo = ntohl(hdl->rmsg.u.data.size);
74 		hdl->rstate = RSTATE_DATA;
75 	} else {
76 		hdl->rtodo = sizeof(struct amsg);
77 		hdl->rstate = RSTATE_MSG;
78 	}
79 	return 1;
80 }
81 
82 /*
83  * write a message, return 0 if not completed
84  */
85 int
86 _aucat_wmsg(struct aucat *hdl, int *eof)
87 {
88 	ssize_t n;
89 	unsigned char *data;
90 
91 	if (hdl->wstate == WSTATE_IDLE) {
92 		hdl->wstate = WSTATE_MSG;
93 		hdl->wtodo = sizeof(struct amsg);
94 	}
95 	if (hdl->wstate != WSTATE_MSG) {
96 		DPRINTF("_aucat_wmsg: bad state\n");
97 		abort();
98 	}
99 	while (hdl->wtodo > 0) {
100 		data = (unsigned char *)&hdl->wmsg;
101 		data += sizeof(struct amsg) - hdl->wtodo;
102 		while ((n = write(hdl->fd, data, hdl->wtodo)) == -1) {
103 			if (errno == EINTR)
104 				continue;
105 			if (errno != EAGAIN) {
106 				*eof = 1;
107 				DPERROR("_aucat_wmsg: write");
108 			}
109 			return 0;
110 		}
111 		hdl->wtodo -= n;
112 	}
113 	if (ntohl(hdl->wmsg.cmd) == AMSG_DATA) {
114 		hdl->wtodo = ntohl(hdl->wmsg.u.data.size);
115 		hdl->wstate = WSTATE_DATA;
116 	} else {
117 		hdl->wtodo = 0xdeadbeef;
118 		hdl->wstate = WSTATE_IDLE;
119 	}
120 	return 1;
121 }
122 
123 size_t
124 _aucat_rdata(struct aucat *hdl, void *buf, size_t len, int *eof)
125 {
126 	ssize_t n;
127 
128 	if (hdl->rstate != RSTATE_DATA) {
129 		DPRINTF("_aucat_rdata: bad state\n");
130 		abort();
131 	}
132 	if (len > hdl->rtodo)
133 		len = hdl->rtodo;
134 	while ((n = read(hdl->fd, buf, len)) == -1) {
135 		if (errno == EINTR)
136 			continue;
137 		if (errno != EAGAIN) {
138 			*eof = 1;
139 			DPERROR("_aucat_rdata: read");
140 		}
141 		return 0;
142 	}
143 	if (n == 0) {
144 		DPRINTF("_aucat_rdata: eof\n");
145 		*eof = 1;
146 		return 0;
147 	}
148 	hdl->rtodo -= n;
149 	if (hdl->rtodo == 0) {
150 		hdl->rstate = RSTATE_MSG;
151 		hdl->rtodo = sizeof(struct amsg);
152 	}
153 	DPRINTFN(2, "_aucat_rdata: read: n = %zd\n", n);
154 	return n;
155 }
156 
157 size_t
158 _aucat_wdata(struct aucat *hdl, const void *buf, size_t len,
159    unsigned int wbpf, int *eof)
160 {
161 	ssize_t n;
162 	size_t datasize;
163 
164 	switch (hdl->wstate) {
165 	case WSTATE_IDLE:
166 		datasize = len;
167 		if (datasize > AMSG_DATAMAX)
168 			datasize = AMSG_DATAMAX;
169 		datasize -= datasize % wbpf;
170 		if (datasize == 0)
171 			datasize = wbpf;
172 		hdl->wmsg.cmd = htonl(AMSG_DATA);
173 		hdl->wmsg.u.data.size = htonl(datasize);
174 		hdl->wtodo = sizeof(struct amsg);
175 		hdl->wstate = WSTATE_MSG;
176 		/* FALLTHROUGH */
177 	case WSTATE_MSG:
178 		if (!_aucat_wmsg(hdl, eof))
179 			return 0;
180 	}
181 	if (len > hdl->wtodo)
182 		len = hdl->wtodo;
183 	if (len == 0) {
184 		DPRINTF("_aucat_wdata: len == 0\n");
185 		abort();
186 	}
187 	while ((n = write(hdl->fd, buf, len)) == -1) {
188 		if (errno == EINTR)
189 			continue;
190 		if (errno != EAGAIN) {
191 			*eof = 1;
192 			DPERROR("_aucat_wdata: write");
193 		}
194 		return 0;
195 	}
196 	DPRINTFN(2, "_aucat_wdata: write: n = %zd\n", n);
197 	hdl->wtodo -= n;
198 	if (hdl->wtodo == 0) {
199 		hdl->wstate = WSTATE_IDLE;
200 		hdl->wtodo = 0xdeadbeef;
201 	}
202 	return n;
203 }
204 
205 static int
206 aucat_mkcookie(unsigned char *cookie)
207 {
208 #define COOKIE_DIR	"/.sndio"
209 #define COOKIE_SUFFIX	"/.sndio/cookie"
210 #define TEMPL_SUFFIX	".XXXXXXXX"
211 	struct stat sb;
212 	char *home, *path = NULL, *tmp = NULL;
213 	size_t home_len, path_len;
214 	int fd, len;
215 
216 	/* please gcc */
217 	path_len = 0xdeadbeef;
218 
219 	/*
220 	 * try to load the cookie
221 	 */
222 	home = issetugid() ? NULL : getenv("HOME");
223 	if (home == NULL)
224 		goto bad_gen;
225 	home_len = strlen(home);
226 	path = malloc(home_len + sizeof(COOKIE_SUFFIX));
227 	if (path == NULL)
228 		goto bad_gen;
229 	memcpy(path, home, home_len);
230 	memcpy(path + home_len, COOKIE_SUFFIX, sizeof(COOKIE_SUFFIX));
231 	path_len = home_len + sizeof(COOKIE_SUFFIX) - 1;
232 	fd = open(path, O_RDONLY);
233 	if (fd == -1) {
234 		if (errno != ENOENT)
235 			DPERROR(path);
236 		goto bad_gen;
237 	}
238 	if (fstat(fd, &sb) == -1) {
239 		DPERROR(path);
240 		goto bad_close;
241 	}
242 	if (sb.st_mode & 0077) {
243 		DPRINTF("%s has wrong permissions\n", path);
244 		goto bad_close;
245 	}
246 	len = read(fd, cookie, AMSG_COOKIELEN);
247 	if (len == -1) {
248 		DPERROR(path);
249 		goto bad_close;
250 	}
251 	if (len != AMSG_COOKIELEN) {
252 		DPRINTF("%s: short read\n", path);
253 		goto bad_close;
254 	}
255 	close(fd);
256 	goto done;
257 bad_close:
258 	close(fd);
259 bad_gen:
260 	/*
261 	 * generate a new cookie
262 	 */
263 	arc4random_buf(cookie, AMSG_COOKIELEN);
264 
265 	/*
266 	 * try to save the cookie
267 	 */
268 
269 	if (home == NULL)
270 		goto done;
271 	tmp = malloc(path_len + sizeof(TEMPL_SUFFIX));
272 	if (tmp == NULL)
273 		goto done;
274 
275 	/* create ~/.sndio directory */
276 	memcpy(tmp, home, home_len);
277 	memcpy(tmp + home_len, COOKIE_DIR, sizeof(COOKIE_DIR));
278 	if (mkdir(tmp, 0755) == -1 && errno != EEXIST)
279 		goto done;
280 
281 	/* create cookie file in it */
282 	memcpy(tmp, path, path_len);
283 	memcpy(tmp + path_len, TEMPL_SUFFIX, sizeof(TEMPL_SUFFIX));
284 	fd = mkstemp(tmp);
285 	if (fd == -1) {
286 		DPERROR(tmp);
287 		goto done;
288 	}
289 	if (write(fd, cookie, AMSG_COOKIELEN) == -1) {
290 		DPERROR(tmp);
291 		unlink(tmp);
292 		close(fd);
293 		goto done;
294 	}
295 	close(fd);
296 	if (rename(tmp, path) == -1) {
297 		DPERROR(tmp);
298 		unlink(tmp);
299 	}
300 done:
301 	free(tmp);
302 	free(path);
303 	return 1;
304 }
305 
306 static int
307 aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit)
308 {
309 	int s, error, opt;
310 	struct addrinfo *ailist, *ai, aihints;
311 	char serv[NI_MAXSERV];
312 
313 	snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT);
314 	memset(&aihints, 0, sizeof(struct addrinfo));
315 	aihints.ai_socktype = SOCK_STREAM;
316 	aihints.ai_protocol = IPPROTO_TCP;
317 	error = getaddrinfo(host, serv, &aihints, &ailist);
318 	if (error) {
319 		DPRINTF("%s: %s\n", host, gai_strerror(error));
320 		return 0;
321 	}
322 	s = -1;
323 	for (ai = ailist; ai != NULL; ai = ai->ai_next) {
324 		s = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC,
325 		    ai->ai_protocol);
326 		if (s == -1) {
327 			DPERROR("socket");
328 			continue;
329 		}
330 	restart:
331 		if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) {
332 			if (errno == EINTR)
333 				goto restart;
334 			DPERROR("connect");
335 			close(s);
336 			s = -1;
337 			continue;
338 		}
339 		break;
340 	}
341 	freeaddrinfo(ailist);
342 	if (s == -1)
343 		return 0;
344 	opt = 1;
345 	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) == -1) {
346 		DPERROR("setsockopt");
347 		close(s);
348 		return 0;
349 	}
350 	hdl->fd = s;
351 	return 1;
352 }
353 
354 static int
355 aucat_connect_un(struct aucat *hdl, unsigned int unit)
356 {
357 	struct sockaddr_un ca;
358 	socklen_t len = sizeof(struct sockaddr_un);
359 	uid_t uid;
360 	int s;
361 
362 	uid = geteuid();
363 	snprintf(ca.sun_path, sizeof(ca.sun_path),
364 	    SOCKPATH_DIR "-%u/" SOCKPATH_FILE "%u", uid, unit);
365 	ca.sun_family = AF_UNIX;
366 	s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
367 	if (s == -1)
368 		return 0;
369 	while (connect(s, (struct sockaddr *)&ca, len) == -1) {
370 		if (errno == EINTR)
371 			continue;
372 		DPERROR(ca.sun_path);
373 		/* try shared server */
374 		snprintf(ca.sun_path, sizeof(ca.sun_path),
375 		    SOCKPATH_DIR "/" SOCKPATH_FILE "%u", unit);
376 		while (connect(s, (struct sockaddr *)&ca, len) == -1) {
377 			if (errno == EINTR)
378 				continue;
379 			DPERROR(ca.sun_path);
380 			close(s);
381 			return 0;
382 		}
383 		break;
384 	}
385 	hdl->fd = s;
386 	DPRINTFN(2, "%s: connected\n", ca.sun_path);
387 	return 1;
388 }
389 
390 static const char *
391 parsestr(const char *str, char *rstr, unsigned int max)
392 {
393 	const char *p = str;
394 
395 	while (*p != '\0' && *p != ',' && *p != '/') {
396 		if (--max == 0) {
397 			DPRINTF("%s: string too long\n", str);
398 			return NULL;
399 		}
400 		*rstr++ = *p++;
401 	}
402 	if (str == p) {
403 		DPRINTF("%s: string expected\n", str);
404 		return NULL;
405 	}
406 	*rstr = '\0';
407 	return p;
408 }
409 
410 int
411 _aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
412 {
413 	extern char *__progname;
414 	int eof;
415 	char host[NI_MAXHOST], opt[AMSG_OPTMAX];
416 	const char *p;
417 	unsigned int unit, devnum, type;
418 
419 	if ((p = _sndio_parsetype(str, "snd")) != NULL)
420 		type = 0;
421 	else if ((p = _sndio_parsetype(str, "midithru")) != NULL)
422 		type = 1;
423 	else if ((p = _sndio_parsetype(str, "midi")) != NULL)
424 		type = 2;
425 	else {
426 		DPRINTF("%s: unsupported device type\n", str);
427 		return -1;
428 	}
429 	if (*p == '@') {
430 		p = parsestr(++p, host, NI_MAXHOST);
431 		if (p == NULL)
432 			return 0;
433 	} else
434 		*host = '\0';
435 	if (*p == ',') {
436 		p = _sndio_parsenum(++p, &unit, 15);
437 		if (p == NULL)
438 			return 0;
439 	} else
440 		unit = 0;
441 	if (*p != '/') {
442 		DPRINTF("%s: '/' expected\n", str);
443 		return 0;
444 	}
445 	p++;
446 	if (type == 0) {
447 		if (*p < '0' || *p > '9') {
448 			devnum = AMSG_NODEV;
449 			p = parsestr(p, opt, AMSG_OPTMAX);
450 			if (p == NULL)
451 				return 0;
452 		} else {
453 			p = _sndio_parsenum(p, &devnum, 15);
454 			if (p == NULL)
455 				return 0;
456 			if (*p == '.') {
457 				p = parsestr(++p, opt, AMSG_OPTMAX);
458 				if (p == NULL)
459 					return 0;
460 			} else
461 				strlcpy(opt, "default", AMSG_OPTMAX);
462 		}
463 	} else {
464 		p = _sndio_parsenum(p, &devnum, 15);
465 		if (p == NULL)
466 			return 0;
467 		memset(opt, 0, sizeof(opt));
468 	}
469 	if (*p != '\0') {
470 		DPRINTF("%s: junk at end of dev name\n", p);
471 		return 0;
472 	}
473 	devnum += type * 16; /* XXX */
474 	DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n",
475 	    host, unit, devnum, opt);
476 	if (host[0] != '\0') {
477 		if (!aucat_connect_tcp(hdl, host, unit))
478 			return 0;
479 	} else {
480 		if (!aucat_connect_un(hdl, unit))
481 			return 0;
482 	}
483 	hdl->rstate = RSTATE_MSG;
484 	hdl->rtodo = sizeof(struct amsg);
485 	hdl->wstate = WSTATE_IDLE;
486 	hdl->wtodo = 0xdeadbeef;
487 	hdl->maxwrite = 0;
488 
489 	/*
490 	 * say hello to server
491 	 */
492 	AMSG_INIT(&hdl->wmsg);
493 	hdl->wmsg.cmd = htonl(AMSG_AUTH);
494 	if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie))
495 		goto bad_connect;
496 	hdl->wtodo = sizeof(struct amsg);
497 	if (!_aucat_wmsg(hdl, &eof))
498 		goto bad_connect;
499 	AMSG_INIT(&hdl->wmsg);
500 	hdl->wmsg.cmd = htonl(AMSG_HELLO);
501 	hdl->wmsg.u.hello.version = AMSG_VERSION;
502 	hdl->wmsg.u.hello.mode = htons(mode);
503 	hdl->wmsg.u.hello.devnum = devnum;
504 	hdl->wmsg.u.hello.id = htonl(getpid());
505 	strlcpy(hdl->wmsg.u.hello.who, __progname,
506 	    sizeof(hdl->wmsg.u.hello.who));
507 	strlcpy(hdl->wmsg.u.hello.opt, opt,
508 	    sizeof(hdl->wmsg.u.hello.opt));
509 	hdl->wtodo = sizeof(struct amsg);
510 	if (!_aucat_wmsg(hdl, &eof))
511 		goto bad_connect;
512 	hdl->rtodo = sizeof(struct amsg);
513 	if (!_aucat_rmsg(hdl, &eof)) {
514 		DPRINTF("aucat_init: mode refused\n");
515 		goto bad_connect;
516 	}
517 	if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) {
518 		DPRINTF("aucat_init: protocol err\n");
519 		goto bad_connect;
520 	}
521 	return 1;
522  bad_connect:
523 	while (close(hdl->fd) == -1 && errno == EINTR)
524 		; /* retry */
525 	return 0;
526 }
527 
528 void
529 _aucat_close(struct aucat *hdl, int eof)
530 {
531 	char dummy[sizeof(struct amsg)];
532 	ssize_t n;
533 
534 	if (!eof) {
535 		AMSG_INIT(&hdl->wmsg);
536 		hdl->wmsg.cmd = htonl(AMSG_BYE);
537 		hdl->wtodo = sizeof(struct amsg);
538 		if (!_aucat_wmsg(hdl, &eof))
539 			goto bad_close;
540 
541 		/*
542 		 * block until the peer disconnects
543 		 */
544 		while (1) {
545 			n = read(hdl->fd, dummy, sizeof(dummy));
546 			if (n == -1) {
547 				if (errno == EINTR)
548 					continue;
549 				break;
550 			}
551 			if (n == 0)
552 				break;
553 		}
554 	}
555  bad_close:
556 	while (close(hdl->fd) == -1 && errno == EINTR)
557 		; /* nothing */
558 }
559 
560 int
561 _aucat_setfl(struct aucat *hdl, int nbio, int *eof)
562 {
563 	if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) == -1) {
564 		DPERROR("_aucat_setfl: fcntl");
565 		*eof = 1;
566 		return 0;
567 	}
568 	return 1;
569 }
570 
571 int
572 _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events)
573 {
574 	if (hdl->rstate == RSTATE_MSG)
575 		events |= POLLIN;
576 	pfd->fd = hdl->fd;
577 	pfd->events = events;
578 	return 1;
579 }
580 
581 int
582 _aucat_revents(struct aucat *hdl, struct pollfd *pfd)
583 {
584 	int revents = pfd->revents;
585 
586 	DPRINTFN(2, "_aucat_revents: revents: %x\n", revents);
587 	return revents;
588 }
589