aboutsummaryrefslogtreecommitdiff
path: root/src/common/compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/compat.c')
-rw-r--r--src/common/compat.c262
1 files changed, 210 insertions, 52 deletions
diff --git a/src/common/compat.c b/src/common/compat.c
index 9b8a0a450..04c9d5923 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -23,6 +23,7 @@
* we can also take out the configure check. */
#define _GNU_SOURCE
+#define COMPAT_PRIVATE
#include "compat.h"
#ifdef _WIN32
@@ -34,6 +35,12 @@
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -109,6 +116,7 @@
#include "util.h"
#include "container.h"
#include "address.h"
+#include "sandbox.h"
/* Inline the strl functions if the platform doesn't have them. */
#ifndef HAVE_STRLCPY
@@ -125,6 +133,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
{
int fd;
#ifdef O_CLOEXEC
+ path = sandbox_intern_string(path);
fd = open(path, flags|O_CLOEXEC, mode);
if (fd >= 0)
return fd;
@@ -175,9 +184,10 @@ tor_mmap_file(const char *filename)
{
int fd; /* router file */
char *string;
- int page_size;
+ int page_size, result;
tor_mmap_t *res;
size_t size, filesize;
+ struct stat st;
tor_assert(filename);
@@ -191,9 +201,22 @@ tor_mmap_file(const char *filename)
return NULL;
}
- /* XXXX why not just do fstat here? */
- size = filesize = (size_t) lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
+ /* Get the size of the file */
+ result = fstat(fd, &st);
+ if (result != 0) {
+ int save_errno = errno;
+ log_warn(LD_FS,
+ "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
+ filename, strerror(errno));
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+ size = filesize = (size_t)(st.st_size);
+ /*
+ * Should we check for weird crap like mmapping a named pipe here,
+ * or just wait for if (!size) below to fail?
+ */
/* ensure page alignment */
page_size = getpagesize();
size += (size%page_size) ? page_size-(size%page_size) : 0;
@@ -224,12 +247,27 @@ tor_mmap_file(const char *filename)
return res;
}
-/** Release storage held for a memory mapping. */
-void
+/** Release storage held for a memory mapping; returns 0 on success,
+ * or -1 on failure (and logs a warning). */
+int
tor_munmap_file(tor_mmap_t *handle)
{
- munmap((char*)handle->data, handle->mapping_size);
- tor_free(handle);
+ int res;
+
+ if (handle == NULL)
+ return 0;
+
+ res = munmap((char*)handle->data, handle->mapping_size);
+ if (res == 0) {
+ /* munmap() succeeded */
+ tor_free(handle);
+ } else {
+ log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
+ strerror(errno));
+ res = -1;
+ }
+
+ return res;
}
#elif defined(_WIN32)
tor_mmap_t *
@@ -311,17 +349,29 @@ tor_mmap_file(const char *filename)
tor_munmap_file(res);
return NULL;
}
-void
+
+/* Unmap the file, and return 0 for success or -1 for failure */
+int
tor_munmap_file(tor_mmap_t *handle)
{
- if (handle->data)
+ if (handle == NULL)
+ return 0;
+
+ if (handle->data) {
/* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
have to be redefined as non-const. */
- UnmapViewOfFile( (LPVOID) handle->data);
+ BOOL ok = UnmapViewOfFile( (LPVOID) handle->data);
+ if (!ok) {
+ log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d",
+ (int)GetLastError());
+ }
+ }
if (handle->mmap_handle != NULL)
CloseHandle(handle->mmap_handle);
tor_free(handle);
+
+ return 0;
}
#else
tor_mmap_t *
@@ -337,13 +387,25 @@ tor_mmap_file(const char *filename)
handle->size = st.st_size;
return handle;
}
-void
+
+/** Unmap the file mapped with tor_mmap_file(), and return 0 for success
+ * or -1 for failure.
+ */
+
+int
tor_munmap_file(tor_mmap_t *handle)
{
- char *d = (char*)handle->data;
+ char *d = NULL;
+ if (handle == NULL)
+ return 0;
+
+ d = (char*)handle->data;
tor_free(d);
memwipe(handle, 0, sizeof(tor_mmap_t));
tor_free(handle);
+
+ /* Can't fail in this mmap()/munmap()-free case */
+ return 0;
}
#endif
@@ -956,24 +1018,40 @@ socket_accounting_unlock(void)
}
/** As close(), but guaranteed to work for sockets across platforms (including
- * Windows, where close()ing a socket doesn't work. Returns 0 on success, -1
- * on failure. */
+ * Windows, where close()ing a socket doesn't work. Returns 0 on success and
+ * the socket error code on failure. */
int
-tor_close_socket(tor_socket_t s)
+tor_close_socket_simple(tor_socket_t s)
{
int r = 0;
/* On Windows, you have to call close() on fds returned by open(),
- * and closesocket() on fds returned by socket(). On Unix, everything
- * gets close()'d. We abstract this difference by always using
- * tor_close_socket to close sockets, and always using close() on
- * files.
- */
-#if defined(_WIN32)
- r = closesocket(s);
-#else
- r = close(s);
-#endif
+ * and closesocket() on fds returned by socket(). On Unix, everything
+ * gets close()'d. We abstract this difference by always using
+ * tor_close_socket to close sockets, and always using close() on
+ * files.
+ */
+ #if defined(_WIN32)
+ r = closesocket(s);
+ #else
+ r = close(s);
+ #endif
+
+ if (r != 0) {
+ int err = tor_socket_errno(-1);
+ log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
+ return err;
+ }
+
+ return r;
+}
+
+/** As tor_close_socket_simple(), but keeps track of the number
+ * of open sockets. Returns 0 on success, -1 on failure. */
+int
+tor_close_socket(tor_socket_t s)
+{
+ int r = tor_close_socket_simple(s);
socket_accounting_lock();
#ifdef DEBUG_SOCKET_COUNTING
@@ -988,13 +1066,11 @@ tor_close_socket(tor_socket_t s)
if (r == 0) {
--n_sockets_open;
} else {
- int err = tor_socket_errno(-1);
- log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
#ifdef _WIN32
- if (err != WSAENOTSOCK)
+ if (r != WSAENOTSOCK)
--n_sockets_open;
#else
- if (err != EBADF)
+ if (r != EBADF)
--n_sockets_open;
#endif
r = -1;
@@ -1040,33 +1116,61 @@ mark_socket_open(tor_socket_t s)
tor_socket_t
tor_open_socket(int domain, int type, int protocol)
{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
+}
+
+/** As socket(), but creates a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_open_socket_nonblocking(int domain, int type, int protocol)
+{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 1);
+}
+
+/** As socket(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_open_socket_with_extensions(int domain, int type, int protocol,
+ int cloexec, int nonblock)
+{
tor_socket_t s;
-#ifdef SOCK_CLOEXEC
- s = socket(domain, type|SOCK_CLOEXEC, protocol);
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = socket(domain, type|ext_flags, 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. */
+ * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK
+ * support, we are running on one without. */
if (errno != EINVAL)
return s;
-#endif /* SOCK_CLOEXEC */
+#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */
s = socket(domain, type, protocol);
if (! SOCKET_OK(s))
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
-#if defined(_WIN32)
- closesocket(s);
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
#else
- close(s);
+ (void)cloexec;
#endif
- return -1;
+
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
-#endif
goto socket_ok; /* So that socket_ok will not be unused. */
@@ -1078,19 +1182,41 @@ tor_open_socket(int domain, int type, int protocol)
return s;
}
-/** As socket(), but counts the number of open sockets. */
+/** As accept(), but counts the number of open sockets. */
tor_socket_t
tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0);
+}
+
+/** As accept(), but returns a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len)
+{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1);
+}
+
+/** As accept(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len, int cloexec, int nonblock)
+{
tor_socket_t s;
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = accept4(sockfd, addr, len, ext_flags);
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. */
+ * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */
if (errno != EINVAL && errno != ENOSYS)
return s;
#endif
@@ -1100,13 +1226,24 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
- close(s);
- return TOR_INVALID_SOCKET;
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
+#else
+ (void)cloexec;
#endif
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
+
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
@@ -1228,6 +1365,18 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return 0;
#else
+ return tor_ersatz_socketpair(family, type, protocol, fd);
+#endif
+}
+
+#ifdef NEED_ERSATZ_SOCKETPAIR
+/**
+ * Helper used to implement socketpair on systems that lack it, by
+ * making a direct connection to localhost.
+ */
+STATIC int
+tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
+{
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
* for now, and really, when localhost is down sometimes, we
@@ -1238,7 +1387,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t acceptor = TOR_INVALID_SOCKET;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
- int size;
+ socklen_t size;
int saved_errno = -1;
if (protocol
@@ -1321,8 +1470,8 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
if (SOCKET_OK(acceptor))
tor_close_socket(acceptor);
return -saved_errno;
-#endif
}
+#endif
/** Number of extra file descriptors to keep in reserve beyond those that we
* tell Tor it's allowed to use. */
@@ -1754,6 +1903,15 @@ get_user_homedir(const char *username)
* actually examine the filesystem; does a purely syntactic modification.
*
* The parent of the root director is considered to be iteself.
+ *
+ * Path separators are the forward slash (/) everywhere and additionally
+ * the backslash (\) on Win32.
+ *
+ * Cuts off any number of trailing path separators but otherwise ignores
+ * them for purposes of finding the parent directory.
+ *
+ * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
+ * did not have any path separators or only had them at the end).
* */
int
get_parent_directory(char *fname)