xref: /openbsd-src/lib/libsndio/aucat.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: aucat.c,v 1.77 2019/07/12 06:30:55 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 = _sndio_parsenum(++p, &devnum, 15);
446 	if (p == NULL)
447 		return 0;
448 	if (*p == '.') {
449 		p = parsestr(++p, opt, AMSG_OPTMAX);
450 		if (p == NULL)
451 			return 0;
452 	} else
453 		strlcpy(opt, "default", AMSG_OPTMAX);
454 	if (*p != '\0') {
455 		DPRINTF("%s: junk at end of dev name\n", p);
456 		return 0;
457 	}
458 	devnum += type * 16; /* XXX */
459 	DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n",
460 	    host, unit, devnum, opt);
461 	if (host[0] != '\0') {
462 		if (!aucat_connect_tcp(hdl, host, unit))
463 			return 0;
464 	} else {
465 		if (!aucat_connect_un(hdl, unit))
466 			return 0;
467 	}
468 	hdl->rstate = RSTATE_MSG;
469 	hdl->rtodo = sizeof(struct amsg);
470 	hdl->wstate = WSTATE_IDLE;
471 	hdl->wtodo = 0xdeadbeef;
472 	hdl->maxwrite = 0;
473 
474 	/*
475 	 * say hello to server
476 	 */
477 	AMSG_INIT(&hdl->wmsg);
478 	hdl->wmsg.cmd = htonl(AMSG_AUTH);
479 	if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie))
480 		goto bad_connect;
481 	hdl->wtodo = sizeof(struct amsg);
482 	if (!_aucat_wmsg(hdl, &eof))
483 		goto bad_connect;
484 	AMSG_INIT(&hdl->wmsg);
485 	hdl->wmsg.cmd = htonl(AMSG_HELLO);
486 	hdl->wmsg.u.hello.version = AMSG_VERSION;
487 	hdl->wmsg.u.hello.mode = htons(mode);
488 	hdl->wmsg.u.hello.devnum = devnum;
489 	hdl->wmsg.u.hello.id = htonl(getpid());
490 	strlcpy(hdl->wmsg.u.hello.who, __progname,
491 	    sizeof(hdl->wmsg.u.hello.who));
492 	strlcpy(hdl->wmsg.u.hello.opt, opt,
493 	    sizeof(hdl->wmsg.u.hello.opt));
494 	hdl->wtodo = sizeof(struct amsg);
495 	if (!_aucat_wmsg(hdl, &eof))
496 		goto bad_connect;
497 	hdl->rtodo = sizeof(struct amsg);
498 	if (!_aucat_rmsg(hdl, &eof)) {
499 		DPRINTF("aucat_init: mode refused\n");
500 		goto bad_connect;
501 	}
502 	if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) {
503 		DPRINTF("aucat_init: protocol err\n");
504 		goto bad_connect;
505 	}
506 	return 1;
507  bad_connect:
508 	while (close(hdl->fd) == -1 && errno == EINTR)
509 		; /* retry */
510 	return 0;
511 }
512 
513 void
514 _aucat_close(struct aucat *hdl, int eof)
515 {
516 	char dummy[sizeof(struct amsg)];
517 	ssize_t n;
518 
519 	if (!eof) {
520 		AMSG_INIT(&hdl->wmsg);
521 		hdl->wmsg.cmd = htonl(AMSG_BYE);
522 		hdl->wtodo = sizeof(struct amsg);
523 		if (!_aucat_wmsg(hdl, &eof))
524 			goto bad_close;
525 
526 		/*
527 		 * block until the peer disconnects
528 		 */
529 		while (1) {
530 			n = read(hdl->fd, dummy, sizeof(dummy));
531 			if (n == -1) {
532 				if (errno == EINTR)
533 					continue;
534 				break;
535 			}
536 			if (n == 0)
537 				break;
538 		}
539 	}
540  bad_close:
541 	while (close(hdl->fd) == -1 && errno == EINTR)
542 		; /* nothing */
543 }
544 
545 int
546 _aucat_setfl(struct aucat *hdl, int nbio, int *eof)
547 {
548 	if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) == -1) {
549 		DPERROR("_aucat_setfl: fcntl");
550 		*eof = 1;
551 		return 0;
552 	}
553 	return 1;
554 }
555 
556 int
557 _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events)
558 {
559 	if (hdl->rstate == RSTATE_MSG)
560 		events |= POLLIN;
561 	pfd->fd = hdl->fd;
562 	pfd->events = events;
563 	return 1;
564 }
565 
566 int
567 _aucat_revents(struct aucat *hdl, struct pollfd *pfd)
568 {
569 	int revents = pfd->revents;
570 
571 	DPRINTFN(2, "_aucat_revents: revents: %x\n", revents);
572 	return revents;
573 }
574