diff options
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 249 |
1 files changed, 124 insertions, 125 deletions
diff --git a/src/common/util.c b/src/common/util.c index a7bce2ea6c..a8b44cbda7 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -79,7 +79,7 @@ #include <malloc/malloc.h> #endif #ifdef HAVE_MALLOC_H -#if !defined(OPENBSD) && !defined(__FreeBSD__) +#if !defined(OpenBSD) && !defined(__FreeBSD__) /* OpenBSD has a malloc.h, but for our purposes, it only exists in order to * scold us for being so stupid as to autodetect its presence. To be fair, * they've done this since 1996, when autoconf was only 5 years old. */ @@ -187,8 +187,9 @@ tor_malloc_zero_(size_t size DMALLOC_PARAMS) * 0xfffe0001. */ #define SQRT_SIZE_MAX_P1 (((size_t)1) << (sizeof(size_t)*4)) -/** Return non-zero if and only if the product of the arguments is exact. */ -static inline int +/** Return non-zero if and only if the product of the arguments is exact, + * and cannot overflow. */ +int size_mul_check(const size_t x, const size_t y) { /* This first check is equivalent to @@ -202,15 +203,6 @@ size_mul_check(const size_t x, const size_t y) x <= SIZE_MAX / y); } -#ifdef TOR_UNIT_TESTS -/** Exposed for unit tests only */ -int -size_mul_check__(const size_t x, const size_t y) -{ - return size_mul_check(x,y); -} -#endif - /** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill * the memory with zero bytes, and return a pointer to the result. * Log and terminate the process on error. (Same as @@ -712,6 +704,19 @@ tor_strisnonupper(const char *s) return 1; } +/** Return true iff every character in <b>s</b> is whitespace space; else + * return false. */ +int +tor_strisspace(const char *s) +{ + while (*s) { + if (!TOR_ISSPACE(*s)) + return 0; + s++; + } + return 1; +} + /** As strcmp, except that either string may be NULL. The NULL string is * considered to be before any non-NULL string. */ int @@ -1803,17 +1808,26 @@ format_iso_time_nospace_usec(char *buf, const struct timeval *tv) /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>, * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on * failure. Ignore extraneous stuff in <b>cp</b> after the end of the time - * string, unless <b>strict</b> is set. */ + * string, unless <b>strict</b> is set. If <b>nospace</b> is set, + * expect the YYYY-MM-DDTHH:MM:SS format. */ int -parse_iso_time_(const char *cp, time_t *t, int strict) +parse_iso_time_(const char *cp, time_t *t, int strict, int nospace) { struct tm st_tm; unsigned int year=0, month=0, day=0, hour=0, minute=0, second=0; int n_fields; - char extra_char; - n_fields = tor_sscanf(cp, "%u-%2u-%2u %2u:%2u:%2u%c", &year, &month, - &day, &hour, &minute, &second, &extra_char); - if (strict ? (n_fields != 6) : (n_fields < 6)) { + char extra_char, separator_char; + n_fields = tor_sscanf(cp, "%u-%2u-%2u%c%2u:%2u:%2u%c", + &year, &month, &day, + &separator_char, + &hour, &minute, &second, &extra_char); + if (strict ? (n_fields != 7) : (n_fields < 7)) { + char *esc = esc_for_log(cp); + log_warn(LD_GENERAL, "ISO time %s was unparseable", esc); + tor_free(esc); + return -1; + } + if (separator_char != (nospace ? 'T' : ' ')) { char *esc = esc_for_log(cp); log_warn(LD_GENERAL, "ISO time %s was unparseable", esc); tor_free(esc); @@ -1855,7 +1869,16 @@ parse_iso_time_(const char *cp, time_t *t, int strict) int parse_iso_time(const char *cp, time_t *t) { - return parse_iso_time_(cp, t, 1); + return parse_iso_time_(cp, t, 1, 0); +} + +/** + * As parse_iso_time, but parses a time encoded by format_iso_time_nospace(). + */ +int +parse_iso_time_nospace(const char *cp, time_t *t) +{ + return parse_iso_time_(cp, t, 1, 1); } /** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh), @@ -2095,7 +2118,7 @@ read_all(tor_socket_t fd, char *buf, size_t count, int isSocket) return -1; } - while (numread != count) { + while (numread < count) { if (isSocket) result = tor_socket_recv(fd, buf+numread, count-numread, 0); else @@ -2270,10 +2293,14 @@ check_private_dir,(const char *dirname, cpd_check_t check, * permissions on the directory will be checked again below.*/ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW); - if (fd == -1) + if (fd == -1) { + log_warn(LD_FS, "Could not reopen recently created directory %s: %s", + dirname, + strerror(errno)); return -1; - else + } else { close(fd); + } } else if (!(check & CPD_CHECK)) { log_warn(LD_FS, "Directory %s does not exist.", dirname); @@ -2601,6 +2628,14 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) if (file_data->rename_on_close) { tor_assert(file_data->tempname && file_data->filename); + if (!abort_write) { + tor_assert(strcmp(file_data->filename, file_data->tempname)); + if (replace_file(file_data->tempname, file_data->filename)) { + log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, + strerror(errno)); + abort_write = r = -1; + } + } if (abort_write) { int res = unlink(file_data->tempname); if (res != 0) { @@ -2609,13 +2644,6 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) file_data->tempname, strerror(errno)); r = -1; } - } else { - tor_assert(strcmp(file_data->filename, file_data->tempname)); - if (replace_file(file_data->tempname, file_data->filename)) { - log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, - strerror(errno)); - r = -1; - } } } @@ -3534,6 +3562,17 @@ smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern, smartlist_add(sl, str); } +/** Append a copy of string to sl */ +void +smartlist_add_strdup(struct smartlist_t *sl, const char *string) +{ + char *copy; + + copy = tor_strdup(string); + + smartlist_add(sl, copy); +} + /** Return a new list containing the filenames in the directory <b>dirname</b>. * Return NULL on error or if <b>dirname</b> is not a directory. */ @@ -3567,7 +3606,7 @@ tor_listdir, (const char *dirname)) #endif if (strcmp(name, ".") && strcmp(name, "..")) { - smartlist_add(result, tor_strdup(name)); + smartlist_add_strdup(result, name); } if (!FindNextFile(handle, &findData)) { DWORD err; @@ -3593,7 +3632,7 @@ tor_listdir, (const char *dirname)) if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; - smartlist_add(result, tor_strdup(de->d_name)); + smartlist_add_strdup(result, de->d_name); } closedir(d); #endif @@ -4136,10 +4175,10 @@ tor_process_get_stdout_pipe(process_handle_t *process_handle) } #else /* DOCDOC tor_process_get_stdout_pipe */ -FILE * +int tor_process_get_stdout_pipe(process_handle_t *process_handle) { - return process_handle->stdout_handle; + return process_handle->stdout_pipe; } #endif @@ -4570,10 +4609,6 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set stderror/stdout/stdin pipes " "nonblocking in parent process: %s", strerror(errno)); } - /* Open the buffered IO streams */ - process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r"); - process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r"); - process_handle->stdin_handle = fdopen(process_handle->stdin_pipe, "r"); *process_handle_out = process_handle; return process_handle->status; @@ -4620,14 +4655,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle, if (process_handle->stdin_pipe) CloseHandle(process_handle->stdin_pipe); #else - if (process_handle->stdout_handle) - fclose(process_handle->stdout_handle); - - if (process_handle->stderr_handle) - fclose(process_handle->stderr_handle); - - if (process_handle->stdin_handle) - fclose(process_handle->stdin_handle); + close(process_handle->stdout_pipe); + close(process_handle->stderr_pipe); + close(process_handle->stdin_pipe); clear_waitpid_callback(process_handle->waitpid_cb); #endif @@ -4864,7 +4894,7 @@ get_current_process_environment_variables(void) char **environ_tmp; /* Not const char ** ? Really? */ for (environ_tmp = get_environment(); *environ_tmp; ++environ_tmp) { - smartlist_add(sl, tor_strdup(*environ_tmp)); + smartlist_add_strdup(sl, *environ_tmp); } return sl; @@ -4913,7 +4943,7 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count, if (count > SIZE_T_CEILING || count > SSIZE_MAX) return -1; - while (numread != count) { + while (numread < count) { /* Check if there is anything to read */ retval = PeekNamedPipe(h, NULL, 0, NULL, &byte_count, NULL); if (!retval) { @@ -4959,19 +4989,19 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count, return (ssize_t)numread; } #else -/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If +/** Read from a handle <b>fd</b> into <b>buf</b>, up to <b>count</b> bytes. If * <b>process</b> is NULL, the function will return immediately if there is * nothing more to read. Otherwise data will be read until end of file, or * <b>count</b> bytes are read. Returns the number of bytes read, or -1 on * error. Sets <b>eof</b> to true if <b>eof</b> is not NULL and the end of the * file has been reached. */ ssize_t -tor_read_all_handle(FILE *h, char *buf, size_t count, +tor_read_all_handle(int fd, char *buf, size_t count, const process_handle_t *process, int *eof) { size_t numread = 0; - char *retval; + ssize_t result; if (eof) *eof = 0; @@ -4979,34 +5009,28 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, if (count > SIZE_T_CEILING || count > SSIZE_MAX) return -1; - while (numread != count) { - /* Use fgets because that is what we use in log_from_pipe() */ - retval = fgets(buf+numread, (int)(count-numread), h); - if (NULL == retval) { - if (feof(h)) { - log_debug(LD_GENERAL, "fgets() reached end of file"); - if (eof) - *eof = 1; + while (numread < count) { + result = read(fd, buf+numread, count-numread); + + if (result == 0) { + log_debug(LD_GENERAL, "read() reached end of file"); + if (eof) + *eof = 1; + break; + } else if (result < 0 && errno == EAGAIN) { + if (process) + continue; + else break; - } else { - if (EAGAIN == errno) { - if (process) - continue; - else - break; - } else { - log_warn(LD_GENERAL, "fgets() from handle failed: %s", - strerror(errno)); - return -1; - } - } + } else if (result < 0) { + log_warn(LD_GENERAL, "read() failed: %s", strerror(errno)); + return -1; } - tor_assert(retval != NULL); - tor_assert(strlen(retval) + numread <= count); - numread += strlen(retval); + + numread += result; } - log_debug(LD_GENERAL, "fgets() read %d bytes from handle", (int)numread); + log_debug(LD_GENERAL, "read() read %d bytes from handle", (int)numread); return (ssize_t)numread; } #endif @@ -5020,7 +5044,7 @@ tor_read_all_from_process_stdout(const process_handle_t *process_handle, return tor_read_all_handle(process_handle->stdout_pipe, buf, count, process_handle); #else - return tor_read_all_handle(process_handle->stdout_handle, buf, count, + return tor_read_all_handle(process_handle->stdout_pipe, buf, count, process_handle, NULL); #endif } @@ -5034,7 +5058,7 @@ tor_read_all_from_process_stderr(const process_handle_t *process_handle, return tor_read_all_handle(process_handle->stderr_pipe, buf, count, process_handle); #else - return tor_read_all_handle(process_handle->stderr_handle, buf, count, + return tor_read_all_handle(process_handle->stderr_pipe, buf, count, process_handle, NULL); #endif } @@ -5228,11 +5252,10 @@ log_from_handle(HANDLE *pipe, int severity) #else /** Return a smartlist containing lines outputted from - * <b>handle</b>. Return NULL on error, and set + * <b>fd</b>. Return NULL on error, and set * <b>stream_status_out</b> appropriately. */ MOCK_IMPL(smartlist_t *, -tor_get_lines_from_handle, (FILE *handle, - enum stream_status *stream_status_out)) +tor_get_lines_from_handle, (int fd, enum stream_status *stream_status_out)) { enum stream_status stream_status; char stdout_buf[400]; @@ -5241,13 +5264,13 @@ tor_get_lines_from_handle, (FILE *handle, while (1) { memset(stdout_buf, 0, sizeof(stdout_buf)); - stream_status = get_string_from_pipe(handle, + stream_status = get_string_from_pipe(fd, stdout_buf, sizeof(stdout_buf) - 1); if (stream_status != IO_STREAM_OKAY) goto done; if (!lines) lines = smartlist_new(); - smartlist_add(lines, tor_strdup(stdout_buf)); + smartlist_add_strdup(lines, stdout_buf); } done: @@ -5255,20 +5278,20 @@ tor_get_lines_from_handle, (FILE *handle, return lines; } -/** Read from stream, and send lines to log at the specified log level. +/** Read from fd, and send lines to log at the specified log level. * Returns 1 if stream is closed normally, -1 if there is a error reading, and * 0 otherwise. Handles lines from tor-fw-helper and * tor_spawn_background() specially. */ static int -log_from_pipe(FILE *stream, int severity, const char *executable, +log_from_pipe(int fd, int severity, const char *executable, int *child_status) { char buf[256]; enum stream_status r; for (;;) { - r = get_string_from_pipe(stream, buf, sizeof(buf) - 1); + r = get_string_from_pipe(fd, buf, sizeof(buf) - 1); if (r == IO_STREAM_CLOSED) { return 1; @@ -5293,7 +5316,7 @@ log_from_pipe(FILE *stream, int severity, const char *executable, } #endif -/** Reads from <b>stream</b> and stores input in <b>buf_out</b> making +/** Reads from <b>fd</b> and stores input in <b>buf_out</b> making * sure it's below <b>count</b> bytes. * If the string has a trailing newline, we strip it off. * @@ -5309,52 +5332,28 @@ log_from_pipe(FILE *stream, int severity, const char *executable, * IO_STREAM_OKAY: If everything went okay and we got a string * in <b>buf_out</b>. */ enum stream_status -get_string_from_pipe(FILE *stream, char *buf_out, size_t count) +get_string_from_pipe(int fd, char *buf_out, size_t count) { - char *retval; - size_t len; + ssize_t ret; tor_assert(count <= INT_MAX); - retval = fgets(buf_out, (int)count, stream); + ret = read(fd, buf_out, count); - if (!retval) { - if (feof(stream)) { - /* Program has closed stream (probably it exited) */ - /* TODO: check error */ - return IO_STREAM_CLOSED; - } else { - if (EAGAIN == errno) { - /* Nothing more to read, try again next time */ - return IO_STREAM_EAGAIN; - } else { - /* There was a problem, abandon this child process */ - return IO_STREAM_TERM; - } - } - } else { - len = strlen(buf_out); - if (len == 0) { - /* this probably means we got a NUL at the start of the string. */ - return IO_STREAM_EAGAIN; - } - - if (buf_out[len - 1] == '\n') { - /* Remove the trailing newline */ - buf_out[len - 1] = '\0'; - } else { - /* No newline; check whether we overflowed the buffer */ - if (!feof(stream)) - log_info(LD_GENERAL, - "Line from stream was truncated: %s", buf_out); - /* TODO: What to do with this error? */ - } + if (ret == 0) + return IO_STREAM_CLOSED; + else if (ret < 0 && errno == EAGAIN) + return IO_STREAM_EAGAIN; + else if (ret < 0) + return IO_STREAM_TERM; - return IO_STREAM_OKAY; - } + if (buf_out[ret - 1] == '\n') { + /* Remove the trailing newline */ + buf_out[ret - 1] = '\0'; + } else + buf_out[ret] = '\0'; - /* We should never get here */ - return IO_STREAM_TERM; + return IO_STREAM_OKAY; } /** Parse a <b>line</b> from tor-fw-helper and issue an appropriate @@ -5591,7 +5590,7 @@ tor_check_port_forwarding(const char *filename, #ifdef _WIN32 stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO); #else - stderr_status = log_from_pipe(child_handle->stderr_handle, + stderr_status = log_from_pipe(child_handle->stderr_pipe, LOG_INFO, filename, &retval); #endif if (handle_fw_helper_output(filename, child_handle) < 0) { |