/* * Compress from a file to a newly malloc'd block. */ int dcc_compress_file_lzo1x(int in_fd, size_t in_len, char **out_buf, size_t *out_len) { char *in_buf = NULL; int ret; if ((in_buf = malloc(in_len)) == NULL) { rs_log_error("allocation of %ld byte buffer failed", (long) in_len); ret = EXIT_OUT_OF_MEMORY; goto out; } if ((ret = dcc_readx(in_fd, in_buf, in_len))) goto out; if ((ret = dcc_compress_lzo1x_alloc(in_buf, in_len, out_buf, out_len))) goto out; out: if (in_buf != NULL) { free(in_buf); } return ret; }
/* * Attempt handshake exchange with the server to indicate client's * desire to authentciate. * * @param to_net_sd. Socket to write to. * * @param from_net_sd. Socket to read from. * * Returns 0 on success, otherwise error. */ static int dcc_gssapi_send_handshake(int to_net_sd, int from_net_sd) { char auth = HANDSHAKE; fd_set sockets; int ret; struct timeval timeout; rs_log_info("Sending handshake."); if ((ret = dcc_writex(to_net_sd, &auth, sizeof(auth))) != 0) { return ret; } rs_log_info("Sent %c.", auth); FD_ZERO(&sockets); FD_SET(from_net_sd, &sockets); timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select(from_net_sd + 1, &sockets, NULL, NULL, &timeout); if (ret < 0) { rs_log_error("select failed: %s.", strerror(errno)); return EXIT_IO_ERROR; } if (ret == 0) { rs_log_error("Timeout - does this server require authentication?"); return EXIT_TIMEOUT; } rs_log_info("Receiving handshake."); if ((ret = dcc_readx(from_net_sd, &auth, sizeof(auth))) != 0) { return ret; } rs_log_info("Received %c.", auth); if (auth != HANDSHAKE) { rs_log_crit("No server handshake."); return EXIT_GSSAPI_FAILED; } return 0; }
/* * Receive notification from an authenticating server as to * whether the client has successfully gained access or not. * * @param sd. Socket to read from. * * Returns 0 on success, otherwise error. */ static int dcc_gssapi_recv_notification(int sd) { char notification; fd_set sockets; int ret; struct timeval timeout; FD_ZERO(&sockets); FD_SET(sd, &sockets); timeout.tv_sec = 1; timeout.tv_usec = 0; ret = select(sd + 1, &sockets, NULL, NULL, &timeout); if (ret < 0) { rs_log_error("select failed: %s.", strerror(errno)); return EXIT_IO_ERROR; } if (ret == 0) { rs_log_error("Timeout - error receiving notification."); return EXIT_TIMEOUT; } if ((ret = dcc_readx(sd, ¬ification, sizeof(notification))) != 0) { rs_log_crit("Failed to receive notification."); return ret; } if (notification != ACCESS) { rs_log_crit("Access denied by server."); rs_log_info("Your principal may be blacklisted or may not be whitelisted."); return EXIT_ACCESS_DENIED; } rs_log_info("Access granted by server."); return 0; }
/** * Read a byte string of length @p l into a newly allocated buffer, returned in @p buf. **/ int dcc_r_str_alloc(int fd, unsigned l, char **buf) { char *s; #if 0 /* never true */ if (l < 0) { rs_log_crit("oops, l < 0"); return EXIT_PROTOCOL_ERROR; } #endif /* rs_trace("read %d byte string", l); */ s = *buf = malloc((size_t) l + 1); if (!s) rs_log_error("malloc failed"); if (dcc_readx(fd, s, (size_t) l)) return EXIT_OUT_OF_MEMORY; s[l] = 0; return 0; }
/** * Read a token and value. The receiver always knows what token name * is expected next -- indeed the names are really only there as a * sanity check and to aid debugging. * * @param ifd fd to read from * @param expected 4-char token that is expected to come in next * @param val receives the parameter value **/ int dcc_r_token_int(int ifd, const char *expected, unsigned *val) { char buf[13], *bum; int ret; if (strlen(expected) != 4) { rs_log_error("expected token \"%s\" seems wrong", expected); return EXIT_PROTOCOL_ERROR; } if ((ret = dcc_readx(ifd, buf, 12))) { rs_log_error("read failed while waiting for token \"%s\"", expected); return ret; } if (memcmp(buf, expected, 4)) { rs_log_error("protocol derailment: expected token \"%s\"", expected); dcc_explain_mismatch(buf, 12, ifd); return EXIT_PROTOCOL_ERROR; } buf[12] = '\0'; /* terminate */ *val = strtoul(&buf[4], &bum, 16); if (bum != &buf[12]) { rs_log_error("failed to parse parameter of token \"%s\"", expected); dcc_explain_mismatch(buf, 12, ifd); return EXIT_PROTOCOL_ERROR; } rs_trace("got %s", buf); return 0; }
/** * Receive @p in_len compressed bytes from @p in_fd, and write the * decompressed form to @p out_fd. * * There's no way for us to know how big the uncompressed form will be, and * there is also no way to grow the decompression buffer if it turns out to * initially be too small. So we assume a ratio of 10x. If it turns out to * be too small, we increase the buffer and try again. Typical compression of * source or object is about 2x to 4x. On modern Unix we should be able to * allocate (and not touch) many megabytes at little cost, since it will just * turn into an anonymous map. * * LZO doesn't have any way to decompress part of the input and then break to * get more output space, so our buffer needs to be big enough in the first * place or we would waste time repeatedly decompressing it. **/ int dcc_r_bulk_lzo1x(int out_fd, int in_fd, unsigned in_len) { int ret, lzo_ret; char *in_buf = NULL, *out_buf = NULL; size_t out_size = 0; lzo_uint out_len; /* NOTE: out_size is the buffer size, out_len is the amount of actual * data. */ if (in_len == 0) return 0; /* just check */ if ((in_buf = malloc(in_len)) == NULL) { rs_log_error("failed to allocate decompression input"); ret = EXIT_OUT_OF_MEMORY; goto out; } if ((ret = dcc_readx(in_fd, in_buf, in_len)) != 0) goto out; #if 0 /* Initial estimate for output buffer. This is intentionally quite low to * exercise the resizing code -- if it works OK then we can scale this * up. */ out_size = 2 * in_len; #else out_size = 8 * in_len; #endif try_again_with_a_bigger_buffer: if ((out_buf = malloc(out_size)) == NULL) { rs_log_error("failed to allocate decompression buffer"); ret = EXIT_OUT_OF_MEMORY; goto out; } out_len = out_size; lzo_ret = lzo1x_decompress_safe((lzo_byte*)in_buf, in_len, (lzo_byte*)out_buf, &out_len, work_mem); if (lzo_ret == LZO_E_OK) { rs_trace("decompressed %ld bytes to %ld bytes: %d%%", (long) in_len, (long) out_len, (int) (out_len ? 100*in_len / out_len : 0)); ret = dcc_writex(out_fd, out_buf, out_len); goto out; } else if (lzo_ret == LZO_E_OUTPUT_OVERRUN) { free(out_buf); out_buf = 0; out_size *= 2; /* FIXME: Make sure this doesn't overflow memory size? */ rs_trace("LZO_E_OUTPUT_OVERRUN, trying again with %lu byte buffer", (unsigned long) out_size); goto try_again_with_a_bigger_buffer; } else { rs_log_error("LZO1X1 decompression failed: %d", lzo_ret); ret = EXIT_IO_ERROR; goto out; } out: free(in_buf); free(out_buf); return ret; }
/* Get the host list from zeroconf */ int dcc_zeroconf_add_hosts(struct dcc_hostdef **ret_list, int *ret_nhosts, int n_slots, struct dcc_hostdef **ret_prev) { char host_file[PATH_MAX], lock_file[PATH_MAX], *s = NULL; int lock_fd = -1, host_fd = -1; int fork_daemon = 0; int r = -1; char *dir; struct stat st; if (get_zeroconf_dir(&dir) != 0) { rs_log_crit("failed to get zeroconf dir.\n"); goto finish; } snprintf(lock_file, sizeof(lock_file), "%s/lock", dir); snprintf(host_file, sizeof(host_file), "%s/hosts", dir); /* Open lock file */ if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno)); goto finish; } /* Try to lock the lock file */ if (generic_lock(lock_fd, 1, 1, 0) >= 0) { /* The lock succeeded => there's no daemon running yet! */ fork_daemon = 1; generic_lock(lock_fd, 1, 0, 0); } close(lock_fd); /* Shall we fork a new daemon? */ if (fork_daemon) { pid_t pid; rs_log_info("Spawning zeroconf daemon.\n"); if ((pid = fork()) == -1) { rs_log_crit("fork() failed: %s\n", strerror(errno)); goto finish; } else if (pid == 0) { int fd; /* Child */ /* Close file descriptors and replace them by /dev/null */ close(0); close(1); close(2); fd = open("/dev/null", O_RDWR); assert(fd == 0); fd = dup(0); assert(fd == 1); fd = dup(0); assert(fd == 2); #ifdef HAVE_SETSID setsid(); #endif chdir("/"); rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); _exit(daemon_proc(host_file, lock_file, n_slots)); } /* Parent */ /* Wait some time for initial host gathering */ usleep(1000000); /* 1000 ms */ } /* Open host list read-only */ if ((host_fd = open(host_file, O_RDONLY)) < 0) { rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno)); goto finish; } /* A read lock */ if (generic_lock(host_fd, 0, 1, 1) < 0) { rs_log_crit("lock failed: %s\n", strerror(errno)); goto finish; } /* Get file size */ if (fstat(host_fd, &st) < 0) { rs_log_crit("stat() failed: %s\n", strerror(errno)); goto finish; } if (st.st_size >= MAX_FILE_SIZE) { rs_log_crit("file too large.\n"); goto finish; } /* read file data */ s = malloc((size_t) st.st_size+1); assert(s); if (dcc_readx(host_fd, s, (size_t) st.st_size) != 0) { rs_log_crit("failed to read from file.\n"); goto finish; } s[st.st_size] = 0; /* Parse host data */ if (dcc_parse_hosts(s, host_file, ret_list, ret_nhosts, ret_prev) != 0) { rs_log_crit("failed to parse host file.\n"); goto finish; } r = 0; finish: if (host_fd >= 0) { generic_lock(host_fd, 0, 0, 1); close(host_fd); } free(s); return r; }
int dcc_r_token(int ifd, char *buf) { return dcc_readx(ifd, buf, 4); }