summaryrefslogtreecommitdiff
path: root/src/libstrongswan/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/utils')
-rw-r--r--src/libstrongswan/utils/backtrace.c253
-rw-r--r--src/libstrongswan/utils/capabilities.c40
-rw-r--r--src/libstrongswan/utils/chunk.c50
-rw-r--r--src/libstrongswan/utils/chunk.h11
-rw-r--r--src/libstrongswan/utils/debug.h2
-rw-r--r--src/libstrongswan/utils/enum.c8
-rw-r--r--src/libstrongswan/utils/enum.h25
-rw-r--r--src/libstrongswan/utils/identification.c21
-rw-r--r--src/libstrongswan/utils/leak_detective.c8
-rw-r--r--src/libstrongswan/utils/optionsfrom.c9
-rw-r--r--src/libstrongswan/utils/parser_helper.c261
-rw-r--r--src/libstrongswan/utils/parser_helper.h161
-rw-r--r--src/libstrongswan/utils/printf_hook/printf_hook_builtin.c124
-rw-r--r--src/libstrongswan/utils/settings.c1520
-rw-r--r--src/libstrongswan/utils/settings.h348
-rw-r--r--src/libstrongswan/utils/test.c71
-rw-r--r--src/libstrongswan/utils/test.h18
-rw-r--r--src/libstrongswan/utils/utils.c279
-rw-r--r--src/libstrongswan/utils/utils.h173
-rw-r--r--src/libstrongswan/utils/utils/strerror.c122
-rw-r--r--src/libstrongswan/utils/utils/strerror.h10
-rw-r--r--src/libstrongswan/utils/windows.c641
-rw-r--r--src/libstrongswan/utils/windows.h584
23 files changed, 2700 insertions, 2039 deletions
diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c
index f1584620b..e694caec7 100644
--- a/src/libstrongswan/utils/backtrace.c
+++ b/src/libstrongswan/utils/backtrace.c
@@ -1,6 +1,7 @@
/*
- * Copyright (C) 2006-2008 Martin Willi
+ * Copyright (C) 2006-2013 Martin Willi
* Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2013 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -15,20 +16,37 @@
#define _GNU_SOURCE
-#ifdef HAVE_DLADDR
-# include <dlfcn.h>
-#endif /* HAVE_DLADDR */
-
#ifdef HAVE_BACKTRACE
# include <execinfo.h>
#endif /* HAVE_BACKTRACE */
-
+#ifdef HAVE_DBGHELP
+# include <winsock2.h>
+# include <windows.h>
+# include <dbghelp.h>
+#endif /* HAVE_DBGHELP */
#include <string.h>
#include "backtrace.h"
#include <utils/debug.h>
+#ifdef WIN32
+# include <psapi.h>
+/* missing in MinGW */
+#ifdef WIN64
+#ifndef GetModuleInformation
+WINBOOL K32GetModuleInformation(HANDLE hProcess, HMODULE hModule,
+ LPMODULEINFO lpmodinfo, DWORD cb);
+#define GetModuleInformation K32GetModuleInformation
+#endif /* !GetModuleInformation */
+#ifndef GetModuleFileNameEx
+DWORD K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule,
+ LPTSTR lpFilename, DWORD nSize);
+#define GetModuleFileNameEx K32GetModuleFileNameExA
+#endif /* !GetModuleFileNameEx */
+#endif /* WIN64 */
+#endif
+
typedef struct private_backtrace_t private_backtrace_t;
/**
@@ -79,12 +97,10 @@ static void println(FILE *file, char *format, ...)
va_end(args);
}
-#ifdef HAVE_DLADDR
-
/**
* Same as tty_escape_get(), but for a potentially NULL FILE*
*/
-static char* esc(FILE *file, tty_escape_t escape)
+static inline char* esc(FILE *file, tty_escape_t escape)
{
if (file)
{
@@ -93,6 +109,35 @@ static char* esc(FILE *file, tty_escape_t escape)
return "";
}
+#ifdef HAVE_DBGHELP
+
+#include <dbghelp.h>
+#include <threading/mutex.h>
+
+/**
+ * Mutex to access non-thread-safe dbghelp functions
+ */
+static mutex_t *dbghelp_mutex;
+
+void backtrace_init()
+{
+ SymSetOptions(SYMOPT_LOAD_LINES);
+ SymInitialize(GetCurrentProcess(), NULL, TRUE);
+ dbghelp_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+}
+
+void backtrace_deinit()
+{
+ dbghelp_mutex->destroy(dbghelp_mutex);
+ SymCleanup(GetCurrentProcess());
+}
+
+#elif defined(HAVE_DLADDR) || defined(HAVE_BFD_H)
+
+#ifdef HAVE_DLADDR
+#include <dlfcn.h>
+#endif
+
#ifdef HAVE_BFD_H
#include <bfd.h>
@@ -352,7 +397,6 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr);
#endif /* __APPLE__ */
-
output = popen(buf, "r");
if (output)
{
@@ -375,7 +419,7 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void* base)
#endif /* HAVE_BFD_H */
-#else /* !HAVE_DLADDR */
+#else /* !HAVE_DLADDR && !HAVE_DBGHELP */
void backtrace_init() {}
void backtrace_deinit() {}
@@ -385,7 +429,7 @@ void backtrace_deinit() {}
METHOD(backtrace_t, log_, void,
private_backtrace_t *this, FILE *file, bool detailed)
{
-#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H)
+#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) || defined(WIN32)
size_t i;
char **strings = NULL;
@@ -425,7 +469,83 @@ METHOD(backtrace_t, log_, void,
}
}
else
-#endif /* HAVE_DLADDR */
+#elif defined(HAVE_DBGHELP)
+ struct {
+ SYMBOL_INFO hdr;
+ char buf[128];
+ } symbol;
+ char filename[MAX_PATH];
+ HINSTANCE module;
+ HANDLE process;
+ DWORD64 displace, frame;
+
+ process = GetCurrentProcess();
+ frame = (uintptr_t)this->frames[i];
+
+ memset(&symbol, 0, sizeof(symbol));
+ symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
+ symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
+
+ dbghelp_mutex->lock(dbghelp_mutex);
+
+ module = (HINSTANCE)SymGetModuleBase64(process, frame);
+
+ if (module && GetModuleFileName(module, filename, sizeof(filename)))
+ {
+ if (SymFromAddr(process, frame, &displace, &symbol.hdr))
+ {
+ println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]",
+ esc(file, TTY_FG_YELLOW), filename,
+ esc(file, TTY_FG_DEF), (void*)module,
+ esc(file, TTY_FG_RED), symbol.hdr.Name,
+ esc(file, TTY_FG_DEF), displace,
+ this->frames[i]);
+ }
+ else
+ {
+ println(file, " %s%s%s @ %p [%p]",
+ esc(file, TTY_FG_YELLOW), filename,
+ esc(file, TTY_FG_DEF), (void*)module, this->frames[i]);
+ }
+ if (detailed)
+ {
+ IMAGEHLP_LINE64 line;
+ DWORD off;
+
+ memset(&line, 0, sizeof(line));
+ line.SizeOfStruct = sizeof(line);
+
+ if (SymGetLineFromAddr64(process, frame, &off, &line))
+ {
+
+ println(file, " -> %s%s:%u%s", esc(file, TTY_FG_GREEN),
+ line.FileName, line.LineNumber,
+ esc(file, TTY_FG_DEF));
+ }
+ }
+ }
+ else
+#elif defined(WIN32)
+ HMODULE module;
+ MODULEINFO info;
+ char filename[MAX_PATH];
+
+ if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+ this->frames[i], &module) &&
+ GetModuleInformation(GetCurrentProcess(), module,
+ &info, sizeof(info)) &&
+ GetModuleFileNameEx(GetCurrentProcess(), module,
+ filename, sizeof(filename)))
+ {
+ println(file, " %s%s%s @ %p [%p]",
+ esc(file, TTY_FG_YELLOW), filename,
+ esc(file, TTY_FG_DEF), info.lpBaseOfDll, this->frames[i]);
+#ifdef HAVE_BFD_H
+ print_sourceline(file, filename, this->frames[i], info.lpBaseOfDll);
+#endif /* HAVE_BFD_H */
+ }
+ else
+#endif /* HAVE_DLADDR/HAVE_DBGHELP */
{
#ifdef HAVE_BACKTRACE
if (!strings)
@@ -442,10 +562,13 @@ METHOD(backtrace_t, log_, void,
println(file, " %p", this->frames[i]);
}
}
+#ifdef HAVE_DBGHELP
+ dbghelp_mutex->unlock(dbghelp_mutex);
+#endif
}
free(strings);
#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */
- println(file, "no support for backtrace()/libunwind");
+ println(file, "no support for capturing backtraces");
#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */
}
@@ -470,7 +593,40 @@ METHOD(backtrace_t, contains_function, bool,
}
}
}
-#endif /* HAVE_DLADDR */
+#elif defined(HAVE_DBGHELP)
+ int i, j;
+ HANDLE process;
+
+ process = GetCurrentProcess();
+
+ dbghelp_mutex->lock(dbghelp_mutex);
+
+ for (i = 0; i < this->frame_count; i++)
+ {
+ struct {
+ SYMBOL_INFO hdr;
+ char buf[128];
+ } symbol;
+
+ memset(&symbol, 0, sizeof(symbol));
+ symbol.hdr.SizeOfStruct = sizeof(symbol.hdr);
+ symbol.hdr.MaxNameLen = sizeof(symbol.buf) - 1;
+
+ if (SymFromAddr(process, (DWORD64)this->frames[i], NULL, &symbol.hdr))
+ {
+ for (j = 0; j < count; j++)
+ {
+ if (streq(symbol.hdr.Name, function[j]))
+ {
+ dbghelp_mutex->unlock(dbghelp_mutex);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ dbghelp_mutex->unlock(dbghelp_mutex);
+#endif /* HAVE_DLADDR/HAVE_DBGHELP */
return FALSE;
}
@@ -584,6 +740,66 @@ static inline int backtrace_unwind(void **frames, int count)
}
#endif /* HAVE_UNWIND */
+#ifdef HAVE_DBGHELP
+
+/**
+ * Windows dbghelp variant for glibc backtrace()
+ */
+static inline int backtrace_win(void **frames, int count)
+{
+ STACKFRAME frame;
+ HANDLE process, thread;
+ DWORD machine;
+ CONTEXT context;
+ int got = 0;
+
+ memset(&frame, 0, sizeof(frame));
+ memset(&context, 0, sizeof(context));
+
+ process = GetCurrentProcess();
+ thread = GetCurrentThread();
+
+#ifdef __x86_64
+ machine = IMAGE_FILE_MACHINE_AMD64;
+
+ frame.AddrPC.Offset = context.Rip;
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Offset = context.Rsp;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Offset = context.Rbp;
+ frame.AddrFrame.Mode = AddrModeFlat;
+#else /* x86 */
+ machine = IMAGE_FILE_MACHINE_I386;
+
+ frame.AddrPC.Offset = context.Eip;
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrStack.Offset = context.Esp;
+ frame.AddrStack.Mode = AddrModeFlat;
+ frame.AddrFrame.Offset = context.Ebp;
+ frame.AddrFrame.Mode = AddrModeFlat;
+#endif /* x86_64/x86 */
+
+ dbghelp_mutex->lock(dbghelp_mutex);
+
+ RtlCaptureContext(&context);
+
+ while (got < count)
+ {
+ if (!StackWalk64(machine, process, thread, &frame, &context,
+ NULL, SymFunctionTableAccess, SymGetModuleBase, NULL))
+ {
+ break;
+ }
+ frames[got++] = (void*)frame.AddrPC.Offset;
+ }
+
+ dbghelp_mutex->unlock(dbghelp_mutex);
+
+ return got;
+}
+
+#endif /* HAVE_DBGHELP */
+
/**
* Get implementation methods of backtrace_t
*/
@@ -612,7 +828,12 @@ backtrace_t *backtrace_create(int skip)
frame_count = backtrace_unwind(frames, countof(frames));
#elif defined(HAVE_BACKTRACE)
frame_count = backtrace(frames, countof(frames));
-#endif /* HAVE_BACKTRACE */
+#elif defined(HAVE_DBGHELP)
+ frame_count = backtrace_win(frames, countof(frames));
+#elif defined(WIN32)
+ frame_count = CaptureStackBackTrace(skip, countof(frames), frames, NULL);
+ skip = 0;
+#endif
frame_count = max(frame_count - skip, 0);
this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*));
memcpy(this->frames, frames + skip, frame_count * sizeof(void*));
diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c
index c5e90b6c3..923b7d4db 100644
--- a/src/libstrongswan/utils/capabilities.c
+++ b/src/libstrongswan/utils/capabilities.c
@@ -17,24 +17,27 @@
#include "capabilities.h"
+#include <utils/debug.h>
+
#include <errno.h>
#include <string.h>
#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef WIN32
#include <pwd.h>
#include <grp.h>
-#include <unistd.h>
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#endif /* HAVE_PRCTL */
-#include <utils/debug.h>
-
#if !defined(HAVE_GETPWNAM_R) || \
!defined(HAVE_GETGRNAM_R) || \
!defined(HAVE_GETPWUID_R)
# include <threading/mutex.h>
# define EMULATE_R_FUNCS
#endif
+#endif /* !WIN32 */
typedef struct private_capabilities_t private_capabilities_t;
@@ -76,6 +79,8 @@ struct private_capabilities_t {
#endif
};
+#ifndef WIN32
+
/**
* Returns TRUE if the current process/user is member of the given group
*/
@@ -181,6 +186,19 @@ static bool has_capability(private_capabilities_t *this, u_int cap,
#endif /* CAPABILITIES_NATIVE */
}
+#else /* WIN32 */
+
+/**
+ * Verify that the current process has the given capability, dummy variant
+ */
+static bool has_capability(private_capabilities_t *this, u_int cap,
+ bool *ignore)
+{
+ return TRUE;
+}
+
+#endif /* WIN32 */
+
/**
* Keep the given capability if it is held by the current process. Returns
* FALSE, if this is not the case.
@@ -232,13 +250,21 @@ METHOD(capabilities_t, check, bool,
METHOD(capabilities_t, get_uid, uid_t,
private_capabilities_t *this)
{
+#ifdef WIN32
+ return this->uid;
+#else
return this->uid ?: geteuid();
+#endif
}
METHOD(capabilities_t, get_gid, gid_t,
private_capabilities_t *this)
{
+#ifdef WIN32
+ return this->gid;
+#else
return this->gid ?: getegid();
+#endif
}
METHOD(capabilities_t, set_uid, void,
@@ -256,6 +282,7 @@ METHOD(capabilities_t, set_gid, void,
METHOD(capabilities_t, resolve_uid, bool,
private_capabilities_t *this, char *username)
{
+#ifndef WIN32
struct passwd *pwp;
int err;
@@ -284,12 +311,14 @@ METHOD(capabilities_t, resolve_uid, bool,
}
DBG1(DBG_LIB, "resolving user '%s' failed: %s", username,
err ? strerror(err) : "user not found");
+#endif /* !WIN32 */
return FALSE;
}
METHOD(capabilities_t, resolve_gid, bool,
private_capabilities_t *this, char *groupname)
{
+#ifndef WIN32
struct group *grp;
int err;
@@ -318,9 +347,11 @@ METHOD(capabilities_t, resolve_gid, bool,
}
DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname,
err ? strerror(err) : "group not found");
+#endif /* !WIN32 */
return FALSE;
}
+#ifndef WIN32
/**
* Initialize supplementary groups for unprivileged user
*/
@@ -348,10 +379,12 @@ static bool init_supplementary_groups(private_capabilities_t *this)
#endif /* HAVE_GETPWUID_R */
return res == 0;
}
+#endif /* WIN32 */
METHOD(capabilities_t, drop, bool,
private_capabilities_t *this)
{
+#ifndef WIN32
#ifdef HAVE_PRCTL
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
#endif
@@ -404,6 +437,7 @@ METHOD(capabilities_t, drop, bool,
DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u",
geteuid(), getegid());
#endif /* CAPABILITIES */
+#endif /*!WIN32 */
return TRUE;
}
diff --git a/src/libstrongswan/utils/chunk.c b/src/libstrongswan/utils/chunk.c
index 47181719a..1a9674f4d 100644
--- a/src/libstrongswan/utils/chunk.c
+++ b/src/libstrongswan/utils/chunk.c
@@ -24,8 +24,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-#include <pthread.h>
#include <ctype.h>
+#include <time.h>
#include "chunk.h"
@@ -221,7 +221,14 @@ bool chunk_write(chunk_t chunk, char *path, mode_t mask, bool force)
return FALSE;
}
oldmask = umask(mask);
- fd = fopen(path, "w");
+ fd = fopen(path,
+#ifdef WIN32
+ "wb"
+#else
+ "w"
+#endif
+ );
+
if (fd)
{
if (fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd) == chunk.len)
@@ -269,6 +276,12 @@ bool chunk_from_fd(int fd, chunk_t *out)
while (TRUE)
{
len = read(fd, buf + total, bufsize - total);
+#ifdef WIN32
+ if (len == -1 && errno == EBADF)
+ { /* operating on a Winsock socket? */
+ len = recv(fd, buf + total, bufsize - total, 0);
+ }
+#endif
if (len < 0)
{
free(buf);
@@ -327,10 +340,15 @@ chunk_t *chunk_map(char *path, bool wr)
{
mmaped_chunk_t *chunk;
struct stat sb;
- int tmp;
+ int tmp, flags;
+
+ flags = wr ? O_RDWR : O_RDONLY;
+#ifdef WIN32
+ flags |= O_BINARY;
+#endif
INIT(chunk,
- .fd = open(path, wr ? O_RDWR : O_RDONLY),
+ .fd = open(path, flags),
.wr = wr,
);
@@ -884,9 +902,9 @@ u_int64_t chunk_mac(chunk_t chunk, u_char *key)
}
/**
- * Secret key allocated randomly during first use.
+ * Secret key allocated randomly with chunk_hash_seed().
*/
-static u_char key[16];
+static u_char key[16] = {};
/**
* Static key used in case predictable hash values are required.
@@ -895,20 +913,21 @@ static u_char static_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
/**
- * Only allocate the key once
+ * See header
*/
-static pthread_once_t key_allocated = PTHREAD_ONCE_INIT;
-
-/**
- * Allocate a key on first use, we do this manually to avoid dependencies on
- * plugins.
- */
-static void allocate_key()
+void chunk_hash_seed()
{
+ static bool seeded = FALSE;
ssize_t len;
size_t done = 0;
int fd;
+ if (seeded)
+ {
+ /* just once to have the same seed during the whole process lifetimes */
+ return;
+ }
+
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0)
{
@@ -932,6 +951,7 @@ static void allocate_key()
key[done] = (u_char)random();
}
}
+ seeded = TRUE;
}
/**
@@ -939,7 +959,6 @@ static void allocate_key()
*/
u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
{
- pthread_once(&key_allocated, allocate_key);
/* we could use a mac of the previous hash, but this is faster */
return chunk_mac_inc(chunk, key, ((u_int64_t)hash) << 32 | hash);
}
@@ -949,7 +968,6 @@ u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
*/
u_int32_t chunk_hash(chunk_t chunk)
{
- pthread_once(&key_allocated, allocate_key);
return chunk_mac(chunk, key);
}
diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h
index 33f66caec..9951ff31f 100644
--- a/src/libstrongswan/utils/chunk.h
+++ b/src/libstrongswan/utils/chunk.h
@@ -30,6 +30,8 @@
#include <alloca.h>
#endif
+#include <utils/utils.h>
+
typedef struct chunk_t chunk_t;
/**
@@ -338,6 +340,15 @@ bool chunk_increment(chunk_t chunk);
bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace);
/**
+ * Seed initial key for chunk_hash().
+ *
+ * This call should get invoked once during startup. This is usually done
+ * by calling library_init(). Calling it multiple times is safe, it gets
+ * executed just once.
+ */
+void chunk_hash_seed();
+
+/**
* Computes a 32 bit hash of the given chunk.
*
* @note The output of this function is randomized, that is, it will only
diff --git a/src/libstrongswan/utils/debug.h b/src/libstrongswan/utils/debug.h
index c46d3fe55..f1c8c70ab 100644
--- a/src/libstrongswan/utils/debug.h
+++ b/src/libstrongswan/utils/debug.h
@@ -26,7 +26,7 @@ typedef enum level_t level_t;
#include <stdio.h>
-#include "utils/enum.h"
+#include <utils/utils.h>
/**
* Debug message group.
diff --git a/src/libstrongswan/utils/enum.c b/src/libstrongswan/utils/enum.c
index 3db9a34e0..f96fe2989 100644
--- a/src/libstrongswan/utils/enum.c
+++ b/src/libstrongswan/utils/enum.c
@@ -17,6 +17,7 @@
#include <stdio.h>
#include <library.h>
+#include <utils/utils.h>
#include "enum.h"
@@ -39,7 +40,7 @@ char *enum_to_name(enum_name_t *e, int val)
/**
* See header.
*/
-int enum_from_name(enum_name_t *e, char *name)
+bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val)
{
do
{
@@ -49,12 +50,13 @@ int enum_from_name(enum_name_t *e, char *name)
{
if (name && strcaseeq(name, e->names[i]))
{
- return e->first + i;
+ *val = e->first + i;
+ return TRUE;
}
}
}
while ((e = e->next));
- return -1;
+ return FALSE;
}
/**
diff --git a/src/libstrongswan/utils/enum.h b/src/libstrongswan/utils/enum.h
index a2f97d05e..3c03c2a7b 100644
--- a/src/libstrongswan/utils/enum.h
+++ b/src/libstrongswan/utils/enum.h
@@ -120,9 +120,30 @@ char *enum_to_name(enum_name_t *e, int val);
*
* @param e enum names for this enum value
* @param name name to get enum value for
- * @return enum value, -1 if not found
+ * @param valp variable sized pointer receiving value
+ * @return TRUE if enum name found, FALSE otherwise
*/
-int enum_from_name(enum_name_t *e, char *name);
+#define enum_from_name(e, name, valp) ({ \
+ int _val; \
+ int _found = enum_from_name_as_int(e, name, &_val); \
+ if (_found) \
+ { \
+ *(valp) = _val; \
+ } \
+ _found; })
+
+/**
+ * Convert a enum string back to its enum value, integer pointer variant.
+ *
+ * This variant takes integer pointer only, use enum_from_name() to pass
+ * enum type pointers for the result.
+ *
+ * @param e enum names for this enum value
+ * @param name name to get enum value for
+ * @param val integer pointer receiving value
+ * @return TRUE if enum name found, FALSE otherwise
+ */
+bool enum_from_name_as_int(enum_name_t *e, const char *name, int *val);
/**
* printf hook function for enum_names_t.
diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c
index e7eb63bc6..46ac7e890 100644
--- a/src/libstrongswan/utils/identification.c
+++ b/src/libstrongswan/utils/identification.c
@@ -15,15 +15,12 @@
* for more details.
*/
-#define _GNU_SOURCE
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include "identification.h"
+#include <utils/utils.h>
#include <asn1/oid.h>
#include <asn1/asn1.h>
#include <crypto/hashers/hasher.h>
@@ -397,14 +394,24 @@ static status_t atodn(char *src, chunk_t *dn)
asn1_t rdn_type;
state_t state = SEARCH_OID;
status_t status = SUCCESS;
+ char sep = '\0';
do
{
switch (state)
{
case SEARCH_OID:
- if (*src != ' ' && *src != '/' && *src != ',' && *src != '\0')
+ if (!sep && *src == '/')
+ { /* use / as separator if the string starts with a slash */
+ sep = '/';
+ break;
+ }
+ if (*src != ' ' && *src != '\0')
{
+ if (!sep)
+ { /* use , as separator by default */
+ sep = ',';
+ }
oid.ptr = src;
oid.len = 1;
state = READ_OID;
@@ -444,7 +451,7 @@ static status_t atodn(char *src, chunk_t *dn)
{
break;
}
- else if (*src != ',' && *src != '/' && *src != '\0')
+ else if (*src != sep && *src != '\0')
{
name.ptr = src;
name.len = 1;
@@ -457,7 +464,7 @@ static status_t atodn(char *src, chunk_t *dn)
state = READ_NAME;
/* fall-through */
case READ_NAME:
- if (*src != ',' && *src != '/' && *src != '\0')
+ if (*src != sep && *src != '\0')
{
name.len++;
if (*src == ' ')
diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c
index af29e2100..a2bca193d 100644
--- a/src/libstrongswan/utils/leak_detective.c
+++ b/src/libstrongswan/utils/leak_detective.c
@@ -19,14 +19,11 @@
#include <string.h>
#include <stdio.h>
#include <signal.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
#include <unistd.h>
-#include <syslog.h>
-#include <netdb.h>
#include <locale.h>
+#ifdef HAVE_DLADDR
#include <dlfcn.h>
+#endif
#include <time.h>
#include <errno.h>
@@ -42,6 +39,7 @@
#include "leak_detective.h"
#include <library.h>
+#include <utils/utils.h>
#include <utils/debug.h>
#include <utils/backtrace.h>
#include <collections/hashtable.h>
diff --git a/src/libstrongswan/utils/optionsfrom.c b/src/libstrongswan/utils/optionsfrom.c
index 117071351..6f721c9ef 100644
--- a/src/libstrongswan/utils/optionsfrom.c
+++ b/src/libstrongswan/utils/optionsfrom.c
@@ -90,8 +90,13 @@ METHOD(options_t, from, bool,
}
/* determine the file size */
- fseek(fd, 0, SEEK_END);
- src.len = ftell(fd);
+ if (fseek(fd, 0, SEEK_END) == -1 || (src.len = ftell(fd)) == -1)
+ {
+ DBG1(DBG_LIB, "optionsfrom: unable to determine size of '%s': %s",
+ filename, strerror(errno));
+ fclose(fd);
+ return FALSE;
+ }
rewind(fd);
/* allocate one byte more just in case of a missing final newline */
diff --git a/src/libstrongswan/utils/parser_helper.c b/src/libstrongswan/utils/parser_helper.c
new file mode 100644
index 000000000..4c6aa251f
--- /dev/null
+++ b/src/libstrongswan/utils/parser_helper.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "parser_helper.h"
+
+#include <collections/array.h>
+
+typedef struct private_parser_helper_t private_parser_helper_t;
+typedef struct parser_helper_file_t parser_helper_file_t;
+
+struct private_parser_helper_t {
+
+ /**
+ * Public interface.
+ */
+ parser_helper_t public;
+
+ /**
+ * Stack of included files, as parser_helper_file_t.
+ */
+ array_t *files;
+
+ /**
+ * Helper for parsing strings.
+ */
+ bio_writer_t *writer;
+};
+
+struct parser_helper_file_t {
+
+ /**
+ * File name
+ */
+ char *name;
+
+ /**
+ * File stream
+ */
+ FILE *file;
+
+ /**
+ * Enumerator of paths matching the most recent inclusion pattern.
+ */
+ enumerator_t *matches;
+};
+
+/**
+ * Destroy the given file data.
+ */
+static void parser_helper_file_destroy(parser_helper_file_t *this)
+{
+ if (this->file)
+ {
+ fclose(this->file);
+ }
+ free(this->name);
+ DESTROY_IF(this->matches);
+ free(this);
+}
+
+/**
+ * Returns the current file, if any.
+ */
+static parser_helper_file_t *current_file(private_parser_helper_t *this)
+{
+ parser_helper_file_t *file;
+
+ array_get(this->files, ARRAY_TAIL, &file);
+ if (file->name)
+ {
+ return file;
+ }
+ return NULL;
+}
+
+METHOD(parser_helper_t, file_next, FILE*,
+ private_parser_helper_t *this)
+{
+ parser_helper_file_t *file, *next;
+ char *name;
+
+ array_get(this->files, ARRAY_TAIL, &file);
+ if (!file->matches && file->name)
+ {
+ array_remove(this->files, ARRAY_TAIL, NULL);
+ parser_helper_file_destroy(file);
+ /* continue with previous includes, if any */
+ array_get(this->files, ARRAY_TAIL, &file);
+ }
+ if (file->matches)
+ {
+ while (file->matches->enumerate(file->matches, &name, NULL))
+ {
+ INIT(next,
+ .name = strdup(name),
+ .file = fopen(name, "r"),
+ );
+
+ if (next->file)
+ {
+ array_insert(this->files, ARRAY_TAIL, next);
+ return next->file;
+ }
+ PARSER_DBG2(&this->public, "unable to open '%s'", name);
+ parser_helper_file_destroy(next);
+ }
+ file->matches->destroy(file->matches);
+ file->matches = NULL;
+ }
+ return NULL;
+}
+
+METHOD(parser_helper_t, file_include, void,
+ private_parser_helper_t *this, char *pattern)
+{
+ parser_helper_file_t *file;
+ char pat[PATH_MAX];
+
+ array_get(this->files, ARRAY_TAIL, &file);
+ if (!pattern || !*pattern)
+ {
+ PARSER_DBG1(&this->public, "no include pattern specified, ignored");
+ file->matches = enumerator_create_empty();
+ return;
+ }
+
+ if (!file->name || path_absolute(pattern))
+ { /* absolute path */
+ if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
+ {
+ PARSER_DBG1(&this->public, "include pattern too long, ignored");
+ file->matches = enumerator_create_empty();
+ return;
+ }
+ }
+ else
+ { /* base relative paths to the directory of the current file */
+ char *dir = path_dirname(file->name);
+ if (snprintf(pat, sizeof(pat), "%s%s%s", dir, DIRECTORY_SEPARATOR,
+ pattern) >= sizeof(pat))
+ {
+ PARSER_DBG1(&this->public, "include pattern too long, ignored");
+ free(dir);
+ file->matches = enumerator_create_empty();
+ return;
+ }
+ free(dir);
+ }
+
+ file->matches = enumerator_create_glob(pat);
+ if (!file->matches)
+ { /* if glob(3) is not available, try to load pattern directly */
+ file->matches = enumerator_create_single(strdup(pat), free);
+ }
+}
+
+METHOD(parser_helper_t, string_init, void,
+ private_parser_helper_t *this)
+{
+ chunk_t data;
+
+ data = this->writer->extract_buf(this->writer);
+ chunk_free(&data);
+}
+
+METHOD(parser_helper_t, string_add, void,
+ private_parser_helper_t *this, char *str)
+{
+ this->writer->write_data(this->writer, chunk_from_str(str));
+}
+
+METHOD(parser_helper_t, string_get, char*,
+ private_parser_helper_t *this)
+{
+ chunk_t data;
+
+ this->writer->write_data(this->writer, chunk_from_chars('\0'));
+ data = this->writer->extract_buf(this->writer);
+ return data.ptr;
+}
+
+METHOD(parser_helper_t, destroy, void,
+ private_parser_helper_t *this)
+{
+ array_destroy_function(this->files, (void*)parser_helper_file_destroy, NULL);
+ this->writer->destroy(this->writer);
+ free(this);
+}
+
+/**
+ * Described in header
+ */
+void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...)
+{
+ private_parser_helper_t *this = (private_parser_helper_t*)ctx;
+ parser_helper_file_t *file;
+ char msg[8192];
+ va_list args;
+ int line;
+
+ va_start(args, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ va_end(args);
+
+ file = current_file(this);
+ line = ctx->get_lineno ? ctx->get_lineno(ctx->scanner) : 0;
+ if (file)
+ {
+ dbg(DBG_CFG, level, "%s:%d: %s", file->name, line, msg);
+ }
+ else
+ {
+ dbg(DBG_CFG, level, "%s", msg);
+ }
+}
+
+/**
+ * Described in header
+ */
+parser_helper_t *parser_helper_create(void *context)
+{
+ private_parser_helper_t *this;
+ parser_helper_file_t *sentinel;
+
+ INIT(this,
+ .public = {
+ .context = context,
+ .file_include = _file_include,
+ .file_next = _file_next,
+ .string_init = _string_init,
+ .string_add = _string_add,
+ .string_get = _string_get,
+ .destroy = _destroy,
+ },
+ .files = array_create(0, 0),
+ .writer = bio_writer_create(0),
+ );
+
+ INIT(sentinel,
+ .name = NULL,
+ );
+ array_insert(this->files, ARRAY_TAIL, sentinel);
+
+ return &this->public;
+}
diff --git a/src/libstrongswan/utils/parser_helper.h b/src/libstrongswan/utils/parser_helper.h
new file mode 100644
index 000000000..09ed1991c
--- /dev/null
+++ b/src/libstrongswan/utils/parser_helper.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup parser_helper parser_helper
+ * @{ @ingroup utils
+ */
+
+#ifndef PARSER_HELPER_H_
+#define PARSER_HELPER_H_
+
+#include <utils/debug.h>
+#include <collections/array.h>
+#include <bio/bio_writer.h>
+
+typedef struct parser_helper_t parser_helper_t;
+
+/**
+ * Helper class for flex/bison based parsers.
+ *
+ * <code>PREFIX</code> equals whatever is configure with
+ * <code>%option prefix</code> resp. <code>%name-prefix</code>.
+ */
+struct parser_helper_t {
+
+ /**
+ * A user defined parser context object.
+ */
+ const void *context;
+
+ /**
+ * Opaque object allocated by the lexer, should be set with:
+ * @code
+ * PREFIXlex_init_extra(helper, &helper->scanner).
+ * @endcode
+ */
+ void *scanner;
+
+ /**
+ * Function to determine the current line number (defined by the lexer).
+ *
+ * Basically, this should be assigned to <code>PREFIXget_lineno</code>.
+ *
+ * @param scanner the lexer
+ * @return current line number
+ */
+ int (*get_lineno)(void *scanner);
+
+ /**
+ * Resolves the given include pattern, relative to the location of the
+ * current file.
+ *
+ * Call file_next() to open the next file.
+ *
+ * @param pattern file pattern
+ */
+ void (*file_include)(parser_helper_t *this, char *pattern);
+
+ /**
+ * Get the next file to process.
+ *
+ * This will return NULL if all files matching the most recent pattern
+ * have been handled. If there are other patterns the next call will then
+ * return the next file matching the previous pattern.
+ *
+ * When hitting <code>\<\<EOF\>\></code> first call
+ * @code
+ * PREFIXpop_buffer_state(yyscanner);
+ * @endcode
+ * then call this method to check if there are more files to include for
+ * the most recent call to file_include(), if so, call
+ * @code
+ * PREFIXset_in(file, helper->scanner);
+ * PREFIXpush_buffer_state(PREFIX_create_buffer(file, YY_BUF_SIZE,
+ * helper->scanner), helper->scanner);
+ * @endcode
+ *
+ * If there are no more files to process check
+ * <code>YY_CURRENT_BUFFER</code> and if it is FALSE call yyterminate().
+ *
+ * @return next file to process, or NULL (see comment)
+ */
+ FILE *(*file_next)(parser_helper_t *this);
+
+ /**
+ * Start parsing a string, discards any currently stored data.
+ */
+ void (*string_init)(parser_helper_t *this);
+
+ /**
+ * Append the given string.
+ *
+ * @param str string to append
+ */
+ void (*string_add)(parser_helper_t *this, char *str);
+
+ /**
+ * Extract the current string buffer as null-terminated string. Can only
+ * be called once per string.
+ *
+ * @return allocated string
+ */
+ char *(*string_get)(parser_helper_t *this);
+
+ /**
+ * Destroy this instance.
+ */
+ void (*destroy)(parser_helper_t *this);
+};
+
+/**
+ * Log the given message either as error or warning
+ *
+ * @param level log level
+ * @param ctx current parser context
+ * @param fmt error message format
+ * @param ... additional arguments
+ */
+void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...);
+
+#if DEBUG_LEVEL >= 1
+# define PARSER_DBG1(ctx, fmt, ...) parser_helper_log(1, ctx, fmt, ##__VA_ARGS__)
+#endif
+#if DEBUG_LEVEL >= 2
+# define PARSER_DBG2(ctx, fmt, ...) parser_helper_log(2, ctx, fmt, ##__VA_ARGS__)
+#endif
+#if DEBUG_LEVEL >= 3
+# define PARSER_DBG3(ctx, fmt, ...) parser_helper_log(3, ctx, fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef PARSER_DBG1
+# define PARSER_DBG1(...) {}
+#endif
+#ifndef PARSER_DBG2
+# define PARSER_DBG2(...) {}
+#endif
+#ifndef PARSER_DBG3
+# define PARSER_DBG3(...) {}
+#endif
+
+/**
+ * Create a parser helper object
+ *
+ * @param context user defined parser context
+ * @return parser helper
+ */
+parser_helper_t *parser_helper_create(void *context);
+
+#endif /** PARSER_HELPER_H_ @}*/
diff --git a/src/libstrongswan/utils/printf_hook/printf_hook_builtin.c b/src/libstrongswan/utils/printf_hook/printf_hook_builtin.c
index c79d4b87a..466c673d9 100644
--- a/src/libstrongswan/utils/printf_hook/printf_hook_builtin.c
+++ b/src/libstrongswan/utils/printf_hook/printf_hook_builtin.c
@@ -1104,6 +1104,128 @@ int builtin_vprintf(const char *format, va_list ap)
return builtin_vfprintf(stdout, format, ap);
}
+#ifdef WIN32
+/**
+ * Set TTY color on Windows consoles
+ */
+static void set_console_color(HANDLE handle, int color)
+{
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ struct {
+ /* escape code */
+ int color;
+ /* windows console color combination */
+ WORD attributes;
+ } maps[] = {
+ { 30, 0 },
+ { 31, FOREGROUND_RED },
+ { 32, FOREGROUND_GREEN },
+ { 33, FOREGROUND_GREEN | FOREGROUND_RED },
+ { 34, FOREGROUND_BLUE | FOREGROUND_INTENSITY },
+ { 35, FOREGROUND_RED | FOREGROUND_BLUE },
+ { 36, FOREGROUND_GREEN | FOREGROUND_BLUE },
+ { 37, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED },
+ { 39, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED },
+ { 40, 0 },
+ { 41, BACKGROUND_RED },
+ { 42, BACKGROUND_GREEN },
+ { 43, BACKGROUND_GREEN | BACKGROUND_RED },
+ { 44, BACKGROUND_BLUE },
+ { 45, BACKGROUND_RED | BACKGROUND_BLUE },
+ { 46, BACKGROUND_GREEN | BACKGROUND_BLUE },
+ { 47, BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED },
+ { 49, 0 },
+ };
+ int i;
+
+ if (GetConsoleScreenBufferInfo(handle, &info))
+ {
+ if (color < 40)
+ {
+ info.wAttributes &= ~(FOREGROUND_BLUE | FOREGROUND_GREEN |
+ FOREGROUND_RED | FOREGROUND_INTENSITY);
+ }
+ else
+ {
+ info.wAttributes &= ~(BACKGROUND_BLUE | BACKGROUND_GREEN |
+ BACKGROUND_RED | BACKGROUND_INTENSITY);
+ }
+ for (i = 0; i < countof(maps); i++)
+ {
+ if (maps[i].color == color)
+ {
+ info.wAttributes |= maps[i].attributes;
+ SetConsoleTextAttribute(handle, info.wAttributes);
+ break;
+ }
+ }
+ }
+}
+
+int builtin_vfprintf(FILE *stream, const char *format, va_list ap)
+{
+ char buf[PRINTF_BUF_LEN], *pos, *stop;
+ HANDLE handle;
+ int len, total;
+ DWORD clen, mode;
+
+ total = len = builtin_vsnprintf(buf, sizeof(buf), format, ap);
+ switch (fileno(stream))
+ {
+ case 1:
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ break;
+ case 2:
+ handle = GetStdHandle(STD_ERROR_HANDLE);
+ break;
+ default:
+ handle = INVALID_HANDLE_VALUE;
+ break;
+ }
+ /* GetConsoleMode fails if output redirected */
+ if (handle == INVALID_HANDLE_VALUE || !GetConsoleMode(handle, &mode))
+ {
+ return fwrite(buf, 1, len, stream);
+ }
+ while (len)
+ {
+ pos = &buf[total - len];
+ if (len > 4)
+ {
+ if (pos[0] == '\e' && pos[1] == '[' && pos[4] == 'm')
+ {
+ if (isdigit(pos[3]))
+ {
+ if (pos[2] == '3' || pos[2] == '4')
+ {
+ set_console_color(handle,
+ (pos[2] - '0') * 10 + pos[3] - '0');
+ len -= 5;
+ continue;
+ }
+ }
+ }
+ }
+ stop = memchr(pos + 1, '\e', len);
+ if (stop)
+ {
+ clen = stop - pos;
+ }
+ else
+ {
+ clen = len;
+ }
+ if (clen && !WriteConsole(handle, pos, clen, &clen, NULL))
+ {
+ break;
+ }
+ len -= clen;
+ }
+ return total - len;
+}
+
+#else /* !WIN32 */
+
int builtin_vfprintf(FILE *stream, const char *format, va_list ap)
{
char buf[PRINTF_BUF_LEN];
@@ -1113,6 +1235,8 @@ int builtin_vfprintf(FILE *stream, const char *format, va_list ap)
return fwrite(buf, 1, len, stream);
}
+#endif /* !WIN32 */
+
int builtin_vsprintf(char *str, const char *format, va_list ap)
{
return builtin_vsnprintf(str, ~(size_t)0, format, ap);
diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c
deleted file mode 100644
index cf34fd1cf..000000000
--- a/src/libstrongswan/utils/settings.c
+++ /dev/null
@@ -1,1520 +0,0 @@
-/*
- * Copyright (C) 2010-2014 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif /* HAVE_GLOB_H */
-
-#include "settings.h"
-
-#include "collections/array.h"
-#include "collections/hashtable.h"
-#include "collections/linked_list.h"
-#include "threading/rwlock.h"
-#include "utils/debug.h"
-
-#define MAX_INCLUSION_LEVEL 10
-
-typedef struct private_settings_t private_settings_t;
-typedef struct section_t section_t;
-typedef struct kv_t kv_t;
-
-/**
- * private data of settings
- */
-struct private_settings_t {
-
- /**
- * public functions
- */
- settings_t public;
-
- /**
- * top level section
- */
- section_t *top;
-
- /**
- * contents of loaded files and in-memory settings (char*)
- */
- linked_list_t *contents;
-
- /**
- * lock to safely access the settings
- */
- rwlock_t *lock;
-};
-
-/**
- * section containing subsections and key value pairs
- */
-struct section_t {
-
- /**
- * name of the section
- */
- char *name;
-
- /**
- * fallback sections, as section_t
- */
- array_t *fallbacks;
-
- /**
- * subsections, as section_t
- */
- array_t *sections;
-
- /**
- * key value pairs, as kv_t
- */
- array_t *kv;
-};
-
-/**
- * Key value pair
- */
-struct kv_t {
-
- /**
- * key string, relative
- */
- char *key;
-
- /**
- * value as string
- */
- char *value;
-};
-
-/**
- * create a key/value pair
- */
-static kv_t *kv_create(char *key, char *value)
-{
- kv_t *this;
- INIT(this,
- .key = strdup(key),
- .value = value,
- );
- return this;
-}
-
-/**
- * destroy a key/value pair
- */
-static void kv_destroy(kv_t *this)
-{
- free(this->key);
- free(this);
-}
-
-/**
- * create a section with the given name
- */
-static section_t *section_create(char *name)
-{
- section_t *this;
- INIT(this,
- .name = strdupnull(name),
- );
- return this;
-}
-
-/**
- * destroy a section
- */
-static void section_destroy(section_t *this)
-{
- array_destroy_function(this->sections, (void*)section_destroy, NULL);
- array_destroy_function(this->kv, (void*)kv_destroy, NULL);
- array_destroy(this->fallbacks);
- free(this->name);
- free(this);
-}
-
-/**
- * Purge contents of a section, returns if section can be safely removed.
- */
-static bool section_purge(section_t *this)
-{
- section_t *current;
- int i;
-
- array_destroy_function(this->kv, (void*)kv_destroy, NULL);
- this->kv = NULL;
- /* we ensure sections used as fallback, or configured with fallbacks (or
- * having any such subsections) are not removed */
- for (i = array_count(this->sections) - 1; i >= 0; i--)
- {
- array_get(this->sections, i, &current);
- if (section_purge(current))
- {
- array_remove(this->sections, i, NULL);
- section_destroy(current);
- }
- }
- return !this->fallbacks && !array_count(this->sections);
-}
-
-/**
- * callback to find a section by name
- */
-static int section_find(const void *a, const void *b)
-{
- const char *key = a;
- const section_t *item = b;
- return strcmp(key, item->name);
-}
-
-/**
- * callback to sort sections by name
- */
-static int section_sort(const void *a, const void *b, void *user)
-{
- const section_t *sa = a, *sb = b;
- return strcmp(sa->name, sb->name);
-}
-
-/**
- * callback to find a kv pair by key
- */
-static int kv_find(const void *a, const void *b)
-{
- const char *key = a;
- const kv_t *item = b;
- return strcmp(key, item->key);
-}
-
-/**
- * callback to sort kv pairs by key
- */
-static int kv_sort(const void *a, const void *b, void *user)
-{
- const kv_t *kva = a, *kvb = b;
- return strcmp(kva->key, kvb->key);
-}
-
-/**
- * Print a format key, but consume already processed arguments
- */
-static bool print_key(char *buf, int len, char *start, char *key, va_list args)
-{
- va_list copy;
- char *pos = start;
- bool res;
-
- va_copy(copy, args);
- while (TRUE)
- {
- pos = memchr(pos, '%', key - pos);
- if (!pos)
- {
- break;
- }
- pos++;
- switch (*pos)
- {
- case 'd':
- va_arg(copy, int);
- break;
- case 's':
- va_arg(copy, char*);
- break;
- case 'N':
- va_arg(copy, enum_name_t*);
- va_arg(copy, int);
- break;
- case '%':
- break;
- default:
- DBG1(DBG_CFG, "settings with %%%c not supported!", *pos);
- break;
- }
- pos++;
- }
- res = vsnprintf(buf, len, key, copy) < len;
- va_end(copy);
- return res;
-}
-
-/**
- * Find a section by a given key, using buffered key, reusable buffer.
- * If "ensure" is TRUE, the sections are created if they don't exist.
- */
-static section_t *find_section_buffered(section_t *section,
- char *start, char *key, va_list args, char *buf, int len,
- bool ensure)
-{
- char *pos;
- section_t *found = NULL;
-
- if (section == NULL)
- {
- return NULL;
- }
- pos = strchr(key, '.');
- if (pos)
- {
- *pos = '\0';
- pos++;
- }
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- if (!strlen(buf))
- {
- found = section;
- }
- else if (array_bsearch(section->sections, buf, section_find, &found) == -1)
- {
- if (ensure)
- {
- found = section_create(buf);
- array_insert_create(&section->sections, ARRAY_TAIL, found);
- array_sort(section->sections, section_sort, NULL);
- }
- }
- if (found && pos)
- {
- return find_section_buffered(found, start, pos, args, buf, len, ensure);
- }
- return found;
-}
-
-/**
- * Find all sections via a given key considering fallbacks, using buffered key,
- * reusable buffer.
- */
-static void find_sections_buffered(section_t *section, char *start, char *key,
- va_list args, char *buf, int len, array_t **sections)
-{
- section_t *found = NULL, *fallback;
- char *pos;
- int i;
-
- if (!section)
- {
- return;
- }
- pos = strchr(key, '.');
- if (pos)
- {
- *pos = '\0';
- }
- if (!print_key(buf, len, start, key, args))
- {
- return;
- }
- if (pos)
- { /* restore so we can follow fallbacks */
- *pos = '.';
- }
- if (!strlen(buf))
- {
- found = section;
- }
- else
- {
- array_bsearch(section->sections, buf, section_find, &found);
- }
- if (found)
- {
- if (pos)
- {
- find_sections_buffered(found, start, pos+1, args, buf, len,
- sections);
- }
- else
- {
- array_insert_create(sections, ARRAY_TAIL, found);
- for (i = 0; i < array_count(found->fallbacks); i++)
- {
- array_get(found->fallbacks, i, &fallback);
- array_insert_create(sections, ARRAY_TAIL, fallback);
- }
- }
- }
- if (section->fallbacks)
- {
- for (i = 0; i < array_count(section->fallbacks); i++)
- {
- array_get(section->fallbacks, i, &fallback);
- find_sections_buffered(fallback, start, key, args, buf, len,
- sections);
- }
- }
-}
-
-/**
- * Ensure that the section with the given key exists (thread-safe).
- */
-static section_t *ensure_section(private_settings_t *this, section_t *section,
- const char *key, va_list args)
-{
- char buf[128], keybuf[512];
- section_t *found;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return NULL;
- }
- /* we might have to change the tree */
- this->lock->write_lock(this->lock);
- found = find_section_buffered(section, keybuf, keybuf, args, buf,
- sizeof(buf), TRUE);
- this->lock->unlock(this->lock);
- return found;
-}
-
-/**
- * Find a section by a given key with its fallbacks (not thread-safe!).
- * Sections are returned in depth-first order (array is allocated). NULL is
- * returned if no sections are found.
- */
-static array_t *find_sections(private_settings_t *this, section_t *section,
- char *key, va_list args)
-{
- char buf[128], keybuf[512];
- array_t *sections = NULL;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return NULL;
- }
- find_sections_buffered(section, keybuf, keybuf, args, buf,
- sizeof(buf), &sections);
- return sections;
-}
-
-/**
- * Check if the given fallback section already exists
- */
-static bool fallback_exists(section_t *section, section_t *fallback)
-{
- if (section == fallback)
- {
- return TRUE;
- }
- else if (section->fallbacks)
- {
- section_t *existing;
- int i;
-
- for (i = 0; i < array_count(section->fallbacks); i++)
- {
- array_get(section->fallbacks, i, &existing);
- if (existing == fallback)
- {
- return TRUE;
- }
- }
- }
- return FALSE;
-}
-
-/**
- * Ensure that the section with the given key exists and add the given fallback
- * section (thread-safe).
- */
-static void add_fallback_to_section(private_settings_t *this,
- section_t *section, const char *key, va_list args,
- section_t *fallback)
-{
- char buf[128], keybuf[512];
- section_t *found;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return;
- }
- this->lock->write_lock(this->lock);
- found = find_section_buffered(section, keybuf, keybuf, args, buf,
- sizeof(buf), TRUE);
- if (!fallback_exists(found, fallback))
- {
- /* to ensure sections referred to as fallback are not purged, we create
- * the array there too */
- if (!fallback->fallbacks)
- {
- fallback->fallbacks = array_create(0, 0);
- }
- array_insert_create(&found->fallbacks, ARRAY_TAIL, fallback);
- }
- this->lock->unlock(this->lock);
-}
-
-/**
- * Find the key/value pair for a key, using buffered key, reusable buffer
- * If "ensure" is TRUE, the sections (and key/value pair) are created if they
- * don't exist.
- * Fallbacks are only considered if "ensure" is FALSE.
- */
-static kv_t *find_value_buffered(section_t *section, char *start, char *key,
- va_list args, char *buf, int len, bool ensure)
-{
- int i;
- char *pos;
- kv_t *kv = NULL;
- section_t *found = NULL;
-
- if (section == NULL)
- {
- return NULL;
- }
-
- pos = strchr(key, '.');
- if (pos)
- {
- *pos = '\0';
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- /* restore so we can retry for fallbacks */
- *pos = '.';
- if (!strlen(buf))
- {
- found = section;
- }
- else if (array_bsearch(section->sections, buf, section_find,
- &found) == -1)
- {
- if (ensure)
- {
- found = section_create(buf);
- array_insert_create(&section->sections, ARRAY_TAIL, found);
- array_sort(section->sections, section_sort, NULL);
- }
- }
- if (found)
- {
- kv = find_value_buffered(found, start, pos+1, args, buf, len,
- ensure);
- }
- if (!kv && !ensure && section->fallbacks)
- {
- for (i = 0; !kv && i < array_count(section->fallbacks); i++)
- {
- array_get(section->fallbacks, i, &found);
- kv = find_value_buffered(found, start, key, args, buf, len,
- ensure);
- }
- }
- }
- else
- {
- if (!print_key(buf, len, start, key, args))
- {
- return NULL;
- }
- if (array_bsearch(section->kv, buf, kv_find, &kv) == -1)
- {
- if (ensure)
- {
- kv = kv_create(buf, NULL);
- array_insert_create(&section->kv, ARRAY_TAIL, kv);
- array_sort(section->kv, kv_sort, NULL);
- }
- else if (section->fallbacks)
- {
- for (i = 0; !kv && i < array_count(section->fallbacks); i++)
- {
- array_get(section->fallbacks, i, &found);
- kv = find_value_buffered(found, start, key, args, buf, len,
- ensure);
- }
- }
- }
- }
- return kv;
-}
-
-/**
- * Find the string value for a key (thread-safe).
- */
-static char *find_value(private_settings_t *this, section_t *section,
- char *key, va_list args)
-{
- char buf[128], keybuf[512], *value = NULL;
- kv_t *kv;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return NULL;
- }
- this->lock->read_lock(this->lock);
- kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
- FALSE);
- if (kv)
- {
- value = kv->value;
- }
- this->lock->unlock(this->lock);
- return value;
-}
-
-/**
- * Set a value to a copy of the given string (thread-safe).
- */
-static void set_value(private_settings_t *this, section_t *section,
- char *key, va_list args, char *value)
-{
- char buf[128], keybuf[512];
- kv_t *kv;
-
- if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf))
- {
- return;
- }
- this->lock->write_lock(this->lock);
- kv = find_value_buffered(section, keybuf, keybuf, args, buf, sizeof(buf),
- TRUE);
- if (kv)
- {
- if (!value)
- {
- kv->value = NULL;
- }
- else if (kv->value && (strlen(value) <= strlen(kv->value)))
- { /* overwrite in-place, if possible */
- strcpy(kv->value, value);
- }
- else
- { /* otherwise clone the string and store it in the cache */
- kv->value = strdup(value);
- this->contents->insert_last(this->contents, kv->value);
- }
- }
- this->lock->unlock(this->lock);
-}
-
-METHOD(settings_t, get_str, char*,
- private_settings_t *this, char *key, char *def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- if (value)
- {
- return value;
- }
- return def;
-}
-
-/**
- * Described in header
- */
-inline bool settings_value_as_bool(char *value, bool def)
-{
- if (value)
- {
- if (strcaseeq(value, "1") ||
- strcaseeq(value, "yes") ||
- strcaseeq(value, "true") ||
- strcaseeq(value, "enabled"))
- {
- return TRUE;
- }
- else if (strcaseeq(value, "0") ||
- strcaseeq(value, "no") ||
- strcaseeq(value, "false") ||
- strcaseeq(value, "disabled"))
- {
- return FALSE;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_bool, bool,
- private_settings_t *this, char *key, bool def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_bool(value, def);
-}
-
-/**
- * Described in header
- */
-inline int settings_value_as_int(char *value, int def)
-{
- int intval;
- if (value)
- {
- errno = 0;
- intval = strtol(value, NULL, 10);
- if (errno == 0)
- {
- return intval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_int, int,
- private_settings_t *this, char *key, int def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_int(value, def);
-}
-
-/**
- * Described in header
- */
-inline double settings_value_as_double(char *value, double def)
-{
- double dval;
- if (value)
- {
- errno = 0;
- dval = strtod(value, NULL);
- if (errno == 0)
- {
- return dval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_double, double,
- private_settings_t *this, char *key, double def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_double(value, def);
-}
-
-/**
- * Described in header
- */
-inline u_int32_t settings_value_as_time(char *value, u_int32_t def)
-{
- char *endptr;
- u_int32_t timeval;
- if (value)
- {
- errno = 0;
- timeval = strtoul(value, &endptr, 10);
- if (errno == 0)
- {
- switch (*endptr)
- {
- case 'd': /* time in days */
- timeval *= 24 * 3600;
- break;
- case 'h': /* time in hours */
- timeval *= 3600;
- break;
- case 'm': /* time in minutes */
- timeval *= 60;
- break;
- case 's': /* time in seconds */
- default:
- break;
- }
- return timeval;
- }
- }
- return def;
-}
-
-METHOD(settings_t, get_time, u_int32_t,
- private_settings_t *this, char *key, u_int32_t def, ...)
-{
- char *value;
- va_list args;
-
- va_start(args, def);
- value = find_value(this, this->top, key, args);
- va_end(args);
- return settings_value_as_time(value, def);
-}
-
-METHOD(settings_t, set_str, void,
- private_settings_t *this, char *key, char *value, ...)
-{
- va_list args;
- va_start(args, value);
- set_value(this, this->top, key, args, value);
- va_end(args);
-}
-
-METHOD(settings_t, set_bool, void,
- private_settings_t *this, char *key, bool value, ...)
-{
- va_list args;
- va_start(args, value);
- set_value(this, this->top, key, args, value ? "1" : "0");
- va_end(args);
-}
-
-METHOD(settings_t, set_int, void,
- private_settings_t *this, char *key, int value, ...)
-{
- char val[16];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%d", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_double, void,
- private_settings_t *this, char *key, double value, ...)
-{
- char val[64];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%f", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_time, void,
- private_settings_t *this, char *key, u_int32_t value, ...)
-{
- char val[16];
- va_list args;
- va_start(args, value);
- if (snprintf(val, sizeof(val), "%u", value) < sizeof(val))
- {
- set_value(this, this->top, key, args, val);
- }
- va_end(args);
-}
-
-METHOD(settings_t, set_default_str, bool,
- private_settings_t *this, char *key, char *value, ...)
-{
- char *old;
- va_list args;
-
- va_start(args, value);
- old = find_value(this, this->top, key, args);
- va_end(args);
-
- if (!old)
- {
- va_start(args, value);
- set_value(this, this->top, key, args, value);
- va_end(args);
- return TRUE;
- }
- return FALSE;
-}
-
-/**
- * Data for enumerators
- */
-typedef struct {
- /** settings_t instance */
- private_settings_t *settings;
- /** sections to enumerate */
- array_t *sections;
- /** sections/keys that were already enumerated */
- hashtable_t *seen;
-} enumerator_data_t;
-
-/**
- * Destroy enumerator data
- */
-static void enumerator_destroy(enumerator_data_t *this)
-{
- this->settings->lock->unlock(this->settings->lock);
- this->seen->destroy(this->seen);
- array_destroy(this->sections);
- free(this);
-}
-
-/**
- * Enumerate section names, not sections
- */
-static bool section_filter(hashtable_t *seen, section_t **in, char **out)
-{
- *out = (*in)->name;
- if (seen->get(seen, *out))
- {
- return FALSE;
- }
- seen->put(seen, *out, *out);
- return TRUE;
-}
-
-/**
- * Enumerate sections of the given section
- */
-static enumerator_t *section_enumerator(section_t *section,
- enumerator_data_t *data)
-{
- return enumerator_create_filter(array_create_enumerator(section->sections),
- (void*)section_filter, data->seen, NULL);
-}
-
-METHOD(settings_t, create_section_enumerator, enumerator_t*,
- private_settings_t *this, char *key, ...)
-{
- enumerator_data_t *data;
- array_t *sections;
- va_list args;
-
- this->lock->read_lock(this->lock);
- va_start(args, key);
- sections = find_sections(this, this->top, key, args);
- va_end(args);
-
- if (!sections)
- {
- this->lock->unlock(this->lock);
- return enumerator_create_empty();
- }
- INIT(data,
- .settings = this,
- .sections = sections,
- .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
- );
- return enumerator_create_nested(array_create_enumerator(sections),
- (void*)section_enumerator, data, (void*)enumerator_destroy);
-}
-
-/**
- * Enumerate key and values, not kv_t entries
- */
-static bool kv_filter(hashtable_t *seen, kv_t **in, char **key,
- void *none, char **value)
-{
- *key = (*in)->key;
- if (seen->get(seen, *key))
- {
- return FALSE;
- }
- *value = (*in)->value;
- seen->put(seen, *key, *key);
- return TRUE;
-}
-
-/**
- * Enumerate key/value pairs of the given section
- */
-static enumerator_t *kv_enumerator(section_t *section, enumerator_data_t *data)
-{
- return enumerator_create_filter(array_create_enumerator(section->kv),
- (void*)kv_filter, data->seen, NULL);
-}
-
-METHOD(settings_t, create_key_value_enumerator, enumerator_t*,
- private_settings_t *this, char *key, ...)
-{
- enumerator_data_t *data;
- array_t *sections;
- va_list args;
-
- this->lock->read_lock(this->lock);
- va_start(args, key);
- sections = find_sections(this, this->top, key, args);
- va_end(args);
-
- if (!sections)
- {
- this->lock->unlock(this->lock);
- return enumerator_create_empty();
- }
- INIT(data,
- .settings = this,
- .sections = sections,
- .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
- );
- return enumerator_create_nested(array_create_enumerator(sections),
- (void*)kv_enumerator, data, (void*)enumerator_destroy);
-}
-
-METHOD(settings_t, add_fallback, void,
- private_settings_t *this, const char *key, const char *fallback, ...)
-{
- section_t *section;
- va_list args;
-
- /* find/create the fallback */
- va_start(args, fallback);
- section = ensure_section(this, this->top, fallback, args);
- va_end(args);
-
- va_start(args, fallback);
- add_fallback_to_section(this, this->top, key, args, section);
- va_end(args);
-}
-
-/**
- * parse text, truncate "skip" chars, delimited by term respecting brackets.
- *
- * Chars in "skip" are truncated at the beginning and the end of the resulting
- * token. "term" contains a list of characters to read up to (first match),
- * while "br" contains bracket counterparts found in "term" to skip.
- */
-static char parse(char **text, char *skip, char *term, char *br, char **token)
-{
- char *best = NULL;
- char best_term = '\0';
-
- /* skip leading chars */
- while (strchr(skip, **text))
- {
- (*text)++;
- if (!**text)
- {
- return 0;
- }
- }
- /* mark begin of subtext */
- *token = *text;
- while (*term)
- {
- char *pos = *text;
- int level = 1;
-
- /* find terminator */
- while (*pos)
- {
- if (*pos == *term)
- {
- level--;
- }
- else if (br && *pos == *br)
- {
- level++;
- }
- if (level == 0)
- {
- if (best == NULL || best > pos)
- {
- best = pos;
- best_term = *term;
- }
- break;
- }
- pos++;
- }
- /* try next terminator */
- term++;
- if (br)
- {
- br++;
- }
- }
- if (best)
- {
- /* update input */
- *text = best;
- /* null trailing bytes */
- do
- {
- *best = '\0';
- best--;
- }
- while (best >= *token && strchr(skip, *best));
- /* return found terminator */
- return best_term;
- }
- return 0;
-}
-
-/**
- * Check if "text" starts with "pattern".
- * Characters in "skip" are skipped first. If found, TRUE is returned and "text"
- * is modified to point to the character right after "pattern".
- */
-static bool starts_with(char **text, char *skip, char *pattern)
-{
- char *pos = *text;
- int len = strlen(pattern);
- while (strchr(skip, *pos))
- {
- pos++;
- if (!*pos)
- {
- return FALSE;
- }
- }
- if (strlen(pos) < len || !strneq(pos, pattern, len))
- {
- return FALSE;
- }
- *text = pos + len;
- return TRUE;
-}
-
-/**
- * Check if what follows in "text" is an include statement.
- * If this function returns TRUE, "text" will point to the character right after
- * the include pattern, which is returned in "pattern".
- */
-static bool parse_include(char **text, char **pattern)
-{
- char *pos = *text;
- if (!starts_with(&pos, "\n\t ", "include"))
- {
- return FALSE;
- }
- if (starts_with(&pos, "\t ", "="))
- { /* ignore "include = value" */
- return FALSE;
- }
- *text = pos;
- return parse(text, "\t ", "\n", NULL, pattern) != 0;
-}
-
-/**
- * Forward declaration.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
- char *pattern, section_t *section);
-
-/**
- * Parse a section
- */
-static bool parse_section(linked_list_t *contents, char *file, int level,
- char **text, section_t *section)
-{
- bool finished = FALSE;
- char *key, *value, *inner;
-
- while (!finished)
- {
- if (parse_include(text, &value))
- {
- if (!parse_files(contents, file, level, value, section))
- {
- DBG1(DBG_LIB, "failed to include '%s'", value);
- return FALSE;
- }
- continue;
- }
- switch (parse(text, "\t\n ", "{=#", NULL, &key))
- {
- case '{':
- if (parse(text, "\t ", "}", "{", &inner))
- {
- section_t *sub;
- if (!strlen(key))
- {
- DBG1(DBG_LIB, "skipping section without name in '%s'",
- section->name);
- continue;
- }
- if (array_bsearch(section->sections, key, section_find,
- &sub) == -1)
- {
- sub = section_create(key);
- if (parse_section(contents, file, level, &inner, sub))
- {
- array_insert_create(&section->sections, ARRAY_TAIL,
- sub);
- array_sort(section->sections, section_sort, NULL);
- continue;
- }
- section_destroy(sub);
- }
- else
- { /* extend the existing section */
- if (parse_section(contents, file, level, &inner, sub))
- {
- continue;
- }
- }
- DBG1(DBG_LIB, "parsing subsection '%s' failed", key);
- break;
- }
- DBG1(DBG_LIB, "matching '}' not found near %s", *text);
- break;
- case '=':
- if (parse(text, "\t ", "\n", NULL, &value))
- {
- kv_t *kv;
- if (!strlen(key))
- {
- DBG1(DBG_LIB, "skipping value without key in '%s'",
- section->name);
- continue;
- }
- if (array_bsearch(section->kv, key, kv_find, &kv) == -1)
- {
- kv = kv_create(key, value);
- array_insert_create(&section->kv, ARRAY_TAIL, kv);
- array_sort(section->kv, kv_sort, NULL);
- }
- else
- { /* replace with the most recently read value */
- kv->value = value;
- }
- continue;
- }
- DBG1(DBG_LIB, "parsing value failed near %s", *text);
- break;
- case '#':
- parse(text, "", "\n", NULL, &value);
- continue;
- default:
- finished = TRUE;
- continue;
- }
- return FALSE;
- }
- return TRUE;
-}
-
-/**
- * Parse a file and add the settings to the given section.
- */
-static bool parse_file(linked_list_t *contents, char *file, int level,
- section_t *section)
-{
- bool success;
- char *text, *pos;
- struct stat st;
- FILE *fd;
- int len;
-
- DBG2(DBG_LIB, "loading config file '%s'", file);
- if (stat(file, &st) == -1)
- {
- if (errno == ENOENT)
- {
-#ifdef STRONGSWAN_CONF
- if (streq(file, STRONGSWAN_CONF))
- {
- DBG2(DBG_LIB, "'%s' does not exist, ignored", file);
- }
- else
-#endif
- {
- DBG1(DBG_LIB, "'%s' does not exist, ignored", file);
- }
- return TRUE;
- }
- DBG1(DBG_LIB, "failed to stat '%s': %s", file, strerror(errno));
- return FALSE;
- }
- else if (!S_ISREG(st.st_mode))
- {
- DBG1(DBG_LIB, "'%s' is not a regular file", file);
- return FALSE;
- }
- fd = fopen(file, "r");
- if (fd == NULL)
- {
- DBG1(DBG_LIB, "'%s' is not readable", file);
- return FALSE;
- }
- fseek(fd, 0, SEEK_END);
- len = ftell(fd);
- rewind(fd);
- text = malloc(len + 2);
- text[len] = text[len + 1] = '\0';
- if (fread(text, 1, len, fd) != len)
- {
- free(text);
- fclose(fd);
- return FALSE;
- }
- fclose(fd);
-
- pos = text;
- success = parse_section(contents, file, level, &pos, section);
- if (!success)
- {
- free(text);
- }
- else
- {
- contents->insert_last(contents, text);
- }
- return success;
-}
-
-/**
- * Load the files matching "pattern", which is resolved with glob(3), if
- * available.
- * If the pattern is relative, the directory of "file" is used as base.
- */
-static bool parse_files(linked_list_t *contents, char *file, int level,
- char *pattern, section_t *section)
-{
- bool success = TRUE;
- char pat[PATH_MAX];
-
- if (level > MAX_INCLUSION_LEVEL)
- {
- DBG1(DBG_LIB, "maximum level of %d includes reached, ignored",
- MAX_INCLUSION_LEVEL);
- return TRUE;
- }
-
- if (!strlen(pattern))
- {
- DBG1(DBG_LIB, "empty include pattern, ignored");
- return TRUE;
- }
-
- if (!file || pattern[0] == '/')
- { /* absolute path */
- if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
- {
- DBG1(DBG_LIB, "include pattern too long, ignored");
- return TRUE;
- }
- }
- else
- { /* base relative paths to the directory of the current file */
- char *dir = path_dirname(file);
- if (snprintf(pat, sizeof(pat), "%s/%s", dir, pattern) >= sizeof(pat))
- {
- DBG1(DBG_LIB, "include pattern too long, ignored");
- free(dir);
- return TRUE;
- }
- free(dir);
- }
-#ifdef HAVE_GLOB_H
- {
- int status;
- glob_t buf;
-
- status = glob(pat, GLOB_ERR, NULL, &buf);
- if (status == GLOB_NOMATCH)
- {
- DBG1(DBG_LIB, "no files found matching '%s', ignored", pat);
- }
- else if (status != 0)
- {
- DBG1(DBG_LIB, "expanding file pattern '%s' failed", pat);
- success = FALSE;
- }
- else
- {
- char **expanded;
- for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
- {
- success &= parse_file(contents, *expanded, level + 1, section);
- if (!success)
- {
- break;
- }
- }
- }
- globfree(&buf);
- }
-#else /* HAVE_GLOB_H */
- /* if glob(3) is not available, try to load pattern directly */
- success = parse_file(contents, pat, level + 1, section);
-#endif /* HAVE_GLOB_H */
- return success;
-}
-
-/**
- * Recursivly extends "base" with "extension".
- */
-static void section_extend(section_t *base, section_t *extension)
-{
- enumerator_t *enumerator;
- section_t *sec;
- kv_t *kv;
-
- enumerator = array_create_enumerator(extension->sections);
- while (enumerator->enumerate(enumerator, (void**)&sec))
- {
- section_t *found;
- if (array_bsearch(base->sections, sec->name, section_find,
- &found) != -1)
- {
- section_extend(found, sec);
- }
- else
- {
- array_remove_at(extension->sections, enumerator);
- array_insert_create(&base->sections, ARRAY_TAIL, sec);
- array_sort(base->sections, section_sort, NULL);
- }
- }
- enumerator->destroy(enumerator);
-
- enumerator = array_create_enumerator(extension->kv);
- while (enumerator->enumerate(enumerator, (void**)&kv))
- {
- kv_t *found;
- if (array_bsearch(base->kv, kv->key, kv_find, &found) != -1)
- {
- found->value = kv->value;
- }
- else
- {
- array_remove_at(extension->kv, enumerator);
- array_insert_create(&base->kv, ARRAY_TAIL, kv);
- array_sort(base->kv, kv_sort, NULL);
- }
- }
- enumerator->destroy(enumerator);
-}
-
-/**
- * Load settings from files matching the given file pattern.
- * All sections and values are added relative to "parent".
- * All files (even included ones) have to be loaded successfully.
- */
-static bool load_files_internal(private_settings_t *this, section_t *parent,
- char *pattern, bool merge)
-{
- char *text;
- linked_list_t *contents;
- section_t *section;
-
- if (pattern == NULL)
- {
-#ifdef STRONGSWAN_CONF
- pattern = STRONGSWAN_CONF;
-#else
- return FALSE;
-#endif
- }
-
- contents = linked_list_create();
- section = section_create(NULL);
-
- if (!parse_files(contents, NULL, 0, pattern, section))
- {
- contents->destroy_function(contents, (void*)free);
- section_destroy(section);
- return FALSE;
- }
-
- this->lock->write_lock(this->lock);
- if (!merge)
- {
- section_purge(parent);
- }
- /* extend parent section */
- section_extend(parent, section);
- /* move contents of loaded files to main store */
- while (contents->remove_first(contents, (void**)&text) == SUCCESS)
- {
- this->contents->insert_last(this->contents, text);
- }
- this->lock->unlock(this->lock);
-
- section_destroy(section);
- contents->destroy(contents);
- return TRUE;
-}
-
-METHOD(settings_t, load_files, bool,
- private_settings_t *this, char *pattern, bool merge)
-{
- return load_files_internal(this, this->top, pattern, merge);
-}
-
-METHOD(settings_t, load_files_section, bool,
- private_settings_t *this, char *pattern, bool merge, char *key, ...)
-{
- section_t *section;
- va_list args;
-
- va_start(args, key);
- section = ensure_section(this, this->top, key, args);
- va_end(args);
-
- if (!section)
- {
- return FALSE;
- }
- return load_files_internal(this, section, pattern, merge);
-}
-
-METHOD(settings_t, destroy, void,
- private_settings_t *this)
-{
- section_destroy(this->top);
- this->contents->destroy_function(this->contents, (void*)free);
- this->lock->destroy(this->lock);
- free(this);
-}
-
-/*
- * see header file
- */
-settings_t *settings_create(char *file)
-{
- private_settings_t *this;
-
- INIT(this,
- .public = {
- .get_str = _get_str,
- .get_int = _get_int,
- .get_double = _get_double,
- .get_time = _get_time,
- .get_bool = _get_bool,
- .set_str = _set_str,
- .set_int = _set_int,
- .set_double = _set_double,
- .set_time = _set_time,
- .set_bool = _set_bool,
- .set_default_str = _set_default_str,
- .create_section_enumerator = _create_section_enumerator,
- .create_key_value_enumerator = _create_key_value_enumerator,
- .add_fallback = _add_fallback,
- .load_files = _load_files,
- .load_files_section = _load_files_section,
- .destroy = _destroy,
- },
- .top = section_create(NULL),
- .contents = linked_list_create(),
- .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
- );
-
- load_files(this, file, FALSE);
-
- return &this->public;
-}
diff --git a/src/libstrongswan/utils/settings.h b/src/libstrongswan/utils/settings.h
deleted file mode 100644
index 46403c4d3..000000000
--- a/src/libstrongswan/utils/settings.h
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2010 Tobias Brunner
- * Copyright (C) 2008 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-/**
- * @defgroup settings settings
- * @{ @ingroup utils
- */
-
-#ifndef SETTINGS_H_
-#define SETTINGS_H_
-
-typedef struct settings_t settings_t;
-
-#include "utils.h"
-#include "collections/enumerator.h"
-
-/**
- * Convert a string value returned by a key/value enumerator to a boolean.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_bool()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-bool settings_value_as_bool(char *value, bool def);
-
-/**
- * Convert a string value returned by a key/value enumerator to an integer.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_int()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-int settings_value_as_int(char *value, int def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a double.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_double()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-double settings_value_as_double(char *value, double def);
-
-/**
- * Convert a string value returned by a key/value enumerator to a time value.
- *
- * @see settings_t.create_key_value_enumerator()
- * @see settings_t.get_time()
- * @param value the string value
- * @param def the default value, if value is NULL or invalid
- */
-u_int32_t settings_value_as_time(char *value, u_int32_t def);
-
-/**
- * Generic configuration options read from a config file.
- *
- * The syntax is quite simple:
- * @code
- * settings := (section|keyvalue)*
- * section := name { settings }
- * keyvalue := key = value\n
- * @endcode
- * E.g.:
- * @code
- a = b
- section-one {
- somevalue = asdf
- subsection {
- othervalue = xxx
- }
- yetanother = zz
- }
- section-two {
- }
- @endcode
- *
- * The values are accessed using the get() functions using dotted keys, e.g.
- * section-one.subsection.othervalue
- *
- * Currently only a limited set of printf format specifiers are supported
- * (namely %s, %d and %N, see implementation for details).
- *
- * \section includes Including other files
- * Other files can be included, using the include statement e.g.
- * @code
- * include /somepath/subconfig.conf
- * @endcode
- * Shell patterns like *.conf are possible.
- *
- * If the path is relative, the directory of the file containing the include
- * statement is used as base.
- *
- * Sections loaded from included files extend previously loaded sections,
- * already existing values are replaced.
- *
- * All settings included from files are added relative to the section the
- * include statement is in.
- *
- * The following files result in the same final config as above:
- *
- * @code
- a = b
- section-one {
- somevalue = before include
- include include.conf
- }
- include two.conf
- @endcode
- * include.conf
- * @code
- somevalue = asdf
- subsection {
- othervalue = yyy
- }
- yetanother = zz
- @endcode
- * two.conf
- * @code
- section-one {
- subsection {
- othervalue = xxx
- }
- }
- section-two {
- }
- @endcode
- */
-struct settings_t {
-
- /**
- * Get a settings value as a string.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value pointing to internal string
- */
- char* (*get_str)(settings_t *this, char *key, char *def, ...);
-
- /**
- * Get a boolean yes|no, true|false value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- bool (*get_bool)(settings_t *this, char *key, bool def, ...);
-
- /**
- * Get an integer value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- int (*get_int)(settings_t *this, char *key, int def, ...);
-
- /**
- * Get an double value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key
- */
- double (*get_double)(settings_t *this, char *key, double def, ...);
-
- /**
- * Get a time value.
- *
- * @param key key including sections, printf style format
- * @param def value returned if key not found
- * @param ... argument list for key
- * @return value of the key (in seconds)
- */
- u_int32_t (*get_time)(settings_t *this, char *key, u_int32_t def, ...);
-
- /**
- * Set a string value.
- *
- * @param key key including sections, printf style format
- * @param value value to set (gets cloned)
- * @param ... argument list for key
- */
- void (*set_str)(settings_t *this, char *key, char *value, ...);
-
- /**
- * Set a boolean value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_bool)(settings_t *this, char *key, bool value, ...);
-
- /**
- * Set an integer value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_int)(settings_t *this, char *key, int value, ...);
-
- /**
- * Set an double value.
- *
- * @param key key including sections, printf style format
- * @param value value to set
- * @param ... argument list for key
- */
- void (*set_double)(settings_t *this, char *key, double value, ...);
-
- /**
- * Set a time value.
- *
- * @param key key including sections, printf style format
- * @param def value to set
- * @param ... argument list for key
- */
- void (*set_time)(settings_t *this, char *key, u_int32_t value, ...);
-
- /**
- * Set a default for string value.
- *
- * @param key key including sections, printf style format
- * @param def value to set if unconfigured
- * @param ... argument list for key
- * @return TRUE if a new default value for key has been set
- */
- bool (*set_default_str)(settings_t *this, char *key, char *value, ...);
-
- /**
- * Create an enumerator over subsection names of a section.
- *
- * @param section section including parents, printf style format
- * @param ... argument list for key
- * @return enumerator over subsection names
- */
- enumerator_t* (*create_section_enumerator)(settings_t *this,
- char *section, ...);
-
- /**
- * Create an enumerator over key/value pairs in a section.
- *
- * @param section section name to list key/value pairs of, printf style
- * @param ... argument list for section
- * @return enumerator over (char *key, char *value)
- */
- enumerator_t* (*create_key_value_enumerator)(settings_t *this,
- char *section, ...);
-
- /**
- * Add a fallback for the given section.
- *
- * Example: When the fallback 'section-two' is configured for
- * 'section-one.two' any failed lookup for a section or key in
- * 'section-one.two' will result in a lookup for the same section/key
- * in 'section-two'.
- *
- * @note Lookups are depth-first and currently strictly top-down.
- * For instance, if app.sec had lib1.sec as fallback and lib1 had lib2 as
- * fallback the keys/sections in lib2.sec would not be considered. But if
- * app had lib3 as fallback the contents of lib3.sec would (as app is passed
- * during the initial lookup). In the last example the order during
- * enumerations would be app.sec, lib1.sec, lib3.sec.
- *
- * @note Additional arguments will be applied to both section format
- * strings so they must be compatible.
- *
- * @param section section for which a fallback is configured, printf style
- * @param fallback fallback section, printf style
- * @param ... argument list for section and fallback
- */
- void (*add_fallback)(settings_t *this, const char *section,
- const char *fallback, ...);
-
- /**
- * Load settings from the files matching the given pattern.
- *
- * If merge is TRUE, existing sections are extended, existing values
- * replaced, by those found in the loaded files. If it is FALSE, existing
- * sections are purged before reading the new config.
- *
- * @note If any of the files matching the pattern fails to load, no settings
- * are added at all. So, it's all or nothing.
- *
- * @param pattern file pattern
- * @param merge TRUE to merge config with existing values
- * @return TRUE, if settings were loaded successfully
- */
- bool (*load_files)(settings_t *this, char *pattern, bool merge);
-
- /**
- * Load settings from the files matching the given pattern.
- *
- * If merge is TRUE, existing sections are extended, existing values
- * replaced, by those found in the loaded files. If it is FALSE, existing
- * sections are purged before reading the new config.
- *
- * All settings are loaded relative to the given section. The section is
- * created, if it does not yet exist.
- *
- * @note If any of the files matching the pattern fails to load, no settings
- * are added at all. So, it's all or nothing.
- *
- * @param pattern file pattern
- * @param merge TRUE to merge config with existing values
- * @param section section name of parent section, printf style
- * @param ... argument list for section
- * @return TRUE, if settings were loaded successfully
- */
- bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
- char *section, ...);
-
- /**
- * Destroy a settings instance.
- */
- void (*destroy)(settings_t *this);
-};
-
-/**
- * Load settings from a file.
- *
- * @param file file to read settings from, NULL for default
- * @return settings object
- */
-settings_t *settings_create(char *file);
-
-#endif /** SETTINGS_H_ @}*/
diff --git a/src/libstrongswan/utils/test.c b/src/libstrongswan/utils/test.c
index 624ac4b34..0b0a80f42 100644
--- a/src/libstrongswan/utils/test.c
+++ b/src/libstrongswan/utils/test.c
@@ -20,13 +20,23 @@
/**
* A collection of testable functions
*/
-hashtable_t *testable_functions;
+static hashtable_t *functions = NULL;
+
+#ifndef WIN32
+bool test_runner_available __attribute__((weak));
+#endif
/**
- * The function that actually initializes the hash table above. Provided
- * by the test runner.
+ * Check if we have libtest linkage and need testable functions
*/
-void testable_functions_create() __attribute__((weak));
+static bool has_libtest_linkage()
+{
+#ifdef WIN32
+ return dlsym(RTLD_DEFAULT, "test_runner_available");
+#else
+ return test_runner_available;
+#endif
+}
/*
* Described in header.
@@ -35,33 +45,48 @@ void testable_function_register(char *name, void *fn)
{
bool old = FALSE;
- if (!testable_functions_create)
- { /* not linked to the test runner */
- return;
- }
- else if (!fn && !testable_functions)
- { /* ignore as testable_functions has already been destroyed */
- return;
- }
-
if (lib && lib->leak_detective)
{
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
}
- if (!testable_functions)
- {
- testable_functions_create();
- }
- if (fn)
- {
- testable_functions->put(testable_functions, name, fn);
- }
- else
+
+ if (has_libtest_linkage())
{
- testable_functions->remove(testable_functions, name);
+ if (!functions)
+ {
+ chunk_hash_seed();
+ functions = hashtable_create(hashtable_hash_str,
+ hashtable_equals_str, 8);
+ }
+ if (fn)
+ {
+ functions->put(functions, name, fn);
+ }
+ else
+ {
+ functions->remove(functions, name);
+ if (functions->get_count(functions) == 0)
+ {
+ functions->destroy(functions);
+ functions = NULL;
+ }
+ }
}
+
if (lib && lib->leak_detective)
{
lib->leak_detective->set_state(lib->leak_detective, old);
}
}
+
+/*
+ * Described in header.
+ */
+void* testable_function_get(char *name)
+{
+ if (functions)
+ {
+ return functions->get(functions, name);
+ }
+ return NULL;
+}
diff --git a/src/libstrongswan/utils/test.h b/src/libstrongswan/utils/test.h
index a1b2a2d9b..f9a84713e 100644
--- a/src/libstrongswan/utils/test.h
+++ b/src/libstrongswan/utils/test.h
@@ -24,19 +24,20 @@
#include "collections/hashtable.h"
/**
- * Collection of testable functions.
+ * Register a (possibly static) function so that it can be called from tests.
*
- * @note Is initialized only if libtest is loaded.
+ * @param name name (namespace/function)
+ * @param fn function to register (set to NULL to unregister)
*/
-extern hashtable_t *testable_functions;
+void testable_function_register(char *name, void *fn);
/**
- * Register a (possibly static) function so that it can be called from tests.
+ * Find a previously registered testable function.
*
* @param name name (namespace/function)
- * @param fn function to register (set to NULL to unregister)
+ * @return function, NULL if not found
*/
-void testable_function_register(char *name, void *fn);
+void* testable_function_get(char *name);
/**
* Macro to automatically register/unregister a function that can be called
@@ -82,10 +83,7 @@ static ret (*TEST_##ns##name)(__VA_ARGS__);
*/
#define TEST_FUNCTION(ns, name, ...) \
({ \
- if (testable_functions) \
- { \
- TEST_##ns##name = testable_functions->get(testable_functions, #ns "/" #name); \
- } \
+ TEST_##ns##name = testable_function_get( #ns "/" #name); \
if (!TEST_##ns##name) \
{ \
test_fail_msg(__FILE__, __LINE__, "function " #name " (" #ns ") not found"); \
diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c
index fe80edb82..02a720945 100644
--- a/src/libstrongswan/utils/utils.c
+++ b/src/libstrongswan/utils/utils.c
@@ -15,6 +15,13 @@
*/
#define _GNU_SOURCE /* for memrchr */
+#ifdef WIN32
+/* for GetTickCount64, Windows 7 */
+# define _WIN32_WINNT 0x0601
+#endif
+
+#include "utils.h"
+
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
@@ -24,13 +31,17 @@
#include <limits.h>
#include <dirent.h>
#include <time.h>
-#include <pthread.h>
-
-#include "utils.h"
-
-#include "collections/enumerator.h"
-#include "utils/debug.h"
-#include "utils/chunk.h"
+#ifndef WIN32
+# include <signal.h>
+#endif
+
+#include <library.h>
+#include <utils/debug.h>
+#include <utils/chunk.h>
+#include <collections/enumerator.h>
+#include <threading/spinlock.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
ENUM(status_names, SUCCESS, NEED_MORE,
"SUCCESS",
@@ -216,6 +227,84 @@ char* strreplace(const char *str, const char *search, const char *replace)
return res;
}
+#ifdef WIN32
+
+/**
+ * Flag to indicate signaled wait_sigint()
+ */
+static bool sigint_signaled = FALSE;
+
+/**
+ * Condvar to wait in wait_sigint()
+ */
+static condvar_t *sigint_cond;
+
+/**
+ * Mutex to check signaling()
+ */
+static mutex_t *sigint_mutex;
+
+/**
+ * Control handler to catch ^C
+ */
+static BOOL WINAPI handler(DWORD dwCtrlType)
+{
+ switch (dwCtrlType)
+ {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ sigint_mutex->lock(sigint_mutex);
+ sigint_signaled = TRUE;
+ sigint_cond->signal(sigint_cond);
+ sigint_mutex->unlock(sigint_mutex);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * Windows variant
+ */
+void wait_sigint()
+{
+ SetConsoleCtrlHandler(handler, TRUE);
+
+ sigint_mutex = mutex_create(MUTEX_TYPE_DEFAULT);
+ sigint_cond = condvar_create(CONDVAR_TYPE_DEFAULT);
+
+ sigint_mutex->lock(sigint_mutex);
+ while (!sigint_signaled)
+ {
+ sigint_cond->wait(sigint_cond, sigint_mutex);
+ }
+ sigint_mutex->unlock(sigint_mutex);
+
+ sigint_mutex->destroy(sigint_mutex);
+ sigint_cond->destroy(sigint_cond);
+}
+
+#else /* !WIN32 */
+
+/**
+ * Unix variant
+ */
+void wait_sigint()
+{
+ sigset_t set;
+ int sig;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGTERM);
+
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ sigwait(&set, &sig);
+}
+
+#endif
+
/**
* Described in header.
*/
@@ -223,21 +312,30 @@ char* path_dirname(const char *path)
{
char *pos;
- pos = path ? strrchr(path, '/') : NULL;
+ pos = path ? strrchr(path, DIRECTORY_SEPARATOR[0]) : NULL;
if (pos && !pos[1])
{ /* if path ends with slashes we have to look beyond them */
- while (pos > path && *pos == '/')
+ while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
{ /* skip trailing slashes */
pos--;
}
- pos = memrchr(path, '/', pos - path + 1);
+ pos = memrchr(path, DIRECTORY_SEPARATOR[0], pos - path + 1);
}
if (!pos)
{
+#ifdef WIN32
+ if (path && strlen(path))
+ {
+ if ((isalpha(path[0]) && path[1] == ':'))
+ { /* if just a drive letter given, return that as dirname */
+ return chunk_clone(chunk_from_chars(path[0], ':', 0)).ptr;
+ }
+ }
+#endif
return strdup(".");
}
- while (pos > path && *pos == '/')
+ while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
{ /* skip superfluous slashes */
pos--;
}
@@ -255,19 +353,19 @@ char* path_basename(const char *path)
{
return strdup(".");
}
- pos = strrchr(path, '/');
+ pos = strrchr(path, DIRECTORY_SEPARATOR[0]);
if (pos && !pos[1])
{ /* if path ends with slashes we have to look beyond them */
- while (pos > path && *pos == '/')
+ while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
{ /* skip trailing slashes */
pos--;
}
- if (pos == path && *pos == '/')
+ if (pos == path && *pos == DIRECTORY_SEPARATOR[0])
{ /* contains only slashes */
- return strdup("/");
+ return strdup(DIRECTORY_SEPARATOR);
}
trail = pos + 1;
- pos = memrchr(path, '/', trail - path);
+ pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path);
}
pos = pos ? pos + 1 : (char*)path;
return trail ? strndup(pos, trail - pos) : strdup(pos);
@@ -276,6 +374,33 @@ char* path_basename(const char *path)
/**
* Described in header.
*/
+bool path_absolute(const char *path)
+{
+ if (!path)
+ {
+ return FALSE;
+ }
+#ifdef WIN32
+ if (strpfx(path, "\\\\"))
+ { /* UNC */
+ return TRUE;
+ }
+ if (strlen(path) && isalpha(path[0]) && path[1] == ':')
+ { /* drive letter */
+ return TRUE;
+ }
+#else /* !WIN32 */
+ if (path[0] == DIRECTORY_SEPARATOR[0])
+ {
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+/**
+ * Described in header.
+ */
bool mkdir_p(const char *path, mode_t mode)
{
int len;
@@ -307,7 +432,11 @@ bool mkdir_p(const char *path, mode_t mode)
*pos = '\0';
if (access(full, F_OK) < 0)
{
+#ifdef WIN32
+ if (_mkdir(full) < 0)
+#else
if (mkdir(full, mode) < 0)
+#endif
{
DBG1(DBG_LIB, "failed to create directory %s", full);
return FALSE;
@@ -359,6 +488,9 @@ char* tty_escape_get(int fd, tty_escape_t escape)
case TTY_BOLD:
case TTY_UNDERLINE:
case TTY_BLINKING:
+#ifdef WIN32
+ return "";
+#endif
case TTY_FG_BLACK:
case TTY_FG_RED:
case TTY_FG_GREEN:
@@ -378,7 +510,7 @@ char* tty_escape_get(int fd, tty_escape_t escape)
case TTY_BG_WHITE:
case TTY_BG_DEF:
return enum_to_name(tty_color_names, escape);
- /* warn if a excape code is missing */
+ /* warn if a escape code is missing */
}
return "";
}
@@ -414,7 +546,11 @@ void closefrom(int lowfd)
}
/* ...fall back to closing all fds otherwise */
+#ifdef WIN32
+ maxfd = _getmaxstdio();
+#else
maxfd = (int)sysconf(_SC_OPEN_MAX);
+#endif
if (maxfd < 0)
{
maxfd = 256;
@@ -431,6 +567,19 @@ void closefrom(int lowfd)
*/
time_t time_monotonic(timeval_t *tv)
{
+#ifdef WIN32
+ ULONGLONG ms;
+ time_t s;
+
+ ms = GetTickCount64();
+ s = ms / 1000;
+ if (tv)
+ {
+ tv->tv_sec = s;
+ tv->tv_usec = (ms - (s * 1000)) * 1000;
+ }
+ return s;
+#else /* !WIN32 */
#if defined(HAVE_CLOCK_GETTIME) && \
(defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \
defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
@@ -462,6 +611,7 @@ time_t time_monotonic(timeval_t *tv)
return -1;
}
return tv->tv_sec;
+#endif /* !WIN32 */
}
/**
@@ -511,12 +661,12 @@ void nop()
{
}
-#ifndef HAVE_GCC_ATOMIC_OPERATIONS
+#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
/**
- * We use a single mutex for all refcount variables.
+ * Spinlock for ref_get/put
*/
-static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER;
+static spinlock_t *ref_lock;
/**
* Increase refcount
@@ -525,9 +675,9 @@ refcount_t ref_get(refcount_t *ref)
{
refcount_t current;
- pthread_mutex_lock(&ref_mutex);
+ ref_lock->lock(ref_lock);
current = ++(*ref);
- pthread_mutex_unlock(&ref_mutex);
+ ref_lock->unlock(ref_lock);
return current;
}
@@ -539,16 +689,30 @@ bool ref_put(refcount_t *ref)
{
bool more_refs;
- pthread_mutex_lock(&ref_mutex);
+ ref_lock->lock(ref_lock);
more_refs = --(*ref) > 0;
- pthread_mutex_unlock(&ref_mutex);
+ ref_lock->unlock(ref_lock);
return !more_refs;
}
/**
- * Single mutex for all compare and swap operations.
+ * Current refcount
+ */
+refcount_t ref_cur(refcount_t *ref)
+{
+ refcount_t current;
+
+ ref_lock->lock(ref_lock);
+ current = *ref;
+ ref_lock->unlock(ref_lock);
+
+ return current;
+}
+
+/**
+ * Spinlock for all compare and swap operations.
*/
-static pthread_mutex_t cas_mutex = PTHREAD_MUTEX_INITIALIZER;
+static spinlock_t *cas_lock;
/**
* Compare and swap if equal to old value
@@ -557,16 +721,16 @@ static pthread_mutex_t cas_mutex = PTHREAD_MUTEX_INITIALIZER;
bool cas_##name(type *ptr, type oldval, type newval) \
{ \
bool swapped; \
- pthread_mutex_lock(&cas_mutex); \
+ cas_lock->lock(cas_lock); \
if ((swapped = (*ptr == oldval))) { *ptr = newval; } \
- pthread_mutex_unlock(&cas_mutex); \
+ cas_lock->unlock(cas_lock); \
return swapped; \
}
_cas_impl(bool, bool)
_cas_impl(ptr, void*)
-#endif /* HAVE_GCC_ATOMIC_OPERATIONS */
+#endif /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
#ifdef HAVE_FMEMOPEN_FALLBACK
@@ -614,6 +778,40 @@ FILE *fmemopen(void *buf, size_t size, const char *mode)
#endif /* FMEMOPEN fallback*/
/**
+ * See header
+ */
+void utils_init()
+{
+#ifdef WIN32
+ windows_init();
+#endif
+
+#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
+ ref_lock = spinlock_create();
+ cas_lock = spinlock_create();
+#endif
+
+ strerror_init();
+}
+
+/**
+ * See header
+ */
+void utils_deinit()
+{
+#ifdef WIN32
+ windows_deinit();
+#endif
+
+#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
+ ref_lock->destroy(ref_lock);
+ cas_lock->destroy(cas_lock);
+#endif
+
+ strerror_deinit();
+}
+
+/**
* Described in header.
*/
int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
@@ -625,20 +823,23 @@ int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
};
time_t *time = *((time_t**)(args[0]));
bool utc = *((int*)(args[1]));
- struct tm t;
+ struct tm t, *ret = NULL;
- if (*time == UNDEFINED_TIME)
+ if (*time != UNDEFINED_TIME)
{
- return print_in_hook(data, "--- -- --:--:--%s----",
- utc ? " UTC " : " ");
- }
- if (utc)
- {
- gmtime_r(time, &t);
+ if (utc)
+ {
+ ret = gmtime_r(time, &t);
+ }
+ else
+ {
+ ret = localtime_r(time, &t);
+ }
}
- else
+ if (ret == NULL)
{
- localtime_r(time, &t);
+ return print_in_hook(data, "--- -- --:--:--%s----",
+ utc ? " UTC " : " ");
}
return print_in_hook(data, "%s %02d %02d:%02d:%02d%s%04d",
months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index a55e7d831..1b822dd61 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -26,11 +26,18 @@
#include <stdlib.h>
#include <stddef.h>
#include <sys/time.h>
-#include <arpa/inet.h>
#include <string.h>
-#include "enum.h"
-#include "utils/strerror.h"
+#ifdef WIN32
+# include "windows.h"
+#else
+# define _GNU_SOURCE
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <sched.h>
+#endif
/**
* strongSwan program return codes
@@ -73,6 +80,28 @@
# define TRUE true
#endif /* TRUE */
+#include "enum.h"
+#include "utils/strerror.h"
+
+/**
+ * Directory separator character in paths on this platform
+ */
+#ifdef WIN32
+# define DIRECTORY_SEPARATOR "\\"
+#else
+# define DIRECTORY_SEPARATOR "/"
+#endif
+
+/**
+ * Initialize utility functions
+ */
+void utils_init();
+
+/**
+ * Deinitialize utility functions
+ */
+void utils_deinit();
+
/**
* Helper function that compares two strings for equality
*/
@@ -138,6 +167,48 @@ static inline bool memeq(const void *x, const void *y, size_t len)
}
/**
+ * Calling memcpy() with NULL pointers, even with n == 0, results in undefined
+ * behavior according to the C standard. This version is guaranteed to not
+ * access the pointers if n is 0.
+ */
+static inline void *memcpy_noop(void *dst, const void *src, size_t n)
+{
+ return n ? memcpy(dst, src, n) : dst;
+}
+#ifdef memcpy
+# undef memcpy
+#endif
+#define memcpy(d,s,n) memcpy_noop(d,s,n)
+
+/**
+ * Calling memmove() with NULL pointers, even with n == 0, results in undefined
+ * behavior according to the C standard. This version is guaranteed to not
+ * access the pointers if n is 0.
+ */
+static inline void *memmove_noop(void *dst, const void *src, size_t n)
+{
+ return n ? memmove(dst, src, n) : dst;
+}
+#ifdef memmove
+# undef memmove
+#endif
+#define memmove(d,s,n) memmove_noop(d,s,n)
+
+/**
+ * Calling memset() with a NULL pointer, even with n == 0, results in undefined
+ * behavior according to the C standard. This version is guaranteed to not
+ * access the pointer if n is 0.
+ */
+static inline void *memset_noop(void *s, int c, size_t n)
+{
+ return n ? memset(s, c, n) : s;
+}
+#ifdef memset
+# undef memset
+#endif
+#define memset(s,c,n) memset_noop(s,c,n)
+
+/**
* Macro gives back larger of two values.
*/
#define max(x,y) ({ \
@@ -145,7 +216,6 @@ static inline bool memeq(const void *x, const void *y, size_t len)
typeof(y) _y = (y); \
_x > _y ? _x : _y; })
-
/**
* Macro gives back smaller of two values.
*/
@@ -204,6 +274,45 @@ static inline bool memeq(const void *x, const void *y, size_t len)
static ret name(this, ##__VA_ARGS__)
/**
+ * Callback declaration/definition macro, allowing casted first parameter.
+ *
+ * This is very similar to METHOD, but instead of casting the first parameter
+ * to a public interface, it uses a void*. This allows type safe definition
+ * of a callback function, while using the real type for the first parameter.
+ */
+#define CALLBACK(name, ret, param1, ...) \
+ static ret _cb_##name(union {void *_generic; param1;} \
+ __attribute__((transparent_union)), ##__VA_ARGS__); \
+ static typeof(_cb_##name) *name = (typeof(_cb_##name)*)_cb_##name; \
+ static ret _cb_##name(param1, ##__VA_ARGS__)
+
+/**
+ * This macro allows counting the number of arguments passed to a macro.
+ * Combined with the VA_ARGS_DISPATCH() macro this can be used to implement
+ * macro overloading based on the number of arguments.
+ * 0 to 10 arguments are currently supported.
+ */
+#define VA_ARGS_NUM(...) _VA_ARGS_NUM(0,##__VA_ARGS__,10,9,8,7,6,5,4,3,2,1,0)
+#define _VA_ARGS_NUM(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,NUM,...) NUM
+
+/**
+ * This macro can be used to dispatch a macro call based on the number of given
+ * arguments, for instance:
+ *
+ * @code
+ * #define MY_MACRO(...) VA_ARGS_DISPATCH(MY_MACRO, __VA_ARGS__)(__VA_ARGS__)
+ * #define MY_MACRO1(arg) one_arg(arg)
+ * #define MY_MACRO2(arg1,arg2) two_args(arg1,arg2)
+ * @endcode
+ *
+ * MY_MACRO() can now be called with either one or two arguments, which will
+ * resolve to one_arg(arg) or two_args(arg1,arg2), respectively.
+ */
+#define VA_ARGS_DISPATCH(func, ...) _VA_ARGS_DISPATCH(func, VA_ARGS_NUM(__VA_ARGS__))
+#define _VA_ARGS_DISPATCH(func, num) __VA_ARGS_DISPATCH(func, num)
+#define __VA_ARGS_DISPATCH(func, num) func ## num
+
+/**
* Architecture independent bitfield definition helpers (at least with GCC).
*
* Defines a bitfield with a type t and a fixed size of bitfield members, e.g.:
@@ -260,7 +369,7 @@ static inline bool memeq(const void *x, const void *y, size_t len)
* TODO: since the uintXX_t types are defined by the C99 standard we should
* probably use those anyway
*/
-#ifdef __sun
+#if defined __sun || defined WIN32
#include <stdint.h>
typedef uint8_t u_int8_t;
typedef uint16_t u_int16_t;
@@ -501,6 +610,11 @@ char *translate(char *str, const char *from, const char *to);
char *strreplace(const char *str, const char *search, const char *replace);
/**
+ * Portable function to wait for SIGINT/SIGTERM (or equivalent).
+ */
+void wait_sigint();
+
+/**
* Like dirname(3) returns the directory part of the given null-terminated
* pathname, up to but not including the final '/' (or '.' if no '/' is found).
* Trailing '/' are not counted as part of the pathname.
@@ -527,6 +641,14 @@ char *path_dirname(const char *path);
char *path_basename(const char *path);
/**
+ * Check if a given path is absolute.
+ *
+ * @param path path to check
+ * @return TRUE if absolute, FALSE if relative
+ */
+bool path_absolute(const char *path);
+
+/**
* Creates a directory and all required parent directories.
*
* @param path path to the new directory
@@ -748,22 +870,49 @@ static inline size_t round_down(size_t size, size_t alignment)
*/
typedef u_int refcount_t;
+/* use __atomic* built-ins with GCC 4.7 and newer */
+#ifdef __GNUC__
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
+# define HAVE_GCC_ATOMIC_OPERATIONS
+# endif
+#endif
+
#ifdef HAVE_GCC_ATOMIC_OPERATIONS
+#define ref_get(ref) __atomic_add_fetch(ref, 1, __ATOMIC_RELAXED)
+/* The relaxed memory model works fine for increments as these (usually) don't
+ * change the state of refcounted objects. But here we have to ensure that we
+ * free the right stuff if ref counted objects are mutable. So we have to sync
+ * with other threads that call ref_put(). It would be sufficient to use
+ * __ATOMIC_RELEASE here and then call __atomic_thread_fence() with
+ * __ATOMIC_ACQUIRE if we reach 0, but since we don't have control over the use
+ * of ref_put() we have to make sure. */
+#define ref_put(ref) (!__atomic_sub_fetch(ref, 1, __ATOMIC_ACQ_REL))
+#define ref_cur(ref) __atomic_load_n(ref, __ATOMIC_RELAXED)
+
+#define _cas_impl(ptr, oldval, newval) ({ typeof(oldval) _old = oldval; \
+ __atomic_compare_exchange_n(ptr, &_old, newval, FALSE, \
+ __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); })
+#define cas_bool(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
+#define cas_ptr(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
+
+#elif defined(HAVE_GCC_SYNC_OPERATIONS)
+
#define ref_get(ref) __sync_add_and_fetch(ref, 1)
#define ref_put(ref) (!__sync_sub_and_fetch(ref, 1))
+#define ref_cur(ref) __sync_fetch_and_add(ref, 0)
#define cas_bool(ptr, oldval, newval) \
(__sync_bool_compare_and_swap(ptr, oldval, newval))
#define cas_ptr(ptr, oldval, newval) \
(__sync_bool_compare_and_swap(ptr, oldval, newval))
-#else /* !HAVE_GCC_ATOMIC_OPERATIONS */
+#else /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
/**
* Get a new reference.
*
- * Increments the reference counter atomic.
+ * Increments the reference counter atomically.
*
* @param ref pointer to ref counter
* @return new value of ref
@@ -773,7 +922,7 @@ refcount_t ref_get(refcount_t *ref);
/**
* Put back a unused reference.
*
- * Decrements the reference counter atomic and
+ * Decrements the reference counter atomically and
* says if more references available.
*
* @param ref pointer to ref counter
@@ -782,6 +931,14 @@ refcount_t ref_get(refcount_t *ref);
bool ref_put(refcount_t *ref);
/**
+ * Get the current value of the reference counter.
+ *
+ * @param ref pointer to ref counter
+ * @return current value of ref
+ */
+refcount_t ref_cur(refcount_t *ref);
+
+/**
* Atomically replace value of ptr with newval if it currently equals oldval.
*
* @param ptr pointer to variable
diff --git a/src/libstrongswan/utils/utils/strerror.c b/src/libstrongswan/utils/utils/strerror.c
index 95e463f5f..d35bbec68 100644
--- a/src/libstrongswan/utils/utils/strerror.c
+++ b/src/libstrongswan/utils/utils/strerror.c
@@ -15,7 +15,10 @@
#include <stdlib.h>
#include <string.h>
-#include <pthread.h>
+
+#include <library.h>
+#include <threading/thread_value.h>
+#include <threading/spinlock.h>
#include "strerror.h"
@@ -25,22 +28,16 @@
#define STRERROR_BUF_LEN 256
/**
- * Key to store thread-specific error buffer
- */
-static pthread_key_t strerror_buf_key;
-
-/**
- * Only initialize the key above once
+ * Thread specific strerror buffer, as char*
*/
-static pthread_once_t strerror_buf_key_once = PTHREAD_ONCE_INIT;
+static thread_value_t *strerror_buf;
+#ifndef HAVE_STRERROR_R
/**
- * Create the key used for the thread-specific error buffer
+ * Lock to access strerror() safely
*/
-static void create_strerror_buf_key()
-{
- pthread_key_create(&strerror_buf_key, free);
-}
+static spinlock_t *strerror_lock;
+#endif /* HAVE_STRERROR_R */
/**
* Retrieve the error buffer assigned to the current thread (or create it)
@@ -48,50 +45,103 @@ static void create_strerror_buf_key()
static inline char *get_strerror_buf()
{
char *buf;
+ bool old = FALSE;
- pthread_once(&strerror_buf_key_once, create_strerror_buf_key);
- buf = pthread_getspecific(strerror_buf_key);
+ if (!strerror_buf)
+ {
+ return NULL;
+ }
+
+ buf = strerror_buf->get(strerror_buf);
if (!buf)
{
+ if (lib->leak_detective)
+ {
+ old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
+ }
buf = malloc(STRERROR_BUF_LEN);
- pthread_setspecific(strerror_buf_key, buf);
+ strerror_buf->set(strerror_buf, buf);
+ if (lib->leak_detective)
+ {
+ lib->leak_detective->set_state(lib->leak_detective, old);
+ }
}
return buf;
}
-#ifdef HAVE_STRERROR_R
+/**
+ * Use real strerror() below
+ */
+#undef strerror
+
/*
* Described in header.
*/
const char *strerror_safe(int errnum)
{
- char *buf = get_strerror_buf(), *msg;
+ char *buf, *msg;
-#ifdef STRERROR_R_CHAR_P
+ buf = get_strerror_buf();
+ if (!buf)
+ {
+ /* library not initialized? fallback */
+ return strerror(errnum);
+ }
+#ifdef HAVE_STRERROR_R
+# ifdef STRERROR_R_CHAR_P
/* char* version which may or may not return the original buffer */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN);
-#else
+# else
/* int version returns 0 on success */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf;
-#endif
+# endif
+#else /* HAVE_STRERROR_R */
+ /* use a lock to ensure calling strerror(3) is thread-safe */
+ strerror_lock->lock(strerror_lock);
+ msg = strncpy(buf, strerror(errnum), STRERROR_BUF_LEN);
+ strerror_lock->unlock(strerror_lock);
+ buf[STRERROR_BUF_LEN - 1] = '\0';
+#endif /* HAVE_STRERROR_R */
return msg;
}
-#else /* HAVE_STRERROR_R */
-/* we actually wan't to call strerror(3) below */
-#undef strerror
-/*
- * Described in header.
+
+/**
+ * free() with disabled leak detective
*/
-const char *strerror_safe(int errnum)
+static void free_no_ld(void *buf)
{
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- char *buf = get_strerror_buf();
+ bool old = FALSE;
- /* use a mutex to ensure calling strerror(3) is thread-safe */
- pthread_mutex_lock(&mutex);
- strncpy(buf, strerror(errnum), STRERROR_BUF_LEN);
- pthread_mutex_unlock(&mutex);
- buf[STRERROR_BUF_LEN - 1] = '\0';
- return buf;
+ if (lib->leak_detective)
+ {
+ old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
+ }
+ free(buf);
+ if (lib->leak_detective)
+ {
+ lib->leak_detective->set_state(lib->leak_detective, old);
+ }
+}
+
+/**
+ * See header
+ */
+void strerror_init()
+{
+ strerror_buf = thread_value_create(free_no_ld);
+#ifndef HAVE_STRERROR_R
+ strerror_lock = spinlock_create();
+#endif
+}
+
+/**
+ * See header
+ */
+void strerror_deinit()
+{
+ strerror_buf->destroy(strerror_buf);
+ strerror_buf = NULL;
+#ifndef HAVE_STRERROR_R
+ strerror_lock->destroy(strerror_lock);
+#endif
}
-#endif /* HAVE_STRERROR_R */
diff --git a/src/libstrongswan/utils/utils/strerror.h b/src/libstrongswan/utils/utils/strerror.h
index 2cb76f12e..e1b063842 100644
--- a/src/libstrongswan/utils/utils/strerror.h
+++ b/src/libstrongswan/utils/utils/strerror.h
@@ -33,6 +33,16 @@
const char *strerror_safe(int errnum);
/**
+ * Initialize strerror_safe()
+ */
+void strerror_init();
+
+/**
+ * Deinitialize strerror_safe()
+ */
+void strerror_deinit();
+
+/**
* Replace usages of strerror(3) with thread-safe variant.
*/
#define strerror(errnum) strerror_safe(errnum)
diff --git a/src/libstrongswan/utils/windows.c b/src/libstrongswan/utils/windows.c
new file mode 100644
index 000000000..8820287b1
--- /dev/null
+++ b/src/libstrongswan/utils/windows.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "utils.h"
+
+#include <errno.h>
+
+/**
+ * See header
+ */
+void windows_init()
+{
+ WSADATA wsad;
+
+ /* initialize winsock2 */
+ WSAStartup(MAKEWORD(2, 2), &wsad);
+}
+
+/**
+ * See header
+ */
+void windows_deinit()
+{
+ WSACleanup();
+}
+
+/**
+ * See header
+ */
+int usleep(useconds_t usec)
+{
+ if (usec > 0 && usec < 1000)
+ { /* do not Sleep(0) for small values */
+ usec = 1000;
+ }
+ SleepEx(usec / 1000, TRUE);
+ return 0;
+}
+
+/**
+ * See header.
+ */
+char* strndup(const char *s, size_t n)
+{
+ char *dst;
+
+ n = min(strnlen(s, n), n);
+ dst = malloc(n + 1);
+ memcpy(dst, s, n);
+ dst[n] = '\0';
+
+ return dst;
+}
+
+/*
+ * See header.
+ */
+void *dlopen(const char *filename, int flag)
+{
+ return LoadLibrary(filename);
+}
+
+/**
+ * Load a symbol from known default libs (monolithic build)
+ */
+static void* dlsym_default(const char *name)
+{
+ const char *dlls[] = {
+ "libstrongswan-0.dll",
+ "libhydra-0.dll",
+ "libcharon-0.dll",
+ "libtnccs-0.dll",
+ NULL /* .exe */
+ };
+ HANDLE handle;
+ void *sym = NULL;
+ int i;
+
+ for (i = 0; i < countof(dlls); i++)
+ {
+ handle = GetModuleHandle(dlls[i]);
+ if (handle)
+ {
+ sym = GetProcAddress(handle, name);
+ if (sym)
+ {
+ break;
+ }
+ }
+ }
+ return sym;
+}
+
+/**
+ * Emulate RTLD_NEXT for some known symbols
+ */
+static void* dlsym_next(const char *name)
+{
+ struct {
+ const char *dll;
+ const char *syms[4];
+ } dlls[] = {
+ /* for leak detective */
+ { "msvcrt",
+ { "malloc", "calloc", "realloc", "free" }
+ },
+ };
+ HANDLE handle = NULL;
+ int i, j;
+
+ for (i = 0; i < countof(dlls); i++)
+ {
+ for (j = 0; j < countof(dlls[0].syms); j++)
+ {
+ if (dlls[i].syms[j] && streq(dlls[i].syms[j], name))
+ {
+ handle = GetModuleHandle(dlls[i].dll);
+ break;
+ }
+ }
+ }
+ if (handle)
+ {
+ return GetProcAddress(handle, name);
+ }
+ return handle;
+}
+
+/**
+ * See header.
+ */
+void* dlsym(void *handle, const char *symbol)
+{
+ if (handle == RTLD_DEFAULT)
+ {
+ return dlsym_default(symbol);
+ }
+ if (handle == RTLD_NEXT)
+ {
+ return dlsym_next(symbol);
+ }
+ return GetProcAddress((HMODULE)handle, symbol);
+}
+
+/**
+ * See header.
+ */
+char* dlerror(void)
+{
+ static char buf[128];
+ char *pos;
+ DWORD err;
+
+ err = GetLastError();
+ if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, 0, buf, sizeof(buf), NULL) > 0)
+ {
+ pos = strchr(buf, '\n');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf), "(%u)", err);
+ }
+ return buf;
+}
+
+/**
+ * See header.
+ */
+int dlclose(void *handle)
+{
+ return FreeLibrary((HMODULE)handle);
+}
+
+/**
+ * See header
+ */
+int socketpair(int domain, int type, int protocol, int sv[2])
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ socklen_t len = sizeof(addr);
+ int s, c, sc;
+ BOOL on;
+
+ /* We don't check domain for AF_INET, as we use it as replacement for
+ * AF_UNIX. */
+ if (type != SOCK_STREAM)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (protocol != 0 && protocol != IPPROTO_TCP)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (s == -1)
+ {
+ return -1;
+ }
+ c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (c == -1)
+ {
+ closesocket(s);
+ return -1;
+ }
+ if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == 0 &&
+ getsockname(s,(struct sockaddr*)&addr, &len) == 0 &&
+ listen(s, 0) == 0 &&
+ connect(c, (struct sockaddr*)&addr, sizeof(addr)) == 0)
+ {
+ sc = accept(s, NULL, NULL);
+ if (sc >= 0)
+ {
+ closesocket(s);
+ s = sc;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof(on)) == 0 &&
+ setsockopt(c, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof(on)) == 0)
+ {
+ sv[0] = s;
+ sv[1] = c;
+ return 0;
+ }
+ }
+ }
+ closesocket(s);
+ closesocket(c);
+ return -1;
+}
+
+/**
+ * See header
+ */
+char* getpass(const char *prompt)
+{
+ static char buf[64] = "";
+ char *pos;
+ HANDLE in, out;
+ DWORD mode, written = 0, total, done;
+
+ out = GetStdHandle(STD_OUTPUT_HANDLE);
+ in = GetStdHandle(STD_INPUT_HANDLE);
+
+ if (out == INVALID_HANDLE_VALUE || in == INVALID_HANDLE_VALUE ||
+ !GetConsoleMode(out, &mode) || !GetConsoleMode(in, &mode))
+ {
+ return NULL;
+ }
+
+ total = strlen(prompt);
+ while (written < total)
+ {
+ if (!WriteConsole(out, prompt + written, total - written, &done, NULL))
+ {
+ return NULL;
+ }
+ written += done;
+ }
+
+ if (!SetConsoleMode(in, mode & ~ENABLE_ECHO_INPUT))
+ {
+ return NULL;
+ }
+
+ while (TRUE)
+ {
+ if (!ReadConsole(in, buf, sizeof(buf), &done, NULL))
+ {
+ SetConsoleMode(in, mode);
+ return NULL;
+ }
+ buf[sizeof(buf)-1] = '\0';
+
+ if (done)
+ {
+ pos = strchr(buf, '\r');
+ if (pos)
+ {
+ *pos = '\0';
+ }
+ break;
+ }
+ }
+ SetConsoleMode(in, mode);
+
+ /* append a newline, as we have no echo during input */
+ WriteConsole(out, "\r\n", 2, &done, NULL);
+
+ return buf;
+}
+
+/**
+ * See header.
+ */
+#undef strerror_s
+int strerror_s_extended(char *buf, size_t buflen, int errnum)
+{
+ const char *errstr [] = {
+ /* EADDRINUSE */ "Address in use",
+ /* EADDRNOTAVAIL */ "Address not available",
+ /* EAFNOSUPPORT */ "Address family not supported",
+ /* EALREADY */ "Connection already in progress",
+ /* EBADMSG */ "Bad message",
+ /* ECANCELED */ "Operation canceled",
+ /* ECONNABORTED */ "Connection aborted",
+ /* ECONNREFUSED */ "Connection refused",
+ /* ECONNRESET */ "Connection reset",
+ /* EDESTADDRREQ */ "Destination address required",
+ /* EHOSTUNREACH */ "Host is unreachable",
+ /* EIDRM */ "Identifier removed",
+ /* EINPROGRESS */ "Operation in progress",
+ /* EISCONN */ "Socket is connected",
+ /* ELOOP */ "Too many levels of symbolic links",
+ /* EMSGSIZE */ "Message too large",
+ /* ENETDOWN */ "Network is down",
+ /* ENETRESET */ "Connection aborted by network",
+ /* ENETUNREACH */ "Network unreachable",
+ /* ENOBUFS */ "No buffer space available",
+ /* ENODATA */ "No message is available",
+ /* ENOLINK */ "No link",
+ /* ENOMSG */ "No message of the desired type",
+ /* ENOPROTOOPT */ "Protocol not available",
+ /* ENOSR */ "No stream resources",
+ /* ENOSTR */ "Not a stream",
+ /* ENOTCONN */ "The socket is not connected",
+ /* ENOTRECOVERABLE */ "State not recoverable",
+ /* ENOTSOCK */ "Not a socket",
+ /* ENOTSUP */ "Not supported",
+ /* EOPNOTSUPP */ "Operation not supported on socket",
+ /* EOTHER */ "Other error",
+ /* EOVERFLOW */ "Value too large to be stored in data type",
+ /* EOWNERDEAD */ "Previous owner died",
+ /* EPROTO */ "Protocol error",
+ /* EPROTONOSUPPORT */ "Protocol not supported",
+ /* EPROTOTYPE */ "Protocol wrong type for socket",
+ /* ETIME */ "Timeout",
+ /* ETIMEDOUT */ "Connection timed out",
+ /* ETXTBSY */ "Text file busy",
+ /* EWOULDBLOCK */ "Operation would block",
+ };
+ int offset = EADDRINUSE;
+
+ if (errnum < offset || errnum >= offset + countof(errstr))
+ {
+ return strerror_s(buf, buflen, errnum);
+ }
+ strncpy(buf, errstr[errnum - offset], buflen);
+ buf[buflen - 1] = '\0';
+ return 0;
+}
+
+/**
+ * Set errno for a function setting WSA error on failure
+ */
+static int wserr(int retval)
+{
+ if (retval < 0)
+ {
+ static const struct {
+ DWORD wsa;
+ int err;
+ } map[] = {
+ { WSANOTINITIALISED, EBADF },
+ { WSAENETDOWN, ENETDOWN },
+ { WSAENETRESET, ENETRESET },
+ { WSAECONNABORTED, ECONNABORTED },
+ { WSAESHUTDOWN, ECONNABORTED },
+ { WSAEACCES, EACCES },
+ { WSAEINTR, EINTR },
+ { WSAEINPROGRESS, EINPROGRESS },
+ { WSAEFAULT, EFAULT },
+ { WSAENOBUFS, ENOBUFS },
+ { WSAENOTSOCK, ENOTSOCK },
+ { WSAEOPNOTSUPP, EOPNOTSUPP },
+ { WSAEWOULDBLOCK, EWOULDBLOCK },
+ { WSAEMSGSIZE, EMSGSIZE },
+ { WSAEINVAL, EINVAL },
+ { WSAENOTCONN, ENOTCONN },
+ { WSAEHOSTUNREACH, EHOSTUNREACH },
+ { WSAENETUNREACH, ENETUNREACH },
+ { WSAECONNABORTED, ECONNABORTED },
+ { WSAECONNRESET, ECONNRESET },
+ { WSAETIMEDOUT, ETIMEDOUT },
+ { WSAEMFILE, EMFILE },
+ { WSAEALREADY, EALREADY },
+ { WSAEDESTADDRREQ, EDESTADDRREQ },
+ { WSAEISCONN, EISCONN },
+ { WSAEOPNOTSUPP, EOPNOTSUPP },
+ { WSAEPROTOTYPE, EPROTOTYPE },
+ { WSAENOPROTOOPT, ENOPROTOOPT },
+ { WSAEPROTONOSUPPORT, EPROTONOSUPPORT },
+ { WSAEPFNOSUPPORT, EPROTONOSUPPORT },
+ { WSAEAFNOSUPPORT, EAFNOSUPPORT },
+ { WSAEADDRNOTAVAIL, EADDRNOTAVAIL },
+ { WSAEADDRINUSE, EADDRINUSE },
+ { WSAETIMEDOUT, ETIMEDOUT },
+ { WSAECONNREFUSED, ECONNREFUSED },
+ { WSAELOOP, ELOOP },
+ { WSAENAMETOOLONG, ENAMETOOLONG },
+ { WSAENOTEMPTY, ENOTEMPTY },
+ { WSAEPROTOTYPE, EPROTOTYPE },
+ { WSAVERNOTSUPPORTED, ENOTSUP },
+ };
+ DWORD wsa, i;
+
+ wsa = WSAGetLastError();
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].wsa == wsa)
+ {
+ errno = map[i].err;
+ return retval;
+ }
+ }
+ errno = ENOENT;
+ return retval;
+ }
+ errno = 0;
+ return retval;
+}
+
+/**
+ * Check and clear the dontwait flag
+ */
+static bool check_dontwait(int *flags)
+{
+ if (*flags & MSG_DONTWAIT)
+ {
+ *flags &= ~MSG_DONTWAIT;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * See header
+ */
+#undef shutdown
+int windows_shutdown(int sockfd, int how)
+{
+ return wserr(shutdown(sockfd, how));
+}
+
+/**
+ * See header
+ */
+#undef accept
+int windows_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+ return wserr(accept(sockfd, addr, addrlen));
+}
+
+/**
+ * See header
+ */
+#undef bind
+int windows_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+ return wserr(bind(sockfd, addr, addrlen));
+}
+
+/**
+ * See header
+ */
+#undef connect
+int windows_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
+{
+ return wserr(connect(sockfd, addr, addrlen));
+}
+
+/**
+ * See header
+ */
+#undef getsockname
+int windows_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
+{
+ return wserr(getsockname(sockfd, addr, addrlen));
+}
+
+/**
+ * See header
+ */
+#undef getsockopt
+int windows_getsockopt(int sockfd, int level, int optname,
+ void *optval, socklen_t *optlen)
+{
+ return wserr(getsockopt(sockfd, level, optname, optval, optlen));
+}
+
+/**
+ * See header
+ */
+#undef setsockopt
+int windows_setsockopt(int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen)
+{
+ return wserr(setsockopt(sockfd, level, optname, optval, optlen));
+}
+
+/**
+ * See header
+ */
+#undef socket
+int windows_socket(int domain, int type, int protocol)
+{
+ return wserr(socket(domain, type, protocol));
+}
+
+/**
+ * See header
+ */
+#undef select
+int windows_select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout)
+{
+ return wserr(select(nfds, readfds, writefds, exceptfds, timeout));
+}
+
+/**
+ * See header
+ */
+#undef close
+int windows_close(int fd)
+{
+ int ret;
+
+ ret = close(fd);
+ if (ret == -1 && errno == EBADF)
+ { /* Winsock socket? */
+ ret = wserr(closesocket(fd));
+ }
+ return ret;
+}
+
+/**
+ * See header
+ */
+#undef recv
+ssize_t windows_recv(int sockfd, void *buf, size_t len, int flags)
+{
+ u_long on = 1, off = 0;
+ ssize_t outlen = -1;
+
+ if (!check_dontwait(&flags))
+ {
+ return wserr(recv(sockfd, buf, len, flags));
+ }
+ if (wserr(ioctlsocket(sockfd, FIONBIO, &on) == 0))
+ {
+ outlen = wserr(recv(sockfd, buf, len, flags));
+ ioctlsocket(sockfd, FIONBIO, &off);
+ }
+ return outlen;
+}
+
+/**
+ * See header
+ */
+#undef recvfrom
+ssize_t windows_recvfrom(int sockfd, void *buf, size_t len, int flags,
+ struct sockaddr *src_addr, socklen_t *addrlen)
+{
+ u_long on = 1, off = 0;
+ ssize_t outlen = -1;
+
+ if (!check_dontwait(&flags))
+ {
+ return wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen));
+ }
+ if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0)
+ {
+ outlen = wserr(recvfrom(sockfd, buf, len, flags, src_addr, addrlen));
+ ioctlsocket(sockfd, FIONBIO, &off);
+ }
+ return outlen;
+}
+
+/**
+ * See header
+ */
+#undef send
+ssize_t windows_send(int sockfd, const void *buf, size_t len, int flags)
+{
+ u_long on = 1, off = 0;
+ ssize_t outlen = -1;
+
+ if (!check_dontwait(&flags))
+ {
+ return wserr(send(sockfd, buf, len, flags));
+ }
+ if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0)
+ {
+ outlen = wserr(send(sockfd, buf, len, flags));
+ ioctlsocket(sockfd, FIONBIO, &off);
+ }
+ return outlen;
+}
+
+/**
+ * See header
+ */
+#undef sendto
+ssize_t windows_sendto(int sockfd, const void *buf, size_t len, int flags,
+ const struct sockaddr *dest_addr, socklen_t addrlen)
+{
+ u_long on = 1, off = 0;
+ ssize_t outlen = -1;
+
+ if (!check_dontwait(&flags))
+ {
+ return wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen));
+ }
+ if (wserr(ioctlsocket(sockfd, FIONBIO, &on)) == 0)
+ {
+ outlen = wserr(sendto(sockfd, buf, len, flags, dest_addr, addrlen));
+ ioctlsocket(sockfd, FIONBIO, &off);
+ }
+ return outlen;
+}
diff --git a/src/libstrongswan/utils/windows.h b/src/libstrongswan/utils/windows.h
new file mode 100644
index 000000000..3761e10ab
--- /dev/null
+++ b/src/libstrongswan/utils/windows.h
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup windows windows
+ * @{ @ingroup utils
+ */
+
+#ifndef WINDOWS_H_
+#define WINDOWS_H_
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <direct.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* undef Windows variants evaluating values more than once */
+#undef min
+#undef max
+
+/* interface is defined as an alias to "struct" in basetypes.h, but
+ * we use it here and there as ordinary identifier. */
+#undef interface
+
+/* used by Windows API, but we have our own */
+#undef CALLBACK
+
+/* UID/GID types for capabilities, even if not supported */
+typedef u_int uid_t;
+typedef u_int gid_t;
+
+/**
+ * Initialize Windows libraries
+ */
+void windows_init();
+
+/**
+ * Deinitialize windows libraries
+ */
+void windows_deinit();
+
+/**
+ * Replacement for random(3)
+ */
+static inline long random(void)
+{
+ return rand();
+}
+
+/**
+ * Replacement for srandom(3)
+ */
+static inline void srandom(unsigned int seed)
+{
+ srand(seed);
+}
+
+/**
+ * Replacement of sched_yield(2) from <sched.h>
+ */
+static inline int sched_yield(void)
+{
+ Sleep(0);
+ return 0;
+}
+
+/**
+ * Replacement of sleep(3), cancellable by thread_cancel()
+ */
+#define sleep sleep_cancellable
+static inline int sleep_cancellable(unsigned int seconds)
+{
+ SleepEx(seconds * 1000, TRUE);
+ return 0;
+}
+
+/**
+ * Replacement of usleep(3), cancellable, ms resolution only
+ */
+int usleep(useconds_t usec);
+
+/**
+ * strdup(3), the Windows variant can't free(strdup("")) and others
+ */
+#define strdup strdup_windows
+static inline char* strdup_windows(const char *src)
+{
+ size_t len;
+ char *dst;
+
+ len = strlen(src) + 1;
+ dst = malloc(len);
+ memcpy(dst, src, len);
+ return dst;
+}
+
+/**
+ * strndup(3)
+ */
+char* strndup(const char *s, size_t n);
+
+/**
+ * Provided via ws2_32
+ */
+#ifndef InetNtop
+const char WINAPI *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+#endif
+
+/**
+ * Provided via ws2_32
+ */
+#ifndef InetPton
+int WINAPI inet_pton(int af, const char *src, void *dst);
+#endif
+
+/**
+ * Provided by printf hook backend
+ */
+int asprintf(char **strp, const char *fmt, ...);
+
+/**
+ * Provided by printf hook backend
+ */
+int vasprintf(char **strp, const char *fmt, va_list ap);
+
+/**
+ * timeradd(3) from <sys/time.h>
+ */
+static inline void timeradd(struct timeval *a, struct timeval *b,
+ struct timeval *res)
+{
+ res->tv_sec = a->tv_sec + b->tv_sec;
+ res->tv_usec = a->tv_usec + b->tv_usec;
+ if (res->tv_usec >= 1000000)
+ {
+ res->tv_usec -= 1000000;
+ res->tv_sec++;
+ }
+}
+
+/**
+ * timersub(3) from <sys/time.h>
+ */
+static inline void timersub(struct timeval *a, struct timeval *b,
+ struct timeval *res)
+{
+ res->tv_sec = a->tv_sec - b->tv_sec;
+ res->tv_usec = a->tv_usec - b->tv_usec;
+ if (res->tv_usec < 0)
+ {
+ res->tv_usec += 1000000;
+ res->tv_sec--;
+ }
+}
+
+/**
+ * gmtime_r(3) from <time.h>
+ */
+static inline struct tm *gmtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *ret;
+
+ /* gmtime_s() and friends seem not to be implemented/functioning.
+ * Relying on gmtime() on Windows works as well, as it uses thread
+ * specific buffers. */
+ ret = gmtime(timep);
+ if (ret)
+ {
+ memcpy(result, ret, sizeof(*result));
+ }
+ return ret;
+}
+
+/**
+ * localtime_r(3) from <time.h>
+ */
+static inline struct tm *localtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *ret;
+
+ /* localtime_s() and friends seem not to be implemented/functioning.
+ * Relying on localtime() on Windows works as well, as it uses thread
+ * specific buffers. */
+ ret = localtime(timep);
+ if (ret)
+ {
+ memcpy(result, ret, sizeof(*result));
+ }
+ return ret;
+}
+
+/**
+ * setenv(3) from <stdlib.h>, overwrite flag is ignored
+ */
+static inline int setenv(const char *name, const char *value, int overwrite)
+{
+ if (SetEnvironmentVariableA(name, value) == 0)
+ { /* failed */
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Lazy binding, ignored on Windows
+ */
+#define RTLD_LAZY 1
+
+/**
+ * Default handle targeting .exe
+ */
+#define RTLD_DEFAULT (NULL)
+
+/**
+ * Find symbol in next library
+ */
+#define RTLD_NEXT ((void*)~(uintptr_t)0)
+
+/**
+ * dlopen(3) from <dlfcn.h>
+ */
+void* dlopen(const char *filename, int flag);
+
+/**
+ * dlsym() from <dlfcn.h>
+ */
+void* dlsym(void *handle, const char *symbol);
+
+/**
+ * dlerror(3) from <dlfcn.h>, currently not thread save
+ */
+char* dlerror(void);
+
+/**
+ * dlclose() from <dlfcn.h>
+ */
+int dlclose(void *handle);
+
+/**
+ * socketpair(2) for SOCK_STREAM, uses TCP on loopback
+ */
+int socketpair(int domain, int type, int protocol, int sv[2]);
+
+/**
+ * getpass(3) on Windows consoles
+ */
+char* getpass(const char *prompt);
+#define HAVE_GETPASS
+
+/**
+ * Map MSG_DONTWAIT to the reserved, but deprecated MSG_INTERRUPT
+ */
+#define MSG_DONTWAIT MSG_INTERRUPT
+
+/**
+ * shutdown(2) "how"-aliases, to use Unix variant on Windows
+ */
+#define SHUT_RD SD_RECEIVE
+#define SHUT_WR SD_SEND
+#define SHUT_RDWR SD_BOTH
+
+/**
+ * shutdown(2) setting errno
+ */
+#define shutdown windows_shutdown
+int windows_shutdown(int sockfd, int how);
+
+/**
+ * accept(2) setting errno
+ */
+#define accept windows_accept
+int windows_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+
+/**
+ * bind(2) setting errno
+ */
+#define bind windows_bind
+int windows_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+
+/**
+ * connect(2) setting errno
+ */
+#define connect windows_connect
+int windows_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+
+/**
+ * getsockname(2) setting errno
+ */
+#define getsockname windows_getsockname
+int windows_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+
+/**
+ * getsockopt(2) setting errno
+ */
+#define getsockopt windows_getsockopt
+int windows_getsockopt(int sockfd, int level, int optname,
+ void *optval, socklen_t *optlen);
+
+/**
+ * setsockopt(2) setting errno
+ */
+#define setsockopt windows_setsockopt
+int windows_setsockopt(int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen);
+
+/**
+ * socket(2) setting errno
+ */
+#define socket windows_socket
+int windows_socket(int domain, int type, int protocol);
+
+/**
+ * select(2) setting errno
+ */
+#define select windows_select
+int windows_select(int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timeval *timeout);
+
+/**
+ * close(2) working for file handles and Winsock sockets
+ */
+#define close windows_close
+int windows_close(int fd);
+
+/**
+ * recv(2) with support for MSG_DONTWAIT
+ */
+#define recv windows_recv
+ssize_t windows_recv(int sockfd, void *buf, size_t len, int flags);
+
+/**
+ * recvfrom(2) with support for MSG_DONTWAIT
+ */
+#define recvfrom windows_recvfrom
+ssize_t windows_recvfrom(int sockfd, void *buf, size_t len, int flags,
+ struct sockaddr *src_addr, socklen_t *addrlen);
+
+/**
+ * recvfrom(2) with support for MSG_DONTWAIT
+ */
+#define send windows_send
+ssize_t windows_send(int sockfd, const void *buf, size_t len, int flags);
+
+/**
+ * recvfrom(2) with support for MSG_DONTWAIT
+ */
+#define sendto windows_send
+ssize_t windows_sendto(int sockfd, const void *buf, size_t len, int flags,
+ const struct sockaddr *dest_addr, socklen_t addrlen);
+
+/**
+ * Declaration missing on older WinGW
+ */
+_CRTIMP errno_t strerror_s(char *buf, size_t size, int errnum);
+
+/**
+ * strerror_s, but supporting POSIX compatibility errno >= 100
+ */
+#define strerror_s strerror_s_extended
+int strerror_s_extended(char *buf, size_t buflen, int errnum);
+
+/**
+ * strerror_r(2) replacement, XSI variant
+ */
+static inline int strerror_r(int errnum, char *buf, size_t buflen)
+{
+ return strerror_s(buf, buflen, errnum);
+}
+#define HAVE_STRERROR_R /* but not STRERROR_R_CHAR_P */
+
+/**
+ * MinGW does provide extended errno values. Windows itself knowns them
+ * for POSIX compatibility; we define them as well.
+ */
+#ifndef EADDRINUSE
+#define EADDRINUSE 100
+#endif
+#ifndef EADDRNOTAVAIL
+#define EADDRNOTAVAIL 101
+#endif
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT 102
+#endif
+#ifndef EALREADY
+#define EALREADY 103
+#endif
+#ifndef EBADMSG
+#define EBADMSG 104
+#endif
+#ifndef ECANCELED
+#define ECANCELED 105
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED 106
+#endif
+#ifndef ECONNREFUSED
+#define ECONNREFUSED 107
+#endif
+#ifndef ECONNRESET
+#define ECONNRESET 108
+#endif
+#ifndef EDESTADDRREQ
+#define EDESTADDRREQ 109
+#endif
+#ifndef EHOSTUNREACH
+#define EHOSTUNREACH 110
+#endif
+#ifndef EIDRM
+#define EIDRM 111
+#endif
+#ifndef EINPROGRESS
+#define EINPROGRESS 112
+#endif
+#ifndef EISCONN
+#define EISCONN 113
+#endif
+#ifndef ELOOP
+#define ELOOP 114
+#endif
+#ifndef EMSGSIZE
+#define EMSGSIZE 115
+#endif
+#ifndef ENETDOWN
+#define ENETDOWN 116
+#endif
+#ifndef ENETRESET
+#define ENETRESET 117
+#endif
+#ifndef ENETUNREACH
+#define ENETUNREACH 118
+#endif
+#ifndef ENOBUFS
+#define ENOBUFS 119
+#endif
+#ifndef ENODATA
+#define ENODATA 120
+#endif
+#ifndef ENOLINK
+#define ENOLINK 121
+#endif
+#ifndef ENOMSG
+#define ENOMSG 122
+#endif
+#ifndef ENOPROTOOPT
+#define ENOPROTOOPT 123
+#endif
+#ifndef ENOSR
+#define ENOSR 124
+#endif
+#ifndef ENOSTR
+#define ENOSTR 125
+#endif
+#ifndef ENOTCONN
+#define ENOTCONN 126
+#endif
+#ifndef ENOTRECOVERABLE
+#define ENOTRECOVERABLE 127
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK 128
+#endif
+#ifndef ENOTSUP
+#define ENOTSUP 129
+#endif
+#ifndef EOPNOTSUPP
+#define EOPNOTSUPP 130
+#endif
+#ifndef EOTHER
+#define EOTHER 131
+#endif
+#ifndef EOVERFLOW
+#define EOVERFLOW 132
+#endif
+#ifndef EOWNERDEAD
+#define EOWNERDEAD 133
+#endif
+#ifndef EPROTO
+#define EPROTO 134
+#endif
+#ifndef EPROTONOSUPPORT
+#define EPROTONOSUPPORT 135
+#endif
+#ifndef EPROTOTYPE
+#define EPROTOTYPE 136
+#endif
+#ifndef ETIME
+#define ETIME 137
+#endif
+#ifndef ETIMEDOUT
+#define ETIMEDOUT 138
+#endif
+#ifndef ETXTBSY
+#define ETXTBSY 139
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK 140
+#endif
+
+
+/* Windows does not support "ll" format printf length modifiers. Mingw
+ * therefore maps these to the Windows specific I64 length modifier. That
+ * won't work for us, as we use our own printf backend on Windows, which works
+ * just fine with "ll". */
+#undef PRId64
+#define PRId64 "lld"
+#undef PRId64
+#define PRId64 "lld"
+#undef PRIdLEAST64
+#define PRIdLEAST64 "lld"
+#undef PRIdFAST64
+#define PRIdFAST64 "lld"
+#undef PRIdMAX
+#define PRIdMAX "lld"
+#undef PRIi64
+#define PRIi64 "lli"
+#undef PRIiLEAST64
+#define PRIiLEAST64 "lli"
+#undef PRIiFAST64
+#define PRIiFAST64 "lli"
+#undef PRIiMAX
+#define PRIiMAX "lli"
+#undef PRIo64
+#define PRIo64 "llo"
+#undef PRIoLEAST64
+#define PRIoLEAST64 "llo"
+#undef PRIoFAST64
+#define PRIoFAST64 "llo"
+#undef PRIoMAX
+#define PRIoMAX "llo"
+#undef PRIu64
+#define PRIu64 "llu"
+#undef PRIuLEAST64
+#define PRIuLEAST64 "llu"
+#undef PRIuFAST64
+#define PRIuFAST64 "llu"
+#undef PRIuMAX
+#define PRIuMAX "llu"
+#undef PRIx64
+#define PRIx64 "llx"
+#undef PRIxLEAST64
+#define PRIxLEAST64 "llx"
+#undef PRIxFAST64
+#define PRIxFAST64 "llx"
+#undef PRIxMAX
+#define PRIxMAX "llx"
+#undef PRIX64
+#define PRIX64 "llX"
+#undef PRIXLEAST64
+#define PRIXLEAST64 "llX"
+#undef PRIXFAST64
+#define PRIXFAST64 "llX"
+#undef PRIXMAX
+#define PRIXMAX "llX"
+
+#ifdef _WIN64
+# undef PRIdPTR
+# define PRIdPTR "lld"
+# undef PRIiPTR
+# define PRIiPTR "lli"
+# undef PRIoPTR
+# define PRIoPTR "llo"
+# undef PRIuPTR
+# define PRIuPTR "llu"
+# undef PRIxPTR
+# define PRIxPTR "llx"
+# undef PRIXPTR
+# define PRIXPTR "llX"
+#endif /* _WIN64 */
+
+#endif /** WINDOWS_H_ @}*/