diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 81c63b0eed39432878f78727f60a1e7499645199 (patch) | |
tree | 82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libstrongswan/utils | |
parent | c5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff) | |
download | vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip |
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libstrongswan/utils')
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, ¤t); - 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(§ion->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), §ions); - 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(§ion->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(§ion->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(§ion->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(§ion->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_ @}*/ |