xref: /openbsd-src/lib/libsndio/aucat.c (revision 792569533f15de8006b365b67d4ab085eed12d7d)
1*79256953Sratchov /*	$OpenBSD: aucat.c,v 1.79 2021/11/07 20:51:47 ratchov Exp $	*/
282bfc72bSratchov /*
382bfc72bSratchov  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
482bfc72bSratchov  *
582bfc72bSratchov  * Permission to use, copy, modify, and distribute this software for any
682bfc72bSratchov  * purpose with or without fee is hereby granted, provided that the above
782bfc72bSratchov  * copyright notice and this permission notice appear in all copies.
882bfc72bSratchov  *
982bfc72bSratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1082bfc72bSratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1182bfc72bSratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1282bfc72bSratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1382bfc72bSratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1482bfc72bSratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1582bfc72bSratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1682bfc72bSratchov  */
1782bfc72bSratchov 
1882bfc72bSratchov #include <sys/types.h>
1982bfc72bSratchov #include <sys/socket.h>
20edc7bd3bSratchov #include <sys/stat.h>
2182bfc72bSratchov #include <sys/un.h>
2282bfc72bSratchov 
23edc7bd3bSratchov #include <netinet/in.h>
24ad38cbdeSratchov #include <netinet/tcp.h>
25edc7bd3bSratchov #include <netdb.h>
26edc7bd3bSratchov 
2782bfc72bSratchov #include <errno.h>
2882bfc72bSratchov #include <fcntl.h>
29edc7bd3bSratchov #include <limits.h>
3082bfc72bSratchov #include <poll.h>
3182bfc72bSratchov #include <stdio.h>
3282bfc72bSratchov #include <stdlib.h>
3382bfc72bSratchov #include <string.h>
3482bfc72bSratchov #include <unistd.h>
3582bfc72bSratchov 
3682bfc72bSratchov #include "aucat.h"
3782bfc72bSratchov #include "debug.h"
3882bfc72bSratchov 
39fcda7a7eSratchov 
4082bfc72bSratchov /*
4182bfc72bSratchov  * read a message, return 0 if not completed
4282bfc72bSratchov  */
4382bfc72bSratchov int
_aucat_rmsg(struct aucat * hdl,int * eof)44d418f94bSratchov _aucat_rmsg(struct aucat *hdl, int *eof)
4582bfc72bSratchov {
4682bfc72bSratchov 	ssize_t n;
4782bfc72bSratchov 	unsigned char *data;
4882bfc72bSratchov 
4982bfc72bSratchov 	if (hdl->rstate != RSTATE_MSG) {
50d418f94bSratchov 		DPRINTF("_aucat_rmsg: bad state\n");
5182bfc72bSratchov 		abort();
5282bfc72bSratchov 	}
5382bfc72bSratchov 	while (hdl->rtodo > 0) {
5482bfc72bSratchov 		data = (unsigned char *)&hdl->rmsg;
5582bfc72bSratchov 		data += sizeof(struct amsg) - hdl->rtodo;
5695d14907Sratchov 		while ((n = read(hdl->fd, data, hdl->rtodo)) == -1) {
5782bfc72bSratchov 			if (errno == EINTR)
5882bfc72bSratchov 				continue;
5982bfc72bSratchov 			if (errno != EAGAIN) {
6082bfc72bSratchov 				*eof = 1;
61d418f94bSratchov 				DPERROR("_aucat_rmsg: read");
6282bfc72bSratchov 			}
6382bfc72bSratchov 			return 0;
6482bfc72bSratchov 		}
6582bfc72bSratchov 		if (n == 0) {
66d418f94bSratchov 			DPRINTF("_aucat_rmsg: eof\n");
6782bfc72bSratchov 			*eof = 1;
6882bfc72bSratchov 			return 0;
6982bfc72bSratchov 		}
7082bfc72bSratchov 		hdl->rtodo -= n;
7182bfc72bSratchov 	}
72b5fec221Sratchov 	if (ntohl(hdl->rmsg.cmd) == AMSG_DATA) {
73b5fec221Sratchov 		hdl->rtodo = ntohl(hdl->rmsg.u.data.size);
7482bfc72bSratchov 		hdl->rstate = RSTATE_DATA;
7582bfc72bSratchov 	} else {
7682bfc72bSratchov 		hdl->rtodo = sizeof(struct amsg);
7782bfc72bSratchov 		hdl->rstate = RSTATE_MSG;
7882bfc72bSratchov 	}
7982bfc72bSratchov 	return 1;
8082bfc72bSratchov }
8182bfc72bSratchov 
8282bfc72bSratchov /*
8382bfc72bSratchov  * write a message, return 0 if not completed
8482bfc72bSratchov  */
8582bfc72bSratchov int
_aucat_wmsg(struct aucat * hdl,int * eof)86d418f94bSratchov _aucat_wmsg(struct aucat *hdl, int *eof)
8782bfc72bSratchov {
8882bfc72bSratchov 	ssize_t n;
8982bfc72bSratchov 	unsigned char *data;
9082bfc72bSratchov 
91b90e880fSjsg 	if (hdl->wstate == WSTATE_IDLE) {
9282bfc72bSratchov 		hdl->wstate = WSTATE_MSG;
9382bfc72bSratchov 		hdl->wtodo = sizeof(struct amsg);
94b90e880fSjsg 	}
9582bfc72bSratchov 	if (hdl->wstate != WSTATE_MSG) {
96d418f94bSratchov 		DPRINTF("_aucat_wmsg: bad state\n");
9782bfc72bSratchov 		abort();
9882bfc72bSratchov 	}
9982bfc72bSratchov 	while (hdl->wtodo > 0) {
10082bfc72bSratchov 		data = (unsigned char *)&hdl->wmsg;
10182bfc72bSratchov 		data += sizeof(struct amsg) - hdl->wtodo;
10295d14907Sratchov 		while ((n = write(hdl->fd, data, hdl->wtodo)) == -1) {
10382bfc72bSratchov 			if (errno == EINTR)
10482bfc72bSratchov 				continue;
10582bfc72bSratchov 			if (errno != EAGAIN) {
10682bfc72bSratchov 				*eof = 1;
107d418f94bSratchov 				DPERROR("_aucat_wmsg: write");
10882bfc72bSratchov 			}
10982bfc72bSratchov 			return 0;
11082bfc72bSratchov 		}
11182bfc72bSratchov 		hdl->wtodo -= n;
11282bfc72bSratchov 	}
113b5fec221Sratchov 	if (ntohl(hdl->wmsg.cmd) == AMSG_DATA) {
114b5fec221Sratchov 		hdl->wtodo = ntohl(hdl->wmsg.u.data.size);
11582bfc72bSratchov 		hdl->wstate = WSTATE_DATA;
11682bfc72bSratchov 	} else {
11782bfc72bSratchov 		hdl->wtodo = 0xdeadbeef;
11882bfc72bSratchov 		hdl->wstate = WSTATE_IDLE;
11982bfc72bSratchov 	}
12082bfc72bSratchov 	return 1;
12182bfc72bSratchov }
12282bfc72bSratchov 
12382bfc72bSratchov size_t
_aucat_rdata(struct aucat * hdl,void * buf,size_t len,int * eof)124d418f94bSratchov _aucat_rdata(struct aucat *hdl, void *buf, size_t len, int *eof)
12582bfc72bSratchov {
12682bfc72bSratchov 	ssize_t n;
12782bfc72bSratchov 
12882bfc72bSratchov 	if (hdl->rstate != RSTATE_DATA) {
129d418f94bSratchov 		DPRINTF("_aucat_rdata: bad state\n");
13082bfc72bSratchov 		abort();
13182bfc72bSratchov 	}
13282bfc72bSratchov 	if (len > hdl->rtodo)
13382bfc72bSratchov 		len = hdl->rtodo;
13495d14907Sratchov 	while ((n = read(hdl->fd, buf, len)) == -1) {
13582bfc72bSratchov 		if (errno == EINTR)
13682bfc72bSratchov 			continue;
13782bfc72bSratchov 		if (errno != EAGAIN) {
13882bfc72bSratchov 			*eof = 1;
139d418f94bSratchov 			DPERROR("_aucat_rdata: read");
14082bfc72bSratchov 		}
14182bfc72bSratchov 		return 0;
14282bfc72bSratchov 	}
14382bfc72bSratchov 	if (n == 0) {
144d418f94bSratchov 		DPRINTF("_aucat_rdata: eof\n");
14582bfc72bSratchov 		*eof = 1;
14682bfc72bSratchov 		return 0;
14782bfc72bSratchov 	}
14882bfc72bSratchov 	hdl->rtodo -= n;
14982bfc72bSratchov 	if (hdl->rtodo == 0) {
15082bfc72bSratchov 		hdl->rstate = RSTATE_MSG;
15182bfc72bSratchov 		hdl->rtodo = sizeof(struct amsg);
15282bfc72bSratchov 	}
153d418f94bSratchov 	DPRINTFN(2, "_aucat_rdata: read: n = %zd\n", n);
15482bfc72bSratchov 	return n;
15582bfc72bSratchov }
15682bfc72bSratchov 
15782bfc72bSratchov size_t
_aucat_wdata(struct aucat * hdl,const void * buf,size_t len,unsigned int wbpf,int * eof)158d418f94bSratchov _aucat_wdata(struct aucat *hdl, const void *buf, size_t len,
1597207b069Sratchov    unsigned int wbpf, int *eof)
16082bfc72bSratchov {
16182bfc72bSratchov 	ssize_t n;
1622e63c6e8Sratchov 	size_t datasize;
16382bfc72bSratchov 
16482bfc72bSratchov 	switch (hdl->wstate) {
16582bfc72bSratchov 	case WSTATE_IDLE:
1662e63c6e8Sratchov 		datasize = len;
1672e63c6e8Sratchov 		if (datasize > AMSG_DATAMAX)
1682e63c6e8Sratchov 			datasize = AMSG_DATAMAX;
1692e63c6e8Sratchov 		datasize -= datasize % wbpf;
1702e63c6e8Sratchov 		if (datasize == 0)
1712e63c6e8Sratchov 			datasize = wbpf;
172b5fec221Sratchov 		hdl->wmsg.cmd = htonl(AMSG_DATA);
1732e63c6e8Sratchov 		hdl->wmsg.u.data.size = htonl(datasize);
17482bfc72bSratchov 		hdl->wtodo = sizeof(struct amsg);
17582bfc72bSratchov 		hdl->wstate = WSTATE_MSG;
17682bfc72bSratchov 		/* FALLTHROUGH */
17782bfc72bSratchov 	case WSTATE_MSG:
178d418f94bSratchov 		if (!_aucat_wmsg(hdl, eof))
17982bfc72bSratchov 			return 0;
18082bfc72bSratchov 	}
18182bfc72bSratchov 	if (len > hdl->wtodo)
18282bfc72bSratchov 		len = hdl->wtodo;
18382bfc72bSratchov 	if (len == 0) {
184d418f94bSratchov 		DPRINTF("_aucat_wdata: len == 0\n");
18582bfc72bSratchov 		abort();
18682bfc72bSratchov 	}
18795d14907Sratchov 	while ((n = write(hdl->fd, buf, len)) == -1) {
18882bfc72bSratchov 		if (errno == EINTR)
18982bfc72bSratchov 			continue;
19082bfc72bSratchov 		if (errno != EAGAIN) {
19182bfc72bSratchov 			*eof = 1;
192d418f94bSratchov 			DPERROR("_aucat_wdata: write");
19382bfc72bSratchov 		}
19482bfc72bSratchov 		return 0;
19582bfc72bSratchov 	}
196d418f94bSratchov 	DPRINTFN(2, "_aucat_wdata: write: n = %zd\n", n);
19782bfc72bSratchov 	hdl->wtodo -= n;
19882bfc72bSratchov 	if (hdl->wtodo == 0) {
19982bfc72bSratchov 		hdl->wstate = WSTATE_IDLE;
20082bfc72bSratchov 		hdl->wtodo = 0xdeadbeef;
20182bfc72bSratchov 	}
20282bfc72bSratchov 	return n;
20382bfc72bSratchov }
20482bfc72bSratchov 
205122afd1dSderaadt static int
aucat_mkcookie(unsigned char * cookie)206edc7bd3bSratchov aucat_mkcookie(unsigned char *cookie)
207edc7bd3bSratchov {
208096da69dSratchov #define COOKIE_DIR	"/.sndio"
209096da69dSratchov #define COOKIE_SUFFIX	"/.sndio/cookie"
21059d9d051Sratchov #define TEMPL_SUFFIX	".XXXXXXXX"
211edc7bd3bSratchov 	struct stat sb;
2129790449aSratchov 	char *home, *path = NULL, *tmp = NULL;
2139790449aSratchov 	size_t home_len, path_len;
2149790449aSratchov 	int fd, len;
2159790449aSratchov 
2169790449aSratchov 	/* please gcc */
2179790449aSratchov 	path_len = 0xdeadbeef;
218edc7bd3bSratchov 
219edc7bd3bSratchov 	/*
220edc7bd3bSratchov 	 * try to load the cookie
221edc7bd3bSratchov 	 */
222217983a9Sratchov 	home = issetugid() ? NULL : getenv("HOME");
223217983a9Sratchov 	if (home == NULL)
224edc7bd3bSratchov 		goto bad_gen;
2259790449aSratchov 	home_len = strlen(home);
2269790449aSratchov 	path = malloc(home_len + sizeof(COOKIE_SUFFIX));
2279790449aSratchov 	if (path == NULL)
2289790449aSratchov 		goto bad_gen;
2299790449aSratchov 	memcpy(path, home, home_len);
2309790449aSratchov 	memcpy(path + home_len, COOKIE_SUFFIX, sizeof(COOKIE_SUFFIX));
2319790449aSratchov 	path_len = home_len + sizeof(COOKIE_SUFFIX) - 1;
232edc7bd3bSratchov 	fd = open(path, O_RDONLY);
23395d14907Sratchov 	if (fd == -1) {
234edc7bd3bSratchov 		if (errno != ENOENT)
235edc7bd3bSratchov 			DPERROR(path);
236edc7bd3bSratchov 		goto bad_gen;
237edc7bd3bSratchov 	}
23895d14907Sratchov 	if (fstat(fd, &sb) == -1) {
239edc7bd3bSratchov 		DPERROR(path);
240edc7bd3bSratchov 		goto bad_close;
241edc7bd3bSratchov 	}
242edc7bd3bSratchov 	if (sb.st_mode & 0077) {
243edc7bd3bSratchov 		DPRINTF("%s has wrong permissions\n", path);
244edc7bd3bSratchov 		goto bad_close;
245edc7bd3bSratchov 	}
246edc7bd3bSratchov 	len = read(fd, cookie, AMSG_COOKIELEN);
24795d14907Sratchov 	if (len == -1) {
248edc7bd3bSratchov 		DPERROR(path);
249edc7bd3bSratchov 		goto bad_close;
250edc7bd3bSratchov 	}
251edc7bd3bSratchov 	if (len != AMSG_COOKIELEN) {
252edc7bd3bSratchov 		DPRINTF("%s: short read\n", path);
253edc7bd3bSratchov 		goto bad_close;
254edc7bd3bSratchov 	}
255edc7bd3bSratchov 	close(fd);
2569790449aSratchov 	goto done;
257edc7bd3bSratchov bad_close:
258edc7bd3bSratchov 	close(fd);
259edc7bd3bSratchov bad_gen:
260edc7bd3bSratchov 	/*
261edc7bd3bSratchov 	 * generate a new cookie
262edc7bd3bSratchov 	 */
263edc7bd3bSratchov 	arc4random_buf(cookie, AMSG_COOKIELEN);
264edc7bd3bSratchov 
265edc7bd3bSratchov 	/*
266edc7bd3bSratchov 	 * try to save the cookie
267edc7bd3bSratchov 	 */
268096da69dSratchov 
269217983a9Sratchov 	if (home == NULL)
2709790449aSratchov 		goto done;
2719790449aSratchov 	tmp = malloc(path_len + sizeof(TEMPL_SUFFIX));
2729790449aSratchov 	if (tmp == NULL)
2739790449aSratchov 		goto done;
274096da69dSratchov 
275096da69dSratchov 	/* create ~/.sndio directory */
276096da69dSratchov 	memcpy(tmp, home, home_len);
277096da69dSratchov 	memcpy(tmp + home_len, COOKIE_DIR, sizeof(COOKIE_DIR));
27895d14907Sratchov 	if (mkdir(tmp, 0755) == -1 && errno != EEXIST)
279096da69dSratchov 		goto done;
280096da69dSratchov 
281096da69dSratchov 	/* create cookie file in it */
2829790449aSratchov 	memcpy(tmp, path, path_len);
2839790449aSratchov 	memcpy(tmp + path_len, TEMPL_SUFFIX, sizeof(TEMPL_SUFFIX));
284edc7bd3bSratchov 	fd = mkstemp(tmp);
285c2d43ecaSderaadt 	if (fd == -1) {
286edc7bd3bSratchov 		DPERROR(tmp);
2879790449aSratchov 		goto done;
288edc7bd3bSratchov 	}
28995d14907Sratchov 	if (write(fd, cookie, AMSG_COOKIELEN) == -1) {
290edc7bd3bSratchov 		DPERROR(tmp);
291edc7bd3bSratchov 		unlink(tmp);
292edc7bd3bSratchov 		close(fd);
2939790449aSratchov 		goto done;
294edc7bd3bSratchov 	}
295edc7bd3bSratchov 	close(fd);
29695d14907Sratchov 	if (rename(tmp, path) == -1) {
297edc7bd3bSratchov 		DPERROR(tmp);
298edc7bd3bSratchov 		unlink(tmp);
299edc7bd3bSratchov 	}
3009790449aSratchov done:
3019790449aSratchov 	free(tmp);
3029790449aSratchov 	free(path);
303edc7bd3bSratchov 	return 1;
304edc7bd3bSratchov }
305edc7bd3bSratchov 
306122afd1dSderaadt static int
aucat_connect_tcp(struct aucat * hdl,char * host,unsigned int unit)3077207b069Sratchov aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit)
308edc7bd3bSratchov {
309ad38cbdeSratchov 	int s, error, opt;
310edc7bd3bSratchov 	struct addrinfo *ailist, *ai, aihints;
311edc7bd3bSratchov 	char serv[NI_MAXSERV];
312edc7bd3bSratchov 
313b3956098Sratchov 	snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT);
314edc7bd3bSratchov 	memset(&aihints, 0, sizeof(struct addrinfo));
315edc7bd3bSratchov 	aihints.ai_socktype = SOCK_STREAM;
316edc7bd3bSratchov 	aihints.ai_protocol = IPPROTO_TCP;
317edc7bd3bSratchov 	error = getaddrinfo(host, serv, &aihints, &ailist);
318edc7bd3bSratchov 	if (error) {
319edc7bd3bSratchov 		DPRINTF("%s: %s\n", host, gai_strerror(error));
320edc7bd3bSratchov 		return 0;
321edc7bd3bSratchov 	}
322edc7bd3bSratchov 	s = -1;
323edc7bd3bSratchov 	for (ai = ailist; ai != NULL; ai = ai->ai_next) {
32480ae68c4Sguenther 		s = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC,
32580ae68c4Sguenther 		    ai->ai_protocol);
32695d14907Sratchov 		if (s == -1) {
327edc7bd3bSratchov 			DPERROR("socket");
328edc7bd3bSratchov 			continue;
329edc7bd3bSratchov 		}
330900581e9Sratchov 	restart:
33195d14907Sratchov 		if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) {
332900581e9Sratchov 			if (errno == EINTR)
333900581e9Sratchov 				goto restart;
334edc7bd3bSratchov 			DPERROR("connect");
335edc7bd3bSratchov 			close(s);
336edc7bd3bSratchov 			s = -1;
337edc7bd3bSratchov 			continue;
338edc7bd3bSratchov 		}
339edc7bd3bSratchov 		break;
340edc7bd3bSratchov 	}
341edc7bd3bSratchov 	freeaddrinfo(ailist);
34295d14907Sratchov 	if (s == -1)
343edc7bd3bSratchov 		return 0;
344ad38cbdeSratchov 	opt = 1;
34595d14907Sratchov 	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) == -1) {
346ad38cbdeSratchov 		DPERROR("setsockopt");
347ad38cbdeSratchov 		close(s);
348ad38cbdeSratchov 		return 0;
349ad38cbdeSratchov 	}
350edc7bd3bSratchov 	hdl->fd = s;
351edc7bd3bSratchov 	return 1;
352edc7bd3bSratchov }
353edc7bd3bSratchov 
354122afd1dSderaadt static int
aucat_connect_un(struct aucat * hdl,unsigned int unit)3557207b069Sratchov aucat_connect_un(struct aucat *hdl, unsigned int unit)
35682bfc72bSratchov {
35782bfc72bSratchov 	struct sockaddr_un ca;
35882bfc72bSratchov 	socklen_t len = sizeof(struct sockaddr_un);
35982bfc72bSratchov 	uid_t uid;
3607e5c91daSratchov 	int s;
3617e5c91daSratchov 
3627e5c91daSratchov 	uid = geteuid();
3637e5c91daSratchov 	snprintf(ca.sun_path, sizeof(ca.sun_path),
364dadd32d9Sratchov 	    SOCKPATH_DIR "-%u/" SOCKPATH_FILE "%u", uid, unit);
3657e5c91daSratchov 	ca.sun_family = AF_UNIX;
36680ae68c4Sguenther 	s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
36795d14907Sratchov 	if (s == -1)
3687e5c91daSratchov 		return 0;
36995d14907Sratchov 	while (connect(s, (struct sockaddr *)&ca, len) == -1) {
3707e5c91daSratchov 		if (errno == EINTR)
3717e5c91daSratchov 			continue;
3727e5c91daSratchov 		DPERROR(ca.sun_path);
3737e5c91daSratchov 		/* try shared server */
3747e5c91daSratchov 		snprintf(ca.sun_path, sizeof(ca.sun_path),
375dadd32d9Sratchov 		    SOCKPATH_DIR "/" SOCKPATH_FILE "%u", unit);
37695d14907Sratchov 		while (connect(s, (struct sockaddr *)&ca, len) == -1) {
3777e5c91daSratchov 			if (errno == EINTR)
3787e5c91daSratchov 				continue;
3797e5c91daSratchov 			DPERROR(ca.sun_path);
3807e5c91daSratchov 			close(s);
3817e5c91daSratchov 			return 0;
3827e5c91daSratchov 		}
3837e5c91daSratchov 		break;
3847e5c91daSratchov 	}
3857e5c91daSratchov 	hdl->fd = s;
386e791f9efSratchov 	DPRINTFN(2, "%s: connected\n", ca.sun_path);
3877e5c91daSratchov 	return 1;
3887e5c91daSratchov }
3897e5c91daSratchov 
390b3956098Sratchov static const char *
parsestr(const char * str,char * rstr,unsigned int max)3917207b069Sratchov parsestr(const char *str, char *rstr, unsigned int max)
392b3956098Sratchov {
393b3956098Sratchov 	const char *p = str;
394b3956098Sratchov 
395b3956098Sratchov 	while (*p != '\0' && *p != ',' && *p != '/') {
396b3956098Sratchov 		if (--max == 0) {
397b3956098Sratchov 			DPRINTF("%s: string too long\n", str);
398b3956098Sratchov 			return NULL;
399b3956098Sratchov 		}
400b3956098Sratchov 		*rstr++ = *p++;
401b3956098Sratchov 	}
402b3956098Sratchov 	if (str == p) {
403b3956098Sratchov 		DPRINTF("%s: string expected\n", str);
404b3956098Sratchov 		return NULL;
405b3956098Sratchov 	}
406b3956098Sratchov 	*rstr = '\0';
407b3956098Sratchov 	return p;
408b3956098Sratchov }
409b3956098Sratchov 
4107e5c91daSratchov int
_aucat_open(struct aucat * hdl,const char * str,unsigned int mode)411163486f8Sratchov _aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
4127e5c91daSratchov {
4137e5c91daSratchov 	extern char *__progname;
414b3956098Sratchov 	int eof;
415b3956098Sratchov 	char host[NI_MAXHOST], opt[AMSG_OPTMAX];
416163486f8Sratchov 	const char *p;
417163486f8Sratchov 	unsigned int unit, devnum, type;
41882bfc72bSratchov 
419163486f8Sratchov 	if ((p = _sndio_parsetype(str, "snd")) != NULL)
420163486f8Sratchov 		type = 0;
421163486f8Sratchov 	else if ((p = _sndio_parsetype(str, "midithru")) != NULL)
422163486f8Sratchov 		type = 1;
423163486f8Sratchov 	else if ((p = _sndio_parsetype(str, "midi")) != NULL)
424163486f8Sratchov 		type = 2;
425163486f8Sratchov 	else {
426163486f8Sratchov 		DPRINTF("%s: unsupported device type\n", str);
427163486f8Sratchov 		return -1;
428163486f8Sratchov 	}
429b3956098Sratchov 	if (*p == '@') {
430b3956098Sratchov 		p = parsestr(++p, host, NI_MAXHOST);
431b3956098Sratchov 		if (p == NULL)
432b3956098Sratchov 			return 0;
433b3956098Sratchov 	} else
434b3956098Sratchov 		*host = '\0';
435b3956098Sratchov 	if (*p == ',') {
4368522ebc4Smiko 		p = _sndio_parsenum(++p, &unit, 15);
437b3956098Sratchov 		if (p == NULL)
438b3956098Sratchov 			return 0;
439b3956098Sratchov 	} else
440b3956098Sratchov 		unit = 0;
441163486f8Sratchov 	if (*p != '/') {
442b3956098Sratchov 		DPRINTF("%s: '/' expected\n", str);
443edc7bd3bSratchov 		return 0;
444edc7bd3bSratchov 	}
44536355b88Sratchov 	p++;
44636355b88Sratchov 	if (type == 0) {
447*79256953Sratchov 		if (*p < '0' || *p > '9') {
44836355b88Sratchov 			devnum = AMSG_NODEV;
44936355b88Sratchov 			p = parsestr(p, opt, AMSG_OPTMAX);
450b3956098Sratchov 			if (p == NULL)
451b3956098Sratchov 				return 0;
45236355b88Sratchov 		} else {
45336355b88Sratchov 			p = _sndio_parsenum(p, &devnum, 15);
454b3956098Sratchov 			if (p == NULL)
455b3956098Sratchov 				return 0;
456*79256953Sratchov 			if (*p == '.') {
457*79256953Sratchov 				p = parsestr(++p, opt, AMSG_OPTMAX);
458*79256953Sratchov 				if (p == NULL)
459*79256953Sratchov 					return 0;
460*79256953Sratchov 			} else
461*79256953Sratchov 				strlcpy(opt, "default", AMSG_OPTMAX);
462*79256953Sratchov 		}
463*79256953Sratchov 	} else {
464*79256953Sratchov 		p = _sndio_parsenum(p, &devnum, 15);
465*79256953Sratchov 		if (p == NULL)
466*79256953Sratchov 			return 0;
46736355b88Sratchov 		memset(opt, 0, sizeof(opt));
46836355b88Sratchov 	}
469b3956098Sratchov 	if (*p != '\0') {
470b3956098Sratchov 		DPRINTF("%s: junk at end of dev name\n", p);
47182bfc72bSratchov 		return 0;
47282bfc72bSratchov 	}
47387bc9f6aSratchov 	devnum += type * 16; /* XXX */
474e791f9efSratchov 	DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n",
475b3956098Sratchov 	    host, unit, devnum, opt);
476b3956098Sratchov 	if (host[0] != '\0') {
4778825335aSratchov 		if (!aucat_connect_tcp(hdl, host, unit))
478edc7bd3bSratchov 			return 0;
479edc7bd3bSratchov 	} else {
4808825335aSratchov 		if (!aucat_connect_un(hdl, unit))
48182bfc72bSratchov 			return 0;
482edc7bd3bSratchov 	}
48382bfc72bSratchov 	hdl->rstate = RSTATE_MSG;
48482bfc72bSratchov 	hdl->rtodo = sizeof(struct amsg);
48582bfc72bSratchov 	hdl->wstate = WSTATE_IDLE;
48682bfc72bSratchov 	hdl->wtodo = 0xdeadbeef;
4875ffd5747Sratchov 	hdl->maxwrite = 0;
48882bfc72bSratchov 
48982bfc72bSratchov 	/*
49082bfc72bSratchov 	 * say hello to server
49182bfc72bSratchov 	 */
49282bfc72bSratchov 	AMSG_INIT(&hdl->wmsg);
493b5fec221Sratchov 	hdl->wmsg.cmd = htonl(AMSG_AUTH);
494edc7bd3bSratchov 	if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie))
495edc7bd3bSratchov 		goto bad_connect;
496edc7bd3bSratchov 	hdl->wtodo = sizeof(struct amsg);
497d418f94bSratchov 	if (!_aucat_wmsg(hdl, &eof))
498edc7bd3bSratchov 		goto bad_connect;
499edc7bd3bSratchov 	AMSG_INIT(&hdl->wmsg);
500b5fec221Sratchov 	hdl->wmsg.cmd = htonl(AMSG_HELLO);
50182bfc72bSratchov 	hdl->wmsg.u.hello.version = AMSG_VERSION;
502b5fec221Sratchov 	hdl->wmsg.u.hello.mode = htons(mode);
503b3956098Sratchov 	hdl->wmsg.u.hello.devnum = devnum;
5042988007fSratchov 	hdl->wmsg.u.hello.id = htonl(getpid());
50582bfc72bSratchov 	strlcpy(hdl->wmsg.u.hello.who, __progname,
50682bfc72bSratchov 	    sizeof(hdl->wmsg.u.hello.who));
50782bfc72bSratchov 	strlcpy(hdl->wmsg.u.hello.opt, opt,
50882bfc72bSratchov 	    sizeof(hdl->wmsg.u.hello.opt));
50982bfc72bSratchov 	hdl->wtodo = sizeof(struct amsg);
510d418f94bSratchov 	if (!_aucat_wmsg(hdl, &eof))
51182bfc72bSratchov 		goto bad_connect;
51282bfc72bSratchov 	hdl->rtodo = sizeof(struct amsg);
513d418f94bSratchov 	if (!_aucat_rmsg(hdl, &eof)) {
51482bfc72bSratchov 		DPRINTF("aucat_init: mode refused\n");
51582bfc72bSratchov 		goto bad_connect;
51682bfc72bSratchov 	}
517b5fec221Sratchov 	if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) {
51882bfc72bSratchov 		DPRINTF("aucat_init: protocol err\n");
51982bfc72bSratchov 		goto bad_connect;
52082bfc72bSratchov 	}
52182bfc72bSratchov 	return 1;
52282bfc72bSratchov  bad_connect:
52395d14907Sratchov 	while (close(hdl->fd) == -1 && errno == EINTR)
52482bfc72bSratchov 		; /* retry */
52582bfc72bSratchov 	return 0;
52682bfc72bSratchov }
52782bfc72bSratchov 
52882bfc72bSratchov void
_aucat_close(struct aucat * hdl,int eof)529d418f94bSratchov _aucat_close(struct aucat *hdl, int eof)
53082bfc72bSratchov {
53114d6d4a2Sratchov 	char dummy[sizeof(struct amsg)];
53214d6d4a2Sratchov 	ssize_t n;
53382bfc72bSratchov 
53482bfc72bSratchov 	if (!eof) {
53582bfc72bSratchov 		AMSG_INIT(&hdl->wmsg);
536b5fec221Sratchov 		hdl->wmsg.cmd = htonl(AMSG_BYE);
53782bfc72bSratchov 		hdl->wtodo = sizeof(struct amsg);
538d418f94bSratchov 		if (!_aucat_wmsg(hdl, &eof))
53982bfc72bSratchov 			goto bad_close;
54014d6d4a2Sratchov 
54114d6d4a2Sratchov 		/*
54214d6d4a2Sratchov 		 * block until the peer disconnects
54314d6d4a2Sratchov 		 */
54414d6d4a2Sratchov 		while (1) {
54514d6d4a2Sratchov 			n = read(hdl->fd, dummy, sizeof(dummy));
54695d14907Sratchov 			if (n == -1) {
54714d6d4a2Sratchov 				if (errno == EINTR)
54814d6d4a2Sratchov 					continue;
54914d6d4a2Sratchov 				break;
55014d6d4a2Sratchov 			}
55114d6d4a2Sratchov 			if (n == 0)
55214d6d4a2Sratchov 				break;
55314d6d4a2Sratchov 		}
55482bfc72bSratchov 	}
55582bfc72bSratchov  bad_close:
55695d14907Sratchov 	while (close(hdl->fd) == -1 && errno == EINTR)
55782bfc72bSratchov 		; /* nothing */
55882bfc72bSratchov }
55982bfc72bSratchov 
56082bfc72bSratchov int
_aucat_setfl(struct aucat * hdl,int nbio,int * eof)561d418f94bSratchov _aucat_setfl(struct aucat *hdl, int nbio, int *eof)
56282bfc72bSratchov {
56395d14907Sratchov 	if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) == -1) {
564d418f94bSratchov 		DPERROR("_aucat_setfl: fcntl");
56582bfc72bSratchov 		*eof = 1;
56682bfc72bSratchov 		return 0;
56782bfc72bSratchov 	}
56882bfc72bSratchov 	return 1;
56982bfc72bSratchov }
57082bfc72bSratchov 
57182bfc72bSratchov int
_aucat_pollfd(struct aucat * hdl,struct pollfd * pfd,int events)572d418f94bSratchov _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events)
57382bfc72bSratchov {
57482bfc72bSratchov 	if (hdl->rstate == RSTATE_MSG)
57582bfc72bSratchov 		events |= POLLIN;
57682bfc72bSratchov 	pfd->fd = hdl->fd;
57782bfc72bSratchov 	pfd->events = events;
57882bfc72bSratchov 	return 1;
57982bfc72bSratchov }
58082bfc72bSratchov 
58182bfc72bSratchov int
_aucat_revents(struct aucat * hdl,struct pollfd * pfd)582d418f94bSratchov _aucat_revents(struct aucat *hdl, struct pollfd *pfd)
58382bfc72bSratchov {
58482bfc72bSratchov 	int revents = pfd->revents;
58582bfc72bSratchov 
586d418f94bSratchov 	DPRINTFN(2, "_aucat_revents: revents: %x\n", revents);
58782bfc72bSratchov 	return revents;
58882bfc72bSratchov }
589