/*! * Read DSI command and data * * @param dsi (rw) DSI handle * * @return DSI function on success, 0 on failure */ int dsi_stream_receive(DSI *dsi) { char block[DSI_BLOCKSIZ]; LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: START"); if (dsi->flags & DSI_DISCONNECTED) return 0; /* read in the header */ if (dsi_buffered_stream_read(dsi, (uint8_t *)block, sizeof(block)) != sizeof(block)) return 0; dsi->header.dsi_flags = block[0]; dsi->header.dsi_command = block[1]; if (dsi->header.dsi_command == 0) return 0; memcpy(&dsi->header.dsi_requestID, block + 2, sizeof(dsi->header.dsi_requestID)); memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code)); memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len)); memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved)); dsi->clientID = ntohs(dsi->header.dsi_requestID); /* make sure we don't over-write our buffers. */ dsi->cmdlen = MIN(ntohl(dsi->header.dsi_len), dsi->server_quantum); if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen) return 0; LOG(log_debug, logtype_dsi, "dsi_stream_receive: DSI cmdlen: %zd", dsi->cmdlen); return block[1]; }
/* * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads * this tries to read larger chunks (8192 bytes) into a buffer. */ static size_t dsi_buffered_stream_read(DSI *dsi, uint8_t *data, const size_t length) { size_t len; size_t buflen; LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length); len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */ dsi->read_count += len; if (len == length) { /* got enough bytes from there ? */ return len; /* yes */ } /* fill the buffer with 8192 bytes or until buffer is full */ buflen = MIN(8192, dsi->end - dsi->eof); if (buflen > 0) { ssize_t ret; ret = recv(dsi->socket, dsi->eof, buflen, 0); if (ret > 0) dsi->eof += ret; } /* now get the remaining data */ if ((buflen = dsi_stream_read(dsi, data + len, length - len)) != length - len) return 0; len += buflen; return len; }
/* fill up buf and then return. this should be called repeatedly * until all the data has been read. i block alarm processing * during the transfer to avoid sending unnecessary tickles. */ size_t dsi_write(DSI *dsi, void *buf, const size_t buflen) { size_t length; if (((length = MIN(buflen, dsi->datasize)) > 0) && ((length = dsi_stream_read(dsi, buf, length)) > 0)) { dsi->datasize -= length; return length; } return 0; }
/* flush any unread buffers. */ void dsi_writeflush(DSI *dsi) { size_t length; while (dsi->datasize > 0) { length = dsi_stream_read(dsi, dsi->data, MIN(sizeof(dsi->data), dsi->datasize)); if (length > 0) dsi->datasize -= length; else break; } }
/* --------------------------------------- * read data. function on success. 0 on failure. data length gets * stored in length variable. this should really use size_t's, but * that would require changes elsewhere. */ int dsi_stream_receive(DSI *dsi, void *buf, const size_t ilength, size_t *rlength) { char block[DSI_BLOCKSIZ]; LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: %u bytes", ilength); if (dsi->flags & DSI_DISCONNECTED) return 0; /* read in the header */ if (dsi_buffered_stream_read(dsi, (u_int8_t *)block, sizeof(block)) != sizeof(block)) return 0; dsi->header.dsi_flags = block[0]; dsi->header.dsi_command = block[1]; /* FIXME, not the right place, but we get a server disconnect without reason in the log */ if (!block[1]) { LOG(log_error, logtype_dsi, "dsi_stream_receive: invalid packet, fatal"); return 0; } memcpy(&dsi->header.dsi_requestID, block + 2, sizeof(dsi->header.dsi_requestID)); memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code)); memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len)); memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved)); dsi->clientID = ntohs(dsi->header.dsi_requestID); /* make sure we don't over-write our buffers. */ *rlength = min(ntohl(dsi->header.dsi_len), ilength); if (dsi_stream_read(dsi, buf, *rlength) != *rlength) return 0; return block[1]; }
/* accept the socket and do a little sanity checking */ static int dsi_tcp_open(DSI *dsi) { pid_t pid; SOCKLEN_T len; len = sizeof(dsi->client); dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client, &len); #ifdef TCPWRAP { struct request_info req; request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL); fromhost(&req); if (!hosts_access(&req)) { LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req)); close(dsi->socket); errno = ECONNREFUSED; dsi->socket = -1; } } #endif /* TCPWRAP */ if (dsi->socket < 0) return -1; getitimer(ITIMER_PROF, &itimer); if (0 == (pid = fork()) ) { /* child */ static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}}; struct sigaction newact, oldact; u_int8_t block[DSI_BLOCKSIZ]; size_t stored; /* Immediateyl mark globally that we're a child now */ parent_or_child = 1; /* reset signals */ server_reset_signal(); #ifndef DEBUGGING /* install an alarm to deal with non-responsive connections */ newact.sa_handler = timeout_handler; sigemptyset(&newact.sa_mask); newact.sa_flags = 0; sigemptyset(&oldact.sa_mask); oldact.sa_flags = 0; setitimer(ITIMER_PROF, &itimer, NULL); if ((sigaction(SIGALRM, &newact, &oldact) < 0) || (setitimer(ITIMER_REAL, &timer, NULL) < 0)) { LOG(log_error, logtype_dsi, "dsi_tcp_open: %s", strerror(errno)); exit(EXITERR_SYS); } #endif /* read in commands. this is similar to dsi_receive except * for the fact that we do some sanity checking to prevent * delinquent connections from causing mischief. */ /* read in the first two bytes */ len = dsi_stream_read(dsi, block, 2); if (!len ) { /* connection already closed, don't log it (normal OSX 10.3 behaviour) */ exit(EXITERR_CLNT); } if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) { LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header"); exit(EXITERR_CLNT); } /* read in the rest of the header */ stored = 2; while (stored < DSI_BLOCKSIZ) { len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored); if (len > 0) stored += len; else { LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno)); exit(EXITERR_CLNT); } } dsi->header.dsi_flags = block[0]; dsi->header.dsi_command = block[1]; memcpy(&dsi->header.dsi_requestID, block + 2, sizeof(dsi->header.dsi_requestID)); memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code)); memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len)); memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved)); dsi->clientID = ntohs(dsi->header.dsi_requestID); /* make sure we don't over-write our buffers. */ dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ); stored = 0; while (stored < dsi->cmdlen) { len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored); if (len > 0) stored += len; else { LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno)); exit(EXITERR_CLNT); } } /* stop timer and restore signal handler */ #ifndef DEBUGGING memset(&timer, 0, sizeof(timer)); setitimer(ITIMER_REAL, &timer, NULL); sigaction(SIGALRM, &oldact, NULL); #endif LOG(log_info, logtype_dsi, "AFP/TCP session from %s:%u", getip_string((struct sockaddr *)&dsi->client), getip_port((struct sockaddr *)&dsi->client)); } /* send back our pid */ return pid; }