summaryrefslogtreecommitdiff
path: root/src/common/compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/compat.c')
-rw-r--r--src/common/compat.c685
1 files changed, 517 insertions, 168 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index a4e50747cd..59e3898deb 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2011, The Tor Project, Inc. */
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -15,13 +15,17 @@
/* This is required on rh7 to make strptime not complain.
* We also need it to make memmem get defined (where available)
*/
-/* XXXX023 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf,
- * and get this (and other important stuff!) automatically */
+/* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf,
+ * and get this (and other important stuff!) automatically. Once we do that,
+ * make sure to also change the extern char **environ detection in
+ * configure.in, because whether that is declared or not depends on whether
+ * we have _GNU_SOURCE defined! Maybe that means that once we take this out,
+ * we can also take out the configure check. */
#define _GNU_SOURCE
#include "compat.h"
-#ifdef MS_WINDOWS
+#ifdef _WIN32
#include <process.h>
#include <windows.h>
#include <sys/locking.h>
@@ -51,6 +55,9 @@
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
+#ifdef HAVE_CRT_EXTERNS_H
+#include <crt_externs.h>
+#endif
#ifndef HAVE_GETTIMEOFDAY
#ifdef HAVE_FTIME
@@ -58,6 +65,14 @@
#endif
#endif
+/* Includes for the process attaching prevention */
+#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
+#include <sys/prctl.h>
+#elif defined(__APPLE__)
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#endif
+
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
@@ -103,7 +118,45 @@
#include "strlcat.c"
#endif
-#ifdef HAVE_SYS_MMAN_H
+/** As open(path, flags, mode), but return an fd with the close-on-exec mode
+ * set. */
+int
+tor_open_cloexec(const char *path, int flags, unsigned mode)
+{
+ int fd;
+#ifdef O_CLOEXEC
+ fd = open(path, flags|O_CLOEXEC, mode);
+ if (fd >= 0)
+ return fd;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * even though we were built on a system with O_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return -1;
+#endif
+
+ fd = open(path, flags, mode);
+#ifdef FD_CLOEXEC
+ if (fd >= 0)
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+ return fd;
+}
+
+/** As fopen(path,mode), but ensures that the O_CLOEXEC bit is set on the
+ * underlying file handle. */
+FILE *
+tor_fopen_cloexec(const char *path, const char *mode)
+{
+ FILE *result = fopen(path, mode);
+#ifdef FD_CLOEXEC
+ if (result != NULL)
+ fcntl(fileno(result), F_SETFD, FD_CLOEXEC);
+#endif
+ return result;
+}
+
+#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN)
/** Try to create a memory mapping for <b>filename</b> and return it. On
* failure, return NULL. Sets errno properly, using ERANGE to mean
* "empty file". */
@@ -118,7 +171,7 @@ tor_mmap_file(const char *filename)
tor_assert(filename);
- fd = open(filename, O_RDONLY, 0);
+ fd = tor_open_cloexec(filename, O_RDONLY, 0);
if (fd<0) {
int save_errno = errno;
int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
@@ -168,47 +221,55 @@ tor_munmap_file(tor_mmap_t *handle)
munmap((char*)handle->data, handle->mapping_size);
tor_free(handle);
}
-#elif defined(MS_WINDOWS)
+#elif defined(_WIN32)
tor_mmap_t *
tor_mmap_file(const char *filename)
{
TCHAR tfilename[MAX_PATH]= {0};
tor_mmap_t *res = tor_malloc_zero(sizeof(tor_mmap_t));
int empty = 0;
- res->file_handle = INVALID_HANDLE_VALUE;
+ HANDLE file_handle = INVALID_HANDLE_VALUE;
+ DWORD size_low, size_high;
+ uint64_t real_size;
res->mmap_handle = NULL;
#ifdef UNICODE
mbstowcs(tfilename,filename,MAX_PATH);
#else
strlcpy(tfilename,filename,MAX_PATH);
#endif
- res->file_handle = CreateFile(tfilename,
- GENERIC_READ, FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- 0);
+ file_handle = CreateFile(tfilename,
+ GENERIC_READ, FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
- if (res->file_handle == INVALID_HANDLE_VALUE)
+ if (file_handle == INVALID_HANDLE_VALUE)
goto win_err;
- res->size = GetFileSize(res->file_handle, NULL);
+ size_low = GetFileSize(file_handle, &size_high);
- if (res->size == 0) {
+ if (size_low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
+ log_warn(LD_FS,"Error getting size of \"%s\".",filename);
+ goto win_err;
+ }
+ if (size_low == 0 && size_high == 0) {
log_info(LD_FS,"File \"%s\" is empty. Ignoring.",filename);
empty = 1;
goto err;
}
+ real_size = (((uint64_t)size_high)<<32) | size_low;
+ if (real_size > SIZE_MAX) {
+ log_warn(LD_FS,"File \"%s\" is too big to map; not trying.",filename);
+ goto err;
+ }
+ res->size = real_size;
- res->mmap_handle = CreateFileMapping(res->file_handle,
+ res->mmap_handle = CreateFileMapping(file_handle,
NULL,
PAGE_READONLY,
-#if SIZEOF_SIZE_T > 4
- (res->base.size >> 32),
-#else
- 0,
-#endif
- (res->size & 0xfffffffful),
+ size_high,
+ size_low,
NULL);
if (res->mmap_handle == NULL)
goto win_err;
@@ -218,6 +279,7 @@ tor_mmap_file(const char *filename)
if (!res->data)
goto win_err;
+ CloseHandle(file_handle);
return res;
win_err: {
DWORD e = GetLastError();
@@ -234,6 +296,8 @@ tor_mmap_file(const char *filename)
err:
if (empty)
errno = ERANGE;
+ if (file_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(file_handle);
tor_munmap_file(res);
return NULL;
}
@@ -247,8 +311,6 @@ tor_munmap_file(tor_mmap_t *handle)
if (handle->mmap_handle != NULL)
CloseHandle(handle->mmap_handle);
- if (handle->file_handle != INVALID_HANDLE_VALUE)
- CloseHandle(handle->file_handle);
tor_free(handle);
}
#else
@@ -270,7 +332,7 @@ tor_munmap_file(tor_mmap_t *handle)
{
char *d = (char*)handle->data;
tor_free(d);
- memset(handle, 0, sizeof(tor_mmap_t));
+ memwipe(handle, 0, sizeof(tor_mmap_t));
tor_free(handle);
}
#endif
@@ -304,7 +366,7 @@ tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
return -1; /* no place for the NUL */
if (size > SIZE_T_CEILING)
return -1;
-#ifdef MS_WINDOWS
+#ifdef _WIN32
r = _vsnprintf(str, size, format, args);
#else
r = vsnprintf(str, size, format, args);
@@ -447,10 +509,13 @@ tor_memmem(const void *_haystack, size_t hlen,
#endif
}
-/* Tables to implement ctypes-replacement TOR_IS*() functions. Each table
+/**
+ * Tables to implement ctypes-replacement TOR_IS*() functions. Each table
* has 256 bits to look up whether a character is in some set or not. This
* fails on non-ASCII platforms, but it is hard to find a platform whose
* character set is not a superset of ASCII nowadays. */
+
+/**@{*/
const uint32_t TOR_ISALPHA_TABLE[8] =
{ 0, 0, 0x7fffffe, 0x7fffffe, 0, 0, 0, 0 };
const uint32_t TOR_ISALNUM_TABLE[8] =
@@ -463,8 +528,10 @@ const uint32_t TOR_ISPRINT_TABLE[8] =
{ 0, 0xffffffff, 0xffffffff, 0x7fffffff, 0, 0, 0, 0x0 };
const uint32_t TOR_ISUPPER_TABLE[8] = { 0, 0, 0x7fffffe, 0, 0, 0, 0, 0 };
const uint32_t TOR_ISLOWER_TABLE[8] = { 0, 0, 0, 0x7fffffe, 0, 0, 0, 0 };
-/* Upper-casing and lowercasing tables to map characters to upper/lowercase
- * equivalents. */
+
+/** Upper-casing and lowercasing tables to map characters to upper/lowercase
+ * equivalents. Used by tor_toupper() and tor_tolower(). */
+/**@{*/
const char TOR_TOUPPER_TABLE[256] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
@@ -501,6 +568,22 @@ const char TOR_TOLOWER_TABLE[256] = {
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
};
+/**@}*/
+
+/** Helper for tor_strtok_r_impl: Advances cp past all characters in
+ * <b>sep</b>, and returns its new value. */
+static char *
+strtok_helper(char *cp, const char *sep)
+{
+ if (sep[1]) {
+ while (*cp && strchr(sep, *cp))
+ ++cp;
+ } else {
+ while (*cp && *cp == *sep)
+ ++cp;
+ }
+ return cp;
+}
/** Implementation of strtok_r for platforms whose coders haven't figured out
* how to write one. Hey guys! You can use this code here for free! */
@@ -508,19 +591,22 @@ char *
tor_strtok_r_impl(char *str, const char *sep, char **lasts)
{
char *cp, *start;
- if (str)
+ tor_assert(*sep);
+ if (str) {
+ str = strtok_helper(str, sep);
+ if (!*str)
+ return NULL;
start = cp = *lasts = str;
- else if (!*lasts)
+ } else if (!*lasts || !**lasts) {
return NULL;
- else
+ } else {
start = cp = *lasts;
+ }
- tor_assert(*sep);
if (sep[1]) {
while (*cp && !strchr(sep, *cp))
++cp;
} else {
- tor_assert(strlen(sep) == 1);
cp = strchr(cp, *sep);
}
@@ -528,12 +614,12 @@ tor_strtok_r_impl(char *str, const char *sep, char **lasts)
*lasts = NULL;
} else {
*cp++ = '\0';
- *lasts = cp;
+ *lasts = strtok_helper(cp, sep);
}
return start;
}
-#ifdef MS_WINDOWS
+#ifdef _WIN32
/** Take a filename and return a pointer to its final element. This
* function is called on __FILE__ to fix a MSVC nit where __FILE__
* contains the full path to the file. This is bad, because it
@@ -633,7 +719,7 @@ set_uint64(void *cp, uint64_t v)
int
replace_file(const char *from, const char *to)
{
-#ifndef MS_WINDOWS
+#ifndef _WIN32
return rename(from,to);
#else
switch (file_status(to))
@@ -695,14 +781,14 @@ tor_lockfile_lock(const char *filename, int blocking, int *locked_out)
*locked_out = 0;
log_info(LD_FS, "Locking \"%s\"", filename);
- fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (fd < 0) {
log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename,
strerror(errno));
return NULL;
}
-#ifdef WIN32
+#ifdef _WIN32
_lseek(fd, 0, SEEK_SET);
if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) {
if (errno != EACCES && errno != EDEADLOCK)
@@ -751,7 +837,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile)
tor_assert(lockfile);
log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename);
-#ifdef WIN32
+#ifdef _WIN32
_lseek(lockfile->fd, 0, SEEK_SET);
if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) {
log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename,
@@ -787,7 +873,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile)
off_t
tor_fd_getpos(int fd)
{
-#ifdef WIN32
+#ifdef _WIN32
return (off_t) _lseek(fd, 0, SEEK_CUR);
#else
return (off_t) lseek(fd, 0, SEEK_CUR);
@@ -798,7 +884,7 @@ tor_fd_getpos(int fd)
int
tor_fd_seekend(int fd)
{
-#ifdef WIN32
+#ifdef _WIN32
return _lseek(fd, 0, SEEK_END) < 0 ? -1 : 0;
#else
return lseek(fd, 0, SEEK_END) < 0 ? -1 : 0;
@@ -851,7 +937,7 @@ tor_close_socket(tor_socket_t s)
* tor_close_socket to close sockets, and always using close() on
* files.
*/
-#if defined(MS_WINDOWS)
+#if defined(_WIN32)
r = closesocket(s);
#else
r = close(s);
@@ -872,7 +958,7 @@ tor_close_socket(tor_socket_t s)
} else {
int err = tor_socket_errno(-1);
log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
-#ifdef WIN32
+#ifdef _WIN32
if (err != WSAENOTSOCK)
--n_sockets_open;
#else
@@ -922,13 +1008,33 @@ mark_socket_open(tor_socket_t s)
tor_socket_t
tor_open_socket(int domain, int type, int protocol)
{
- tor_socket_t s = socket(domain, type, protocol);
- if (SOCKET_OK(s)) {
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
- }
+ tor_socket_t s;
+#ifdef SOCK_CLOEXEC
+ s = socket(domain, type|SOCK_CLOEXEC, protocol);
+ if (SOCKET_OK(s))
+ goto socket_ok;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * even though we were built on a system with SOCK_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return s;
+#endif /* SOCK_CLOEXEC */
+
+ s = socket(domain, type, protocol);
+ if (! SOCKET_OK(s))
+ return s;
+
+#if defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+
+ goto socket_ok; /* So that socket_ok will not be unused. */
+
+ socket_ok:
+ socket_accounting_lock();
+ ++n_sockets_open;
+ mark_socket_open(s);
+ socket_accounting_unlock();
return s;
}
@@ -936,13 +1042,34 @@ tor_open_socket(int domain, int type, int protocol)
tor_socket_t
tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
{
- tor_socket_t s = accept(sockfd, addr, len);
- if (SOCKET_OK(s)) {
- socket_accounting_lock();
- ++n_sockets_open;
- mark_socket_open(s);
- socket_accounting_unlock();
- }
+ tor_socket_t s;
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+ s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
+ if (SOCKET_OK(s))
+ goto socket_ok;
+ /* If we got an error, see if it is ENOSYS. ENOSYS indicates that,
+ * even though we were built on a system with accept4 support, we
+ * are running on one without. Also, check for EINVAL, which indicates that
+ * we are missing SOCK_CLOEXEC support. */
+ if (errno != EINVAL && errno != ENOSYS)
+ return s;
+#endif
+
+ s = accept(sockfd, addr, len);
+ if (!SOCKET_OK(s))
+ return s;
+
+#if defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+
+ goto socket_ok; /* So that socket_ok will not be unused. */
+
+ socket_ok:
+ socket_accounting_lock();
+ ++n_sockets_open;
+ mark_socket_open(s);
+ socket_accounting_unlock();
return s;
}
@@ -962,7 +1089,7 @@ get_n_open_sockets(void)
void
set_socket_nonblocking(tor_socket_t socket)
{
-#if defined(MS_WINDOWS)
+#if defined(_WIN32)
unsigned long nonblocking = 1;
ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking);
#else
@@ -991,22 +1118,45 @@ int
tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
{
//don't use win32 socketpairs (they are always bad)
-#if defined(HAVE_SOCKETPAIR) && !defined(MS_WINDOWS)
+#if defined(HAVE_SOCKETPAIR) && !defined(_WIN32)
int r;
+
+#ifdef SOCK_CLOEXEC
+ r = socketpair(family, type|SOCK_CLOEXEC, protocol, fd);
+ if (r == 0)
+ goto sockets_ok;
+ /* If we got an error, see if it is EINVAL. EINVAL might indicate that,
+ * even though we were built on a system with SOCK_CLOEXEC support, we
+ * are running on one without. */
+ if (errno != EINVAL)
+ return -errno;
+#endif
+
r = socketpair(family, type, protocol, fd);
- if (r == 0) {
- socket_accounting_lock();
- if (fd[0] >= 0) {
- ++n_sockets_open;
- mark_socket_open(fd[0]);
- }
- if (fd[1] >= 0) {
- ++n_sockets_open;
- mark_socket_open(fd[1]);
- }
- socket_accounting_unlock();
+ if (r < 0)
+ return -errno;
+
+#if defined(FD_CLOEXEC)
+ if (SOCKET_OK(fd[0]))
+ fcntl(fd[0], F_SETFD, FD_CLOEXEC);
+ if (SOCKET_OK(fd[1]))
+ fcntl(fd[1], F_SETFD, FD_CLOEXEC);
+#endif
+ goto sockets_ok; /* So that sockets_ok will not be unused. */
+
+ sockets_ok:
+ socket_accounting_lock();
+ if (SOCKET_OK(fd[0])) {
+ ++n_sockets_open;
+ mark_socket_open(fd[0]);
}
- return r < 0 ? -errno : r;
+ if (SOCKET_OK(fd[1])) {
+ ++n_sockets_open;
+ mark_socket_open(fd[1]);
+ }
+ socket_accounting_unlock();
+
+ return 0;
#else
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
@@ -1026,7 +1176,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
|| family != AF_UNIX
#endif
) {
-#ifdef MS_WINDOWS
+#ifdef _WIN32
return -WSAEAFNOSUPPORT;
#else
return -EAFNOSUPPORT;
@@ -1037,7 +1187,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
}
listener = tor_open_socket(AF_INET, type, 0);
- if (listener < 0)
+ if (!SOCKET_OK(listener))
return -tor_socket_errno(-1);
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
@@ -1050,7 +1200,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
goto tidy_up_and_fail;
connector = tor_open_socket(AF_INET, type, 0);
- if (connector < 0)
+ if (!SOCKET_OK(connector))
goto tidy_up_and_fail;
/* We want to find out the port number to connect to. */
size = sizeof(connect_addr);
@@ -1065,7 +1215,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
size = sizeof(listen_addr);
acceptor = tor_accept_socket(listener,
(struct sockaddr *) &listen_addr, &size);
- if (acceptor < 0)
+ if (!SOCKET_OK(acceptor))
goto tidy_up_and_fail;
if (size != sizeof(listen_addr))
goto abort_tidy_up_and_fail;
@@ -1086,7 +1236,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return 0;
abort_tidy_up_and_fail:
-#ifdef MS_WINDOWS
+#ifdef _WIN32
saved_errno = WSAECONNABORTED;
#else
saved_errno = ECONNABORTED; /* I hope this is portable and appropriate. */
@@ -1108,13 +1258,16 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
* tell Tor it's allowed to use. */
#define ULIMIT_BUFFER 32 /* keep 32 extra fd's beyond _ConnLimit */
-/** Learn the maximum allowed number of file descriptors. (Some systems
- * have a low soft limit.
+/** Learn the maximum allowed number of file descriptors, and tell the system
+ * we want to use up to that number. (Some systems have a low soft limit, and
+ * let us set it higher.)
*
* We compute this by finding the largest number that we can use.
* If we can't find a number greater than or equal to <b>limit</b>,
* then we fail: return -1.
*
+ * If <b>limit</b> is 0, then do not adjust the current maximum.
+ *
* Otherwise, return 0 and store the maximum we found inside <b>max_out</b>.*/
int
set_max_file_descriptors(rlim_t limit, int *max_out)
@@ -1128,7 +1281,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
#if defined(CYGWIN) || defined(__CYGWIN__)
const char *platform = "Cygwin";
const unsigned long MAX_CONNECTIONS = 3200;
-#elif defined(MS_WINDOWS)
+#elif defined(_WIN32)
const char *platform = "Windows";
const unsigned long MAX_CONNECTIONS = 15000;
#else
@@ -1147,14 +1300,20 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
limit = MAX_CONNECTIONS;
#else /* HAVE_GETRLIMIT */
struct rlimit rlim;
- tor_assert(limit > 0);
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
log_warn(LD_NET, "Could not get maximum number of file descriptors: %s",
strerror(errno));
return -1;
}
-
+ if (limit == 0) {
+ /* If limit == 0, return the maximum value without setting it. */
+ limit = rlim.rlim_max;
+ if (limit > INT_MAX)
+ limit = INT_MAX;
+ *max_out = (int)limit - ULIMIT_BUFFER;
+ return 0;
+ }
if (rlim.rlim_max < limit) {
log_warn(LD_CONFIG,"We need %lu file descriptors available, and we're "
"limited to %lu. Please change your ulimit -n.",
@@ -1211,7 +1370,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
return 0;
}
-#ifndef MS_WINDOWS
+#ifndef _WIN32
/** Log details of current user and group credentials. Return 0 on
* success. Logs and return -1 on failure.
*/
@@ -1288,31 +1447,19 @@ log_credential_status(void)
return -1;
} else {
int i, retval = 0;
- char *strgid;
char *s = NULL;
- smartlist_t *elts = smartlist_create();
+ smartlist_t *elts = smartlist_new();
for (i = 0; i<ngids; i++) {
- strgid = tor_malloc(11);
- if (tor_snprintf(strgid, 11, "%u", (unsigned)sup_gids[i]) < 0) {
- log_warn(LD_GENERAL, "Error printing supplementary GIDs");
- tor_free(strgid);
- retval = -1;
- goto error;
- }
- smartlist_add(elts, strgid);
+ smartlist_add_asprintf(elts, "%u", (unsigned)sup_gids[i]);
}
s = smartlist_join_strings(elts, " ", 0, NULL);
log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL, "Supplementary groups are: %s",s);
- error:
tor_free(s);
- SMARTLIST_FOREACH(elts, char *, cp,
- {
- tor_free(cp);
- });
+ SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_free(elts);
tor_free(sup_gids);
@@ -1329,7 +1476,7 @@ log_credential_status(void)
int
switch_id(const char *user)
{
-#ifndef MS_WINDOWS
+#ifndef _WIN32
struct passwd *pw = NULL;
uid_t old_uid;
gid_t old_gid;
@@ -1464,6 +1611,58 @@ switch_id(const char *user)
#endif
}
+/* We only use the linux prctl for now. There is no Win32 support; this may
+ * also work on various BSD systems and Mac OS X - send testing feedback!
+ *
+ * On recent Gnu/Linux kernels it is possible to create a system-wide policy
+ * that will prevent non-root processes from attaching to other processes
+ * unless they are the parent process; thus gdb can attach to programs that
+ * they execute but they cannot attach to other processes running as the same
+ * user. The system wide policy may be set with the sysctl
+ * kernel.yama.ptrace_scope or by inspecting
+ * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04.
+ *
+ * This ptrace scope will be ignored on Gnu/Linux for users with
+ * CAP_SYS_PTRACE and so it is very likely that root will still be able to
+ * attach to the Tor process.
+ */
+/** Attempt to disable debugger attachment: return 1 on success, -1 on
+ * failure, and 0 if we don't know how to try on this platform. */
+int
+tor_disable_debugger_attach(void)
+{
+ int r, attempted;
+ r = -1;
+ attempted = 0;
+ log_debug(LD_CONFIG,
+ "Attemping to disable debugger attachment to Tor for "
+ "unprivileged users.");
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL)
+#ifdef PR_SET_DUMPABLE
+ attempted = 1;
+ r = prctl(PR_SET_DUMPABLE, 0);
+#endif
+#endif
+#if defined(__APPLE__) && defined(PT_DENY_ATTACH)
+ if (r < 0) {
+ attempted = 1;
+ r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
+ }
+#endif
+
+ // XXX: TODO - Mac OS X has dtrace and this may be disabled.
+ // XXX: TODO - Windows probably has something similar
+ if (r == 0 && attempted) {
+ log_debug(LD_CONFIG,"Debugger attachment disabled for "
+ "unprivileged users.");
+ return 1;
+ } else if (attempted) {
+ log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s",
+ strerror(errno));
+ }
+ return r;
+}
+
#ifdef HAVE_PWD_H
/** Allocate and return a string containing the home directory for the
* user <b>username</b>. Only works on posix-like systems. */
@@ -1481,14 +1680,18 @@ get_user_homedir(const char *username)
}
#endif
-/** Modify <b>fname</b> to contain the name of the directory */
+/** Modify <b>fname</b> to contain the name of its parent directory. Doesn't
+ * actually examine the filesystem; does a purely syntactic modification.
+ *
+ * The parent of the root director is considered to be iteself.
+ * */
int
get_parent_directory(char *fname)
{
char *cp;
int at_end = 1;
tor_assert(fname);
-#ifdef MS_WINDOWS
+#ifdef _WIN32
/* If we start with, say, c:, then don't consider that the start of the path
*/
if (fname[0] && fname[1] == ':') {
@@ -1503,13 +1706,18 @@ get_parent_directory(char *fname)
*/
cp = fname + strlen(fname);
at_end = 1;
- while (--cp > fname) {
+ while (--cp >= fname) {
int is_sep = (*cp == '/'
-#ifdef MS_WINDOWS
+#ifdef _WIN32
|| *cp == '\\'
#endif
);
if (is_sep) {
+ if (cp == fname) {
+ /* This is the first separator in the file name; don't remove it! */
+ cp[1] = '\0';
+ return 0;
+ }
*cp = '\0';
if (! at_end)
return 0;
@@ -1520,6 +1728,101 @@ get_parent_directory(char *fname)
return -1;
}
+#ifndef _WIN32
+/** Return a newly allocated string containing the output of getcwd(). Return
+ * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since
+ * Hurd hasn't got a PATH_MAX.)
+ */
+static char *
+alloc_getcwd(void)
+{
+ int saved_errno = errno;
+/* We use this as a starting path length. Not too large seems sane. */
+#define START_PATH_LENGTH 128
+/* Nobody has a maxpath longer than this, as far as I know. And if they
+ * do, they shouldn't. */
+#define MAX_SANE_PATH_LENGTH 4096
+ size_t path_length = START_PATH_LENGTH;
+ char *path = tor_malloc(path_length);
+
+ errno = 0;
+ while (getcwd(path, path_length) == NULL) {
+ if (errno == ERANGE && path_length < MAX_SANE_PATH_LENGTH) {
+ path_length*=2;
+ path = tor_realloc(path, path_length);
+ } else {
+ tor_free(path);
+ path = NULL;
+ break;
+ }
+ }
+ errno = saved_errno;
+ return path;
+}
+#endif
+
+/** Expand possibly relative path <b>fname</b> to an absolute path.
+ * Return a newly allocated string, possibly equal to <b>fname</b>. */
+char *
+make_path_absolute(char *fname)
+{
+#ifdef _WIN32
+ char *absfname_malloced = _fullpath(NULL, fname, 1);
+
+ /* We don't want to assume that tor_free can free a string allocated
+ * with malloc. On failure, return fname (it's better than nothing). */
+ char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
+ if (absfname_malloced) free(absfname_malloced);
+
+ return absfname;
+#else
+ char *absfname = NULL, *path = NULL;
+
+ tor_assert(fname);
+
+ if (fname[0] == '/') {
+ absfname = tor_strdup(fname);
+ } else {
+ path = alloc_getcwd();
+ if (path) {
+ tor_asprintf(&absfname, "%s/%s", path, fname);
+ tor_free(path);
+ } else {
+ /* If getcwd failed, the best we can do here is keep using the
+ * relative path. (Perhaps / isn't readable by this UID/GID.) */
+ log_warn(LD_GENERAL, "Unable to find current working directory: %s",
+ strerror(errno));
+ absfname = tor_strdup(fname);
+ }
+ }
+ return absfname;
+#endif
+}
+
+#ifndef HAVE__NSGETENVIRON
+#ifndef HAVE_EXTERN_ENVIRON_DECLARED
+/* Some platforms declare environ under some circumstances, others don't. */
+#ifndef RUNNING_DOXYGEN
+extern char **environ;
+#endif
+#endif
+#endif
+
+/** Return the current environment. This is a portable replacement for
+ * 'environ'. */
+char **
+get_environment(void)
+{
+#ifdef HAVE__NSGETENVIRON
+ /* This is for compatibility between OSX versions. Otherwise (for example)
+ * when we do a mostly-static build on OSX 10.7, the resulting binary won't
+ * work on OSX 10.6. */
+ return *_NSGetEnviron();
+#else
+ return environ;
+#endif
+}
+
/** Set *addr to the IP address (in dotted-quad notation) stored in c.
* Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr),
* but works on Windows and Solaris.)
@@ -1577,7 +1880,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len)
addr->s6_addr[12], addr->s6_addr[13],
addr->s6_addr[14], addr->s6_addr[15]);
}
- if (strlen(buf) > len)
+ if ((strlen(buf) + 1) > len) /* +1 for \0 */
return NULL;
strlcpy(dst, buf, len);
return dst;
@@ -1618,7 +1921,7 @@ tor_inet_ntop(int af, const void *src, char *dst, size_t len)
}
}
*cp = '\0';
- if (strlen(buf) > len)
+ if ((strlen(buf) + 1) > len) /* +1 for \0 */
return NULL;
strlcpy(dst, buf, len);
return dst;
@@ -1678,24 +1981,30 @@ tor_inet_pton(int af, const char *src, void *dst)
return 0;
if (TOR_ISXDIGIT(*src)) {
char *next;
+ ssize_t len;
long r = strtol(src, &next, 16);
- if (next > 4+src)
- return 0;
- if (next == src)
- return 0;
- if (r<0 || r>65536)
+ tor_assert(next != NULL);
+ tor_assert(next != src);
+
+ len = *next == '\0' ? eow - src : next - src;
+ if (len > 4)
return 0;
+ if (len > 1 && !TOR_ISXDIGIT(src[1]))
+ return 0; /* 0x is not valid */
+ tor_assert(r >= 0);
+ tor_assert(r < 65536);
words[i++] = (uint16_t)r;
setWords++;
src = next;
if (*src != ':' && src != eow)
return 0;
++src;
- } else if (*src == ':' && i > 0 && gapPos==-1) {
+ } else if (*src == ':' && i > 0 && gapPos == -1) {
gapPos = i;
++src;
- } else if (*src == ':' && i == 0 && src[1] == ':' && gapPos==-1) {
+ } else if (*src == ':' && i == 0 && src+1 < eow && src[1] == ':' &&
+ gapPos == -1) {
gapPos = i;
src += 2;
} else {
@@ -1755,7 +2064,7 @@ tor_lookup_hostname(const char *name, uint32_t *addr)
void
tor_init_weak_random(unsigned seed)
{
-#ifdef MS_WINDOWS
+#ifdef _WIN32
srand(seed);
#else
srandom(seed);
@@ -1768,7 +2077,7 @@ tor_init_weak_random(unsigned seed)
long
tor_weak_random(void)
{
-#ifdef MS_WINDOWS
+#ifdef _WIN32
return rand();
#else
return random();
@@ -1792,17 +2101,14 @@ get_uname(void)
#ifdef HAVE_UNAME
if (uname(&u) != -1) {
/* (Linux says 0 is success, Solaris says 1 is success) */
- tor_snprintf(uname_result, sizeof(uname_result), "%s %s",
- u.sysname, u.machine);
+ strlcpy(uname_result, u.sysname, sizeof(uname_result));
} else
#endif
{
-#ifdef MS_WINDOWS
+#ifdef _WIN32
OSVERSIONINFOEX info;
int i;
const char *plat = NULL;
- const char *extra = NULL;
- char acsd[MAX_PATH] = {0};
static struct {
unsigned major; unsigned minor; const char *version;
} win_version_table[] = {
@@ -1827,20 +2133,11 @@ get_uname(void)
uname_result_is_set = 1;
return uname_result;
}
-#ifdef UNICODE
- wcstombs(acsd, info.szCSDVersion, MAX_PATH);
-#else
- strlcpy(acsd, info.szCSDVersion, sizeof(acsd));
-#endif
if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) {
if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
plat = "Windows NT 4.0";
else
plat = "Windows 95";
- if (acsd[1] == 'B')
- extra = "OSR2 (B)";
- else if (acsd[1] == 'C')
- extra = "OSR2 (C)";
} else {
for (i=0; win_version_table[i].major>0; ++i) {
if (win_version_table[i].major == info.dwMajorVersion &&
@@ -1850,39 +2147,25 @@ get_uname(void)
}
}
}
- if (plat && !strcmp(plat, "Windows 98")) {
- if (acsd[1] == 'A')
- extra = "SE (A)";
- else if (acsd[1] == 'B')
- extra = "SE (B)";
- }
if (plat) {
- if (!extra)
- extra = acsd;
- tor_snprintf(uname_result, sizeof(uname_result), "%s %s",
- plat, extra);
+ strlcpy(uname_result, plat, sizeof(uname_result));
} else {
if (info.dwMajorVersion > 6 ||
(info.dwMajorVersion==6 && info.dwMinorVersion>2))
tor_snprintf(uname_result, sizeof(uname_result),
- "Very recent version of Windows [major=%d,minor=%d] %s",
- (int)info.dwMajorVersion,(int)info.dwMinorVersion,
- acsd);
+ "Very recent version of Windows [major=%d,minor=%d]",
+ (int)info.dwMajorVersion,(int)info.dwMinorVersion);
else
tor_snprintf(uname_result, sizeof(uname_result),
- "Unrecognized version of Windows [major=%d,minor=%d] %s",
- (int)info.dwMajorVersion,(int)info.dwMinorVersion,
- acsd);
+ "Unrecognized version of Windows [major=%d,minor=%d]",
+ (int)info.dwMajorVersion,(int)info.dwMinorVersion);
}
#if !defined (WINCE)
-#ifdef VER_SUITE_BACKOFFICE
- if (info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
- strlcat(uname_result, " [domain controller]", sizeof(uname_result));
- } else if (info.wProductType == VER_NT_SERVER) {
- strlcat(uname_result, " [server]", sizeof(uname_result));
- } else if (info.wProductType == VER_NT_WORKSTATION) {
- strlcat(uname_result, " [workstation]", sizeof(uname_result));
- }
+#ifdef VER_NT_SERVER
+ if (info.wProductType == VER_NT_SERVER ||
+ info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
+ strlcat(uname_result, " [server]", sizeof(uname_result));
+ }
#endif
#endif
#else
@@ -1995,13 +2278,59 @@ spawn_exit(void)
#endif
}
+/** Implementation logic for compute_num_cpus(). */
+static int
+compute_num_cpus_impl(void)
+{
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ memset(&info, 0, sizeof(info));
+ GetSystemInfo(&info);
+ if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX)
+ return (int)info.dwNumberOfProcessors;
+ else
+ return -1;
+#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
+ long cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpus >= 1 && cpus < INT_MAX)
+ return (int)cpus;
+ else
+ return -1;
+#else
+ return -1;
+#endif
+}
+
+#define MAX_DETECTABLE_CPUS 16
+
+/** Return how many CPUs we are running with. We assume that nobody is
+ * using hot-swappable CPUs, so we don't recompute this after the first
+ * time. Return -1 if we don't know how to tell the number of CPUs on this
+ * system.
+ */
+int
+compute_num_cpus(void)
+{
+ static int num_cpus = -2;
+ if (num_cpus == -2) {
+ num_cpus = compute_num_cpus_impl();
+ tor_assert(num_cpus != -2);
+ if (num_cpus > MAX_DETECTABLE_CPUS)
+ log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I "
+ "will not autodetect any more than %d, though. If you "
+ "want to configure more, set NumCPUs in your torrc",
+ num_cpus, MAX_DETECTABLE_CPUS);
+ }
+ return num_cpus;
+}
+
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
*/
void
tor_gettimeofday(struct timeval *timeval)
{
-#ifdef MS_WINDOWS
+#ifdef _WIN32
/* Epoch bias copied from perl: number of units between windows epoch and
* Unix epoch. */
#define EPOCH_BIAS U64_LITERAL(116444736000000000)
@@ -2046,12 +2375,18 @@ tor_gettimeofday(struct timeval *timeval)
return;
}
-#if defined(TOR_IS_MULTITHREADED) && !defined(MS_WINDOWS)
+#if defined(TOR_IS_MULTITHREADED) && !defined(_WIN32)
/** Defined iff we need to add locks when defining fake versions of reentrant
* versions of time-related functions. */
#define TIME_FNS_NEED_LOCKS
#endif
+/** Helper: Deal with confused or out-of-bounds values from localtime_r and
+ * friends. (On some platforms, they can give out-of-bounds values or can
+ * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise
+ * it's from gmtime. The function returned <b>r</b>, when given <b>timep</b>
+ * as its input. If we need to store new results, store them in
+ * <b>resultbuf</b>. */
static struct tm *
correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
struct tm *r)
@@ -2396,7 +2731,7 @@ tor_cond_new(void)
{
tor_cond_t *cond = tor_malloc_zero(sizeof(tor_cond_t));
InitializeCriticalSection(&cond->mutex);
- cond->events = smartlist_create();
+ cond->events = smartlist_new();
return cond;
}
void
@@ -2590,7 +2925,7 @@ in_main_thread(void)
* should call tor_socket_errno <em>at most once</em> on the failing
* socket to get the error.
*/
-#if defined(MS_WINDOWS)
+#if defined(_WIN32)
int
tor_socket_errno(tor_socket_t sock)
{
@@ -2606,7 +2941,7 @@ tor_socket_errno(tor_socket_t sock)
}
#endif
-#if defined(MS_WINDOWS)
+#if defined(_WIN32)
#define E(code, s) { code, (s " [" #code " ]") }
struct { int code; const char *msg; } windows_socket_errors[] = {
E(WSAEINTR, "Interrupted function call"),
@@ -2688,7 +3023,7 @@ tor_socket_strerror(int e)
int
network_init(void)
{
-#ifdef MS_WINDOWS
+#ifdef _WIN32
/* This silly exercise is necessary before windows will allow
* gethostbyname to work. */
WSADATA WSAData;
@@ -2698,6 +3033,11 @@ network_init(void)
log_warn(LD_NET,"Error initializing windows network layer: code was %d",r);
return -1;
}
+ if (sizeof(SOCKET) != sizeof(tor_socket_t)) {
+ log_warn(LD_BUG,"The tor_socket_t type does not match SOCKET in size; Tor "
+ "might not work. (Sizes are %d and %d respectively.)",
+ (int)sizeof(tor_socket_t), (int)sizeof(SOCKET));
+ }
/* WSAData.iMaxSockets might show the max sockets we're allowed to use.
* We might use it to complain if we're trying to be a server but have
* too few sockets available. */
@@ -2705,7 +3045,7 @@ network_init(void)
return 0;
}
-#ifdef MS_WINDOWS
+#ifdef _WIN32
/** Return a newly allocated string describing the windows system error code
* <b>err</b>. Note that error codes are different from errno. Error codes
* come from GetLastError() when a winapi call fails. errno is set only when
@@ -2715,28 +3055,37 @@ format_win32_error(DWORD err)
{
TCHAR *str = NULL;
char *result;
+ DWORD n;
/* Somebody once decided that this interface was better than strerror(). */
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ n = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPVOID)&str,
+ (LPVOID)&str,
0, NULL);
- if (str) {
+ if (str && n) {
#ifdef UNICODE
- char abuf[1024] = {0};
- wcstombs(abuf,str,1024);
- result = tor_strdup(abuf);
+ size_t len;
+ if (n > 128*1024)
+ len = (128 * 1024) * 2 + 1; /* This shouldn't be possible, but let's
+ * make sure. */
+ else
+ len = n * 2 + 1;
+ result = tor_malloc(len);
+ wcstombs(result,str,len);
+ result[len-1] = '\0';
#else
result = tor_strdup(str);
#endif
- LocalFree(str); /* LocalFree != free() */
} else {
result = tor_strdup("<unformattable error>");
}
+ if (str) {
+ LocalFree(str); /* LocalFree != free() */
+ }
return result;
}
#endif