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