xref: /openbsd-src/lib/libsndio/aucat.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: aucat.c,v 1.71 2016/01/09 08:27:24 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)) < 0) {
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)) < 0) {
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)) < 0) {
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)) < 0) {
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_SUFFIX	"/.aucat_cookie"
209 #define TEMPL_SUFFIX	".XXXXXXXX"
210 	struct stat sb;
211 	char *home, *path = NULL, *tmp = NULL;
212 	size_t home_len, path_len;
213 	int fd, len;
214 
215 	/* please gcc */
216 	path_len = 0xdeadbeef;
217 
218 	/*
219 	 * try to load the cookie
220 	 */
221 	home = issetugid() ? NULL : getenv("HOME");
222 	if (home == NULL)
223 		goto bad_gen;
224 	home_len = strlen(home);
225 	path = malloc(home_len + sizeof(COOKIE_SUFFIX));
226 	if (path == NULL)
227 		goto bad_gen;
228 	memcpy(path, home, home_len);
229 	memcpy(path + home_len, COOKIE_SUFFIX, sizeof(COOKIE_SUFFIX));
230 	path_len = home_len + sizeof(COOKIE_SUFFIX) - 1;
231 	fd = open(path, O_RDONLY);
232 	if (fd < 0) {
233 		if (errno != ENOENT)
234 			DPERROR(path);
235 		goto bad_gen;
236 	}
237 	if (fstat(fd, &sb) < 0) {
238 		DPERROR(path);
239 		goto bad_close;
240 	}
241 	if (sb.st_mode & 0077) {
242 		DPRINTF("%s has wrong permissions\n", path);
243 		goto bad_close;
244 	}
245 	len = read(fd, cookie, AMSG_COOKIELEN);
246 	if (len < 0) {
247 		DPERROR(path);
248 		goto bad_close;
249 	}
250 	if (len != AMSG_COOKIELEN) {
251 		DPRINTF("%s: short read\n", path);
252 		goto bad_close;
253 	}
254 	close(fd);
255 	goto done;
256 bad_close:
257 	close(fd);
258 bad_gen:
259 	/*
260 	 * generate a new cookie
261 	 */
262 	arc4random_buf(cookie, AMSG_COOKIELEN);
263 
264 	/*
265 	 * try to save the cookie
266 	 */
267 	if (home == NULL)
268 		goto done;
269 	tmp = malloc(path_len + sizeof(TEMPL_SUFFIX));
270 	if (tmp == NULL)
271 		goto done;
272 	memcpy(tmp, path, path_len);
273 	memcpy(tmp + path_len, TEMPL_SUFFIX, sizeof(TEMPL_SUFFIX));
274 	fd = mkstemp(tmp);
275 	if (fd < 0) {
276 		DPERROR(tmp);
277 		goto done;
278 	}
279 	if (write(fd, cookie, AMSG_COOKIELEN) < 0) {
280 		DPERROR(tmp);
281 		unlink(tmp);
282 		close(fd);
283 		goto done;
284 	}
285 	close(fd);
286 	if (rename(tmp, path) < 0) {
287 		DPERROR(tmp);
288 		unlink(tmp);
289 	}
290 done:
291 	free(tmp);
292 	free(path);
293 	return 1;
294 }
295 
296 static int
297 aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit)
298 {
299 	int s, error, opt;
300 	struct addrinfo *ailist, *ai, aihints;
301 	char serv[NI_MAXSERV];
302 
303 	snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT);
304 	memset(&aihints, 0, sizeof(struct addrinfo));
305 	aihints.ai_socktype = SOCK_STREAM;
306 	aihints.ai_protocol = IPPROTO_TCP;
307 	error = getaddrinfo(host, serv, &aihints, &ailist);
308 	if (error) {
309 		DPRINTF("%s: %s\n", host, gai_strerror(error));
310 		return 0;
311 	}
312 	s = -1;
313 	for (ai = ailist; ai != NULL; ai = ai->ai_next) {
314 		s = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC,
315 		    ai->ai_protocol);
316 		if (s < 0) {
317 			DPERROR("socket");
318 			continue;
319 		}
320 	restart:
321 		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
322 			if (errno == EINTR)
323 				goto restart;
324 			DPERROR("connect");
325 			close(s);
326 			s = -1;
327 			continue;
328 		}
329 		break;
330 	}
331 	freeaddrinfo(ailist);
332 	if (s < 0)
333 		return 0;
334 	opt = 1;
335 	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) < 0) {
336 		DPERROR("setsockopt");
337 		close(s);
338 		return 0;
339 	}
340 	hdl->fd = s;
341 	return 1;
342 }
343 
344 static int
345 aucat_connect_un(struct aucat *hdl, unsigned int unit)
346 {
347 	struct sockaddr_un ca;
348 	socklen_t len = sizeof(struct sockaddr_un);
349 	uid_t uid;
350 	int s;
351 
352 	uid = geteuid();
353 	snprintf(ca.sun_path, sizeof(ca.sun_path),
354 	    SOCKPATH_DIR "-%u/" SOCKPATH_FILE "%u", uid, unit);
355 	ca.sun_family = AF_UNIX;
356 	s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
357 	if (s < 0)
358 		return 0;
359 	while (connect(s, (struct sockaddr *)&ca, len) < 0) {
360 		if (errno == EINTR)
361 			continue;
362 		DPERROR(ca.sun_path);
363 		/* try shared server */
364 		snprintf(ca.sun_path, sizeof(ca.sun_path),
365 		    SOCKPATH_DIR "/" SOCKPATH_FILE "%u", unit);
366 		while (connect(s, (struct sockaddr *)&ca, len) < 0) {
367 			if (errno == EINTR)
368 				continue;
369 			DPERROR(ca.sun_path);
370 			close(s);
371 			return 0;
372 		}
373 		break;
374 	}
375 	hdl->fd = s;
376 	DPRINTFN(2, "%s: connected\n", ca.sun_path);
377 	return 1;
378 }
379 
380 static const char *
381 parsedev(const char *str, unsigned int *rval)
382 {
383 	const char *p = str;
384 	unsigned int val;
385 
386 	for (val = 0; *p >= '0' && *p <= '9'; p++) {
387 		val = 10 * val + (*p - '0');
388 		if (val >= 16) {
389 			DPRINTF("%s: number too large\n", str);
390 			return NULL;
391 		}
392 	}
393 	if (p == str) {
394 		DPRINTF("%s: number expected\n", str);
395 		return NULL;
396 	}
397 	*rval = val;
398 	return p;
399 }
400 
401 static const char *
402 parsestr(const char *str, char *rstr, unsigned int max)
403 {
404 	const char *p = str;
405 
406 	while (*p != '\0' && *p != ',' && *p != '/') {
407 		if (--max == 0) {
408 			DPRINTF("%s: string too long\n", str);
409 			return NULL;
410 		}
411 		*rstr++ = *p++;
412 	}
413 	if (str == p) {
414 		DPRINTF("%s: string expected\n", str);
415 		return NULL;
416 	}
417 	*rstr = '\0';
418 	return p;
419 }
420 
421 int
422 _aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
423 {
424 	extern char *__progname;
425 	int eof;
426 	char host[NI_MAXHOST], opt[AMSG_OPTMAX];
427 	const char *p;
428 	unsigned int unit, devnum, type;
429 
430 	if ((p = _sndio_parsetype(str, "snd")) != NULL)
431 		type = 0;
432 	else if ((p = _sndio_parsetype(str, "midithru")) != NULL)
433 		type = 1;
434 	else if ((p = _sndio_parsetype(str, "midi")) != NULL)
435 		type = 2;
436 	else {
437 		DPRINTF("%s: unsupported device type\n", str);
438 		return -1;
439 	}
440 	if (*p == '@') {
441 		p = parsestr(++p, host, NI_MAXHOST);
442 		if (p == NULL)
443 			return 0;
444 	} else
445 		*host = '\0';
446 	if (*p == ',') {
447 		p = parsedev(++p, &unit);
448 		if (p == NULL)
449 			return 0;
450 	} else
451 		unit = 0;
452 	if (*p != '/') {
453 		DPRINTF("%s: '/' expected\n", str);
454 		return 0;
455 	}
456 	p = parsedev(++p, &devnum);
457 	if (p == NULL)
458 		return 0;
459 	if (*p == '.') {
460 		p = parsestr(++p, opt, AMSG_OPTMAX);
461 		if (p == NULL)
462 			return 0;
463 	} else
464 		strlcpy(opt, "default", AMSG_OPTMAX);
465 	if (*p != '\0') {
466 		DPRINTF("%s: junk at end of dev name\n", p);
467 		return 0;
468 	}
469 	devnum += type * 16; /* XXX */
470 	DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n",
471 	    host, unit, devnum, opt);
472 	if (host[0] != '\0') {
473 		if (!aucat_connect_tcp(hdl, host, unit))
474 			return 0;
475 	} else {
476 		if (!aucat_connect_un(hdl, unit))
477 			return 0;
478 	}
479 	hdl->rstate = RSTATE_MSG;
480 	hdl->rtodo = sizeof(struct amsg);
481 	hdl->wstate = WSTATE_IDLE;
482 	hdl->wtodo = 0xdeadbeef;
483 	hdl->maxwrite = 0;
484 
485 	/*
486 	 * say hello to server
487 	 */
488 	AMSG_INIT(&hdl->wmsg);
489 	hdl->wmsg.cmd = htonl(AMSG_AUTH);
490 	if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie))
491 		goto bad_connect;
492 	hdl->wtodo = sizeof(struct amsg);
493 	if (!_aucat_wmsg(hdl, &eof))
494 		goto bad_connect;
495 	AMSG_INIT(&hdl->wmsg);
496 	hdl->wmsg.cmd = htonl(AMSG_HELLO);
497 	hdl->wmsg.u.hello.version = AMSG_VERSION;
498 	hdl->wmsg.u.hello.mode = htons(mode);
499 	hdl->wmsg.u.hello.devnum = devnum;
500 	strlcpy(hdl->wmsg.u.hello.who, __progname,
501 	    sizeof(hdl->wmsg.u.hello.who));
502 	strlcpy(hdl->wmsg.u.hello.opt, opt,
503 	    sizeof(hdl->wmsg.u.hello.opt));
504 	hdl->wtodo = sizeof(struct amsg);
505 	if (!_aucat_wmsg(hdl, &eof))
506 		goto bad_connect;
507 	hdl->rtodo = sizeof(struct amsg);
508 	if (!_aucat_rmsg(hdl, &eof)) {
509 		DPRINTF("aucat_init: mode refused\n");
510 		goto bad_connect;
511 	}
512 	if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) {
513 		DPRINTF("aucat_init: protocol err\n");
514 		goto bad_connect;
515 	}
516 	return 1;
517  bad_connect:
518 	while (close(hdl->fd) < 0 && errno == EINTR)
519 		; /* retry */
520 	return 0;
521 }
522 
523 void
524 _aucat_close(struct aucat *hdl, int eof)
525 {
526 	char dummy[1];
527 
528 	if (!eof) {
529 		AMSG_INIT(&hdl->wmsg);
530 		hdl->wmsg.cmd = htonl(AMSG_BYE);
531 		hdl->wtodo = sizeof(struct amsg);
532 		if (!_aucat_wmsg(hdl, &eof))
533 			goto bad_close;
534 		while (read(hdl->fd, dummy, 1) < 0 && errno == EINTR)
535 			; /* nothing */
536 	}
537  bad_close:
538 	while (close(hdl->fd) < 0 && errno == EINTR)
539 		; /* nothing */
540 }
541 
542 int
543 _aucat_setfl(struct aucat *hdl, int nbio, int *eof)
544 {
545 	if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) < 0) {
546 		DPERROR("_aucat_setfl: fcntl");
547 		*eof = 1;
548 		return 0;
549 	}
550 	return 1;
551 }
552 
553 int
554 _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events)
555 {
556 	if (hdl->rstate == RSTATE_MSG)
557 		events |= POLLIN;
558 	pfd->fd = hdl->fd;
559 	pfd->events = events;
560 	return 1;
561 }
562 
563 int
564 _aucat_revents(struct aucat *hdl, struct pollfd *pfd)
565 {
566 	int revents = pfd->revents;
567 
568 	DPRINTFN(2, "_aucat_revents: revents: %x\n", revents);
569 	return revents;
570 }
571