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