GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libsndio/aucat.c Lines: 0 297 0.0 %
Date: 2017-11-07 Branches: 0 267 0.0 %

Line Branch Exec Source
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
}