1/* 2 * Copyright (c) 1998 Adrian Sun (asun@zoology.washington.edu) 3 * Copyright (c) 2010,2011,2012 Frank Lahm <franklahm@googlemail.com> 4 * All rights reserved. See COPYRIGHT. 5 * 6 * this file provides the following functions: 7 * dsi_stream_write: just write a bunch of bytes. 8 * dsi_stream_read: just read a bunch of bytes. 9 * dsi_stream_send: send a DSI header + data. 10 * dsi_stream_receive: read a DSI header + data. 11 */ 12 13#ifdef HAVE_CONFIG_H 14#include "config.h" 15#endif /* HAVE_CONFIG_H */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <unistd.h> 20#include <string.h> 21#include <errno.h> 22#include <sys/types.h> 23#include <sys/socket.h> 24#include <sys/uio.h> 25 26#ifdef HAVE_SENDFILEV 27#include <sys/sendfile.h> 28#endif 29 30#include <atalk/logger.h> 31#include <atalk/dsi.h> 32#include <atalk/util.h> 33 34#ifndef MSG_MORE 35#define MSG_MORE 0x8000 36#endif 37 38#ifndef MSG_DONTWAIT 39#define MSG_DONTWAIT 0x40 40#endif 41 42/* Pack a DSI header in wire format */ 43static void dsi_header_pack_reply(const DSI *dsi, char *buf) 44{ 45 buf[0] = dsi->header.dsi_flags; 46 buf[1] = dsi->header.dsi_command; 47 memcpy(buf + 2, &dsi->header.dsi_requestID, sizeof(dsi->header.dsi_requestID)); 48 memcpy(buf + 4, &dsi->header.dsi_data.dsi_code, sizeof(dsi->header.dsi_data.dsi_code)); 49 memcpy(buf + 8, &dsi->header.dsi_len, sizeof(dsi->header.dsi_len)); 50 memcpy(buf + 12, &dsi->header.dsi_reserved, sizeof(dsi->header.dsi_reserved)); 51} 52 53/* 54 * afpd is sleeping too much while trying to send something. 55 * May be there's no reader or the reader is also sleeping in write, 56 * look if there's some data for us to read, hopefully it will wake up 57 * the reader so we can write again. 58 * 59 * @returns 0 when is possible to send again, -1 on error 60 */ 61static int dsi_peek(DSI *dsi) 62{ 63 static int warned = 0; 64 fd_set readfds, writefds; 65 int len; 66 int maxfd; 67 int ret; 68 69 LOG(log_debug, logtype_dsi, "dsi_peek"); 70 71 maxfd = dsi->socket + 1; 72 73 while (1) { 74 if (dsi->socket == -1) 75 /* eg dsi_disconnect() might have disconnected us */ 76 return -1; 77 FD_ZERO(&readfds); 78 FD_ZERO(&writefds); 79 80 if (dsi->eof < dsi->end) { 81 /* space in read buffer */ 82 FD_SET( dsi->socket, &readfds); 83 } else { 84 if (!warned) { 85 warned = 1; 86 LOG(log_note, logtype_dsi, "dsi_peek: readahead buffer is full, possibly increase -dsireadbuf option"); 87 LOG(log_note, logtype_dsi, "dsi_peek: dsireadbuf: %d, DSI quantum: %d, effective buffer size: %d", 88 dsi->dsireadbuf, 89 dsi->server_quantum ? dsi->server_quantum : DSI_SERVQUANT_DEF, 90 dsi->end - dsi->buffer); 91 } 92 } 93 94 FD_SET( dsi->socket, &writefds); 95 96 /* No timeout: if there's nothing to read nor nothing to write, 97 * we've got nothing to do at all */ 98 if ((ret = select( maxfd, &readfds, &writefds, NULL, NULL)) <= 0) { 99 if (ret == -1 && errno == EINTR) 100 /* we might have been interrupted by out timer, so restart select */ 101 continue; 102 /* give up */ 103 LOG(log_error, logtype_dsi, "dsi_peek: unexpected select return: %d %s", 104 ret, ret < 0 ? strerror(errno) : ""); 105 return -1; 106 } 107 108 if (FD_ISSET(dsi->socket, &writefds)) { 109 /* we can write again */ 110 LOG(log_debug, logtype_dsi, "dsi_peek: can write again"); 111 break; 112 } 113 114 /* Check if there's sth to read, hopefully reading that will unblock the client */ 115 if (FD_ISSET(dsi->socket, &readfds)) { 116 len = dsi->end - dsi->eof; /* it's ensured above that there's space */ 117 118 if ((len = recv(dsi->socket, dsi->eof, len, 0)) <= 0) { 119 if (len == 0) { 120 LOG(log_error, logtype_dsi, "dsi_peek: EOF"); 121 return -1; 122 } 123 LOG(log_error, logtype_dsi, "dsi_peek: read: %s", strerror(errno)); 124 if (errno == EAGAIN) 125 continue; 126 return -1; 127 } 128 LOG(log_debug, logtype_dsi, "dsi_peek: read %d bytes", len); 129 130 dsi->eof += len; 131 } 132 } 133 134 return 0; 135} 136 137/* 138 * Return all bytes up to count from dsi->buffer if there are any buffered there 139 */ 140static size_t from_buf(DSI *dsi, uint8_t *buf, size_t count) 141{ 142 size_t nbe = 0; 143 144 if (dsi->buffer == NULL) 145 /* afpd master has no DSI buffering */ 146 return 0; 147 148 LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count); 149 150 nbe = dsi->eof - dsi->start; 151 152 if (nbe > 0) { 153 nbe = MIN((size_t)nbe, count); 154 memcpy(buf, dsi->start, nbe); 155 dsi->start += nbe; 156 157 if (dsi->eof == dsi->start) 158 dsi->start = dsi->eof = dsi->buffer; 159 } 160 161 LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u", 162 dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe); 163 164 return nbe; 165} 166 167/* 168 * Get bytes from buffer dsi->buffer or read from socket 169 * 170 * 1. Check if there are bytes in the the dsi->buffer buffer. 171 * 2. Return bytes from (1) if yes. 172 * Note: this may return fewer bytes then requested in count !! 173 * 3. If the buffer was empty, read from the socket. 174 */ 175static ssize_t buf_read(DSI *dsi, uint8_t *buf, size_t count) 176{ 177 ssize_t len; 178 179 LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count); 180 181 if (!count) 182 return 0; 183 184 len = from_buf(dsi, buf, count); /* 1. */ 185 if (len) 186 return len; /* 2. */ 187 188 len = readt(dsi->socket, buf, count, 0, 0); /* 3. */ 189 190 LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len); 191 192 return len; 193} 194 195/* 196 * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads 197 * this tries to read larger chunks (8192 bytes) into a buffer. 198 */ 199static size_t dsi_buffered_stream_read(DSI *dsi, uint8_t *data, const size_t length) 200{ 201 size_t len; 202 size_t buflen; 203 204 LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length); 205 206 len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */ 207 dsi->read_count += len; 208 if (len == length) { /* got enough bytes from there ? */ 209 return len; /* yes */ 210 } 211 212 /* fill the buffer with 8192 bytes or until buffer is full */ 213 buflen = MIN(8192, dsi->end - dsi->eof); 214 if (buflen > 0) { 215 ssize_t ret; 216 ret = recv(dsi->socket, dsi->eof, buflen, 0); 217 if (ret > 0) 218 dsi->eof += ret; 219 } 220 221 /* now get the remaining data */ 222 if ((buflen = dsi_stream_read(dsi, data + len, length - len)) != length - len) 223 return 0; 224 len += buflen; 225 226 return len; 227} 228 229/* --------------------------------------- 230*/ 231static void block_sig(DSI *dsi) 232{ 233 dsi->in_write++; 234} 235 236/* --------------------------------------- 237*/ 238static void unblock_sig(DSI *dsi) 239{ 240 dsi->in_write--; 241} 242 243/********************************************************************************* 244 * Public functions 245 *********************************************************************************/ 246 247/*! 248 * Communication error with the client, enter disconnected state 249 * 250 * 1. close the socket 251 * 2. set the DSI_DISCONNECTED flag, remove possible sleep flags 252 * 253 * @returns 0 if successfully entered disconnected state 254 * -1 if ppid is 1 which means afpd master died 255 * or euid == 0 ie where still running as root (unauthenticated session) 256 */ 257int dsi_disconnect(DSI *dsi) 258{ 259 LOG(log_note, logtype_dsi, "dsi_disconnect: entering disconnected state"); 260 dsi->proto_close(dsi); /* 1 */ 261 dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP); /* 2 */ 262 dsi->flags |= DSI_DISCONNECTED; 263 if (geteuid() == 0) 264 return -1; 265 return 0; 266} 267 268/* ------------------------------ 269 * write raw data. return actual bytes read. checks against EINTR 270 * aren't necessary if all of the signals have SA_RESTART 271 * specified. */ 272ssize_t dsi_stream_write(DSI *dsi, void *data, const size_t length, int mode) 273{ 274 size_t written; 275 ssize_t len; 276 unsigned int flags; 277 278 dsi->in_write++; 279 written = 0; 280 281 LOG(log_maxdebug, logtype_dsi, "dsi_stream_write(send: %zd bytes): START", length); 282 283 if (dsi->flags & DSI_DISCONNECTED) 284 return -1; 285 286 if (mode & DSI_MSG_MORE) 287 flags = MSG_MORE; 288 else 289 flags = 0; 290 291 while (written < length) { 292 len = send(dsi->socket, (uint8_t *) data + written, length - written, flags); 293 if (len >= 0) { 294 written += len; 295 continue; 296 } 297 298 if (errno == EINTR) 299 continue; 300 301 if (errno == EAGAIN || errno == EWOULDBLOCK) { 302 LOG(log_debug, logtype_dsi, "dsi_stream_write: send: %s", strerror(errno)); 303 304 if (mode == DSI_NOWAIT && written == 0) { 305 /* DSI_NOWAIT is used by attention give up in this case. */ 306 written = -1; 307 goto exit; 308 } 309 310 /* Try to read sth. in order to break up possible deadlock */ 311 if (dsi_peek(dsi) != 0) { 312 written = -1; 313 goto exit; 314 } 315 /* Now try writing again */ 316 continue; 317 } 318 319 LOG(log_error, logtype_dsi, "dsi_stream_write: %s", strerror(errno)); 320 written = -1; 321 goto exit; 322 } 323 324 dsi->write_count += written; 325 LOG(log_maxdebug, logtype_dsi, "dsi_stream_write(send: %zd bytes): END", length); 326 327exit: 328 dsi->in_write--; 329 return written; 330} 331 332/* --------------------------------- 333*/ 334#ifdef WITH_SENDFILE 335ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const size_t length, const int err) 336{ 337 int ret = 0; 338 size_t written = 0; 339 size_t total = length; 340 ssize_t len; 341 off_t pos = offset; 342 char block[DSI_BLOCKSIZ]; 343#ifdef HAVE_SENDFILEV 344 int sfvcnt; 345 struct sendfilevec vec[2]; 346 ssize_t nwritten; 347#elif defined(FREEBSD) 348 ssize_t nwritten; 349 void *hdrp; 350 struct sf_hdtr hdr; 351 struct iovec iovec; 352 hdr.headers = &iovec; 353 hdr.hdr_cnt = 1; 354 hdr.trailers = NULL; 355 hdr.trl_cnt = 0; 356 hdrp = &hdr; 357#endif 358 359 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length); 360 361 if (dsi->flags & DSI_DISCONNECTED) 362 return -1; 363 364 dsi->in_write++; 365 366 dsi->flags |= DSI_NOREPLY; 367 dsi->header.dsi_flags = DSIFL_REPLY; 368 dsi->header.dsi_len = htonl(length); 369 dsi->header.dsi_data.dsi_code = htonl(err); 370 dsi_header_pack_reply(dsi, block); 371 372#ifdef HAVE_SENDFILEV 373 total += DSI_BLOCKSIZ; 374 sfvcnt = 2; 375 vec[0].sfv_fd = SFV_FD_SELF; 376 vec[0].sfv_flag = 0; 377 /* Cast to unsigned long to prevent sign extension of the 378 * pointer value for the LFS case; see Apache PR 39463. */ 379 vec[0].sfv_off = (unsigned long)block; 380 vec[0].sfv_len = DSI_BLOCKSIZ; 381 vec[1].sfv_fd = fromfd; 382 vec[1].sfv_flag = 0; 383 vec[1].sfv_off = offset; 384 vec[1].sfv_len = length; 385#elif defined(FREEBSD) 386 iovec.iov_base = block; 387 iovec.iov_len = DSI_BLOCKSIZ; 388#else 389 dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE); 390#endif 391 392 while (written < total) { 393#ifdef HAVE_SENDFILEV 394 nwritten = 0; 395 len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten); 396#elif defined(FREEBSD) 397 len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0); 398 if (len == 0) 399 len = nwritten; 400#else 401 len = sys_sendfile(dsi->socket, fromfd, &pos, total - written); 402#endif 403 if (len < 0) { 404 switch (errno) { 405 case EINTR: 406 case EAGAIN: 407 len = 0; 408#if defined(HAVE_SENDFILEV) || defined(FREEBSD) 409 len = (size_t)nwritten; 410#elif defined(SOLARIS) 411 if (pos > offset) { 412 /* we actually have sent sth., adjust counters and keep trying */ 413 len = pos - offset; 414 offset = pos; 415 } 416#endif /* HAVE_SENDFILEV */ 417 418 if (dsi_peek(dsi) != 0) { 419 ret = -1; 420 goto exit; 421 } 422 break; 423 default: 424 LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno)); 425 ret = -1; 426 goto exit; 427 } 428 } else if (len == 0) { 429 /* afpd is going to exit */ 430 ret = -1; 431 goto exit; 432 } 433#ifdef HAVE_SENDFILEV 434 if (sfvcnt == 2 && len >= vec[0].sfv_len) { 435 vec[1].sfv_off += len - vec[0].sfv_len; 436 vec[1].sfv_len -= len - vec[0].sfv_len; 437 438 vec[0] = vec[1]; 439 sfvcnt = 1; 440 } else { 441 vec[0].sfv_off += len; 442 vec[0].sfv_len -= len; 443 } 444#elif defined(FREEBSD) 445 if (hdrp) { 446 if (len >= iovec.iov_len) { 447 hdrp = NULL; 448 len -= iovec.iov_len; /* len now contains how much sendfile() actually sent from the file */ 449 } else { 450 iovec.iov_len -= len; 451 iovec.iov_base += len; 452 len = 0; 453 } 454 } 455 pos += len; 456#endif /* HAVE_SENDFILEV */ 457 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len); 458 written += len; 459 } 460#ifdef HAVE_SENDFILEV 461 written -= DSI_BLOCKSIZ; 462#endif 463 dsi->write_count += written; 464 465exit: 466 dsi->in_write--; 467 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: written: %zd", written); 468 if (ret != 0) 469 return -1; 470 return written; 471} 472#endif 473 474 475/* 476 * Essentially a loop around buf_read() to ensure "length" bytes are read 477 * from dsi->buffer and/or the socket. 478 * 479 * @returns length on success, some value smaller then length indicates an error 480 */ 481size_t dsi_stream_read(DSI *dsi, void *data, const size_t length) 482{ 483 size_t stored; 484 ssize_t len; 485 486 if (dsi->flags & DSI_DISCONNECTED) 487 return 0; 488 489 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes)", length); 490 491 stored = 0; 492 while (stored < length) { 493 len = buf_read(dsi, (uint8_t *) data + stored, length - stored); 494 if (len == -1 && (errno == EINTR || errno == EAGAIN)) { 495 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read: select read loop"); 496 continue; 497 } else if (len > 0) { 498 stored += len; 499 } else { /* eof or error */ 500 /* don't log EOF error if it's just after connect (OSX 10.3 probe) */ 501 if (len || stored || dsi->read_count) { 502 if (! (dsi->flags & DSI_DISCONNECTED)) { 503 LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s", 504 len, (len < 0) ? strerror(errno) : "unexpected EOF"); 505 } 506 return 0; 507 } 508 break; 509 } 510 } 511 512 dsi->read_count += stored; 513 514 LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes): got: %u", length, stored); 515 return stored; 516} 517 518/* --------------------------------------- 519 * write data. 0 on failure. this assumes that dsi_len will never 520 * cause an overflow in the data buffer. 521 */ 522int dsi_stream_send(DSI *dsi, void *buf, size_t length) 523{ 524 char block[DSI_BLOCKSIZ]; 525 struct iovec iov[2]; 526 int iovecs = 2; 527 size_t towrite; 528 ssize_t len; 529 530 LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): START", length); 531 532 if (dsi->flags & DSI_DISCONNECTED) 533 return 0; 534 535 dsi_header_pack_reply(dsi, block); 536 537 if (!length) { /* just write the header */ 538 LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): DSI header, no data", sizeof(block)); 539 length = (dsi_stream_write(dsi, block, sizeof(block), 0) == sizeof(block)); 540 return length; /* really 0 on failure, 1 on success */ 541 } 542 543 /* block signals */ 544 block_sig(dsi); 545 iov[0].iov_base = block; 546 iov[0].iov_len = sizeof(block); 547 iov[1].iov_base = buf; 548 iov[1].iov_len = length; 549 550 towrite = sizeof(block) + length; 551 dsi->write_count += towrite; 552 while (towrite > 0) { 553 if (((len = writev(dsi->socket, iov, iovecs)) == -1 && errno == EINTR) || (len == 0)) 554 continue; 555 556 if ((size_t)len == towrite) /* wrote everything out */ 557 break; 558 else if (len < 0) { /* error */ 559 if (errno == EAGAIN || errno == EWOULDBLOCK) { 560 if (dsi_peek(dsi) == 0) { 561 continue; 562 } 563 } 564 LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno)); 565 unblock_sig(dsi); 566 return 0; 567 } 568 569 towrite -= len; 570 if (towrite > length) { /* skip part of header */ 571 iov[0].iov_base = (char *) iov[0].iov_base + len; 572 iov[0].iov_len -= len; 573 } else { /* skip to data */ 574 if (iovecs == 2) { 575 iovecs = 1; 576 len -= iov[0].iov_len; 577 iov[0] = iov[1]; 578 } 579 iov[0].iov_base = (char *) iov[0].iov_base + len; 580 iov[0].iov_len -= len; 581 } 582 } 583 584 LOG(log_maxdebug, logtype_dsi, "dsi_stream_send(%u bytes): END", length); 585 586 unblock_sig(dsi); 587 return 1; 588} 589 590 591/*! 592 * Read DSI command and data 593 * 594 * @param dsi (rw) DSI handle 595 * 596 * @return DSI function on success, 0 on failure 597 */ 598int dsi_stream_receive(DSI *dsi) 599{ 600 char block[DSI_BLOCKSIZ]; 601 602 LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: START"); 603 604 if (dsi->flags & DSI_DISCONNECTED) 605 return 0; 606 607 /* read in the header */ 608 if (dsi_buffered_stream_read(dsi, (uint8_t *)block, sizeof(block)) != sizeof(block)) 609 return 0; 610 611 dsi->header.dsi_flags = block[0]; 612 dsi->header.dsi_command = block[1]; 613 614 if (dsi->header.dsi_command == 0) 615 return 0; 616 617 memcpy(&dsi->header.dsi_requestID, block + 2, sizeof(dsi->header.dsi_requestID)); 618 memcpy(&dsi->header.dsi_data.dsi_code, block + 4, sizeof(dsi->header.dsi_data.dsi_code)); 619 memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len)); 620 memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved)); 621 dsi->clientID = ntohs(dsi->header.dsi_requestID); 622 623 /* make sure we don't over-write our buffers. */ 624 dsi->cmdlen = MIN(ntohl(dsi->header.dsi_len), dsi->server_quantum); 625 if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen) 626 return 0; 627 628 LOG(log_debug, logtype_dsi, "dsi_stream_receive: DSI cmdlen: %zd", dsi->cmdlen); 629 630 return block[1]; 631} 632