diff options
Diffstat (limited to 'src/libstrongswan/utils')
31 files changed, 7535 insertions, 3287 deletions
diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c index cb83d9830..93031908a 100644 --- a/src/libstrongswan/utils/backtrace.c +++ b/src/libstrongswan/utils/backtrace.c @@ -27,6 +27,8 @@ #include "backtrace.h" +#include <utils/debug.h> + typedef struct private_backtrace_t private_backtrace_t; /** @@ -50,16 +52,344 @@ struct private_backtrace_t { void *frames[]; }; +/** + * Forward declaration of method getter + */ +static backtrace_t get_methods(); + +/** + * Write a format string with arguments to a FILE line, if it is NULL to DBG + */ +static void println(FILE *file, char *format, ...) +{ + char buf[512]; + va_list args; + + va_start(args, format); + if (file) + { + vfprintf(file, format, args); + fputs("\n", file); + } + else + { + vsnprintf(buf, sizeof(buf), format, args); + DBG1(DBG_LIB, "%s", buf); + } + 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) +{ + if (file) + { + return tty_escape_get(fileno(file), escape); + } + return ""; +} + +#ifdef HAVE_BFD_H + +#include <bfd.h> +#include <collections/hashtable.h> +#include <threading/mutex.h> + +/** + * Hashtable-cached bfd handle + */ +typedef struct { + /** binary file name on disk */ + char *filename; + /** bfd handle */ + bfd *abfd; + /** loaded symbols */ + asymbol **syms; +} bfd_entry_t; + +/** + * Destroy a bfd_entry + */ +static void bfd_entry_destroy(bfd_entry_t *this) +{ + free(this->filename); + free(this->syms); + bfd_close(this->abfd); + free(this); +} + +/** + * Data to pass to find_addr() + */ +typedef struct { + /** used bfd entry */ + bfd_entry_t *entry; + /** backtrace address */ + bfd_vma vma; + /** stream to log to */ + FILE *file; + /** TRUE if complete */ + bool found; +} bfd_find_data_t; + +/** + * bfd entry cache + */ +static hashtable_t *bfds; + +static mutex_t *bfd_mutex; + +/** + * Hashtable hash function + */ +static u_int bfd_hash(char *key) +{ + return chunk_hash(chunk_create(key, strlen(key))); +} + +/** + * Hashtable equals function + */ +static bool bfd_equals(char *a, char *b) +{ + return streq(a, b); +} + +/** + * See header. + */ +void backtrace_init() +{ + bfd_init(); + bfds = hashtable_create((hashtable_hash_t)bfd_hash, + (hashtable_equals_t)bfd_equals, 8); + bfd_mutex = mutex_create(MUTEX_TYPE_DEFAULT); +} + +/** + * See header. + */ +void backtrace_deinit() +{ + enumerator_t *enumerator; + bfd_entry_t *entry; + char *key; + + enumerator = bfds->create_enumerator(bfds); + while (enumerator->enumerate(enumerator, &key, &entry)) + { + bfds->remove_at(bfds, enumerator); + bfd_entry_destroy(entry); + } + enumerator->destroy(enumerator); + + bfds->destroy(bfds); + bfd_mutex->destroy(bfd_mutex); +} + +/** + * Find and print information to an address + */ +static void find_addr(bfd *abfd, asection *section, bfd_find_data_t *data) +{ + bfd_size_type size; + bfd_vma vma; + const char *source; + const char *function; + char fbuf[512] = "", sbuf[512] = ""; + u_int line; + + if (!data->found || (bfd_get_section_flags(abfd, section) & SEC_ALLOC) != 0) + { + vma = bfd_get_section_vma(abfd, section); + if (data->vma >= vma) + { + size = bfd_get_section_size(section); + if (data->vma < vma + size) + { + data->found = bfd_find_nearest_line(abfd, section, + data->entry->syms, data->vma - vma, + &source, &function, &line); + if (data->found) + { + if (source || function) + { + if (function) + { + snprintf(fbuf, sizeof(fbuf), "%s%s() ", + esc(data->file, TTY_FG_BLUE), function); + } + if (source) + { + snprintf(sbuf, sizeof(sbuf), "%s@ %s:%d", + esc(data->file, TTY_FG_GREEN), source, line); + } + println(data->file, " -> %s%s%s", fbuf, sbuf, + esc(data->file, TTY_FG_DEF)); + } + } + } + } + } +} + +/** + * Find a cached bfd entry, create'n'cache if not found + */ +static bfd_entry_t *get_bfd_entry(char *filename) +{ + bool dynamic = FALSE, ok = FALSE; + bfd_entry_t *entry; + long size; + + /* check cache */ + entry = bfds->get(bfds, filename); + if (entry) + { + return entry; + } + + INIT(entry, + .abfd = bfd_openr(filename, NULL), + ); + + if (!entry->abfd) + { + free(entry); + return NULL; + } +#ifdef BFD_DECOMPRESS + entry->abfd->flags |= BFD_DECOMPRESS; +#endif + if (bfd_check_format(entry->abfd, bfd_archive) == 0 && + bfd_check_format_matches(entry->abfd, bfd_object, NULL)) + { + if (bfd_get_file_flags(entry->abfd) & HAS_SYMS) + { + size = bfd_get_symtab_upper_bound(entry->abfd); + if (size == 0) + { + size = bfd_get_dynamic_symtab_upper_bound(entry->abfd); + } + if (size >= 0) + { + entry->syms = malloc(size); + if (dynamic) + { + ok = bfd_canonicalize_dynamic_symtab(entry->abfd, + entry->syms) >= 0; + } + else + { + ok = bfd_canonicalize_symtab(entry->abfd, + entry->syms) >= 0; + } + } + } + } + if (ok) + { + entry->filename = strdup(filename); + bfds->put(bfds, entry->filename, entry); + return entry; + } + bfd_entry_destroy(entry); + return NULL; +} + +/** + * Print the source file with line number to file, libbfd variant + */ +static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) +{ + bfd_entry_t *entry; + bfd_find_data_t data = { + .file = file, + .vma = (uintptr_t)ptr, + }; + bool old = FALSE; + + bfd_mutex->lock(bfd_mutex); + if (lib->leak_detective) + { + old = lib->leak_detective->set_state(lib->leak_detective, FALSE); + } + entry = get_bfd_entry(filename); + if (entry) + { + data.entry = entry; + bfd_map_over_sections(entry->abfd, (void*)find_addr, &data); + } + if (lib->leak_detective) + { + lib->leak_detective->set_state(lib->leak_detective, old); + } + bfd_mutex->unlock(bfd_mutex); +} + +#else /* !HAVE_BFD_H */ + +void backtrace_init() {} +void backtrace_deinit() {} + +/** + * Print the source file with line number to file, slow addr2line variant + */ +static void print_sourceline(FILE *file, char *filename, void *ptr, void* base) +{ + char buf[1024]; + FILE *output; + int c, i = 0; + +#ifdef __APPLE__ + snprintf(buf, sizeof(buf), "atos -o %s -l %p %p 2>&1 | tail -n1", + filename, base, ptr); +#else /* !__APPLE__ */ + snprintf(buf, sizeof(buf), "addr2line -e %s %p", filename, ptr); +#endif /* __APPLE__ */ + + + output = popen(buf, "r"); + if (output) + { + while (i < sizeof(buf)) + { + c = getc(output); + if (c == '\n' || c == EOF) + { + buf[i++] = 0; + break; + } + buf[i++] = c; + } + pclose(output); + + println(file, " -> %s%s%s", esc(file, TTY_FG_GREEN), buf, + esc(file, TTY_FG_DEF)); + } +} + +#endif /* HAVE_BFD_H */ + +#else /* !HAVE_DLADDR */ + +void backtrace_init() {} +void backtrace_deinit() {} + +#endif /* HAVE_DLADDR */ + METHOD(backtrace_t, log_, void, private_backtrace_t *this, FILE *file, bool detailed) { -#ifdef HAVE_BACKTRACE +#if defined(HAVE_BACKTRACE) || defined(HAVE_LIBUNWIND_H) size_t i; - char **strings; - - strings = backtrace_symbols(this->frames, this->frame_count); + char **strings = NULL; - fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count); + println(file, " dumping %d stack frame addresses:", this->frame_count); for (i = 0; i < this->frame_count; i++) { #ifdef HAVE_DLADDR @@ -67,9 +397,6 @@ METHOD(backtrace_t, log_, void, if (dladdr(this->frames[i], &info)) { - char cmd[1024]; - FILE *output; - int c; void *ptr = this->frames[i]; if (strstr(info.dli_fname, ".so")) @@ -78,53 +405,48 @@ METHOD(backtrace_t, log_, void, } if (info.dli_sname) { - fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%tx) [%p]\n", - info.dli_fname, info.dli_fbase, info.dli_sname, - this->frames[i] - info.dli_saddr, this->frames[i]); + println(file, " %s%s%s @ %p (%s%s%s+0x%tx) [%p]", + esc(file, TTY_FG_YELLOW), info.dli_fname, + esc(file, TTY_FG_DEF), info.dli_fbase, + esc(file, TTY_FG_RED), info.dli_sname, + esc(file, TTY_FG_DEF), this->frames[i] - info.dli_saddr, + this->frames[i]); } else { - fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname, - info.dli_fbase, this->frames[i]); + println(file, " %s%s%s @ %p [%p]", + esc(file, TTY_FG_YELLOW), info.dli_fname, + esc(file, TTY_FG_DEF), info.dli_fbase, this->frames[i]); } - if (detailed) + if (detailed && info.dli_fname[0]) { - fprintf(file, " -> \e[32m"); - snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", - info.dli_fname, ptr); - output = popen(cmd, "r"); - if (output) - { - while (TRUE) - { - c = getc(output); - if (c == '\n' || c == EOF) - { - break; - } - fputc(c, file); - } - pclose(output); - } - else - { - #endif /* HAVE_DLADDR */ - fprintf(file, " %s\n", strings[i]); - #ifdef HAVE_DLADDR - } - fprintf(file, "\n\e[0m"); + print_sourceline(file, (char*)info.dli_fname, + ptr, info.dli_fbase); } } else +#endif /* HAVE_DLADDR */ { - fprintf(file, " %s\n", strings[i]); +#ifdef HAVE_BACKTRACE + if (!strings) + { + strings = backtrace_symbols(this->frames, this->frame_count); + } + if (strings) + { + println(file, " %s", strings[i]); + } + else +#endif /* HAVE_BACKTRACE */ + { + println(file, " %p", this->frames[i]); + } } -#endif /* HAVE_DLADDR */ } - free (strings); -#else /* !HAVE_BACKTRACE */ - fprintf(file, "C library does not support backtrace().\n"); -#endif /* HAVE_BACKTRACE */ + free(strings); +#else /* !HAVE_BACKTRACE && !HAVE_LIBUNWIND_H */ + println(file, "no support for backtrace()/libunwind"); +#endif /* HAVE_BACKTRACE/HAVE_LIBUNWIND_H */ } METHOD(backtrace_t, contains_function, bool, @@ -214,12 +536,69 @@ METHOD(backtrace_t, create_frame_enumerator, enumerator_t*, return &enumerator->public; } +METHOD(backtrace_t, clone, backtrace_t*, + private_backtrace_t *this) +{ + private_backtrace_t *clone; + + clone = malloc(sizeof(private_backtrace_t) + + this->frame_count * sizeof(void*)); + memcpy(clone->frames, this->frames, this->frame_count * sizeof(void*)); + clone->frame_count = this->frame_count; + + clone->public = get_methods(); + + return &clone->public; +} + METHOD(backtrace_t, destroy, void, private_backtrace_t *this) { free(this); } +#ifdef HAVE_LIBUNWIND_H +#define UNW_LOCAL_ONLY +#include <libunwind.h> + +/** + * libunwind variant for glibc backtrace() + */ +static inline int backtrace_unwind(void **frames, int count) +{ + unw_context_t context; + unw_cursor_t cursor; + unw_word_t ip; + int depth = 0; + + unw_getcontext(&context); + unw_init_local(&cursor, &context); + do + { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + frames[depth++] = (void*)ip; + } + while (depth < count && unw_step(&cursor) > 0); + + return depth; +} +#endif /* HAVE_UNWIND */ + +/** + * Get implementation methods of backtrace_t + */ +static backtrace_t get_methods() +{ + return (backtrace_t) { + .log = _log_, + .contains_function = _contains_function, + .equals = _equals, + .clone = _clone, + .create_frame_enumerator = _create_frame_enumerator, + .destroy = _destroy, + }; +} + /** * See header */ @@ -229,7 +608,9 @@ backtrace_t *backtrace_create(int skip) void *frames[50]; int frame_count = 0; -#ifdef HAVE_BACKTRACE +#ifdef HAVE_LIBUNWIND_H + frame_count = backtrace_unwind(frames, countof(frames)); +#elif defined(HAVE_BACKTRACE) frame_count = backtrace(frames, countof(frames)); #endif /* HAVE_BACKTRACE */ frame_count = max(frame_count - skip, 0); @@ -237,14 +618,24 @@ backtrace_t *backtrace_create(int skip) memcpy(this->frames, frames + skip, frame_count * sizeof(void*)); this->frame_count = frame_count; - this->public = (backtrace_t) { - .log = _log_, - .contains_function = _contains_function, - .equals = _equals, - .create_frame_enumerator = _create_frame_enumerator, - .destroy = _destroy, - }; + this->public = get_methods(); return &this->public; } +/** + * See header + */ +void backtrace_dump(char *label, FILE *file, bool detailed) +{ + backtrace_t *backtrace; + + backtrace = backtrace_create(2); + + if (label) + { + println(file, "Debug backtrace: %s", label); + } + backtrace->log(backtrace, file, detailed); + backtrace->destroy(backtrace); +} diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h index 9d59d2503..416f58898 100644 --- a/src/libstrongswan/utils/backtrace.h +++ b/src/libstrongswan/utils/backtrace.h @@ -35,7 +35,10 @@ struct backtrace_t { /** * Log the backtrace to a FILE stream. * - * @param file FILE to log backtrace to + * If no file pointer is given, the backtrace is reported over the debug + * framework to the registered dbg() callback function. + * + * @param file FILE to log backtrace to, NULL for dbg() function * @param detailed TRUE to resolve line/file using addr2line (slow) */ void (*log)(backtrace_t *this, FILE *file, bool detailed); @@ -56,6 +59,14 @@ struct backtrace_t { * @return TRUE if backtraces are equal */ bool (*equals)(backtrace_t *this, backtrace_t *other); + + /** + * Create a copy of this backtrace. + * + * @return cloned copy + */ + backtrace_t* (*clone)(backtrace_t *this); + /** * Create an enumerator over the stack frame addresses. * @@ -77,4 +88,23 @@ struct backtrace_t { */ backtrace_t *backtrace_create(int skip); +/** + * Create a backtrace, dump it and clean it up. + * + * @param label description to print for this backtrace, or NULL + * @param file FILE to log backtrace to, NULL to dbg() function + * @param detailed TRUE to resolve line/file using addr2line (slow) + */ +void backtrace_dump(char *label, FILE *file, bool detailed); + +/** + * Initialize backtracing framework. + */ +void backtrace_init(); + +/** + * Deinitialize backtracing framework. + */ +void backtrace_deinit(); + #endif /** BACKTRACE_H_ @}*/ diff --git a/src/libstrongswan/utils/capabilities.c b/src/libstrongswan/utils/capabilities.c new file mode 100644 index 000000000..c5e90b6c3 --- /dev/null +++ b/src/libstrongswan/utils/capabilities.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2012-2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 "capabilities.h" + +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#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 + +typedef struct private_capabilities_t private_capabilities_t; + +/** + * Private data of an capabilities_t object. + */ +struct private_capabilities_t { + + /** + * Public capabilities_t interface. + */ + capabilities_t public; + + /** + * user ID to switch during rights dropping + */ + uid_t uid; + + /** + * group ID to switch during rights dropping + */ + gid_t gid; + + /** + * capabilities to keep + */ +#ifdef CAPABILITIES_LIBCAP + cap_t caps; +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + struct __user_cap_data_struct caps[2]; +#endif /* CAPABILITIES_NATIVE */ + +#ifdef EMULATE_R_FUNCS + /** + * mutex to emulate get(pw|gr)nam_r functions + */ + mutex_t *mutex; +#endif +}; + +/** + * Returns TRUE if the current process/user is member of the given group + */ +static bool has_group(gid_t group) +{ + gid_t *groups; + long ngroups, i; + bool found = FALSE; + + if (group == getegid()) + { /* it's unspecified if this is part of the list below or not */ + return TRUE; + } + ngroups = sysconf(_SC_NGROUPS_MAX); + if (ngroups == -1) + { + DBG1(DBG_LIB, "getting groups for current process failed: %s", + strerror(errno)); + return FALSE; + } + groups = calloc(ngroups + 1, sizeof(gid_t)); + ngroups = getgroups(ngroups, groups); + if (ngroups == -1) + { + DBG1(DBG_LIB, "getting groups for current process failed: %s", + strerror(errno)); + free(groups); + return FALSE; + } + for (i = 0; i < ngroups; i++) + { + if (group == groups[i]) + { + found = TRUE; + break; + } + } + free(groups); + return found; +} + +/** + * Verify that the current process has the given capability + */ +static bool has_capability(private_capabilities_t *this, u_int cap, + bool *ignore) +{ + if (cap == CAP_CHOWN) + { /* if new files/UNIX sockets are created they should be owned by the + * configured user and group. This requires a call to chown(2). But + * CAP_CHOWN is not always required. */ + if (!this->uid || geteuid() == this->uid) + { /* if the owner does not change CAP_CHOWN is not needed */ + if (!this->gid || has_group(this->gid)) + { /* the same applies if the owner is a member of the group */ + if (ignore) + { /* we don't have to keep this, if requested */ + *ignore = TRUE; + } + return TRUE; + } + } + } +#ifndef CAPABILITIES + /* if we can't check the actual capabilities assume only root has it */ + return geteuid() == 0; +#endif /* !CAPABILITIES */ +#ifdef CAPABILITIES_LIBCAP + cap_flag_value_t val; + cap_t caps; + bool ok; + + caps = cap_get_proc(); + if (!caps) + { + return FALSE; + } + ok = cap_get_flag(caps, cap, CAP_PERMITTED, &val) == 0 && val == CAP_SET; + cap_free(caps); + return ok; +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + struct __user_cap_header_struct header = { +#if defined(_LINUX_CAPABILITY_VERSION_3) + .version = _LINUX_CAPABILITY_VERSION_3, +#elif defined(_LINUX_CAPABILITY_VERSION_2) + .version = _LINUX_CAPABILITY_VERSION_2, +#elif defined(_LINUX_CAPABILITY_VERSION_1) + .version = _LINUX_CAPABILITY_VERSION_1, +#else + .version = _LINUX_CAPABILITY_VERSION, +#endif + }; + struct __user_cap_data_struct caps[2]; + int i = 0; + + if (cap >= 32) + { + i++; + cap -= 32; + } + return capget(&header, caps) == 0 && caps[i].permitted & (1 << cap); +#endif /* CAPABILITIES_NATIVE */ +} + +/** + * Keep the given capability if it is held by the current process. Returns + * FALSE, if this is not the case. + */ +static bool keep_capability(private_capabilities_t *this, u_int cap) +{ +#ifdef CAPABILITIES_LIBCAP + cap_set_flag(this->caps, CAP_EFFECTIVE, 1, &cap, CAP_SET); + cap_set_flag(this->caps, CAP_INHERITABLE, 1, &cap, CAP_SET); + cap_set_flag(this->caps, CAP_PERMITTED, 1, &cap, CAP_SET); +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + int i = 0; + + if (cap >= 32) + { + i++; + cap -= 32; + } + this->caps[i].effective |= 1 << cap; + this->caps[i].permitted |= 1 << cap; + this->caps[i].inheritable |= 1 << cap; +#endif /* CAPABILITIES_NATIVE */ + return TRUE; +} + +METHOD(capabilities_t, keep, bool, + private_capabilities_t *this, u_int cap) +{ + bool ignore = FALSE; + + if (!has_capability(this, cap, &ignore)) + { + return FALSE; + } + else if (ignore) + { /* don't keep capabilities that are not required */ + return TRUE; + } + return keep_capability(this, cap); +} + +METHOD(capabilities_t, check, bool, + private_capabilities_t *this, u_int cap) +{ + return has_capability(this, cap, NULL); +} + +METHOD(capabilities_t, get_uid, uid_t, + private_capabilities_t *this) +{ + return this->uid ?: geteuid(); +} + +METHOD(capabilities_t, get_gid, gid_t, + private_capabilities_t *this) +{ + return this->gid ?: getegid(); +} + +METHOD(capabilities_t, set_uid, void, + private_capabilities_t *this, uid_t uid) +{ + this->uid = uid; +} + +METHOD(capabilities_t, set_gid, void, + private_capabilities_t *this, gid_t gid) +{ + this->gid = gid; +} + +METHOD(capabilities_t, resolve_uid, bool, + private_capabilities_t *this, char *username) +{ + struct passwd *pwp; + int err; + +#ifdef HAVE_GETPWNAM_R + struct passwd passwd; + char buf[1024]; + + err = getpwnam_r(username, &passwd, buf, sizeof(buf), &pwp); + if (pwp) + { + this->uid = pwp->pw_uid; + } +#else /* HAVE GETPWNAM_R */ + this->mutex->lock(this->mutex); + pwp = getpwnam(username); + if (pwp) + { + this->uid = pwp->pw_uid; + } + err = errno; + this->mutex->unlock(this->mutex); +#endif /* HAVE GETPWNAM_R */ + if (pwp) + { + return TRUE; + } + DBG1(DBG_LIB, "resolving user '%s' failed: %s", username, + err ? strerror(err) : "user not found"); + return FALSE; +} + +METHOD(capabilities_t, resolve_gid, bool, + private_capabilities_t *this, char *groupname) +{ + struct group *grp; + int err; + +#ifdef HAVE_GETGRNAM_R + struct group group; + char buf[1024]; + + err = getgrnam_r(groupname, &group, buf, sizeof(buf), &grp); + if (grp) + { + this->gid = grp->gr_gid; + } +#else /* HAVE_GETGRNAM_R */ + this->mutex->lock(this->mutex); + grp = getgrnam(groupname); + if (grp) + { + this->gid = grp->gr_gid; + } + err = errno; + this->mutex->unlock(this->mutex); +#endif /* HAVE_GETGRNAM_R */ + if (grp) + { + return TRUE; + } + DBG1(DBG_LIB, "resolving user '%s' failed: %s", groupname, + err ? strerror(err) : "group not found"); + return FALSE; +} + +/** + * Initialize supplementary groups for unprivileged user + */ +static bool init_supplementary_groups(private_capabilities_t *this) +{ + struct passwd *pwp; + int res = -1; + +#ifdef HAVE_GETPWUID_R + struct passwd pwd; + char buf[1024]; + + if (getpwuid_r(this->uid, &pwd, buf, sizeof(buf), &pwp) == 0 && pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } +#else /* HAVE_GETPWUID_R */ + this->mutex->lock(this->mutex); + pwp = getpwuid(this->uid); + if (pwp) + { + res = initgroups(pwp->pw_name, this->gid); + } + this->mutex->unlock(this->mutex); +#endif /* HAVE_GETPWUID_R */ + return res == 0; +} + +METHOD(capabilities_t, drop, bool, + private_capabilities_t *this) +{ +#ifdef HAVE_PRCTL + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); +#endif + + if (this->uid && !init_supplementary_groups(this)) + { + DBG1(DBG_LIB, "initializing supplementary groups for %u failed", + this->uid); + return FALSE; + } + if (this->gid && setgid(this->gid) != 0) + { + DBG1(DBG_LIB, "change to unprivileged group %u failed: %s", + this->gid, strerror(errno)); + return FALSE; + } + if (this->uid && setuid(this->uid) != 0) + { + DBG1(DBG_LIB, "change to unprivileged user %u failed: %s", + this->uid, strerror(errno)); + return FALSE; + } + +#ifdef CAPABILITIES_LIBCAP + if (cap_set_proc(this->caps) != 0) + { + DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno)); + return FALSE; + } +#endif /* CAPABILITIES_LIBCAP */ +#ifdef CAPABILITIES_NATIVE + struct __user_cap_header_struct header = { +#if defined(_LINUX_CAPABILITY_VERSION_3) + .version = _LINUX_CAPABILITY_VERSION_3, +#elif defined(_LINUX_CAPABILITY_VERSION_2) + .version = _LINUX_CAPABILITY_VERSION_2, +#elif defined(_LINUX_CAPABILITY_VERSION_1) + .version = _LINUX_CAPABILITY_VERSION_1, +#else + .version = _LINUX_CAPABILITY_VERSION, +#endif + }; + if (capset(&header, this->caps) != 0) + { + DBG1(DBG_LIB, "dropping capabilities failed: %s", strerror(errno)); + return FALSE; + } +#endif /* CAPABILITIES_NATIVE */ +#ifdef CAPABILITIES + DBG1(DBG_LIB, "dropped capabilities, running as uid %u, gid %u", + geteuid(), getegid()); +#endif /* CAPABILITIES */ + return TRUE; +} + +METHOD(capabilities_t, destroy, void, + private_capabilities_t *this) +{ +#ifdef EMULATE_R_FUNCS + this->mutex->destroy(this->mutex); +#endif /* EMULATE_R_FUNCS */ +#ifdef CAPABILITIES_LIBCAP + cap_free(this->caps); +#endif /* CAPABILITIES_LIBCAP */ + free(this); +} + +/** + * See header + */ +capabilities_t *capabilities_create() +{ + private_capabilities_t *this; + + INIT(this, + .public = { + .keep = _keep, + .check = _check, + .get_uid = _get_uid, + .get_gid = _get_gid, + .set_uid = _set_uid, + .set_gid = _set_gid, + .resolve_uid = _resolve_uid, + .resolve_gid = _resolve_gid, + .drop = _drop, + .destroy = _destroy, + }, + ); + +#ifdef CAPABILITIES_LIBCAP + this->caps = cap_init(); +#endif /* CAPABILITIES_LIBCAP */ + +#ifdef EMULATE_R_FUNCS + this->mutex = mutex_create(MUTEX_TYPE_DEFAULT); +#endif /* EMULATE_R_FUNCS */ + + return &this->public; +} diff --git a/src/libstrongswan/utils/capabilities.h b/src/libstrongswan/utils/capabilities.h new file mode 100644 index 000000000..fe11a4dfc --- /dev/null +++ b/src/libstrongswan/utils/capabilities.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 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 capabilities capabilities + * @{ @ingroup utils + */ + +#ifndef CAPABILITIES_H_ +#define CAPABILITIES_H_ + +typedef struct capabilities_t capabilities_t; + +#include <library.h> +#ifdef HAVE_SYS_CAPABILITY_H +# include <sys/capability.h> +#elif defined(CAPABILITIES_NATIVE) +# include <linux/capability.h> +#endif + +#ifndef CAP_CHOWN +# define CAP_CHOWN 0 +#endif +#ifndef CAP_NET_BIND_SERVICE +# define CAP_NET_BIND_SERVICE 10 +#endif +#ifndef CAP_NET_ADMIN +# define CAP_NET_ADMIN 12 +#endif +#ifndef CAP_NET_RAW +# define CAP_NET_RAW 13 +#endif + +/** + * POSIX capability dropping abstraction layer. + */ +struct capabilities_t { + + /** + * Register a capability to keep while calling drop(). Verifies that the + * capability is currently held. + * + * @note CAP_CHOWN is handled specially as it might not be required. + * + * @param cap capability to keep + * @return FALSE if the capability is currently not held + */ + bool (*keep)(capabilities_t *this, + u_int cap) __attribute__((warn_unused_result)); + + /** + * Check if the given capability is currently held. + * + * @note CAP_CHOWN is handled specially as it might not be required. + * + * @param cap capability to check + * @return TRUE if the capability is currently held + */ + bool (*check)(capabilities_t *this, u_int cap); + + /** + * Get the user ID set through set_uid/resolve_uid. + * + * @return currently set user ID + */ + uid_t (*get_uid)(capabilities_t *this); + + /** + * Get the group ID set through set_gid/resolve_gid. + * + * @return currently set group ID + */ + gid_t (*get_gid)(capabilities_t *this); + + /** + * Set the numerical user ID to use during rights dropping. + * + * @param uid user ID to use + */ + void (*set_uid)(capabilities_t *this, uid_t uid); + + /** + * Set the numerical group ID to use during rights dropping. + * + * @param gid group ID to use + */ + void (*set_gid)(capabilities_t *this, gid_t gid); + + /** + * Resolve a username and set the user ID accordingly. + * + * @param username username get the uid for + * @return TRUE if username resolved and uid set + */ + bool (*resolve_uid)(capabilities_t *this, char *username); + + /** + * Resolve a groupname and set the group ID accordingly. + * + * @param groupname groupname to get the gid for + * @return TRUE if groupname resolved and gid set + */ + bool (*resolve_gid)(capabilities_t *this, char *groupname); + + /** + * Drop all capabilities not previously passed to keep(), switch to UID/GID. + * + * @return TRUE if capability drop successful + */ + bool (*drop)(capabilities_t *this); + + /** + * Destroy a capabilities_t. + */ + void (*destroy)(capabilities_t *this); +}; + +/** + * Create a capabilities instance. + */ +capabilities_t *capabilities_create(); + +#endif /** CAPABILITIES_H_ @}*/ diff --git a/src/libstrongswan/utils/chunk.c b/src/libstrongswan/utils/chunk.c new file mode 100644 index 000000000..04f3eea7d --- /dev/null +++ b/src/libstrongswan/utils/chunk.c @@ -0,0 +1,821 @@ +/* + * Copyright (C) 2008-2013 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <ctype.h> + +#include "chunk.h" +#include "debug.h" + +/** + * Empty chunk. + */ +chunk_t chunk_empty = { NULL, 0 }; + +/** + * Described in header. + */ +chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk) +{ + chunk_t clone = chunk_empty; + + if (chunk.ptr && chunk.len > 0) + { + clone.ptr = ptr; + clone.len = chunk.len; + memcpy(clone.ptr, chunk.ptr, chunk.len); + } + + return clone; +} + +/** + * Described in header. + */ +size_t chunk_length(const char* mode, ...) +{ + va_list chunks; + size_t length = 0; + + va_start(chunks, mode); + while (TRUE) + { + switch (*mode++) + { + case 'm': + case 'c': + case 's': + { + chunk_t ch = va_arg(chunks, chunk_t); + length += ch.len; + continue; + } + default: + break; + } + break; + } + va_end(chunks); + return length; +} + +/** + * Described in header. + */ +chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...) +{ + va_list chunks; + chunk_t construct = chunk_create(ptr, 0); + + va_start(chunks, mode); + while (TRUE) + { + bool free_chunk = FALSE, clear_chunk = FALSE; + chunk_t ch; + + switch (*mode++) + { + case 's': + clear_chunk = TRUE; + /* FALL */ + case 'm': + free_chunk = TRUE; + /* FALL */ + case 'c': + ch = va_arg(chunks, chunk_t); + memcpy(ptr, ch.ptr, ch.len); + ptr += ch.len; + construct.len += ch.len; + if (clear_chunk) + { + chunk_clear(&ch); + } + else if (free_chunk) + { + free(ch.ptr); + } + continue; + default: + break; + } + break; + } + va_end(chunks); + + return construct; +} + +/** + * Described in header. + */ +void chunk_split(chunk_t chunk, const char *mode, ...) +{ + va_list chunks; + u_int len; + chunk_t *ch; + + va_start(chunks, mode); + while (TRUE) + { + if (*mode == '\0') + { + break; + } + len = va_arg(chunks, u_int); + ch = va_arg(chunks, chunk_t*); + /* a null chunk means skip len bytes */ + if (ch == NULL) + { + chunk = chunk_skip(chunk, len); + continue; + } + switch (*mode++) + { + case 'm': + { + ch->len = min(chunk.len, len); + if (ch->len) + { + ch->ptr = chunk.ptr; + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + case 'a': + { + ch->len = min(chunk.len, len); + if (ch->len) + { + ch->ptr = malloc(ch->len); + memcpy(ch->ptr, chunk.ptr, ch->len); + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + case 'c': + { + ch->len = min(ch->len, chunk.len); + ch->len = min(ch->len, len); + if (ch->len) + { + memcpy(ch->ptr, chunk.ptr, ch->len); + } + else + { + ch->ptr = NULL; + } + chunk = chunk_skip(chunk, ch->len); + continue; + } + default: + break; + } + break; + } + va_end(chunks); +} + +/** + * Described in header. + */ +bool chunk_write(chunk_t chunk, char *path, char *label, mode_t mask, bool force) +{ + mode_t oldmask; + FILE *fd; + bool good = FALSE; + + if (!force && access(path, F_OK) == 0) + { + DBG1(DBG_LIB, " %s file '%s' already exists", label, path); + return FALSE; + } + oldmask = umask(mask); + fd = fopen(path, "w"); + if (fd) + { + if (fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd) == chunk.len) + { + DBG1(DBG_LIB, " written %s file '%s' (%d bytes)", + label, path, chunk.len); + good = TRUE; + } + else + { + DBG1(DBG_LIB, " writing %s file '%s' failed: %s", + label, path, strerror(errno)); + } + fclose(fd); + } + else + { + DBG1(DBG_LIB, " could not open %s file '%s': %s", label, path, + strerror(errno)); + } + umask(oldmask); + return good; +} + + +/** hex conversion digits */ +static char hexdig_upper[] = "0123456789ABCDEF"; +static char hexdig_lower[] = "0123456789abcdef"; + +/** + * Described in header. + */ +chunk_t chunk_to_hex(chunk_t chunk, char *buf, bool uppercase) +{ + int i, len; + char *hexdig = hexdig_lower; + + if (uppercase) + { + hexdig = hexdig_upper; + } + + len = chunk.len * 2; + if (!buf) + { + buf = malloc(len + 1); + } + buf[len] = '\0'; + + for (i = 0; i < chunk.len; i++) + { + buf[i*2] = hexdig[(chunk.ptr[i] >> 4) & 0xF]; + buf[i*2+1] = hexdig[(chunk.ptr[i] ) & 0xF]; + } + return chunk_create(buf, len); +} + +/** + * convert a signle hex character to its binary value + */ +static char hex2bin(char hex) +{ + switch (hex) + { + case '0' ... '9': + return hex - '0'; + case 'A' ... 'F': + return hex - 'A' + 10; + case 'a' ... 'f': + return hex - 'a' + 10; + default: + return 0; + } +} + +/** + * Described in header. + */ +chunk_t chunk_from_hex(chunk_t hex, char *buf) +{ + int i, len; + u_char *ptr; + bool odd = FALSE; + + /* subtract the number of optional ':' separation characters */ + len = hex.len; + ptr = hex.ptr; + for (i = 0; i < hex.len; i++) + { + if (*ptr++ == ':') + { + len--; + } + } + + /* compute the number of binary bytes */ + if (len % 2) + { + odd = TRUE; + len++; + } + len /= 2; + + /* allocate buffer memory unless provided by caller */ + if (!buf) + { + buf = malloc(len); + } + + /* buffer is filled from the right */ + memset(buf, 0, len); + hex.ptr += hex.len; + + for (i = len - 1; i >= 0; i--) + { + /* skip separation characters */ + if (*(--hex.ptr) == ':') + { + --hex.ptr; + } + buf[i] = hex2bin(*hex.ptr); + if (i > 0 || !odd) + { + buf[i] |= hex2bin(*(--hex.ptr)) << 4; + } + } + return chunk_create(buf, len); +} + +/** base 64 conversion digits */ +static char b64digits[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Described in header. + */ +chunk_t chunk_to_base64(chunk_t chunk, char *buf) +{ + int i, len; + char *pos; + + len = chunk.len + ((3 - chunk.len % 3) % 3); + if (!buf) + { + buf = malloc(len * 4 / 3 + 1); + } + pos = buf; + for (i = 0; i < len; i+=3) + { + *pos++ = b64digits[chunk.ptr[i] >> 2]; + if (i+1 >= chunk.len) + { + *pos++ = b64digits[(chunk.ptr[i] & 0x03) << 4]; + *pos++ = '='; + *pos++ = '='; + break; + } + *pos++ = b64digits[((chunk.ptr[i] & 0x03) << 4) | (chunk.ptr[i+1] >> 4)]; + if (i+2 >= chunk.len) + { + *pos++ = b64digits[(chunk.ptr[i+1] & 0x0F) << 2]; + *pos++ = '='; + break; + } + *pos++ = b64digits[((chunk.ptr[i+1] & 0x0F) << 2) | (chunk.ptr[i+2] >> 6)]; + *pos++ = b64digits[chunk.ptr[i+2] & 0x3F]; + } + *pos = '\0'; + return chunk_create(buf, len * 4 / 3); +} + +/** + * convert a base 64 digit to its binary form (inversion of b64digits array) + */ +static int b642bin(char b64) +{ + switch (b64) + { + case 'A' ... 'Z': + return b64 - 'A'; + case 'a' ... 'z': + return ('Z' - 'A' + 1) + b64 - 'a'; + case '0' ... '9': + return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + b64 - '0'; + case '+': + case '-': + return 62; + case '/': + case '_': + return 63; + case '=': + return 0; + default: + return -1; + } +} + +/** + * Described in header. + */ +chunk_t chunk_from_base64(chunk_t base64, char *buf) +{ + u_char *pos, byte[4]; + int i, j, len, outlen; + + len = base64.len / 4 * 3; + if (!buf) + { + buf = malloc(len); + } + pos = base64.ptr; + outlen = 0; + for (i = 0; i < len; i+=3) + { + outlen += 3; + for (j = 0; j < 4; j++) + { + if (*pos == '=') + { + outlen--; + } + byte[j] = b642bin(*pos++); + } + buf[i] = (byte[0] << 2) | (byte[1] >> 4); + buf[i+1] = (byte[1] << 4) | (byte[2] >> 2); + buf[i+2] = (byte[2] << 6) | (byte[3]); + } + return chunk_create(buf, outlen); +} + +/** base 32 conversion digits */ +static char b32digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +/** + * Described in header. + */ +chunk_t chunk_to_base32(chunk_t chunk, char *buf) +{ + int i, len; + char *pos; + + len = chunk.len + ((5 - chunk.len % 5) % 5); + if (!buf) + { + buf = malloc(len * 8 / 5 + 1); + } + pos = buf; + for (i = 0; i < len; i+=5) + { + *pos++ = b32digits[chunk.ptr[i] >> 3]; + if (i+1 >= chunk.len) + { + *pos++ = b32digits[(chunk.ptr[i] & 0x07) << 2]; + memset(pos, '=', 6); + pos += 6; + break; + } + *pos++ = b32digits[((chunk.ptr[i] & 0x07) << 2) | + (chunk.ptr[i+1] >> 6)]; + *pos++ = b32digits[(chunk.ptr[i+1] & 0x3E) >> 1]; + if (i+2 >= chunk.len) + { + *pos++ = b32digits[(chunk.ptr[i+1] & 0x01) << 4]; + memset(pos, '=', 4); + pos += 4; + break; + } + *pos++ = b32digits[((chunk.ptr[i+1] & 0x01) << 4) | + (chunk.ptr[i+2] >> 4)]; + if (i+3 >= chunk.len) + { + *pos++ = b32digits[(chunk.ptr[i+2] & 0x0F) << 1]; + memset(pos, '=', 3); + pos += 3; + break; + } + *pos++ = b32digits[((chunk.ptr[i+2] & 0x0F) << 1) | + (chunk.ptr[i+3] >> 7)]; + *pos++ = b32digits[(chunk.ptr[i+3] & 0x7F) >> 2]; + if (i+4 >= chunk.len) + { + *pos++ = b32digits[(chunk.ptr[i+3] & 0x03) << 3]; + *pos++ = '='; + break; + } + *pos++ = b32digits[((chunk.ptr[i+3] & 0x03) << 3) | + (chunk.ptr[i+4] >> 5)]; + *pos++ = b32digits[chunk.ptr[i+4] & 0x1F]; + } + *pos = '\0'; + return chunk_create(buf, len * 8 / 5); +} + +/** + * Described in header. + */ +int chunk_compare(chunk_t a, chunk_t b) +{ + int compare_len = a.len - b.len; + int len = (compare_len < 0)? a.len : b.len; + + if (compare_len != 0 || len == 0) + { + return compare_len; + } + return memcmp(a.ptr, b.ptr, len); +}; + + +/** + * Described in header. + */ +bool chunk_increment(chunk_t chunk) +{ + int i; + + for (i = chunk.len - 1; i >= 0; i--) + { + if (++chunk.ptr[i] != 0) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Remove non-printable characters from a chunk. + */ +bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace) +{ + bool printable = TRUE; + int i; + + if (sane) + { + *sane = chunk_clone(chunk); + } + for (i = 0; i < chunk.len; i++) + { + if (!isprint(chunk.ptr[i])) + { + if (sane) + { + sane->ptr[i] = replace; + } + printable = FALSE; + } + } + return printable; +} + +/** + * Helper functions for chunk_mac() + */ +static inline u_int64_t sipget(u_char *in) +{ + u_int64_t v = 0; + int i; + + for (i = 0; i < 64; i += 8, ++in) + { + v |= ((u_int64_t)*in) << i; + } + return v; +} + +static inline u_int64_t siprotate(u_int64_t v, int shift) +{ + return (v << shift) | (v >> (64 - shift)); +} + +static inline void sipround(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2, + u_int64_t *v3) +{ + *v0 += *v1; + *v1 = siprotate(*v1, 13); + *v1 ^= *v0; + *v0 = siprotate(*v0, 32); + + *v2 += *v3; + *v3 = siprotate(*v3, 16); + *v3 ^= *v2; + + *v2 += *v1; + *v1 = siprotate(*v1, 17); + *v1 ^= *v2; + *v2 = siprotate(*v2, 32); + + *v0 += *v3; + *v3 = siprotate(*v3, 21); + *v3 ^= *v0; +} + +static inline void sipcompress(u_int64_t *v0, u_int64_t *v1, u_int64_t *v2, + u_int64_t *v3, u_int64_t m) +{ + *v3 ^= m; + sipround(v0, v1, v2, v3); + sipround(v0, v1, v2, v3); + *v0 ^= m; +} + +static inline u_int64_t siplast(size_t len, u_char *pos) +{ + u_int64_t b; + int rem = len & 7; + + b = ((u_int64_t)len) << 56; + switch (rem) + { + case 7: + b |= ((u_int64_t)pos[6]) << 48; + case 6: + b |= ((u_int64_t)pos[5]) << 40; + case 5: + b |= ((u_int64_t)pos[4]) << 32; + case 4: + b |= ((u_int64_t)pos[3]) << 24; + case 3: + b |= ((u_int64_t)pos[2]) << 16; + case 2: + b |= ((u_int64_t)pos[1]) << 8; + case 1: + b |= ((u_int64_t)pos[0]); + break; + case 0: + break; + } + return b; +} + +/** + * Caculate SipHash-2-4 with an optional first block given as argument. + */ +static u_int64_t chunk_mac_inc(chunk_t chunk, u_char *key, u_int64_t m) +{ + u_int64_t v0, v1, v2, v3, k0, k1; + size_t len = chunk.len; + u_char *pos = chunk.ptr, *end; + + end = chunk.ptr + len - (len % 8); + + k0 = sipget(key); + k1 = sipget(key + 8); + + v0 = k0 ^ 0x736f6d6570736575ULL; + v1 = k1 ^ 0x646f72616e646f6dULL; + v2 = k0 ^ 0x6c7967656e657261ULL; + v3 = k1 ^ 0x7465646279746573ULL; + + if (m) + { + sipcompress(&v0, &v1, &v2, &v3, m); + } + + /* compression with c = 2 */ + for (; pos != end; pos += 8) + { + m = sipget(pos); + sipcompress(&v0, &v1, &v2, &v3, m); + } + sipcompress(&v0, &v1, &v2, &v3, siplast(len, pos)); + + /* finalization with d = 4 */ + v2 ^= 0xff; + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + sipround(&v0, &v1, &v2, &v3); + return v0 ^ v1 ^ v2 ^ v3; +} + +/** + * Described in header. + */ +u_int64_t chunk_mac(chunk_t chunk, u_char *key) +{ + return chunk_mac_inc(chunk, key, 0); +} + +/** + * Secret key allocated randomly during first use. + */ +static u_char key[16]; + +/** + * Static key used in case predictable hash values are required. + */ +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 + */ +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() +{ + ssize_t len; + size_t done = 0; + int fd; + + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) + { + while (done < sizeof(key)) + { + len = read(fd, key + done, sizeof(key) - done); + if (len < 0) + { + break; + } + done += len; + } + close(fd); + } + /* on error we use random() to generate the key (better than nothing) */ + if (done < sizeof(key)) + { + srandom(time(NULL) + getpid()); + for (; done < sizeof(key); done++) + { + key[done] = (u_char)random(); + } + } +} + +/** + * Described in header. + */ +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); +} + +/** + * Described in header. + */ +u_int32_t chunk_hash(chunk_t chunk) +{ + pthread_once(&key_allocated, allocate_key); + return chunk_mac(chunk, key); +} + +/** + * Described in header. + */ +u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash) +{ /* we could use a mac of the previous hash, but this is faster */ + return chunk_mac_inc(chunk, static_key, ((u_int64_t)hash) << 32 | hash); +} + +/** + * Described in header. + */ +u_int32_t chunk_hash_static(chunk_t chunk) +{ + return chunk_mac(chunk, static_key); +} + +/** + * Described in header. + */ +int chunk_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + chunk_t *chunk = *((chunk_t**)(args[0])); + bool first = TRUE; + chunk_t copy = *chunk; + int written = 0; + + if (!spec->hash) + { + u_int chunk_len = chunk->len; + const void *new_args[] = {&chunk->ptr, &chunk_len}; + return mem_printf_hook(data, spec, new_args); + } + + while (copy.len > 0) + { + if (first) + { + first = FALSE; + } + else + { + written += print_in_hook(data, ":"); + } + written += print_in_hook(data, "%02x", *copy.ptr++); + copy.len--; + } + return written; +} diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h new file mode 100644 index 000000000..34ba77357 --- /dev/null +++ b/src/libstrongswan/utils/chunk.h @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2008-2013 Tobias Brunner + * Copyright (C) 2005-2008 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 chunk chunk + * @{ @ingroup utils + */ + +#ifndef CHUNK_H_ +#define CHUNK_H_ + +#include <string.h> +#include <stdarg.h> +#include <sys/types.h> +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + +typedef struct chunk_t chunk_t; + +/** + * General purpose pointer/length abstraction. + */ +struct chunk_t { + /** Pointer to start of data */ + u_char *ptr; + /** Length of data in bytes */ + size_t len; +}; + +#include "utils.h" + +/** + * A { NULL, 0 }-chunk handy for initialization. + */ +extern chunk_t chunk_empty; + +/** + * Create a new chunk pointing to "ptr" with length "len" + */ +static inline chunk_t chunk_create(u_char *ptr, size_t len) +{ + chunk_t chunk = {ptr, len}; + return chunk; +} + +/** + * Create a clone of a chunk pointing to "ptr" + */ +chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk); + +/** + * Calculate length of multiple chunks + */ +size_t chunk_length(const char *mode, ...); + +/** + * Concatenate chunks into a chunk pointing to "ptr". + * + * The mode string specifies the number of chunks, and how to handle each of + * them with a single character: 'c' for copy (allocate new chunk), 'm' for move + * (free given chunk) or 's' for sensitive-move (clear given chunk, then free). + */ +chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...); + +/** + * Split up a chunk into parts, "mode" is a string of "a" (alloc), + * "c" (copy) and "m" (move). Each letter say for the corresponding chunk if + * it should get allocated on heap, copied into existing chunk, or the chunk + * should point into "chunk". The length of each part is an argument before + * each target chunk. E.g.: + * chunk_split(chunk, "mcac", 3, &a, 7, &b, 5, &c, d.len, &d); + */ +void chunk_split(chunk_t chunk, const char *mode, ...); + +/** + * Write the binary contents of a chunk_t to a file + * + * @param chunk contents to write to file + * @param path path where file is written to + * @param label label specifying file type + * @param mask file mode creation mask + * @param force overwrite existing file by force + * @return TRUE if write operation was successful + */ +bool chunk_write(chunk_t chunk, char *path, char *label, mode_t mask, bool force); + +/** + * Convert a chunk of data to hex encoding. + * + * The resulting string is '\\0' terminated, but the chunk does not include + * the '\\0'. If buf is supplied, it must hold at least (chunk.len * 2 + 1). + * + * @param chunk data to convert to hex encoding + * @param buf buffer to write to, NULL to malloc + * @param uppercase TRUE to use uppercase letters + * @return chunk of encoded data + */ +chunk_t chunk_to_hex(chunk_t chunk, char *buf, bool uppercase); + +/** + * Convert a hex encoded in a binary chunk. + * + * If buf is supplied, it must hold at least (hex.len / 2) + (hex.len % 2) + * bytes. It is filled by the right to give correct values for short inputs. + * + * @param hex hex encoded input data + * @param buf buffer to write decoded data, NULL to malloc + * @return converted data + */ +chunk_t chunk_from_hex(chunk_t hex, char *buf); + +/** + * Convert a chunk of data to its base64 encoding. + * + * The resulting string is '\\0' terminated, but the chunk does not include + * the '\\0'. If buf is supplied, it must hold at least (chunk.len * 4 / 3 + 1). + * + * @param chunk data to convert + * @param buf buffer to write to, NULL to malloc + * @return chunk of encoded data + */ +chunk_t chunk_to_base64(chunk_t chunk, char *buf); + +/** + * Convert a base64 in a binary chunk. + * + * If buf is supplied, it must hold at least (base64.len / 4 * 3). + * + * @param base64 base64 encoded input data + * @param buf buffer to write decoded data, NULL to malloc + * @return converted data + */ +chunk_t chunk_from_base64(chunk_t base64, char *buf); + +/** + * Convert a chunk of data to its base32 encoding. + * + * The resulting string is '\\0' terminated, but the chunk does not include + * the '\\0'. If buf is supplied, it must hold (chunk.len * 8 / 5 + 1) bytes. + * + * @param chunk data to convert + * @param buf buffer to write to, NULL to malloc + * @return chunk of encoded data + */ +chunk_t chunk_to_base32(chunk_t chunk, char *buf); + +/** + * Free contents of a chunk + */ +static inline void chunk_free(chunk_t *chunk) +{ + free(chunk->ptr); + *chunk = chunk_empty; +} + +/** + * Overwrite the contents of a chunk and free it + */ +static inline void chunk_clear(chunk_t *chunk) +{ + if (chunk->ptr) + { + memwipe(chunk->ptr, chunk->len); + chunk_free(chunk); + } +} + +/** + * Initialize a chunk using a char array + */ +#define chunk_from_chars(...) ((chunk_t){(char[]){__VA_ARGS__}, sizeof((char[]){__VA_ARGS__})}) + +/** + * Initialize a chunk to point to a thing + */ +#define chunk_from_thing(thing) chunk_create((char*)&(thing), sizeof(thing)) + +/** + * Initialize a chunk from a string, not containing 0-terminator + */ +#define chunk_from_str(str) ({char *x = (str); chunk_create(x, strlen(x));}) + +/** + * Allocate a chunk on the heap + */ +#define chunk_alloc(bytes) ({size_t x = (bytes); chunk_create(x ? malloc(x) : NULL, x);}) + +/** + * Allocate a chunk on the stack + */ +#define chunk_alloca(bytes) ({size_t x = (bytes); chunk_create(x ? alloca(x) : NULL, x);}) + +/** + * Clone a chunk on heap + */ +#define chunk_clone(chunk) ({chunk_t x = (chunk); chunk_create_clone(x.len ? malloc(x.len) : NULL, x);}) + +/** + * Clone a chunk on stack + */ +#define chunk_clonea(chunk) ({chunk_t x = (chunk); chunk_create_clone(x.len ? alloca(x.len) : NULL, x);}) + +/** + * Concatenate chunks into a chunk on heap + */ +#define chunk_cat(mode, ...) chunk_create_cat(malloc(chunk_length(mode, __VA_ARGS__)), mode, __VA_ARGS__) + +/** + * Concatenate chunks into a chunk on stack + */ +#define chunk_cata(mode, ...) chunk_create_cat(alloca(chunk_length(mode, __VA_ARGS__)), mode, __VA_ARGS__) + +/** + * Skip n bytes in chunk (forward pointer, shorten length) + */ +static inline chunk_t chunk_skip(chunk_t chunk, size_t bytes) +{ + if (chunk.len > bytes) + { + chunk.ptr += bytes; + chunk.len -= bytes; + return chunk; + } + return chunk_empty; +} + +/** + * Skip a leading zero-valued byte + */ +static inline chunk_t chunk_skip_zero(chunk_t chunk) +{ + if (chunk.len > 1 && *chunk.ptr == 0x00) + { + chunk.ptr++; + chunk.len--; + } + return chunk; +} + + +/** + * Compare two chunks, returns zero if a equals b + * or negative/positive if a is small/greater than b + */ +int chunk_compare(chunk_t a, chunk_t b); + +/** + * Compare two chunks for equality, + * NULL chunks are never equal. + */ +static inline bool chunk_equals(chunk_t a, chunk_t b) +{ + return a.ptr != NULL && b.ptr != NULL && + a.len == b.len && memeq(a.ptr, b.ptr, a.len); +} + +/** + * Compare two chunks (given as pointers) for equality (useful as callback), + * NULL chunks are never equal. + */ +static inline bool chunk_equals_ptr(chunk_t *a, chunk_t *b) +{ + return a != NULL && b != NULL && chunk_equals(*a, *b); +} + +/** + * Increment a chunk, as it would reprensent a network order integer. + * + * @param chunk chunk to increment + * @return TRUE if an overflow occurred + */ +bool chunk_increment(chunk_t chunk); + +/** + * Check if a chunk has printable characters only. + * + * If sane is given, chunk is cloned into sane and all non printable characters + * get replaced by "replace". + * + * @param chunk chunk to check for printability + * @param sane pointer where sane version is allocated, or NULL + * @param replace character to use for replaceing unprintable characters + * @return TRUE if all characters in chunk are printable + */ +bool chunk_printable(chunk_t chunk, chunk_t *sane, char replace); + +/** + * Computes a 32 bit hash of the given chunk. + * + * @note The output of this function is randomized, that is, it will only + * produce the same output for the same input when calling it from the same + * process. For a more predictable hash function use chunk_hash_static() + * instead. + * + * @note This hash is only intended for hash tables not for cryptographic + * purposes. + * + * @param chunk data to hash + * @return hash value + */ +u_int32_t chunk_hash(chunk_t chunk); + +/** + * Incremental version of chunk_hash. Use this to hash two or more chunks. + * + * @param chunk data to hash + * @param hash previous hash value + * @return hash value + */ +u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash); + +/** + * Computes a 32 bit hash of the given chunk. + * + * Compared to chunk_hash() this will always calculate the same output for the + * same input. Therefore, it should not be used for hash tables (to prevent + * hash flooding). + * + * @note This hash is not intended for cryptographic purposes. + * + * @param chunk data to hash + * @return hash value + */ +u_int32_t chunk_hash_static(chunk_t chunk); + +/** + * Incremental version of chunk_hash_static(). Use this to hash two or more + * chunks in a predictable way. + * + * @param chunk data to hash + * @param hash previous hash value + * @return hash value + */ +u_int32_t chunk_hash_static_inc(chunk_t chunk, u_int32_t hash); + +/** + * Computes a quick MAC from the given chunk and key using SipHash. + * + * The key must have a length of 128-bit (16 bytes). + * + * @note While SipHash has strong features using it for cryptographic purposes + * is not recommended (in particular because of the rather short output size). + * + * @param chunk data to process + * @param key key to use + * @return MAC for given input and key + */ +u_int64_t chunk_mac(chunk_t chunk, u_char *key); + +/** + * printf hook function for chunk_t. + * + * Arguments are: + * chunk_t *chunk + * Use #-modifier to print a compact version + */ +int chunk_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +#endif /** CHUNK_H_ @}*/ diff --git a/src/libstrongswan/utils/debug.c b/src/libstrongswan/utils/debug.c new file mode 100644 index 000000000..e8c9e6b98 --- /dev/null +++ b/src/libstrongswan/utils/debug.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stdarg.h> + +#include "debug.h" + +ENUM(debug_names, DBG_DMN, DBG_LIB, + "DMN", + "MGR", + "IKE", + "CHD", + "JOB", + "CFG", + "KNL", + "NET", + "ASN", + "ENC", + "TNC", + "IMC", + "IMV", + "PTS", + "TLS", + "APP", + "ESP", + "LIB", +); + +ENUM(debug_lower_names, DBG_DMN, DBG_LIB, + "dmn", + "mgr", + "ike", + "chd", + "job", + "cfg", + "knl", + "net", + "asn", + "enc", + "tnc", + "imc", + "imv", + "pts", + "tls", + "app", + "esp", + "lib", +); + +/** + * level logged by the default logger + */ +static level_t default_level = 1; + +/** + * stream logged to by the default logger + */ +static FILE *default_stream = NULL; + +/** + * default dbg function which printf all to stderr + */ +void dbg_default(debug_t group, level_t level, char *fmt, ...) +{ + if (!default_stream) + { + default_stream = stderr; + } + if (level <= default_level) + { + va_list args; + + va_start(args, fmt); + vfprintf(default_stream, fmt, args); + fprintf(default_stream, "\n"); + va_end(args); + } +} + +/** + * set the level logged by the default stderr logger + */ +void dbg_default_set_level(level_t level) +{ + default_level = level; +} + +/** + * set the stream logged by dbg_default() to + */ +void dbg_default_set_stream(FILE *stream) +{ + default_stream = stream; +} + +/** + * The registered debug hook. + */ +void (*dbg) (debug_t group, level_t level, char *fmt, ...) = dbg_default; + diff --git a/src/libstrongswan/utils/debug.h b/src/libstrongswan/utils/debug.h new file mode 100644 index 000000000..c46d3fe55 --- /dev/null +++ b/src/libstrongswan/utils/debug.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2006 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 debug debug + * @{ @ingroup utils + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +typedef enum debug_t debug_t; +typedef enum level_t level_t; + +#include <stdio.h> + +#include "utils/enum.h" + +/** + * Debug message group. + */ +enum debug_t { + /** daemon specific */ + DBG_DMN, + /** IKE_SA_MANAGER */ + DBG_MGR, + /** IKE_SA */ + DBG_IKE, + /** CHILD_SA */ + DBG_CHD, + /** job processing */ + DBG_JOB, + /** configuration backends */ + DBG_CFG, + /** kernel interface */ + DBG_KNL, + /** networking/sockets */ + DBG_NET, + /** low-level encoding/decoding (ASN.1, X.509 etc.) */ + DBG_ASN, + /** message encoding/decoding */ + DBG_ENC, + /** trusted network connect */ + DBG_TNC, + /** integrity measurement client */ + DBG_IMC, + /** integrity measurement verifier */ + DBG_IMV, + /** platform trust service */ + DBG_PTS, + /** libtls */ + DBG_TLS, + /** applications other than daemons */ + DBG_APP, + /** libipsec */ + DBG_ESP, + /** libstrongswan */ + DBG_LIB, + /** number of groups */ + DBG_MAX, + /** pseudo group with all groups */ + DBG_ANY = DBG_MAX, +}; + +/** + * short names of debug message group. + */ +extern enum_name_t *debug_names; + +/** + * short names of debug message group, lower case. + */ +extern enum_name_t *debug_lower_names; + +/** + * Debug levels used to control output verbosity. + */ +enum level_t { + /** absolutely silent */ + LEVEL_SILENT = -1, + /** most important auditing logs */ + LEVEL_AUDIT = 0, + /** control flow */ + LEVEL_CTRL = 1, + /** diagnose problems */ + LEVEL_DIAG = 2, + /** raw binary blobs */ + LEVEL_RAW = 3, + /** including sensitive data (private keys) */ + LEVEL_PRIVATE = 4, +}; + +#ifndef DEBUG_LEVEL +# define DEBUG_LEVEL 4 +#endif /* DEBUG_LEVEL */ + +/** debug macros, they call the dbg function hook */ +#if DEBUG_LEVEL >= 0 +# define DBG0(group, fmt, ...) dbg(group, 0, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 1 +# define DBG1(group, fmt, ...) dbg(group, 1, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 2 +# define DBG2(group, fmt, ...) dbg(group, 2, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 3 +# define DBG3(group, fmt, ...) dbg(group, 3, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ +#if DEBUG_LEVEL >= 4 +# define DBG4(group, fmt, ...) dbg(group, 4, fmt, ##__VA_ARGS__) +#endif /* DEBUG_LEVEL */ + +#ifndef DBG0 +# define DBG0(...) {} +#endif +#ifndef DBG1 +# define DBG1(...) {} +#endif +#ifndef DBG2 +# define DBG2(...) {} +#endif +#ifndef DBG3 +# define DBG3(...) {} +#endif +#ifndef DBG4 +# define DBG4(...) {} +#endif + +/** dbg function hook, uses dbg_default() by default */ +extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); + +/** default logging function */ +void dbg_default(debug_t group, level_t level, char *fmt, ...); + +/** set the level logged by dbg_default() */ +void dbg_default_set_level(level_t level); + +/** set the stream logged by dbg_default() to */ +void dbg_default_set_stream(FILE *stream); + +#endif /** DEBUG_H_ @}*/ diff --git a/src/libstrongswan/utils/enum.c b/src/libstrongswan/utils/enum.c new file mode 100644 index 000000000..3db9a34e0 --- /dev/null +++ b/src/libstrongswan/utils/enum.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 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. + */ + +#include <stddef.h> +#include <stdio.h> + +#include <library.h> + +#include "enum.h" + +/** + * See header. + */ +char *enum_to_name(enum_name_t *e, int val) +{ + do + { + if (val >= e->first && val <= e->last) + { + return e->names[val - e->first]; + } + } + while ((e = e->next)); + return NULL; +} + +/** + * See header. + */ +int enum_from_name(enum_name_t *e, char *name) +{ + do + { + int i, count = e->last - e->first + 1; + + for (i = 0; i < count; i++) + { + if (name && strcaseeq(name, e->names[i])) + { + return e->first + i; + } + } + } + while ((e = e->next)); + return -1; +} + +/** + * Described in header. + */ +int enum_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + enum_name_t *ed = *((enum_name_t**)(args[0])); + int val = *((int*)(args[1])); + char *name, buf[32]; + + name = enum_to_name(ed, val); + if (name == NULL) + { + snprintf(buf, sizeof(buf), "(%d)", val); + name = buf; + } + if (spec->minus) + { + return print_in_hook(data, "%-*s", spec->width, name); + } + return print_in_hook(data, "%*s", spec->width, name); +} diff --git a/src/libstrongswan/utils/enum.h b/src/libstrongswan/utils/enum.h new file mode 100644 index 000000000..df8dbf8c1 --- /dev/null +++ b/src/libstrongswan/utils/enum.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2006-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 enum enum + * @{ @ingroup utils + */ + +#ifndef ENUM_H_ +#define ENUM_H_ + +#include "printf_hook.h" + +typedef struct enum_name_t enum_name_t; + +/** + * Struct to store names for enums. + * + * To print the string representation of enumeration values, the strings + * are stored in these structures. Every enum_name contains a range + * of strings, multiple ranges are linked together. + * Use the convenience macros to define these linked ranges. + * + * For a single range, use: + * @code + ENUM(name, first, last, string1, string2, ...) + @endcode + * For multiple linked ranges, use: + * @code + ENUM_BEGIN(name, first, last, string1, string2, ...) + ENUM_NEXT(name, first, last, last_from_previous, string3, ...) + ENUM_NEXT(name, first, last, last_from_previous, string4, ...) + ENUM_END(name, last_from_previous) + @endcode + * The ENUM and the ENUM_END define a enum_name_t pointer with the name supplied + * in "name". + * + * Resolving of enum names is done using a printf hook. A printf fromat + * character %N is replaced by the enum string. Printf needs two arguments to + * resolve a %N, the enum_name_t* (the defined name in ENUM_BEGIN) followed + * by the numerical enum value. + */ +struct enum_name_t { + /** value of the first enum string */ + int first; + /** value of the last enum string */ + int last; + /** next enum_name_t in list */ + enum_name_t *next; + /** array of strings containing names from first to last */ + char *names[]; +}; + +/** + * Begin a new enum_name list. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param ... a list of strings + */ +#define ENUM_BEGIN(name, first, last, ...) static enum_name_t name##last = {first, last, NULL, { __VA_ARGS__ }} + +/** + * Continue a enum name list startetd with ENUM_BEGIN. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param prev enum value of the "last" defined in ENUM_BEGIN/previous ENUM_NEXT + * @param ... a list of strings + */ +#define ENUM_NEXT(name, first, last, prev, ...) static enum_name_t name##last = {first, last, &name##prev, { __VA_ARGS__ }} + +/** + * Complete enum name list started with ENUM_BEGIN. + * + * @param name name of the enum_name list + * @param prev enum value of the "last" defined in ENUM_BEGIN/previous ENUM_NEXT + */ +#define ENUM_END(name, prev) enum_name_t *name = &name##prev; + +/** + * Define a enum name with only one range. + * + * This is a convenience macro to use when a enum_name list contains only + * one range, and is equal as defining ENUM_BEGIN followed by ENUM_END. + * + * @param name name of the enum_name list + * @param first enum value of the first enum string + * @param last enum value of the last enum string + * @param ... a list of strings + */ +#define ENUM(name, first, last, ...) ENUM_BEGIN(name, first, last, __VA_ARGS__); ENUM_END(name, last) + +/** + * Convert a enum value to its string representation. + * + * @param e enum names for this enum value + * @param val enum value to get string for + * @return string for enum, NULL if not found + */ +char *enum_to_name(enum_name_t *e, int val); + +/** + * Convert a enum string back to its enum value. + * + * @param e enum names for this enum value + * @param name name to get enum value for + * @return enum value, -1 if not found + */ +int enum_from_name(enum_name_t *e, char *name); + +/** + * printf hook function for enum_names_t. + * + * Arguments are: + * enum_names_t *names, int value + */ +int enum_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +#endif /** ENUM_H_ @}*/ diff --git a/src/libstrongswan/utils/enumerator.c b/src/libstrongswan/utils/enumerator.c deleted file mode 100644 index fb461b448..000000000 --- a/src/libstrongswan/utils/enumerator.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright (C) 2008 Tobias Brunner - * Copyright (C) 2007 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. - */ - -#include "enumerator.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <limits.h> -#include <stdio.h> -#include <dirent.h> -#include <errno.h> -#include <string.h> - -#include <debug.h> - -/** - * Implementation of enumerator_create_empty().enumerate - */ -static bool enumerate_empty(enumerator_t *enumerator, ...) -{ - return FALSE; -} - -/** - * See header - */ -enumerator_t* enumerator_create_empty() -{ - enumerator_t *this = malloc_thing(enumerator_t); - this->enumerate = enumerate_empty; - this->destroy = (void*)free; - return this; -} - -/** - * Enumerator implementation for directory enumerator - */ -typedef struct { - /** implements enumerator_t */ - enumerator_t public; - /** directory handle */ - DIR *dir; - /** absolute path of current file */ - char full[PATH_MAX]; - /** where directory part of full ends and relative file gets written */ - char *full_end; -} dir_enum_t; - -/** - * Implementation of enumerator_create_directory().destroy - */ -static void destroy_dir_enum(dir_enum_t *this) -{ - closedir(this->dir); - free(this); -} - -/** - * Implementation of enumerator_create_directory().enumerate - */ -static bool enumerate_dir_enum(dir_enum_t *this, char **relative, - char **absolute, struct stat *st) -{ - struct dirent *entry = readdir(this->dir); - size_t remaining; - int len; - - if (!entry) - { - return FALSE; - } - if (streq(entry->d_name, ".") || streq(entry->d_name, "..")) - { - return enumerate_dir_enum(this, relative, absolute, st); - } - if (relative) - { - *relative = entry->d_name; - } - if (absolute || st) - { - remaining = sizeof(this->full) - (this->full_end - this->full); - len = snprintf(this->full_end, remaining, "%s", entry->d_name); - if (len < 0 || len >= remaining) - { - DBG1(DBG_LIB, "buffer too small to enumerate file '%s'", - entry->d_name); - return FALSE; - } - if (absolute) - { - *absolute = this->full; - } - if (st) - { - if (stat(this->full, st)) - { - DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->full, - strerror(errno)); - return FALSE; - } - } - } - return TRUE; -} - -/** - * See header - */ -enumerator_t* enumerator_create_directory(char *path) -{ - int len; - dir_enum_t *this = malloc_thing(dir_enum_t); - this->public.enumerate = (void*)enumerate_dir_enum; - this->public.destroy = (void*)destroy_dir_enum; - - if (*path == '\0') - { - path = "./"; - } - len = snprintf(this->full, sizeof(this->full)-1, "%s", path); - if (len < 0 || len >= sizeof(this->full)-1) - { - DBG1(DBG_LIB, "path string '%s' too long", path); - free(this); - return NULL; - } - /* append a '/' if not already done */ - if (this->full[len-1] != '/') - { - this->full[len++] = '/'; - this->full[len] = '\0'; - } - this->full_end = &this->full[len]; - - this->dir = opendir(path); - if (this->dir == NULL) - { - DBG1(DBG_LIB, "opening directory '%s' failed: %s", path, strerror(errno)); - free(this); - return NULL; - } - return &this->public; -} - -/** - * Enumerator implementation for directory enumerator - */ -typedef struct { - /** implements enumerator_t */ - enumerator_t public; - /** string to parse */ - char *string; - /** current position */ - char *pos; - /** separater chars */ - char *sep; - /** trim chars */ - char *trim; -} token_enum_t; - -/** - * Implementation of enumerator_create_token().destroy - */ -static void destroy_token_enum(token_enum_t *this) -{ - free(this->string); - free(this); -} - -/** - * Implementation of enumerator_create_token().enumerate - */ -static bool enumerate_token_enum(token_enum_t *this, char **token) -{ - char *pos = NULL, *tmp, *sep, *trim; - bool last = FALSE; - - /* trim leading characters/separators */ - while (*this->pos) - { - trim = this->trim; - while (*trim) - { - if (*trim == *this->pos) - { - this->pos++; - break; - } - trim++; - } - sep = this->sep; - while (*sep) - { - if (*sep == *this->pos) - { - this->pos++; - break; - } - sep++; - } - if (!*trim && !*sep) - { - break; - } - } - - switch (*this->pos) - { - case '"': - case '\'': - { - /* read quoted token */ - tmp = strchr(this->pos + 1, *this->pos); - if (tmp) - { - *token = this->pos + 1; - *tmp = '\0'; - this->pos = tmp + 1; - return TRUE; - } - /* unterminated string, FALL-THROUGH */ - } - default: - { - /* find nearest separator */ - sep = this->sep; - while (*sep) - { - tmp = strchr(this->pos, *sep); - if (tmp && (pos == NULL || tmp < pos)) - { - pos = tmp; - } - sep++; - } - *token = this->pos; - if (pos) - { - *pos = '\0'; - this->pos = pos + 1; - } - else - { - last = TRUE; - pos = this->pos = strchr(this->pos, '\0'); - } - break; - } - } - - /* trim trailing characters/separators */ - pos--; - while (pos >= *token) - { - trim = this->trim; - while (*trim) - { - if (*trim == *pos) - { - *(pos--) = '\0'; - break; - } - trim++; - } - sep = this->sep; - while (*sep) - { - if (*sep == *pos) - { - *(pos--) = '\0'; - break; - } - sep++; - } - if (!*trim && !*sep) - { - break; - } - } - - if (!last || pos >= *token) - { - return TRUE; - } - return FALSE; -} - -/** - * See header - */ -enumerator_t* enumerator_create_token(char *string, char *sep, char *trim) -{ - token_enum_t *enumerator = malloc_thing(token_enum_t); - - enumerator->public.enumerate = (void*)enumerate_token_enum; - enumerator->public.destroy = (void*)destroy_token_enum; - enumerator->string = strdup(string); - enumerator->pos = enumerator->string; - enumerator->sep = sep; - enumerator->trim = trim; - - return &enumerator->public; -} - -/** - * enumerator for nested enumerations - */ -typedef struct { - /* implements enumerator_t */ - enumerator_t public; - /* outer enumerator */ - enumerator_t *outer; - /* inner enumerator */ - enumerator_t *inner; - /* constructor for inner enumerator */ - enumerator_t *(*create_inner)(void *outer, void *data); - /* data to pass to constructor above */ - void *data; - /* destructor for data */ - void (*destroy_data)(void *data); -} nested_enumerator_t; - - -/** - * Implementation of enumerator_create_nested().enumerate() - */ -static bool enumerate_nested(nested_enumerator_t *this, void *v1, void *v2, - void *v3, void *v4, void *v5) -{ - while (TRUE) - { - while (this->inner == NULL) - { - void *outer; - - if (!this->outer->enumerate(this->outer, &outer)) - { - return FALSE; - } - this->inner = this->create_inner(outer, this->data); - } - if (this->inner->enumerate(this->inner, v1, v2, v3, v4, v5)) - { - return TRUE; - } - this->inner->destroy(this->inner); - this->inner = NULL; - } -} - -/** - * Implementation of enumerator_create_nested().destroy() - **/ -static void destroy_nested(nested_enumerator_t *this) -{ - if (this->destroy_data) - { - this->destroy_data(this->data); - } - DESTROY_IF(this->inner); - this->outer->destroy(this->outer); - free(this); -} - -/** - * See header - */ -enumerator_t *enumerator_create_nested(enumerator_t *outer, - enumerator_t *(inner_constructor)(void *outer, void *data), - void *data, void (*destroy_data)(void *data)) -{ - nested_enumerator_t *enumerator = malloc_thing(nested_enumerator_t); - - enumerator->public.enumerate = (void*)enumerate_nested; - enumerator->public.destroy = (void*)destroy_nested; - enumerator->outer = outer; - enumerator->inner = NULL; - enumerator->create_inner = (void*)inner_constructor; - enumerator->data = data; - enumerator->destroy_data = destroy_data; - - return &enumerator->public; -} - -/** - * enumerator for filtered enumerator - */ -typedef struct { - enumerator_t public; - enumerator_t *unfiltered; - void *data; - bool (*filter)(void *data, ...); - void (*destructor)(void *data); -} filter_enumerator_t; - -/** - * Implementation of enumerator_create_filter().destroy - */ -static void destroy_filter(filter_enumerator_t *this) -{ - if (this->destructor) - { - this->destructor(this->data); - } - this->unfiltered->destroy(this->unfiltered); - free(this); -} - -/** - * Implementation of enumerator_create_filter().enumerate - */ -static bool enumerate_filter(filter_enumerator_t *this, void *o1, void *o2, - void *o3, void *o4, void *o5) -{ - void *i1, *i2, *i3, *i4, *i5; - - while (this->unfiltered->enumerate(this->unfiltered, &i1, &i2, &i3, &i4, &i5)) - { - if (this->filter(this->data, &i1, o1, &i2, o2, &i3, o3, &i4, o4, &i5, o5)) - { - return TRUE; - } - } - return FALSE; -} - -/** - * see header - */ -enumerator_t *enumerator_create_filter(enumerator_t *unfiltered, - bool (*filter)(void *data, ...), - void *data, void (*destructor)(void *data)) -{ - filter_enumerator_t *this = malloc_thing(filter_enumerator_t); - - this->public.enumerate = (void*)enumerate_filter; - this->public.destroy = (void*)destroy_filter; - this->unfiltered = unfiltered; - this->filter = filter; - this->data = data; - this->destructor = destructor; - - return &this->public; -} - -/** - * enumerator for cleaner enumerator - */ -typedef struct { - enumerator_t public; - enumerator_t *wrapped; - void (*cleanup)(void *data); - void *data; -} cleaner_enumerator_t; - -/** - * Implementation of enumerator_create_cleanup().destroy - */ -static void destroy_cleaner(cleaner_enumerator_t *this) -{ - this->cleanup(this->data); - this->wrapped->destroy(this->wrapped); - free(this); -} - -/** - * Implementation of enumerator_create_cleaner().enumerate - */ -static bool enumerate_cleaner(cleaner_enumerator_t *this, void *v1, void *v2, - void *v3, void *v4, void *v5) -{ - return this->wrapped->enumerate(this->wrapped, v1, v2, v3, v4, v5); -} - -/** - * see header - */ -enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped, - void (*cleanup)(void *data), void *data) -{ - cleaner_enumerator_t *this = malloc_thing(cleaner_enumerator_t); - - this->public.enumerate = (void*)enumerate_cleaner; - this->public.destroy = (void*)destroy_cleaner; - this->wrapped = wrapped; - this->cleanup = cleanup; - this->data = data; - - return &this->public; -} - -/** - * enumerator for single enumerator - */ -typedef struct { - enumerator_t public; - void *item; - void (*cleanup)(void *item); - bool done; -} single_enumerator_t; - -/** - * Implementation of enumerator_create_single().destroy - */ -static void destroy_single(single_enumerator_t *this) -{ - if (this->cleanup) - { - this->cleanup(this->item); - } - free(this); -} - -/** - * Implementation of enumerator_create_single().enumerate - */ -static bool enumerate_single(single_enumerator_t *this, void **item) -{ - if (this->done) - { - return FALSE; - } - *item = this->item; - this->done = TRUE; - return TRUE; -} - -/** - * see header - */ -enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item)) -{ - single_enumerator_t *this = malloc_thing(single_enumerator_t); - - this->public.enumerate = (void*)enumerate_single; - this->public.destroy = (void*)destroy_single; - this->item = item; - this->cleanup = cleanup; - this->done = FALSE; - - return &this->public; -} - diff --git a/src/libstrongswan/utils/enumerator.h b/src/libstrongswan/utils/enumerator.h deleted file mode 100644 index 12b5712ae..000000000 --- a/src/libstrongswan/utils/enumerator.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2007 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 enumerator enumerator - * @{ @ingroup utils - */ - -#ifndef ENUMERATOR_H_ -#define ENUMERATOR_H_ - -typedef struct enumerator_t enumerator_t; - -#include "../utils.h" - -/** - * Enumerator interface, allows enumeration over collections. - */ -struct enumerator_t { - - /** - * Enumerate collection. - * - * The enumerate function takes a variable argument list containing - * pointers where the enumerated values get written. - * - * @param ... variable list of enumerated items, implementation dependent - * @return TRUE if pointers returned - */ - bool (*enumerate)(enumerator_t *this, ...); - - /** - * Destroy a enumerator instance. - */ - void (*destroy)(enumerator_t *this); -}; - -/** - * Create an enumerator which enumerates over nothing - * - * @return an enumerator over no values - */ -enumerator_t* enumerator_create_empty(); - -/** - * Create an enumerator which enumerates over a single item - * - * @param item item to enumerate - * @param cleanup cleanup function called on destroy with the item - * @return an enumerator over a single value - */ -enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item)); - -/** - * Create an enumerator over files/subdirectories in a directory. - * - * This enumerator_t.enumerate() function returns a (to the directory) relative - * filename (as a char*), an absolute filename (as a char*) and a file status - * (to a struct stat), which all may be NULL. "." and ".." entries are - * skipped. Example: - * - * @code - char *rel, *abs; - struct stat st; - enumerator_t *e; - - e = enumerator_create_directory("/tmp"); - if (e) - { - while (e->enumerate(e, &rel, &abs, &st)) - { - if (S_ISDIR(st.st_mode) && *rel != '.') - { - printf("%s\n", abs); - } - } - e->destroy(e); - } - @endcode - * - * @param path path of the directory - * @return the directory enumerator, NULL on failure - */ -enumerator_t* enumerator_create_directory(char *path); - -/** - * Create an enumerator over tokens of a string. - * - * Tokens are separated by one of the characters in sep and trimmed by the - * characters in trim. - * - * @param string string to parse - * @param sep separator characters - * @param trim characters to trim from tokens - * @return enumerator over char* tokens - */ -enumerator_t* enumerator_create_token(char *string, char *sep, char *trim); - -/** - * Creates an enumerator which enumerates over enumerated enumerators :-). - * - * The variable argument list of enumeration values is limit to 5. - * - * @param outer outer enumerator - * @param inner_constructor constructor to inner enumerator - * @param data data to pass to each inner_constructor call - * @param destroy_data destructor to pass to data - * @return the nested enumerator - */ -enumerator_t *enumerator_create_nested(enumerator_t *outer, - enumerator_t *(*inner_constructor)(void *outer, void *data), - void *data, void (*destroy_data)(void *data)); - -/** - * Creates an enumerator which filters output of another enumerator. - * - * The filter function receives the user supplied "data" followed by a - * unfiltered enumeration item, followed by an output pointer where to write - * the filtered data. Then the next input/output pair follows. - * It returns TRUE to deliver the - * values to the caller of enumerate(), FALSE to filter this enumeration. - * - * The variable argument list of enumeration values is limit to 5. - * - * @param unfiltered unfiltered enumerator to wrap, gets destroyed - * @param filter filter function - * @param data user data to supply to filter - * @param destructor destructor function to clean up data after use - * @return the filtered enumerator - */ -enumerator_t *enumerator_create_filter(enumerator_t *unfiltered, - bool (*filter)(void *data, ...), - void *data, void (*destructor)(void *data)); - -/** - * Create an enumerator wrapper which does a cleanup on destroy. - * - * @param wrapped wrapped enumerator - * @param cleanup cleanup function called on destroy - * @param data user data to supply to cleanup - * @return the enumerator with cleanup - */ -enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped, - void (*cleanup)(void *data), void *data); - -#endif /** ENUMERATOR_H_ @}*/ diff --git a/src/libstrongswan/utils/hashtable.c b/src/libstrongswan/utils/hashtable.c deleted file mode 100644 index 33f645170..000000000 --- a/src/libstrongswan/utils/hashtable.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (C) 2008-2011 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 "hashtable.h" - -/** The maximum capacity of the hash table (MUST be a power of 2) */ -#define MAX_CAPACITY (1 << 30) - -typedef struct pair_t pair_t; - -/** - * This pair holds a pointer to the key and value it represents. - */ -struct pair_t { - /** - * Key of a hash table item. - */ - void *key; - - /** - * Value of a hash table item. - */ - void *value; - - /** - * Cached hash (used in case of a resize). - */ - u_int hash; - - /** - * Next pair in an overflow list. - */ - pair_t *next; -}; - -/** - * Creates an empty pair object. - */ -static inline pair_t *pair_create(void *key, void *value, u_int hash) -{ - pair_t *this; - - INIT(this, - .key = key, - .value = value, - .hash = hash, - ); - - return this; -} - -typedef struct private_hashtable_t private_hashtable_t; - -/** - * Private data of a hashtable_t object. - * - */ -struct private_hashtable_t { - /** - * Public part of hash table. - */ - hashtable_t public; - - /** - * The number of items in the hash table. - */ - u_int count; - - /** - * The current capacity of the hash table (always a power of 2). - */ - u_int capacity; - - /** - * The current mask to calculate the row index (capacity - 1). - */ - u_int mask; - - /** - * The load factor. - */ - float load_factor; - - /** - * The actual table. - */ - pair_t **table; - - /** - * The hashing function. - */ - hashtable_hash_t hash; - - /** - * The equality function. - */ - hashtable_equals_t equals; -}; - -typedef struct private_enumerator_t private_enumerator_t; - -/** - * hash table enumerator implementation - */ -struct private_enumerator_t { - - /** - * implements enumerator interface - */ - enumerator_t enumerator; - - /** - * associated hash table - */ - private_hashtable_t *table; - - /** - * current row index - */ - u_int row; - - /** - * number of remaining items in hashtable - */ - u_int count; - - /** - * current pair - */ - pair_t *current; - - /** - * previous pair (used by remove_at) - */ - pair_t *prev; - -}; - -/** - * This function returns the next-highest power of two for the given number. - * The algorithm works by setting all bits on the right-hand side of the most - * significant 1 to 1 and then increments the whole number so it rolls over - * to the nearest power of two. Note: returns 0 for n == 0 - */ -static u_int get_nearest_powerof2(u_int n) -{ - u_int i; - - --n; - for (i = 1; i < sizeof(u_int) * 8; i <<= 1) - { - n |= n >> i; - } - return ++n; -} - -/** - * Init hash table parameters - */ -static void init_hashtable(private_hashtable_t *this, u_int capacity) -{ - capacity = max(1, min(capacity, MAX_CAPACITY)); - this->capacity = get_nearest_powerof2(capacity); - this->mask = this->capacity - 1; - this->load_factor = 0.75; - - this->table = calloc(this->capacity, sizeof(pair_t*)); -} - -/** - * Double the size of the hash table and rehash all the elements. - */ -static void rehash(private_hashtable_t *this) -{ - pair_t **old_table; - u_int row, old_capacity; - - if (this->capacity >= MAX_CAPACITY) - { - return; - } - - old_capacity = this->capacity; - old_table = this->table; - - init_hashtable(this, old_capacity << 1); - - for (row = 0; row < old_capacity; row++) - { - pair_t *pair, *next; - u_int new_row; - - pair = old_table[row]; - while (pair) - { /* insert pair at the front of new bucket*/ - next = pair->next; - new_row = pair->hash & this->mask; - pair->next = this->table[new_row]; - this->table[new_row] = pair; - pair = next; - } - } - free(old_table); -} - -METHOD(hashtable_t, put, void*, - private_hashtable_t *this, void *key, void *value) -{ - void *old_value = NULL; - pair_t *pair; - u_int hash, row; - - hash = this->hash(key); - row = hash & this->mask; - pair = this->table[row]; - while (pair) - { /* search existing bucket for key */ - if (this->equals(key, pair->key)) - { - old_value = pair->value; - pair->value = value; - pair->key = key; - break; - } - pair = pair->next; - } - if (!pair) - { /* insert at the front of bucket */ - pair = pair_create(key, value, hash); - pair->next = this->table[row]; - this->table[row] = pair; - this->count++; - } - if (this->count >= this->capacity * this->load_factor) - { - rehash(this); - } - return old_value; -} - -METHOD(hashtable_t, get, void*, - private_hashtable_t *this, void *key) -{ - void *value = NULL; - pair_t *pair; - - pair = this->table[this->hash(key) & this->mask]; - while (pair) - { - if (this->equals(key, pair->key)) - { - value = pair->value; - break; - } - pair = pair->next; - } - return value; -} - -METHOD(hashtable_t, remove_, void*, - private_hashtable_t *this, void *key) -{ - void *value = NULL; - pair_t *pair, *prev = NULL; - u_int row; - - row = this->hash(key) & this->mask; - pair = this->table[row]; - while (pair) - { - if (this->equals(key, pair->key)) - { - if (prev) - { - prev->next = pair->next; - } - else - { - this->table[row] = pair->next; - } - value = pair->value; - this->count--; - free(pair); - break; - } - prev = pair; - pair = pair->next; - } - return value; -} - -METHOD(hashtable_t, remove_at, void, - private_hashtable_t *this, private_enumerator_t *enumerator) -{ - if (enumerator->table == this && enumerator->current) - { - pair_t *current = enumerator->current; - if (enumerator->prev) - { - enumerator->prev->next = current->next; - } - else - { - this->table[enumerator->row] = current->next; - } - enumerator->current = enumerator->prev; - free(current); - this->count--; - } -} - -METHOD(hashtable_t, get_count, u_int, - private_hashtable_t *this) -{ - return this->count; -} - -METHOD(enumerator_t, enumerate, bool, - private_enumerator_t *this, void **key, void **value) -{ - while (this->count && this->row < this->table->capacity) - { - this->prev = this->current; - if (this->current) - { - this->current = this->current->next; - } - else - { - this->current = this->table->table[this->row]; - } - if (this->current) - { - if (key) - { - *key = this->current->key; - } - if (value) - { - *value = this->current->value; - } - this->count--; - return TRUE; - } - this->row++; - } - return FALSE; -} - -METHOD(hashtable_t, create_enumerator, enumerator_t*, - private_hashtable_t *this) -{ - private_enumerator_t *enumerator; - - INIT(enumerator, - .enumerator = { - .enumerate = (void*)_enumerate, - .destroy = (void*)free, - }, - .table = this, - .count = this->count, - ); - - return &enumerator->enumerator; -} - -METHOD(hashtable_t, destroy, void, - private_hashtable_t *this) -{ - pair_t *pair, *next; - u_int row; - - for (row = 0; row < this->capacity; row++) - { - pair = this->table[row]; - while (pair) - { - next = pair->next; - free(pair); - pair = next; - } - } - free(this->table); - free(this); -} - -/* - * Described in header. - */ -hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals, - u_int capacity) -{ - private_hashtable_t *this; - - INIT(this, - .public = { - .put = _put, - .get = _get, - .remove = _remove_, - .remove_at = (void*)_remove_at, - .get_count = _get_count, - .create_enumerator = _create_enumerator, - .destroy = _destroy, - }, - .hash = hash, - .equals = equals, - ); - - init_hashtable(this, capacity); - - return &this->public; -} - diff --git a/src/libstrongswan/utils/hashtable.h b/src/libstrongswan/utils/hashtable.h deleted file mode 100644 index 27aca9b68..000000000 --- a/src/libstrongswan/utils/hashtable.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2008-2010 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 hashtable hashtable - * @{ @ingroup utils - */ - -#ifndef HASHTABLE_H_ -#define HASHTABLE_H_ - -#include <utils/enumerator.h> - -typedef struct hashtable_t hashtable_t; - -/** - * Prototype for a function that computes the hash code from the given key. - * - * @param key key to hash - * @return hash code - */ -typedef u_int (*hashtable_hash_t)(void *key); - -/** - * Prototype for a function that compares the two keys for equality. - * - * @param key first key (the one we are looking for) - * @param other_key second key - * @return TRUE if the keys are equal - */ -typedef bool (*hashtable_equals_t)(void *key, void *other_key); - -/** - * Class implementing a hash table. - * - * General purpose hash table. This hash table is not synchronized. - */ -struct hashtable_t { - - /** - * Create an enumerator over the hash table key/value pairs. - * - * @return enumerator over (void *key, void *value) - */ - enumerator_t *(*create_enumerator) (hashtable_t *this); - - /** - * Adds the given value with the given key to the hash table, if there - * exists no entry with that key. NULL is returned in this case. - * Otherwise the existing value is replaced and the function returns the - * old value. - * - * @param key the key to store - * @param value the value to store - * @return NULL if no item was replaced, the old value otherwise - */ - void *(*put) (hashtable_t *this, void *key, void *value); - - /** - * Returns the value with the given key, if the hash table contains such an - * entry, otherwise NULL is returned. - * - * @param key the key of the requested value - * @return the value, NULL if not found - */ - void *(*get) (hashtable_t *this, void *key); - - /** - * Removes the value with the given key from the hash table and returns the - * removed value (or NULL if no such value existed). - * - * @param key the key of the value to remove - * @return the removed value, NULL if not found - */ - void *(*remove) (hashtable_t *this, void *key); - - /** - * Removes the key and value pair from the hash table at which the given - * enumerator currently points. - * - * @param enumerator enumerator, from create_enumerator - */ - void (*remove_at) (hashtable_t *this, enumerator_t *enumerator); - - /** - * Gets the number of items in the hash table. - * - * @return number of items - */ - u_int (*get_count) (hashtable_t *this); - - /** - * Destroys a hash table object. - */ - void (*destroy) (hashtable_t *this); - -}; - -/** - * Creates an empty hash table object. - * - * @param hash hash function - * @param equals equals function - * @param capacity initial capacity - * @return hashtable_t object. - */ -hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals, - u_int capacity); - -#endif /** HASHTABLE_H_ @}*/ diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c deleted file mode 100644 index d3020a5d0..000000000 --- a/src/libstrongswan/utils/host.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Copyright (C) 2006-2009 Tobias Brunner - * Copyright (C) 2006 Daniel Roethlisberger - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 <sys/socket.h> -#include <netdb.h> -#include <string.h> - -#include "host.h" - -#include <debug.h> - -#define IPV4_LEN 4 -#define IPV6_LEN 16 - -typedef struct private_host_t private_host_t; - -/** - * Private Data of a host object. - */ -struct private_host_t { - /** - * Public data - */ - host_t public; - - /** - * low-lewel structure, which stores the address - */ - union { - /** generic type */ - struct sockaddr address; - /** maximum sockaddr size */ - struct sockaddr_storage address_max; - /** IPv4 address */ - struct sockaddr_in address4; - /** IPv6 address */ - struct sockaddr_in6 address6; - }; - /** - * length of address structure - */ - socklen_t socklen; -}; - - -METHOD(host_t, get_sockaddr, sockaddr_t*, - private_host_t *this) -{ - return &(this->address); -} - -METHOD(host_t, get_sockaddr_len, socklen_t*, - private_host_t *this) -{ - return &(this->socklen); -} - -METHOD(host_t, is_anyaddr, bool, - private_host_t *this) -{ - switch (this->address.sa_family) - { - case AF_INET: - { - u_int8_t zeroes[IPV4_LEN]; - - memset(zeroes, 0, IPV4_LEN); - return memeq(zeroes, &(this->address4.sin_addr.s_addr), IPV4_LEN); - } - case AF_INET6: - { - u_int8_t zeroes[IPV6_LEN]; - - memset(zeroes, 0, IPV6_LEN); - return memeq(zeroes, &(this->address6.sin6_addr.s6_addr), IPV6_LEN); - } - default: - { - return FALSE; - } - } -} - -/** - * Described in header. - */ -int host_printf_hook(char *dst, size_t dstlen, printf_hook_spec_t *spec, - const void *const *args) -{ - private_host_t *this = *((private_host_t**)(args[0])); - char buffer[INET6_ADDRSTRLEN + 16]; - - if (this == NULL) - { - snprintf(buffer, sizeof(buffer), "(null)"); - } - else if (is_anyaddr(this)) - { - snprintf(buffer, sizeof(buffer), "%%any%s", - this->address.sa_family == AF_INET6 ? "6" : ""); - } - else - { - void *address; - u_int16_t port; - int len; - - address = &this->address6.sin6_addr; - port = this->address6.sin6_port; - - switch (this->address.sa_family) - { - case AF_INET: - address = &this->address4.sin_addr; - port = this->address4.sin_port; - /* fall */ - case AF_INET6: - - if (inet_ntop(this->address.sa_family, address, - buffer, sizeof(buffer)) == NULL) - { - snprintf(buffer, sizeof(buffer), - "(address conversion failed)"); - } - else if (spec->hash) - { - len = strlen(buffer); - snprintf(buffer + len, sizeof(buffer) - len, - "[%d]", ntohs(port)); - } - break; - default: - snprintf(buffer, sizeof(buffer), "(family not supported)"); - break; - } - } - if (spec->minus) - { - return print_in_hook(dst, dstlen, "%-*s", spec->width, buffer); - } - return print_in_hook(dst, dstlen, "%*s", spec->width, buffer); -} - -METHOD(host_t, get_address, chunk_t, - private_host_t *this) -{ - chunk_t address = chunk_empty; - - switch (this->address.sa_family) - { - case AF_INET: - { - address.ptr = (char*)&(this->address4.sin_addr.s_addr); - address.len = IPV4_LEN; - return address; - } - case AF_INET6: - { - address.ptr = (char*)&(this->address6.sin6_addr.s6_addr); - address.len = IPV6_LEN; - return address; - } - default: - { - /* return empty chunk */ - return address; - } - } -} - -METHOD(host_t, get_family, int, - private_host_t *this) -{ - return this->address.sa_family; -} - -METHOD(host_t, get_port, u_int16_t, - private_host_t *this) -{ - switch (this->address.sa_family) - { - case AF_INET: - { - return ntohs(this->address4.sin_port); - } - case AF_INET6: - { - return ntohs(this->address6.sin6_port); - } - default: - { - return 0; - } - } -} - -METHOD(host_t, set_port, void, - private_host_t *this, u_int16_t port) -{ - switch (this->address.sa_family) - { - case AF_INET: - { - this->address4.sin_port = htons(port); - break; - } - case AF_INET6: - { - this->address6.sin6_port = htons(port); - break; - } - default: - { - break; - } - } -} - -METHOD(host_t, clone_, host_t*, - private_host_t *this) -{ - private_host_t *new; - - new = malloc_thing(private_host_t); - memcpy(new, this, sizeof(private_host_t)); - - return &new->public; -} - -/** - * Implements host_t.ip_equals - */ -static bool ip_equals(private_host_t *this, private_host_t *other) -{ - if (this->address.sa_family != other->address.sa_family) - { - /* 0.0.0.0 and 0::0 are equal */ - return (is_anyaddr(this) && is_anyaddr(other)); - } - - switch (this->address.sa_family) - { - case AF_INET: - { - return memeq(&this->address4.sin_addr, &other->address4.sin_addr, - sizeof(this->address4.sin_addr)); - } - case AF_INET6: - { - return memeq(&this->address6.sin6_addr, &other->address6.sin6_addr, - sizeof(this->address6.sin6_addr)); - } - default: - break; - } - return FALSE; -} - -/** - * Implements host_t.get_differences - */ -static host_diff_t get_differences(host_t *this, host_t *other) -{ - host_diff_t ret = HOST_DIFF_NONE; - - if (!this->ip_equals(this, other)) - { - ret |= HOST_DIFF_ADDR; - } - - if (this->get_port(this) != other->get_port(other)) - { - ret |= HOST_DIFF_PORT; - } - - return ret; -} - -/** - * Implements host_t.equals - */ -static bool equals(private_host_t *this, private_host_t *other) -{ - if (!ip_equals(this, other)) - { - return FALSE; - } - - switch (this->address.sa_family) - { - case AF_INET: - { - return (this->address4.sin_port == other->address4.sin_port); - } - case AF_INET6: - { - return (this->address6.sin6_port == other->address6.sin6_port); - } - default: - break; - } - return FALSE; -} - -METHOD(host_t, destroy, void, - private_host_t *this) -{ - free(this); -} - -/** - * Creates an empty host_t object - */ -static private_host_t *host_create_empty(void) -{ - private_host_t *this; - - INIT(this, - .public = { - .get_sockaddr = _get_sockaddr, - .get_sockaddr_len = _get_sockaddr_len, - .clone = _clone_, - .get_family = _get_family, - .get_address = _get_address, - .get_port = _get_port, - .set_port = _set_port, - .get_differences = get_differences, - .ip_equals = (bool (*)(host_t *,host_t *))ip_equals, - .equals = (bool (*)(host_t *,host_t *)) equals, - .is_anyaddr = _is_anyaddr, - .destroy = _destroy, - }, - ); - - return this; -} - -/* - * Create a %any host with port - */ -static host_t *host_create_any_port(int family, u_int16_t port) -{ - host_t *this; - - this = host_create_any(family); - this->set_port(this, port); - return this; -} - -/* - * Described in header. - */ -host_t *host_create_from_string(char *string, u_int16_t port) -{ - private_host_t *this; - - if (streq(string, "%any")) - { - return host_create_any_port(AF_INET, port); - } - if (streq(string, "%any6")) - { - return host_create_any_port(AF_INET6, port); - } - - this = host_create_empty(); - if (strchr(string, '.')) - { - this->address.sa_family = AF_INET; - } - else - { - this->address.sa_family = AF_INET6; - } - switch (this->address.sa_family) - { - case AF_INET: - { - if (inet_pton(AF_INET, string, &this->address4.sin_addr) <=0) - { - break; - } - this->address4.sin_port = htons(port); - this->socklen = sizeof(struct sockaddr_in); - return &this->public; - } - case AF_INET6: - { - if (inet_pton(AF_INET6, string, &this->address6.sin6_addr) <=0) - { - break; - } - this->address6.sin6_port = htons(port); - this->socklen = sizeof(struct sockaddr_in6); - return &this->public; - } - default: - { - break; - } - } - free(this); - return NULL; -} - -/* - * Described in header. - */ -host_t *host_create_from_sockaddr(sockaddr_t *sockaddr) -{ - private_host_t *this = host_create_empty(); - - switch (sockaddr->sa_family) - { - case AF_INET: - { - memcpy(&this->address4, sockaddr, sizeof(struct sockaddr_in)); - this->socklen = sizeof(struct sockaddr_in); - return &this->public; - } - case AF_INET6: - { - memcpy(&this->address6, sockaddr, sizeof(struct sockaddr_in6)); - this->socklen = sizeof(struct sockaddr_in6); - return &this->public; - } - default: - break; - } - free(this); - return NULL; -} - -/* - * Described in header. - */ -host_t *host_create_from_dns(char *string, int af, u_int16_t port) -{ - private_host_t *this; - struct addrinfo hints, *result; - int error; - - if (streq(string, "%any")) - { - return host_create_any_port(af ? af : AF_INET, port); - } - if (streq(string, "%any6")) - { - return host_create_any_port(af ? af : AF_INET6, port); - } - if (af == AF_INET && strchr(string, ':')) - { /* do not try to convert v6 addresses for v4 family */ - return NULL; - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = af; - error = getaddrinfo(string, NULL, &hints, &result); - if (error != 0) - { - DBG1(DBG_LIB, "resolving '%s' failed: %s", string, gai_strerror(error)); - return NULL; - } - /* result is a linked list, but we use only the first address */ - this = (private_host_t*)host_create_from_sockaddr(result->ai_addr); - freeaddrinfo(result); - if (this) - { - switch (this->address.sa_family) - { - case AF_INET: - this->address4.sin_port = htons(port); - break; - case AF_INET6: - this->address6.sin6_port = htons(port); - break; - } - return &this->public; - } - return NULL; -} - -/* - * Described in header. - */ -host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port) -{ - private_host_t *this; - - switch (family) - { - case AF_INET: - if (address.len < IPV4_LEN) - { - return NULL; - } - address.len = IPV4_LEN; - break; - case AF_INET6: - if (address.len < IPV6_LEN) - { - return NULL; - } - address.len = IPV6_LEN; - break; - case AF_UNSPEC: - switch (address.len) - { - case IPV4_LEN: - family = AF_INET; - break; - case IPV6_LEN: - family = AF_INET6; - break; - default: - return NULL; - } - break; - default: - return NULL; - } - this = host_create_empty(); - this->address.sa_family = family; - switch (family) - { - case AF_INET: - memcpy(&this->address4.sin_addr.s_addr, address.ptr, address.len); - this->address4.sin_port = htons(port); - this->socklen = sizeof(struct sockaddr_in); - break; - case AF_INET6: - memcpy(&this->address6.sin6_addr.s6_addr, address.ptr, address.len); - this->address6.sin6_port = htons(port); - this->socklen = sizeof(struct sockaddr_in6); - break; - } - return &this->public; -} - -/* - * Described in header. - */ -host_t *host_create_from_subnet(char *string, int *bits) -{ - char *pos, buf[64]; - host_t *net; - - pos = strchr(string, '/'); - if (pos) - { - if (pos - string >= sizeof(buf)) - { - return NULL; - } - strncpy(buf, string, pos - string); - buf[pos - string] = '\0'; - *bits = atoi(pos + 1); - return host_create_from_string(buf, 0); - } - net = host_create_from_string(string, 0); - if (net) - { - if (net->get_family(net) == AF_INET) - { - *bits = 32; - } - else - { - *bits = 128; - } - } - return net; -} - -/* - * Described in header. - */ -host_t *host_create_any(int family) -{ - private_host_t *this = host_create_empty(); - - memset(&this->address_max, 0, sizeof(struct sockaddr_storage)); - this->address.sa_family = family; - - switch (family) - { - case AF_INET: - { - this->socklen = sizeof(struct sockaddr_in); - return &(this->public); - } - case AF_INET6: - { - this->socklen = sizeof(struct sockaddr_in6); - return &this->public; - } - default: - break; - } - free(this); - return NULL; -} diff --git a/src/libstrongswan/utils/host.h b/src/libstrongswan/utils/host.h deleted file mode 100644 index 0a1be6e47..000000000 --- a/src/libstrongswan/utils/host.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2006-2009 Tobias Brunner - * Copyright (C) 2006 Daniel Roethlisberger - * Copyright (C) 2005-2008 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 host host - * @{ @ingroup utils - */ - -#ifndef HOST_H_ -#define HOST_H_ - -typedef enum host_diff_t host_diff_t; -typedef struct host_t host_t; - -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <chunk.h> - -/** - * Differences between two hosts. They differ in - * address, port, or both. - */ -enum host_diff_t { - HOST_DIFF_NONE = 0, - HOST_DIFF_ADDR = 1, - HOST_DIFF_PORT = 2, -}; - -/** - * Representates a Host - * - * Host object, identifies a address:port pair and defines some - * useful functions on it. - */ -struct host_t { - - /** - * Build a clone of this host object. - * - * @return cloned host - */ - host_t *(*clone) (host_t *this); - - /** - * Get a pointer to the internal sockaddr struct. - * - * This is used for sending and receiving via sockets. - * - * @return pointer to the internal sockaddr structure - */ - sockaddr_t *(*get_sockaddr) (host_t *this); - - /** - * Get the length of the sockaddr struct. - * - * Depending on the family, the length of the sockaddr struct - * is different. Use this function to get the length of the sockaddr - * struct returned by get_sock_addr. - * - * This is used for sending and receiving via sockets. - * - * @return length of the sockaddr struct - */ - socklen_t *(*get_sockaddr_len) (host_t *this); - - /** - * Gets the family of the address - * - * @return family - */ - int (*get_family) (host_t *this); - - /** - * Checks if the ip address of host is set to default route. - * - * @return TRUE if host is 0.0.0.0 or 0::0, FALSE otherwise - */ - bool (*is_anyaddr) (host_t *this); - - /** - * Get the address of this host as chunk_t - * - * Returned chunk points to internal data. - * - * @return address string, - */ - chunk_t (*get_address) (host_t *this); - - /** - * Get the port of this host - * - * @return port number - */ - u_int16_t (*get_port) (host_t *this); - - /** - * Set the port of this host - * - * @param port port numer - */ - void (*set_port) (host_t *this, u_int16_t port); - - /** - * Compare the ips of two hosts hosts. - * - * @param other the other to compare - * @return TRUE if addresses are equal. - */ - bool (*ip_equals) (host_t *this, host_t *other); - - /** - * Compare two hosts, with port. - * - * @param other the other to compare - * @return TRUE if addresses and ports are equal. - */ - bool (*equals) (host_t *this, host_t *other); - - /** - * Compare two hosts and return the differences. - * - * @param other the other to compare - * @return differences in a combination of host_diff_t's - */ - host_diff_t (*get_differences) (host_t *this, host_t *other); - - /** - * Destroy this host object. - */ - void (*destroy) (host_t *this); -}; - -/** - * Constructor to create a host_t object from an address string. - * - * @param string string of an address, such as "152.96.193.130" - * @param port port number - * @return host_t, NULL if string not an address. - */ -host_t *host_create_from_string(char *string, u_int16_t port); - -/** - * Constructor to create a host_t from a DNS name. - * - * @param string hostname to resolve - * @param family family to prefer, 0 for first match - * @param port port number - * @return host_t, NULL lookup failed - */ -host_t *host_create_from_dns(char *string, int family, u_int16_t port); - -/** - * Constructor to create a host_t object from an address chunk. - * - * If family is AF_UNSPEC, it is guessed using address.len. - * - * @param family Address family, such as AF_INET or AF_INET6 - * @param address address as chunk_t in network order - * @param port port number - * @return host_t, NULL if family not supported/chunk invalid - */ -host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port); - -/** - * Constructor to create a host_t object from a sockaddr struct - * - * @param sockaddr sockaddr struct which contains family, address and port - * @return host_t, NULL if family not supported - */ -host_t *host_create_from_sockaddr(sockaddr_t *sockaddr); - -/** - * Create a host from a CIDR subnet definition (1.2.3.0/24), return bits. - * - * @param string string to parse - * @param bits gets the number of network bits in CIDR notation - * @return network start address, NULL on error - */ -host_t *host_create_from_subnet(char *string, int *bits); - -/** - * Create a host without an address, a "any" host. - * - * @param family family of the any host - * @return host_t, NULL if family not supported - */ -host_t *host_create_any(int family); - -/** - * printf hook function for host_t. - * - * Arguments are: - * host_t *host - * Use #-modifier to include port number - */ -int host_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, - const void *const *args); - -#endif /** HOST_H_ @}*/ diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 9f0007f78..5df3e5fe2 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2009-2012 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -49,10 +49,10 @@ ENUM_BEGIN(id_type_names, ID_ANY, ID_KEY_ID, "ID_DER_ASN1_DN", "ID_DER_ASN1_GN", "ID_KEY_ID"); -ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_MYID, ID_KEY_ID, +ENUM_NEXT(id_type_names, ID_DER_ASN1_GN_URI, ID_USER_ID, ID_KEY_ID, "ID_DER_ASN1_GN_URI", - "ID_MYID"); -ENUM_END(id_type_names, ID_MYID); + "ID_USER_ID"); +ENUM_END(id_type_names, ID_USER_ID); /** * coding of X.501 distinguished name @@ -277,6 +277,23 @@ METHOD(identification_t, create_part_enumerator, enumerator_t*, } /** + * Print a separator between two RDNs + */ +static inline bool print_separator(char **buf, size_t *len) +{ + int written; + + written = snprintf(*buf, *len, ", "); + if (written < 0 || written >= *len) + { + return FALSE; + } + *buf += written; + *len -= written; + return TRUE; +} + +/** * Print a DN with all its RDN in a buffer to present it to the user */ static void dntoa(chunk_t dn, char *buf, size_t len) @@ -292,8 +309,14 @@ static void dntoa(chunk_t dn, char *buf, size_t len) { empty = FALSE; - oid = asn1_known_oid(oid_data); + /* previous RDN was empty but it wasn't the last one */ + if (finished && !print_separator(&buf, &len)) + { + break; + } + finished = FALSE; + oid = asn1_known_oid(oid_data); if (oid == OID_UNKNOWN) { written = snprintf(buf, len, "%#B=", &oid_data); @@ -310,7 +333,7 @@ static void dntoa(chunk_t dn, char *buf, size_t len) len -= written; chunk_printable(data, &printable, '?'); - written = snprintf(buf, len, "%.*s", printable.len, printable.ptr); + written = snprintf(buf, len, "%.*s", (int)printable.len, printable.ptr); chunk_free(&printable); if (written < 0 || written >= len) { @@ -319,21 +342,19 @@ static void dntoa(chunk_t dn, char *buf, size_t len) buf += written; len -= written; - if (data.ptr + data.len != dn.ptr + dn.len) - { - written = snprintf(buf, len, ", "); - if (written < 0 || written >= len) - { - break; - } - buf += written; - len -= written; + if (!data.ptr) + { /* we can't calculate if we're finished, assume we are */ + finished = TRUE; } - else + else if (data.ptr + data.len == dn.ptr + dn.len) { finished = TRUE; break; } + else if (!print_separator(&buf, &len)) + { + break; + } } if (empty) { @@ -377,7 +398,7 @@ static status_t atodn(char *src, chunk_t *dn) switch (state) { case SEARCH_OID: - if (*src != ' ' && *src != '/' && *src != ',') + if (*src != ' ' && *src != '/' && *src != ',' && *src != '\0') { oid.ptr = src; oid.len = 1; @@ -414,14 +435,22 @@ static status_t atodn(char *src, chunk_t *dn) } break; case SEARCH_NAME: - if (*src != ' ' && *src != '=') + if (*src == ' ' || *src == '=') + { + break; + } + else if (*src != ',' && *src != '/' && *src != '\0') { name.ptr = src; name.len = 1; whitespace = 0; state = READ_NAME; + break; } - break; + name = chunk_empty; + whitespace = 0; + state = READ_NAME; + /* fall-through */ case READ_NAME: if (*src != ',' && *src != '/' && *src != '\0') { @@ -473,6 +502,11 @@ static status_t atodn(char *src, chunk_t *dn) } } while (*src++ != '\0'); + if (state == READ_OID) + { /* unterminated OID */ + status = INVALID_ARG; + } + /* build the distinguished name sequence */ { int i; @@ -485,7 +519,6 @@ static status_t atodn(char *src, chunk_t *dn) free(rdns[i].ptr); } } - if (status != SUCCESS) { free(dn->ptr); @@ -748,8 +781,8 @@ METHOD(identification_t, matches_dn, id_match_t, /** * Described in header. */ -int identification_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, - const void *const *args) +int identification_printf_hook(printf_hook_data_t *data, + printf_hook_spec_t *spec, const void *const *args) { private_identification_t *this = *((private_identification_t**)(args[0])); chunk_t proper; @@ -757,7 +790,7 @@ int identification_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, if (this == NULL) { - return print_in_hook(dst, len, "%*s", spec->width, "(null)"); + return print_in_hook(data, "%*s", spec->width, "(null)"); } switch (this->type) @@ -782,40 +815,38 @@ int identification_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, case ID_FQDN: case ID_RFC822_ADDR: case ID_DER_ASN1_GN_URI: + case ID_USER_ID: chunk_printable(this->encoded, &proper, '?'); - snprintf(buf, sizeof(buf), "%.*s", proper.len, proper.ptr); + snprintf(buf, sizeof(buf), "%.*s", (int)proper.len, proper.ptr); chunk_free(&proper); break; case ID_DER_ASN1_DN: dntoa(this->encoded, buf, sizeof(buf)); break; case ID_DER_ASN1_GN: - snprintf(buf, sizeof(buf), "(ASN.1 general Name"); + snprintf(buf, sizeof(buf), "(ASN.1 general name)"); break; case ID_KEY_ID: if (chunk_printable(this->encoded, NULL, '?') && this->encoded.len != HASH_SIZE_SHA1) { /* fully printable, use ascii version */ - snprintf(buf, sizeof(buf), "%.*s", - this->encoded.len, this->encoded.ptr); + snprintf(buf, sizeof(buf), "%.*s", (int)this->encoded.len, + this->encoded.ptr); } else { /* not printable, hex dump */ snprintf(buf, sizeof(buf), "%#B", &this->encoded); } break; - case ID_MYID: - snprintf(buf, sizeof(buf), "%%myid"); - break; default: snprintf(buf, sizeof(buf), "(unknown ID type: %d)", this->type); break; } if (spec->minus) { - return print_in_hook(dst, len, "%-*s", spec->width, buf); + return print_in_hook(data, "%-*s", spec->width, buf); } - return print_in_hook(dst, len, "%*s", spec->width, buf); + return print_in_hook(data, "%*s", spec->width, buf); } METHOD(identification_t, clone_, identification_t*, @@ -865,6 +896,7 @@ static private_identification_t *identification_create(id_type_t type) break; case ID_FQDN: case ID_RFC822_ADDR: + case ID_USER_ID: this->public.matches = _matches_string; this->public.equals = _equals_strcasecmp; this->public.contains_wildcards = _contains_wildcards_memchr; @@ -908,14 +940,15 @@ identification_t *identification_create_from_string(char *string) else { this = identification_create(ID_KEY_ID); - this->encoded = chunk_clone(chunk_create(string, strlen(string))); + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } else if (strchr(string, '@') == NULL) { - if (streq(string, "%any") - || streq(string, "%any6") + if (streq(string, "") + || streq(string, "%any") + || streq(string, "%any6") || streq(string, "0.0.0.0") || streq(string, "*") || streq(string, "::") @@ -940,11 +973,7 @@ identification_t *identification_create_from_string(char *string) else { /* not IPv4, mostly FQDN */ this = identification_create(ID_FQDN); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } @@ -961,11 +990,7 @@ identification_t *identification_create_from_string(char *string) else { /* not IPv4/6 fallback to KEY_ID */ this = identification_create(ID_KEY_ID); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); } return &this->public; } @@ -975,34 +1000,30 @@ identification_t *identification_create_from_string(char *string) { if (*string == '@') { - if (*(string + 1) == '#') + string++; + if (*string == '#') { this = identification_create(ID_KEY_ID); - string += 2; - this->encoded = chunk_from_hex( - chunk_create(string, strlen(string)), NULL); + this->encoded = chunk_from_hex(chunk_from_str(string + 1), NULL); + return &this->public; + } + else if (*string == '@') + { + this = identification_create(ID_USER_FQDN); + this->encoded = chunk_clone(chunk_from_str(string + 1)); return &this->public; } else { this = identification_create(ID_FQDN); - string += 1; - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_clone(chunk_from_str(string)); return &this->public; } } else { this = identification_create(ID_RFC822_ADDR); - this->encoded.len = strlen(string); - if (this->encoded.len) - { - this->encoded.ptr = strdup(string); - } + this->encoded = chunk_from_str(strdup(string)); return &this->public; } } @@ -1015,9 +1036,16 @@ identification_t * identification_create_from_data(chunk_t data) { char buf[data.len + 1]; - /* use string constructor */ - snprintf(buf, sizeof(buf), "%.*s", data.len, data.ptr); - return identification_create_from_string(buf); + if (is_asn1(data)) + { + return identification_create_from_encoding(ID_DER_ASN1_DN, data); + } + else + { + /* use string constructor */ + snprintf(buf, sizeof(buf), "%.*s", (int)data.len, data.ptr); + return identification_create_from_string(buf); + } } /* @@ -1065,4 +1093,3 @@ identification_t *identification_create_from_sockaddr(sockaddr_t *sockaddr) } } } - diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h index 3978b23f3..e62446879 100644 --- a/src/libstrongswan/utils/identification.h +++ b/src/libstrongswan/utils/identification.h @@ -29,8 +29,8 @@ typedef struct identification_t identification_t; typedef enum id_match_t id_match_t; typedef enum id_part_t id_part_t; -#include <chunk.h> -#include <utils/enumerator.h> +#include <utils/chunk.h> +#include <collections/enumerator.h> /** * Matches returned from identification_t.match @@ -126,14 +126,14 @@ enum id_type_t { ID_KEY_ID = 11, /** - * private type which represents a GeneralName of type URI + * Private ID type which represents a GeneralName of type URI */ ID_DER_ASN1_GN_URI = 201, /** - * Private ID used by the pluto daemon for opportunistic encryption + * Private ID type which represents a user ID */ - ID_MYID = 203, + ID_USER_ID = 202 }; /** @@ -241,7 +241,6 @@ struct identification_t { * no match at all, 1 means a bad match, and 2 a slightly better match. * * @param other the ID containing one or more wildcards - * @param wildcards returns the number of wildcards, may be NULL * @return match value as described above */ id_match_t (*matches) (identification_t *this, identification_t *other); @@ -342,7 +341,7 @@ identification_t * identification_create_from_sockaddr(sockaddr_t *sockaddr); * Arguments are: * identification_t *identification */ -int identification_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec, - const void *const *args); +int identification_printf_hook(printf_hook_data_t *data, + printf_hook_spec_t *spec, const void *const *args); #endif /** IDENTIFICATION_H_ @}*/ diff --git a/src/libstrongswan/utils/integrity_checker.c b/src/libstrongswan/utils/integrity_checker.c new file mode 100644 index 000000000..d59a76232 --- /dev/null +++ b/src/libstrongswan/utils/integrity_checker.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2009 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 "integrity_checker.h" + +#include <dlfcn.h> +#include <link.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "debug.h" +#include "library.h" + +typedef struct private_integrity_checker_t private_integrity_checker_t; + +/** + * Private data of an integrity_checker_t object. + */ +struct private_integrity_checker_t { + + /** + * Public integrity_checker_t interface. + */ + integrity_checker_t public; + + /** + * dlopen handle to checksum library + */ + void *handle; + + /** + * checksum array + */ + integrity_checksum_t *checksums; + + /** + * number of checksums in array + */ + int checksum_count; +}; + +METHOD(integrity_checker_t, build_file, u_int32_t, + private_integrity_checker_t *this, char *file, size_t *len) +{ + u_int32_t checksum; + chunk_t contents; + struct stat sb; + void *addr; + int fd; + + fd = open(file, O_RDONLY); + if (fd == -1) + { + DBG1(DBG_LIB, " opening '%s' failed: %s", file, strerror(errno)); + return 0; + } + + if (fstat(fd, &sb) == -1) + { + DBG1(DBG_LIB, " getting file size of '%s' failed: %s", file, + strerror(errno)); + close(fd); + return 0; + } + + addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + { + DBG1(DBG_LIB, " mapping '%s' failed: %s", file, strerror(errno)); + close(fd); + return 0; + } + + *len = sb.st_size; + contents = chunk_create(addr, sb.st_size); + checksum = chunk_hash_static(contents); + + munmap(addr, sb.st_size); + close(fd); + + return checksum; +} + +/** + * dl_iterate_phdr callback function + */ +static int callback(struct dl_phdr_info *dlpi, size_t size, Dl_info *dli) +{ + /* We are looking for the dlpi_addr matching the address of our dladdr(). + * dl_iterate_phdr() returns such an address for other (unknown) objects + * in very rare cases (e.g. in a chrooted gentoo, but only if + * the checksum_builder is invoked by 'make'). As a workaround, we filter + * objects by dlpi_name; valid objects have a library name. + */ + if (dli->dli_fbase == (void*)dlpi->dlpi_addr && + dlpi->dlpi_name && *dlpi->dlpi_name) + { + int i; + + for (i = 0; i < dlpi->dlpi_phnum; i++) + { + const ElfW(Phdr) *sgmt = &dlpi->dlpi_phdr[i]; + + /* we are interested in the executable LOAD segment */ + if (sgmt->p_type == PT_LOAD && (sgmt->p_flags & PF_X)) + { + /* safe begin of segment in dli_fbase */ + dli->dli_fbase = (void*)sgmt->p_vaddr + dlpi->dlpi_addr; + /* safe end of segment in dli_saddr */ + dli->dli_saddr = dli->dli_fbase + sgmt->p_memsz; + return 1; + } + } + } + return 0; +} + +METHOD(integrity_checker_t, build_segment, u_int32_t, + private_integrity_checker_t *this, void *sym, size_t *len) +{ + chunk_t segment; + Dl_info dli; + + if (dladdr(sym, &dli) == 0) + { + DBG1(DBG_LIB, " unable to locate symbol: %s", dlerror()); + return 0; + } + /* we reuse the Dl_info struct as in/out parameter */ + if (!dl_iterate_phdr((void*)callback, &dli)) + { + DBG1(DBG_LIB, " executable section not found"); + return 0; + } + + segment = chunk_create(dli.dli_fbase, dli.dli_saddr - dli.dli_fbase); + *len = segment.len; + return chunk_hash_static(segment); +} + +/** + * Find a checksum by its name + */ +static integrity_checksum_t *find_checksum(private_integrity_checker_t *this, + char *name) +{ + int i; + + for (i = 0; i < this->checksum_count; i++) + { + if (streq(this->checksums[i].name, name)) + { + return &this->checksums[i]; + } + } + return NULL; +} + +METHOD(integrity_checker_t, check_file, bool, + private_integrity_checker_t *this, char *name, char *file) +{ + integrity_checksum_t *cs; + u_int32_t sum; + size_t len = 0; + + cs = find_checksum(this, name); + if (!cs) + { + DBG1(DBG_LIB, " '%s' file checksum not found", name); + return FALSE; + } + sum = build_file(this, file, &len); + if (!sum) + { + return FALSE; + } + if (cs->file_len != len) + { + DBG1(DBG_LIB, " invalid '%s' file size: %u bytes, expected %u bytes", + name, len, cs->file_len); + return FALSE; + } + if (cs->file != sum) + { + DBG1(DBG_LIB, " invalid '%s' file checksum: %08x, expected %08x", + name, sum, cs->file); + return FALSE; + } + DBG2(DBG_LIB, " valid '%s' file checksum: %08x", name, sum); + return TRUE; +} + +METHOD(integrity_checker_t, check_segment, bool, + private_integrity_checker_t *this, char *name, void *sym) +{ + integrity_checksum_t *cs; + u_int32_t sum; + size_t len = 0; + + cs = find_checksum(this, name); + if (!cs) + { + DBG1(DBG_LIB, " '%s' segment checksum not found", name); + return FALSE; + } + sum = build_segment(this, sym, &len); + if (!sum) + { + return FALSE; + } + if (cs->segment_len != len) + { + DBG1(DBG_LIB, " invalid '%s' segment size: %u bytes," + " expected %u bytes", name, len, cs->segment_len); + return FALSE; + } + if (cs->segment != sum) + { + DBG1(DBG_LIB, " invalid '%s' segment checksum: %08x, expected %08x", + name, sum, cs->segment); + return FALSE; + } + DBG2(DBG_LIB, " valid '%s' segment checksum: %08x", name, sum); + return TRUE; +} + +METHOD(integrity_checker_t, check, bool, + private_integrity_checker_t *this, char *name, void *sym) +{ + Dl_info dli; + + if (dladdr(sym, &dli) == 0) + { + DBG1(DBG_LIB, "unable to locate symbol: %s", dlerror()); + return FALSE; + } + if (!check_file(this, name, (char*)dli.dli_fname)) + { + return FALSE; + } + if (!check_segment(this, name, sym)) + { + return FALSE; + } + return TRUE; +} + +METHOD(integrity_checker_t, destroy, void, + private_integrity_checker_t *this) +{ + if (this->handle) + { + dlclose(this->handle); + } + free(this); +} + +/** + * See header + */ +integrity_checker_t *integrity_checker_create(char *checksum_library) +{ + private_integrity_checker_t *this; + + INIT(this, + .public = { + .check_file = _check_file, + .build_file = _build_file, + .check_segment = _check_segment, + .build_segment = _build_segment, + .check = _check, + .destroy = _destroy, + }, + ); + + if (checksum_library) + { + this->handle = dlopen(checksum_library, RTLD_LAZY); + if (this->handle) + { + int *checksum_count; + + this->checksums = dlsym(this->handle, "checksums"); + checksum_count = dlsym(this->handle, "checksum_count"); + if (this->checksums && checksum_count) + { + this->checksum_count = *checksum_count; + } + else + { + DBG1(DBG_LIB, "checksum library '%s' invalid", + checksum_library); + } + } + else + { + DBG1(DBG_LIB, "loading checksum library '%s' failed", + checksum_library); + } + } + return &this->public; +} + diff --git a/src/libstrongswan/utils/integrity_checker.h b/src/libstrongswan/utils/integrity_checker.h new file mode 100644 index 000000000..afaa114b3 --- /dev/null +++ b/src/libstrongswan/utils/integrity_checker.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 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 integrity_checker integrity_checker + * @{ @ingroup utils + */ + +#ifndef INTEGRITY_CHECKER_H_ +#define INTEGRITY_CHECKER_H_ + +#include "utils.h" + +typedef struct integrity_checker_t integrity_checker_t; +typedef struct integrity_checksum_t integrity_checksum_t; + +/** + * Struct to hold a precalculated checksum, implemented in the checksum library. + */ +struct integrity_checksum_t { + /* name of the checksum */ + char *name; + /* size in bytes of the file on disk */ + size_t file_len; + /* checksum of the file on disk */ + u_int32_t file; + /* size in bytes of executable segment in memory */ + size_t segment_len; + /* checksum of the executable segment in memory */ + u_int32_t segment; +}; + +/** + * Code integrity checker to detect non-malicious file manipulation. + * + * The integrity checker reads the checksums from a separate library + * libchecksum.so to compare the checksums. + */ +struct integrity_checker_t { + + /** + * Check the integrity of a file on disk. + * + * @param name name to lookup checksum + * @param file path to file + * @return TRUE if integrity tested successfully + */ + bool (*check_file)(integrity_checker_t *this, char *name, char *file); + + /** + * Build the integrity checksum of a file on disk. + * + * @param file path to file + * @param len return length in bytes of file + * @return checksum, 0 on error + */ + u_int32_t (*build_file)(integrity_checker_t *this, char *file, size_t *len); + + /** + * Check the integrity of the code segment in memory. + * + * @param name name to lookup checksum + * @param sym a symbol in the segment to check + * @return TRUE if integrity tested successfully + */ + bool (*check_segment)(integrity_checker_t *this, char *name, void *sym); + /** + * Build the integrity checksum of a code segment in memory. + * + * @param sym a symbol in the segment to check + * @param len return length in bytes of code segment in memory + * @return checksum, 0 on error + */ + u_int32_t (*build_segment)(integrity_checker_t *this, void *sym, size_t *len); + + /** + * Check both, on disk file integrity and loaded segment. + * + * @param name name to lookup checksum + * @param sym a symbol to look up library and segment + * @return TRUE if integrity tested successfully + */ + bool (*check)(integrity_checker_t *this, char *name, void *sym); + + /** + * Destroy a integrity_checker_t. + */ + void (*destroy)(integrity_checker_t *this); +}; + +/** + * Create a integrity_checker instance. + * + * @param checksum_library library containing checksums + */ +integrity_checker_t *integrity_checker_create(char *checksum_library); + +#endif /** INTEGRITY_CHECKER_H_ @}*/ diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 0a8789335..ffbc62085 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006-2008 Martin Willi + * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2006-2013 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -14,27 +15,38 @@ */ #define _GNU_SOURCE -#include <sched.h> #include <stddef.h> #include <string.h> #include <stdio.h> -#include <malloc.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <syslog.h> -#include <pthread.h> #include <netdb.h> #include <locale.h> +#include <dlfcn.h> +#include <time.h> +#include <errno.h> + +#ifdef __APPLE__ +#include <sys/mman.h> +#include <malloc/malloc.h> +/* overload some of our types clashing with mach */ +#define host_t strongswan_host_t +#define processor_t strongswan_processor_t +#define thread_t strongswan_thread_t +#endif /* __APPLE__ */ #include "leak_detective.h" #include <library.h> -#include <debug.h> +#include <utils/debug.h> #include <utils/backtrace.h> -#include <utils/hashtable.h> +#include <collections/hashtable.h> +#include <threading/thread_value.h> +#include <threading/spinlock.h> typedef struct private_leak_detective_t private_leak_detective_t; @@ -69,21 +81,6 @@ struct private_leak_detective_t { */ #define MEMORY_ALLOC_PATTERN 0xEE - -static void install_hooks(void); -static void uninstall_hooks(void); -static void *malloc_hook(size_t, const void *); -static void *realloc_hook(void *, size_t, const void *); -static void free_hook(void*, const void *); - -void *(*old_malloc_hook)(size_t, const void *); -void *(*old_realloc_hook)(void *, size_t, const void *); -void (*old_free_hook)(void*, const void *); - -static u_int count_malloc = 0; -static u_int count_free = 0; -static u_int count_realloc = 0; - typedef struct memory_header_t memory_header_t; typedef struct memory_tail_t memory_tail_t; @@ -108,6 +105,11 @@ struct memory_header_t { backtrace_t *backtrace; /** + * Padding to make sizeof(memory_header_t) == 32 + */ + u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0]; + + /** * Number of bytes following after the header */ u_int32_t bytes; @@ -136,50 +138,337 @@ struct memory_tail_t { * the others on it... */ static memory_header_t first_header = { - magic: MEMORY_HEADER_MAGIC, - bytes: 0, - backtrace: NULL, - previous: NULL, - next: NULL + .magic = MEMORY_HEADER_MAGIC, }; /** - * are the hooks currently installed? + * Spinlock to access header linked list */ -static bool installed = FALSE; +static spinlock_t *lock; + +/** + * Is leak detection currently enabled? + */ +static bool enabled = FALSE; + +/** + * Is leak detection disabled for the current thread? + */ +static thread_value_t *thread_disabled; /** * Installs the malloc hooks, enables leak detection */ -static void install_hooks() +static void enable_leak_detective() +{ + enabled = TRUE; +} + +/** + * Uninstalls the malloc hooks, disables leak detection + */ +static void disable_leak_detective() +{ + enabled = FALSE; +} + +/** + * Enable/Disable leak detective for the current thread + * + * @return Previous value + */ +static bool enable_thread(bool enable) +{ + bool before; + + before = thread_disabled->get(thread_disabled) == NULL; + thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE); + return before; +} + +/** + * Add a header to the beginning of the list + */ +static void add_hdr(memory_header_t *hdr) { - if (!installed) + lock->lock(lock); + hdr->next = first_header.next; + if (hdr->next) { - old_malloc_hook = __malloc_hook; - old_realloc_hook = __realloc_hook; - old_free_hook = __free_hook; - __malloc_hook = malloc_hook; - __realloc_hook = realloc_hook; - __free_hook = free_hook; - installed = TRUE; + hdr->next->previous = hdr; } + hdr->previous = &first_header; + first_header.next = hdr; + lock->unlock(lock); } /** - * Uninstalls the malloc hooks, disables leak detection + * Remove a header from the list + */ +static void remove_hdr(memory_header_t *hdr) +{ + lock->lock(lock); + if (hdr->next) + { + hdr->next->previous = hdr->previous; + } + hdr->previous->next = hdr->next; + lock->unlock(lock); +} + +/** + * Check if a header is in the list + */ +static bool has_hdr(memory_header_t *hdr) +{ + memory_header_t *current; + bool found = FALSE; + + lock->lock(lock); + for (current = &first_header; current != NULL; current = current->next) + { + if (current == hdr) + { + found = TRUE; + break; + } + } + lock->unlock(lock); + + return found; +} + +#ifdef __APPLE__ + +/** + * Copy of original default zone, with functions we call in hooks + */ +static malloc_zone_t original; + +/** + * Call original malloc() + */ +static void* real_malloc(size_t size) +{ + return original.malloc(malloc_default_zone(), size); +} + +/** + * Call original free() + */ +static void real_free(void *ptr) +{ + original.free(malloc_default_zone(), ptr); +} + +/** + * Call original realloc() + */ +static void* real_realloc(void *ptr, size_t size) +{ + return original.realloc(malloc_default_zone(), ptr, size); +} + +/** + * Hook definition: static function with _hook suffix, takes additional zone + */ +#define HOOK(ret, name, ...) \ + static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__) + +/** + * forward declaration of hooks + */ +HOOK(void*, malloc, size_t bytes); +HOOK(void*, calloc, size_t nmemb, size_t size); +HOOK(void*, valloc, size_t size); +HOOK(void, free, void *ptr); +HOOK(void*, realloc, void *old, size_t bytes); + +/** + * malloc zone size(), must consider the memory header prepended + */ +HOOK(size_t, size, const void *ptr) +{ + bool before; + size_t size; + + if (enabled) + { + before = enable_thread(FALSE); + if (before) + { + ptr -= sizeof(memory_header_t); + } + } + size = original.size(malloc_default_zone(), ptr); + if (enabled) + { + enable_thread(before); + } + return size; +} + +/** + * Version of malloc zones we currently support + */ +#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */ + +/** + * Hook-in our malloc functions into the default zone + */ +static bool register_hooks() +{ + malloc_zone_t *zone; + void *page; + + zone = malloc_default_zone(); + if (zone->version != MALLOC_ZONE_VERSION) + { + DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)", + zone->version, MALLOC_ZONE_VERSION); + return FALSE; + } + + original = *zone; + + page = (void*)((uintptr_t)zone / getpagesize() * getpagesize()); + if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0) + { + DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno)); + return FALSE; + } + + zone->size = size_hook; + zone->malloc = malloc_hook; + zone->calloc = calloc_hook; + zone->valloc = valloc_hook; + zone->free = free_hook; + zone->realloc = realloc_hook; + + /* those other functions can be NULLed out to not use them */ + zone->batch_malloc = NULL; + zone->batch_free = NULL; + zone->memalign = NULL; + zone->free_definite_size = NULL; + + return TRUE; +} + +#else /* !__APPLE__ */ + +/** + * dlsym() might do a malloc(), but we can't do one before we get the malloc() + * function pointer. Use this minimalistic malloc implementation instead. + */ +static void* malloc_for_dlsym(size_t size) +{ + static char buf[1024] = {}; + static size_t used = 0; + char *ptr; + + /* roundup to a multiple of 32 */ + size = (size - 1) / 32 * 32 + 32; + + if (used + size > sizeof(buf)) + { + return NULL; + } + ptr = buf + used; + used += size; + return ptr; +} + +/** + * Lookup a malloc function, while disabling wrappers + */ +static void* get_malloc_fn(char *name) +{ + bool before = FALSE; + void *fn; + + if (enabled) + { + before = enable_thread(FALSE); + } + fn = dlsym(RTLD_NEXT, name); + if (enabled) + { + enable_thread(before); + } + return fn; +} + +/** + * Call original malloc() + */ +static void* real_malloc(size_t size) +{ + static void* (*fn)(size_t size); + static int recursive = 0; + + if (!fn) + { + /* checking recursiveness should actually be thread-specific. But as + * it is very likely that the first allocation is done before we go + * multi-threaded, we keep it simple. */ + if (recursive) + { + return malloc_for_dlsym(size); + } + recursive++; + fn = get_malloc_fn("malloc"); + recursive--; + } + return fn(size); +} + +/** + * Call original free() + */ +static void real_free(void *ptr) +{ + static void (*fn)(void *ptr); + + if (!fn) + { + fn = get_malloc_fn("free"); + } + return fn(ptr); +} + +/** + * Call original realloc() */ -static void uninstall_hooks() +static void* real_realloc(void *ptr, size_t size) { - if (installed) + static void* (*fn)(void *ptr, size_t size); + + if (!fn) { - __malloc_hook = old_malloc_hook; - __free_hook = old_free_hook; - __realloc_hook = old_realloc_hook; - installed = FALSE; + fn = get_malloc_fn("realloc"); } + return fn(ptr, size); } /** + * Hook definition: plain function overloading existing malloc calls + */ +#define HOOK(ret, name, ...) ret name(__VA_ARGS__) + +/** + * Hook initialization when not using hooks, resolve functions. + */ +static bool register_hooks() +{ + void *buf = real_malloc(8); + real_realloc(buf, 16); + real_free(buf); + return TRUE; +} + +#endif /* !__APPLE__ */ + +/** * Leak report white list * * List of functions using static allocation buffers or should be suppressed @@ -188,17 +477,12 @@ static void uninstall_hooks() char *whitelist[] = { /* backtraces, including own */ "backtrace_create", + "safe_strerror", /* pthread stuff */ "pthread_create", "pthread_setspecific", "__pthread_setspecific", /* glibc functions */ - "mktime", - "ctime", - "__gmtime_r", - "localtime_r", - "tzset", - "time_printf_hook", "inet_ntoa", "strerror", "getprotobyname", @@ -224,6 +508,9 @@ char *whitelist[] = { "getpwent_r", "setpwent", "endpwent", + "getspnam_r", + "getpwuid_r", + "initgroups", /* ignore dlopen, as we do not dlclose to get proper leak reports */ "dlopen", "dlerror", @@ -243,18 +530,16 @@ char *whitelist[] = { "Curl_client_write", /* ClearSilver */ "nerr_init", - /* OpenSSL */ - "RSA_new_method", - "DH_new_method", - "ENGINE_load_builtin_engines", - "OPENSSL_config", - "ecdsa_check", - "ERR_put_error", /* libgcrypt */ "gcry_control", "gcry_check_version", "gcry_randomize", "gcry_create_nonce", + /* OpenSSL: These are needed for unit-tests only, the openssl plugin + * does properly clean up any memory during destroy(). */ + "ECDSA_do_sign_ex", + "ECDSA_verify", + "RSA_new_method", /* NSPR */ "PR_CallOnce", /* libapr */ @@ -273,6 +558,14 @@ char *whitelist[] = { "gnutls_global_init", }; +/** + * Some functions are hard to whitelist, as they don't use a symbol directly. + * Use some static initialization to suppress them on leak reports + */ +static void init_static_allocations() +{ + tzset(); +} /** * Hashtable hash function @@ -305,7 +598,8 @@ static bool equals(backtrace_t *a, backtrace_t *b) * Summarize and print backtraces */ static int print_traces(private_leak_detective_t *this, - FILE *out, int thresh, bool detailed, int *whitelisted) + FILE *out, int thresh, int thresh_count, + bool detailed, int *whitelisted, size_t *sum) { int leaks = 0; memory_header_t *hdr; @@ -319,11 +613,13 @@ static int print_traces(private_leak_detective_t *this, /** number of allocations */ u_int count; } *entry; + bool before; - uninstall_hooks(); + before = enable_thread(FALSE); entries = hashtable_create((hashtable_hash_t)hash, (hashtable_equals_t)equals, 1024); + lock->lock(lock); for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) { if (whitelisted && @@ -342,29 +638,37 @@ static int print_traces(private_leak_detective_t *this, else { INIT(entry, - .backtrace = hdr->backtrace, + .backtrace = hdr->backtrace->clone(hdr->backtrace), .bytes = hdr->bytes, .count = 1, ); - entries->put(entries, hdr->backtrace, entry); + entries->put(entries, entry->backtrace, entry); + } + if (sum) + { + *sum += hdr->bytes; } leaks++; } + lock->unlock(lock); enumerator = entries->create_enumerator(entries); while (enumerator->enumerate(enumerator, NULL, &entry)) { - if (!thresh || entry->bytes >= thresh) + if (out && + (!thresh || entry->bytes >= thresh) && + (!thresh_count || entry->count >= thresh_count)) { fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n", entry->bytes, entry->count, entry->bytes / entry->count); entry->backtrace->log(entry->backtrace, out, detailed); } + entry->backtrace->destroy(entry->backtrace); free(entry); } enumerator->destroy(enumerator); entries->destroy(entries); - install_hooks(); + enable_thread(before); return leaks; } @@ -373,9 +677,10 @@ METHOD(leak_detective_t, report, void, { if (lib->leak_detective) { - int leaks = 0, whitelisted = 0; + int leaks, whitelisted = 0; + size_t sum = 0; - leaks = print_traces(this, stderr, 0, detailed, &whitelisted); + leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum); switch (leaks) { case 0: @@ -385,7 +690,7 @@ METHOD(leak_detective_t, report, void, fprintf(stderr, "One leak detected"); break; default: - fprintf(stderr, "%d leaks detected", leaks); + fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum); break; } fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted); @@ -396,85 +701,115 @@ METHOD(leak_detective_t, report, void, } } +METHOD(leak_detective_t, leaks, int, + private_leak_detective_t *this) +{ + if (lib->leak_detective) + { + int leaks, whitelisted = 0; + + leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL); + return leaks; + } + return 0; +} + +METHOD(leak_detective_t, set_state, bool, + private_leak_detective_t *this, bool enable) +{ + return enable_thread(enable); +} + METHOD(leak_detective_t, usage, void, private_leak_detective_t *this, FILE *out) { - int oldpolicy, thresh; bool detailed; - pthread_t thread_id = pthread_self(); - struct sched_param oldparams, params; + int thresh, thresh_count; + size_t sum = 0; thresh = lib->settings->get_int(lib->settings, "libstrongswan.leak_detective.usage_threshold", 10240); + thresh_count = lib->settings->get_int(lib->settings, + "libstrongswan.leak_detective.usage_threshold_count", 0); detailed = lib->settings->get_bool(lib->settings, "libstrongswan.leak_detective.detailed", TRUE); - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - print_traces(this, out, thresh, detailed, NULL); + print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + fprintf(out, "Total memory usage: %zu\n", sum); } /** - * Hook function for malloc() + * Wrapped malloc() function */ -void *malloc_hook(size_t bytes, const void *caller) +HOOK(void*, malloc, size_t bytes) { memory_header_t *hdr; memory_tail_t *tail; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; + bool before; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); + if (!enabled || thread_disabled->get(thread_disabled)) + { + return real_malloc(bytes); + } - count_malloc++; - uninstall_hooks(); - hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); tail = ((void*)hdr) + bytes + sizeof(memory_header_t); /* set to something which causes crashes */ memset(hdr, MEMORY_ALLOC_PATTERN, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + before = enable_thread(FALSE); + hdr->backtrace = backtrace_create(2); + enable_thread(before); + hdr->magic = MEMORY_HEADER_MAGIC; hdr->bytes = bytes; - hdr->backtrace = backtrace_create(3); tail->magic = MEMORY_TAIL_MAGIC; - install_hooks(); - /* insert at the beginning of the list */ - hdr->next = first_header.next; - if (hdr->next) - { - hdr->next->previous = hdr; - } - hdr->previous = &first_header; - first_header.next = hdr; - - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + add_hdr(hdr); return hdr + 1; } /** - * Hook function for free() + * Wrapped calloc() function + */ +HOOK(void*, calloc, size_t nmemb, size_t size) +{ + void *ptr; + + size *= nmemb; + ptr = malloc(size); + memset(ptr, 0, size); + + return ptr; +} + +/** + * Wrapped valloc(), TODO: currently not supported */ -void free_hook(void *ptr, const void *caller) +HOOK(void*, valloc, size_t size) { - memory_header_t *hdr, *current; + DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing"); + return NULL; +} + +/** + * Wrapped free() function + */ +HOOK(void, free, void *ptr) +{ + memory_header_t *hdr; memory_tail_t *tail; backtrace_t *backtrace; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; - bool found = FALSE; + bool before; + if (!enabled || thread_disabled->get(thread_disabled)) + { + real_free(ptr); + return; + } /* allow freeing of NULL */ if (ptr == NULL) { @@ -483,25 +818,11 @@ void free_hook(void *ptr, const void *caller) hdr = ptr - sizeof(memory_header_t); tail = ptr + hdr->bytes; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); - - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - count_free++; - uninstall_hooks(); + before = enable_thread(FALSE); if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { - for (current = &first_header; current != NULL; current = current->next) - { - if (current == hdr) - { - found = TRUE; - break; - } - } - if (found) + if (has_hdr(hdr)) { /* memory was allocated by our hooks but is corrupted */ fprintf(stderr, "freeing corrupted memory (%p): " @@ -511,100 +832,96 @@ void free_hook(void *ptr, const void *caller) else { /* memory was not allocated by our hooks */ - fprintf(stderr, "freeing invalid memory (%p)", ptr); + fprintf(stderr, "freeing invalid memory (%p)\n", ptr); } - backtrace = backtrace_create(3); + backtrace = backtrace_create(2); backtrace->log(backtrace, stderr, TRUE); backtrace->destroy(backtrace); } else { - /* remove item from list */ - if (hdr->next) - { - hdr->next->previous = hdr->previous; - } - hdr->previous->next = hdr->next; + remove_hdr(hdr); + hdr->backtrace->destroy(hdr->backtrace); /* clear MAGIC, set mem to something remarkable */ memset(hdr, MEMORY_FREE_PATTERN, sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t)); - free(hdr); + real_free(hdr); } - - install_hooks(); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); + enable_thread(before); } /** - * Hook function for realloc() + * Wrapped realloc() function */ -void *realloc_hook(void *old, size_t bytes, const void *caller) +HOOK(void*, realloc, void *old, size_t bytes) { memory_header_t *hdr; memory_tail_t *tail; backtrace_t *backtrace; - pthread_t thread_id = pthread_self(); - int oldpolicy; - struct sched_param oldparams, params; + bool before; + if (!enabled || thread_disabled->get(thread_disabled)) + { + return real_realloc(old, bytes); + } /* allow reallocation of NULL */ if (old == NULL) { - return malloc_hook(bytes, caller); + return malloc(bytes); + } + /* handle zero size as a free() */ + if (bytes == 0) + { + free(old); + return NULL; } hdr = old - sizeof(memory_header_t); tail = old + hdr->bytes; - pthread_getschedparam(thread_id, &oldpolicy, &oldparams); + remove_hdr(hdr); - params.__sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(thread_id, SCHED_FIFO, ¶ms); - - count_realloc++; - uninstall_hooks(); if (hdr->magic != MEMORY_HEADER_MAGIC || tail->magic != MEMORY_TAIL_MAGIC) { - fprintf(stderr, "reallocating invalid memory (%p): " - "header magic 0x%x, tail magic 0x%x:\n", - old, hdr->magic, tail->magic); - backtrace = backtrace_create(3); + fprintf(stderr, "reallocating invalid memory (%p):\n" + "header magic 0x%x:\n", old, hdr->magic); + backtrace = backtrace_create(2); backtrace->log(backtrace, stderr, TRUE); backtrace->destroy(backtrace); } - /* clear tail magic, allocate, set tail magic */ - memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); - hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); + else + { + /* clear tail magic, allocate, set tail magic */ + memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); + } + hdr = real_realloc(hdr, + sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); tail = ((void*)hdr) + bytes + sizeof(memory_header_t); tail->magic = MEMORY_TAIL_MAGIC; /* update statistics */ hdr->bytes = bytes; + + before = enable_thread(FALSE); hdr->backtrace->destroy(hdr->backtrace); - hdr->backtrace = backtrace_create(3); + hdr->backtrace = backtrace_create(2); + enable_thread(before); + + add_hdr(hdr); - /* update header of linked list neighbours */ - if (hdr->next) - { - hdr->next->previous = hdr; - } - hdr->previous->next = hdr; - install_hooks(); - pthread_setschedparam(thread_id, oldpolicy, &oldparams); return hdr + 1; } METHOD(leak_detective_t, destroy, void, private_leak_detective_t *this) { - if (installed) - { - uninstall_hooks(); - } + disable_leak_detective(); + lock->destroy(lock); + thread_disabled->destroy(thread_disabled); free(this); } @@ -618,25 +935,24 @@ leak_detective_t *leak_detective_create() INIT(this, .public = { .report = _report, + .leaks = _leaks, .usage = _usage, + .set_state = _set_state, .destroy = _destroy, }, ); - if (getenv("LEAK_DETECTIVE_DISABLE") == NULL) - { - cpu_set_t mask; + lock = spinlock_create(); + thread_disabled = thread_value_create(NULL); - CPU_ZERO(&mask); - CPU_SET(0, &mask); + init_static_allocations(); - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0) + if (getenv("LEAK_DETECTIVE_DISABLE") == NULL) + { + if (register_hooks()) { - fprintf(stderr, "setting CPU affinity failed: %m"); + enable_leak_detective(); } - - install_hooks(); } return &this->public; } - diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h index 8c80d2532..7a29e81d7 100644 --- a/src/libstrongswan/utils/leak_detective.h +++ b/src/libstrongswan/utils/leak_detective.h @@ -43,6 +43,13 @@ struct leak_detective_t { void (*report)(leak_detective_t *this, bool detailed); /** + * Number of detected leaks. + * + * @return number of leaks + */ + int (*leaks)(leak_detective_t *this); + + /** * Report current memory usage to out. * * @param out target to write usage report to @@ -50,6 +57,14 @@ struct leak_detective_t { void (*usage)(leak_detective_t *this, FILE *out); /** + * Enable/disable leak detective hooks for the current thread. + * + * @param TRUE to enable, FALSE to disable + * @return state active before calling set_state + */ + bool (*set_state)(leak_detective_t *this, bool enabled); + + /** * Destroy a leak_detective instance. */ void (*destroy)(leak_detective_t *this); @@ -61,4 +76,3 @@ struct leak_detective_t { leak_detective_t *leak_detective_create(); #endif /** LEAK_DETECTIVE_H_ @}*/ - diff --git a/src/libstrongswan/utils/linked_list.c b/src/libstrongswan/utils/linked_list.c deleted file mode 100644 index 59d416f2f..000000000 --- a/src/libstrongswan/utils/linked_list.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (C) 2007-2011 Tobias Brunner - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 <stdlib.h> - -#include "linked_list.h" - -typedef struct element_t element_t; - -/** - * This element holds a pointer to the value it represents. - */ -struct element_t { - - /** - * Value of a list item. - */ - void *value; - - /** - * Previous list element. - * - * NULL if first element in list. - */ - element_t *previous; - - /** - * Next list element. - * - * NULL if last element in list. - */ - element_t *next; -}; - -/** - * Creates an empty linked list object. - */ -element_t *element_create(void *value) -{ - element_t *this; - INIT(this, - .value = value, - ); - return this; -} - - -typedef struct private_linked_list_t private_linked_list_t; - -/** - * Private data of a linked_list_t object. - * - */ -struct private_linked_list_t { - /** - * Public part of linked list. - */ - linked_list_t public; - - /** - * Number of items in the list. - */ - int count; - - /** - * First element in list. - * NULL if no elements in list. - */ - element_t *first; - - /** - * Last element in list. - * NULL if no elements in list. - */ - element_t *last; -}; - -typedef struct private_enumerator_t private_enumerator_t; - -/** - * linked lists enumerator implementation - */ -struct private_enumerator_t { - - /** - * implements enumerator interface - */ - enumerator_t enumerator; - - /** - * associated linked list - */ - private_linked_list_t *list; - - /** - * current item - */ - element_t *current; - - /** - * enumerator has enumerated all items - */ - bool finished; -}; - -METHOD(enumerator_t, enumerate, bool, - private_enumerator_t *this, void **item) -{ - if (this->finished) - { - return FALSE; - } - if (!this->current) - { - this->current = this->list->first; - } - else - { - this->current = this->current->next; - } - if (!this->current) - { - this->finished = TRUE; - return FALSE; - } - *item = this->current->value; - return TRUE; -} - -METHOD(linked_list_t, create_enumerator, enumerator_t*, - private_linked_list_t *this) -{ - private_enumerator_t *enumerator; - - INIT(enumerator, - .enumerator = { - .enumerate = (void*)_enumerate, - .destroy = (void*)free, - }, - .list = this, - ); - - return &enumerator->enumerator; -} - -METHOD(linked_list_t, reset_enumerator, void, - private_linked_list_t *this, private_enumerator_t *enumerator) -{ - enumerator->current = NULL; - enumerator->finished = FALSE; -} - -METHOD(linked_list_t, has_more, bool, - private_linked_list_t *this, private_enumerator_t *enumerator) -{ - if (enumerator->current) - { - return enumerator->current->next != NULL; - } - return !enumerator->finished && this->first != NULL; -} - -METHOD(linked_list_t, get_count, int, - private_linked_list_t *this) -{ - return this->count; -} - -METHOD(linked_list_t, insert_first, void, - private_linked_list_t *this, void *item) -{ - element_t *element; - - element = element_create(item); - if (this->count == 0) - { - /* first entry in list */ - this->first = element; - this->last = element; - } - else - { - element->next = this->first; - this->first->previous = element; - this->first = element; - } - this->count++; -} - -/** - * unlink an element form the list, returns following element - */ -static element_t* remove_element(private_linked_list_t *this, - element_t *element) -{ - element_t *next, *previous; - - next = element->next; - previous = element->previous; - free(element); - if (next) - { - next->previous = previous; - } - else - { - this->last = previous; - } - if (previous) - { - previous->next = next; - } - else - { - this->first = next; - } - if (--this->count == 0) - { - this->first = NULL; - this->last = NULL; - } - return next; -} - -METHOD(linked_list_t, get_first, status_t, - private_linked_list_t *this, void **item) -{ - if (this->count == 0) - { - return NOT_FOUND; - } - *item = this->first->value; - return SUCCESS; -} - -METHOD(linked_list_t, remove_first, status_t, - private_linked_list_t *this, void **item) -{ - if (get_first(this, item) == SUCCESS) - { - remove_element(this, this->first); - return SUCCESS; - } - return NOT_FOUND; -} - -METHOD(linked_list_t, insert_last, void, - private_linked_list_t *this, void *item) -{ - element_t *element; - - element = element_create(item); - if (this->count == 0) - { - /* first entry in list */ - this->first = element; - this->last = element; - } - else - { - element->previous = this->last; - this->last->next = element; - this->last = element; - } - this->count++; -} - -METHOD(linked_list_t, insert_before, void, - private_linked_list_t *this, private_enumerator_t *enumerator, - void *item) -{ - element_t *current, *element; - - current = enumerator->current; - if (!current) - { - if (enumerator->finished) - { - this->public.insert_last(&this->public, item); - } - else - { - this->public.insert_first(&this->public, item); - } - return; - } - element = element_create(item); - if (current->previous) - { - current->previous->next = element; - element->previous = current->previous; - current->previous = element; - element->next = current; - } - else - { - current->previous = element; - element->next = current; - this->first = element; - } - this->count++; -} - -METHOD(linked_list_t, replace, void*, - private_linked_list_t *this, private_enumerator_t *enumerator, - void *item) -{ - void *old = NULL; - - if (enumerator->current) - { - old = enumerator->current->value; - enumerator->current->value = item; - } - return old; -} - -METHOD(linked_list_t, get_last, status_t, - private_linked_list_t *this, void **item) -{ - if (this->count == 0) - { - return NOT_FOUND; - } - *item = this->last->value; - return SUCCESS; -} - -METHOD(linked_list_t, remove_last, status_t, - private_linked_list_t *this, void **item) -{ - if (get_last(this, item) == SUCCESS) - { - remove_element(this, this->last); - return SUCCESS; - } - return NOT_FOUND; -} - -METHOD(linked_list_t, remove_, int, - private_linked_list_t *this, void *item, bool (*compare)(void*,void*)) -{ - element_t *current = this->first; - int removed = 0; - - while (current) - { - if ((compare && compare(current->value, item)) || - (!compare && current->value == item)) - { - removed++; - current = remove_element(this, current); - } - else - { - current = current->next; - } - } - return removed; -} - -METHOD(linked_list_t, remove_at, void, - private_linked_list_t *this, private_enumerator_t *enumerator) -{ - element_t *current; - - if (enumerator->current) - { - current = enumerator->current; - enumerator->current = current->previous; - remove_element(this, current); - } -} - -METHOD(linked_list_t, find_first, status_t, - private_linked_list_t *this, linked_list_match_t match, - void **item, void *d1, void *d2, void *d3, void *d4, void *d5) -{ - element_t *current = this->first; - - while (current) - { - if ((match && match(current->value, d1, d2, d3, d4, d5)) || - (!match && item && current->value == *item)) - { - if (item != NULL) - { - *item = current->value; - } - return SUCCESS; - } - current = current->next; - } - return NOT_FOUND; -} - -METHOD(linked_list_t, find_last, status_t, - private_linked_list_t *this, linked_list_match_t match, - void **item, void *d1, void *d2, void *d3, void *d4, void *d5) -{ - element_t *current = this->last; - - while (current) - { - if ((match && match(current->value, d1, d2, d3, d4, d5)) || - (!match && item && current->value == *item)) - { - if (item != NULL) - { - *item = current->value; - } - return SUCCESS; - } - current = current->previous; - } - return NOT_FOUND; -} - -METHOD(linked_list_t, invoke_offset, void, - private_linked_list_t *this, size_t offset, - void *d1, void *d2, void *d3, void *d4, void *d5) -{ - element_t *current = this->first; - linked_list_invoke_t *method; - - while (current) - { - method = current->value + offset; - (*method)(current->value, d1, d2, d3, d4, d5); - current = current->next; - } -} - -METHOD(linked_list_t, invoke_function, void, - private_linked_list_t *this, linked_list_invoke_t fn, - void *d1, void *d2, void *d3, void *d4, void *d5) -{ - element_t *current = this->first; - - while (current) - { - fn(current->value, d1, d2, d3, d4, d5); - current = current->next; - } -} - -METHOD(linked_list_t, clone_offset, linked_list_t*, - private_linked_list_t *this, size_t offset) -{ - element_t *current = this->first; - linked_list_t *clone; - - clone = linked_list_create(); - while (current) - { - void* (**method)(void*) = current->value + offset; - clone->insert_last(clone, (*method)(current->value)); - current = current->next; - } - - return clone; -} - -METHOD(linked_list_t, clone_function, linked_list_t*, - private_linked_list_t *this, void* (*fn)(void*)) -{ - element_t *current = this->first; - linked_list_t *clone; - - clone = linked_list_create(); - while (current) - { - clone->insert_last(clone, fn(current->value)); - current = current->next; - } - return clone; -} - -METHOD(linked_list_t, destroy, void, - private_linked_list_t *this) -{ - void *value; - - /* Remove all list items before destroying list */ - while (remove_first(this, &value) == SUCCESS) - { - /* values are not destroyed so memory leaks are possible - * if list is not empty when deleting */ - } - free(this); -} - -METHOD(linked_list_t, destroy_offset, void, - private_linked_list_t *this, size_t offset) -{ - element_t *current = this->first, *next; - - while (current) - { - void (**method)(void*) = current->value + offset; - (*method)(current->value); - next = current->next; - free(current); - current = next; - } - free(this); -} - -METHOD(linked_list_t, destroy_function, void, - private_linked_list_t *this, void (*fn)(void*)) -{ - element_t *current = this->first, *next; - - while (current) - { - fn(current->value); - next = current->next; - free(current); - current = next; - } - free(this); -} - -/* - * Described in header. - */ -linked_list_t *linked_list_create() -{ - private_linked_list_t *this; - - INIT(this, - .public = { - .get_count = _get_count, - .create_enumerator = _create_enumerator, - .reset_enumerator = (void*)_reset_enumerator, - .has_more = (void*)_has_more, - .get_first = _get_first, - .get_last = _get_last, - .find_first = (void*)_find_first, - .find_last = (void*)_find_last, - .insert_first = _insert_first, - .insert_last = _insert_last, - .insert_before = (void*)_insert_before, - .replace = (void*)_replace, - .remove_first = _remove_first, - .remove_last = _remove_last, - .remove = _remove_, - .remove_at = (void*)_remove_at, - .invoke_offset = (void*)_invoke_offset, - .invoke_function = (void*)_invoke_function, - .clone_offset = _clone_offset, - .clone_function = _clone_function, - .destroy = _destroy, - .destroy_offset = _destroy_offset, - .destroy_function = _destroy_function, - }, - ); - - return &this->public; -} diff --git a/src/libstrongswan/utils/linked_list.h b/src/libstrongswan/utils/linked_list.h deleted file mode 100644 index 293ca8661..000000000 --- a/src/libstrongswan/utils/linked_list.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2007-2011 Tobias Brunner - * Copyright (C) 2005-2008 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 linked_list linked_list - * @{ @ingroup utils - */ - -#ifndef LINKED_LIST_H_ -#define LINKED_LIST_H_ - -typedef struct linked_list_t linked_list_t; - -#include <utils/enumerator.h> - -/** - * Method to match elements in a linked list (used in find_* functions) - * - * @param item current list item - * @param ... user supplied data (only pointers, at most 5) - * @return - * - TRUE, if the item matched - * - FALSE, otherwise - */ -typedef bool (*linked_list_match_t)(void *item, ...); - -/** - * Method to be invoked on elements in a linked list (used in invoke_* functions) - * - * @param item current list item - * @param ... user supplied data (only pointers, at most 5) - */ -typedef void (*linked_list_invoke_t)(void *item, ...); - -/** - * Class implementing a double linked list. - * - * General purpose linked list. This list is not synchronized. - */ -struct linked_list_t { - - /** - * Gets the count of items in the list. - * - * @return number of items in list - */ - int (*get_count) (linked_list_t *this); - - /** - * Create an enumerator over the list. - * - * @note The enumerator's position is invalid before the first call - * to enumerate(). - * - * @return enumerator over list items - */ - enumerator_t* (*create_enumerator)(linked_list_t *this); - - /** - * Resets the enumerator's current position to the beginning of the list. - * - * @param enumerator enumerator to reset - */ - void (*reset_enumerator)(linked_list_t *this, enumerator_t *enumerator); - - /** - * Checks if there are more elements following after the enumerator's - * current position. - * - * @param enumerator enumerator to check - */ - bool (*has_more)(linked_list_t *this, enumerator_t *enumerator); - - /** - * Inserts a new item at the beginning of the list. - * - * @param item item value to insert in list - */ - void (*insert_first) (linked_list_t *this, void *item); - - /** - * Removes the first item in the list and returns its value. - * - * @param item returned value of first item, or NULL - * @return SUCCESS, or NOT_FOUND if list is empty - */ - status_t (*remove_first) (linked_list_t *this, void **item); - - /** - * Inserts a new item before the item the enumerator currently points to. - * - * If this method is called before starting the enumeration the item is - * inserted first. If it is called after all items have been enumerated - * the item is inserted last. This is helpful when inserting items into - * a sorted list. - * - * @note The position of the enumerator is not changed. - * - * @param enumerator enumerator with position - * @param item item value to insert in list - */ - void (*insert_before)(linked_list_t *this, enumerator_t *enumerator, - void *item); - - /** - * Replaces the item the enumerator currently points to with the given item. - * - * @param enumerator enumerator with position - * @param item item value to replace current item with - * @return current item or NULL if the enumerator is at an - * invalid position - */ - void *(*replace)(linked_list_t *this, enumerator_t *enumerator, void *item); - - /** - * Remove an item from the list where the enumerator points to. - * - * @param enumerator enumerator with position - */ - void (*remove_at)(linked_list_t *this, enumerator_t *enumerator); - - /** - * Remove items from the list matching the given item. - * - * If a compare function is given, it is called for each item, with the - * first parameter being the current list item and the second parameter - * being the supplied item. Return TRUE from the compare function to remove - * the item, return FALSE to keep it in the list. - * - * If compare is NULL, comparison is done by pointers. - * - * @param item item to remove/pass to comparator - * @param compare compare function, or NULL - * @return number of removed items - */ - int (*remove)(linked_list_t *this, void *item, bool (*compare)(void*,void*)); - - /** - * Returns the value of the first list item without removing it. - * - * @param item returned value of first item - * @return SUCCESS, NOT_FOUND if list is empty - */ - status_t (*get_first) (linked_list_t *this, void **item); - - /** - * Inserts a new item at the end of the list. - * - * @param item value to insert into list - */ - void (*insert_last) (linked_list_t *this, void *item); - - /** - * Removes the last item in the list and returns its value. - * - * @param item returned value of last item, or NULL - * @return SUCCESS, NOT_FOUND if list is empty - */ - status_t (*remove_last) (linked_list_t *this, void **item); - - /** - * Returns the value of the last list item without removing it. - * - * @param item returned value of last item - * @return SUCCESS, NOT_FOUND if list is empty - */ - status_t (*get_last) (linked_list_t *this, void **item); - - /** Find the first matching element in the list. - * - * The first object passed to the match function is the current list item, - * followed by the user supplied data. - * If the supplied function returns TRUE this function returns SUCCESS, and - * the current object is returned in the third parameter, otherwise, - * the next item is checked. - * - * If match is NULL, *item and the current object are compared. - * - * @warning Only use pointers as user supplied data. - * - * @param match comparison function to call on each object, or NULL - * @param item the list item, if found - * @param ... user data to supply to match function (limited to 5 arguments) - * @return SUCCESS if found, NOT_FOUND otherwise - */ - status_t (*find_first) (linked_list_t *this, linked_list_match_t match, - void **item, ...); - - /** Find the last matching element in the list. - * - * The first object passed to the match function is the current list item, - * followed by the user supplied data. - * If the supplied function returns TRUE this function returns SUCCESS, and - * the current object is returned in the third parameter, otherwise, - * the next item is checked. - * - * If match is NULL, *item and the current object are compared. - * - * @warning Only use pointers as user supplied data. - * - * @param match comparison function to call on each object, or NULL - * @param item the list item, if found - * @param ... user data to supply to match function (limited to 5 arguments) - * @return SUCCESS if found, NOT_FOUND otherwise - */ - status_t (*find_last) (linked_list_t *this, linked_list_match_t match, - void **item, ...); - - /** - * Invoke a method on all of the contained objects. - * - * If a linked list contains objects with function pointers, - * invoke() can call a method on each of the objects. The - * method is specified by an offset of the function pointer, - * which can be evalutated at compile time using the offsetof - * macro, e.g.: list->invoke(list, offsetof(object_t, method)); - * - * @warning Only use pointers as user supplied data. - * - * @param offset offset of the method to invoke on objects - * @param ... user data to supply to called function (limited to 5 arguments) - */ - void (*invoke_offset) (linked_list_t *this, size_t offset, ...); - - /** - * Invoke a function on all of the contained objects. - * - * @warning Only use pointers as user supplied data. - * - * @param function offset of the method to invoke on objects - * @param ... user data to supply to called function (limited to 5 arguments) - */ - void (*invoke_function) (linked_list_t *this, linked_list_invoke_t function, ...); - - /** - * Clones a list and its objects using the objects' clone method. - * - * @param offset offset ot the objects clone function - * @return cloned list - */ - linked_list_t *(*clone_offset) (linked_list_t *this, size_t offset); - - /** - * Clones a list and its objects using a given function. - * - * @param function function that clones an object - * @return cloned list - */ - linked_list_t *(*clone_function) (linked_list_t *this, void*(*)(void*)); - - /** - * Destroys a linked_list object. - */ - void (*destroy) (linked_list_t *this); - - /** - * Destroys a list and its objects using the destructor. - * - * If a linked list and the contained objects should be destroyed, use - * destroy_offset. The supplied offset specifies the destructor to - * call on each object. The offset may be calculated using the offsetof - * macro, e.g.: list->destroy_offset(list, offsetof(object_t, destroy)); - * - * @param offset offset of the objects destructor - */ - void (*destroy_offset) (linked_list_t *this, size_t offset); - - /** - * Destroys a list and its contents using a a cleanup function. - * - * If a linked list and its contents should get destroyed using a specific - * cleanup function, use destroy_function. This is useful when the - * list contains malloc()-ed blocks which should get freed, - * e.g.: list->destroy_function(list, free); - * - * @param function function to call on each object - */ - void (*destroy_function) (linked_list_t *this, void (*)(void*)); -}; - -/** - * Creates an empty linked list object. - * - * @return linked_list_t object. - */ -linked_list_t *linked_list_create(void); - -#endif /** LINKED_LIST_H_ @}*/ diff --git a/src/libstrongswan/utils/optionsfrom.c b/src/libstrongswan/utils/optionsfrom.c index 5fd4cfd4d..117071351 100644 --- a/src/libstrongswan/utils/optionsfrom.c +++ b/src/libstrongswan/utils/optionsfrom.c @@ -2,22 +2,22 @@ * Copyright (C) 2007-2008 Andreas Steffen * Hochschule fuer Technik Rapperswil * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Library 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/lgpl.txt>. + * 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 library is distributed in the hope that it will be useful, but + * 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 Library General Public - * License for more details. + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. */ #include <stdio.h> #include <errno.h> #include <library.h> -#include <debug.h> +#include <utils/debug.h> #include <utils/lexparser.h> #include "optionsfrom.h" diff --git a/src/libstrongswan/utils/printf_hook.c b/src/libstrongswan/utils/printf_hook.c new file mode 100644 index 000000000..f030f45c8 --- /dev/null +++ b/src/libstrongswan/utils/printf_hook.c @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2006-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. + */ + +#include "printf_hook.h" + +#include "utils.h" +#include "debug.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +typedef struct private_printf_hook_t private_printf_hook_t; +typedef struct printf_hook_handler_t printf_hook_handler_t; + +#define PRINTF_BUF_LEN 8192 +#define ARGS_MAX 3 + +/** + * private data of printf_hook + */ +struct private_printf_hook_t { + + /** + * public functions + */ + printf_hook_t public; +}; + +/** + * struct with information about a registered handler + */ +struct printf_hook_handler_t { + + /** + * callback function + */ + printf_hook_function_t hook; + + /** + * number of arguments + */ + int numargs; + + /** + * types of the arguments + */ + int argtypes[ARGS_MAX]; + +#ifdef USE_VSTR + /** + * name required for Vstr + */ + char *name; +#endif +}; + +/* A-Z | 6 other chars | a-z */ +#define NUM_HANDLERS 58 +static printf_hook_handler_t *printf_hooks[NUM_HANDLERS]; + +#define SPEC_TO_INDEX(spec) ((int)(spec) - (int)'A') +#define IS_VALID_SPEC(spec) (SPEC_TO_INDEX(spec) > -1 && SPEC_TO_INDEX(spec) < NUM_HANDLERS) + +#if !defined(USE_VSTR) && \ + (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER)) + +/** + * Printf hook print function. This is actually of type "printf_function", + * however glibc does it typedef to function, but uclibc to a pointer. + * So we redefine it here. + */ +static int custom_print(FILE *stream, const struct printf_info *info, + const void *const *args) +{ + printf_hook_spec_t spec; + printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; + printf_hook_data_t data = { + .stream = stream, + }; + + spec.hash = info->alt; + spec.plus = info->showsign; + spec.minus = info->left; + spec.width = info->width; + + return handler->hook(&data, &spec, args); +} + +/** + * Printf hook arginfo function, which is actually of type + * "printf_arginfo_[size_]function". + */ +static int custom_arginfo(const struct printf_info *info, size_t n, int *argtypes +#ifdef HAVE_PRINTF_SPECIFIER + , int *size +#endif + ) +{ + int i; + printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(info->spec)]; + + if (handler->numargs <= n) + { + for (i = 0; i < handler->numargs; ++i) + { + argtypes[i] = handler->argtypes[i]; + } + } + /* we never set "size", as we have no user defined types */ + return handler->numargs; +} + +#else + +#include <errno.h> +#include <unistd.h> /* for STDOUT_FILENO */ + +/** + * These are used below, whenever the public wrapper functions are called before + * initialization or after destruction. + */ +#undef vprintf +#undef vfprintf +#undef vsnprintf + +/** + * Vstr custom format specifier callback function. + */ +static int custom_fmt_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *fmt_spec) +{ + int i; + const void *args[ARGS_MAX]; + printf_hook_spec_t spec; + printf_hook_handler_t *handler = printf_hooks[SPEC_TO_INDEX(fmt_spec->name[0])]; + printf_hook_data_t data = { + .base = base, + .pos = pos, + }; + + for (i = 0; i < handler->numargs; i++) + { + switch(handler->argtypes[i]) + { + case PRINTF_HOOK_ARGTYPE_INT: + args[i] = VSTR_FMT_CB_ARG_PTR(fmt_spec, i); + break; + case PRINTF_HOOK_ARGTYPE_POINTER: + args[i] = &VSTR_FMT_CB_ARG_PTR(fmt_spec, i); + break; + } + } + + spec.hash = fmt_spec->fmt_hash; + spec.plus = fmt_spec->fmt_plus; + spec.minus = fmt_spec->fmt_minus; + spec.width = fmt_spec->fmt_field_width; + + handler->hook(&data, &spec, args); + return 1; +} + +/** + * Add a custom format handler to the given Vstr_conf object + */ +static void vstr_fmt_add_handler(Vstr_conf *conf, printf_hook_handler_t *handler) +{ + int *at = handler->argtypes; + switch(handler->numargs) + { + case 1: + vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], + VSTR_TYPE_FMT_END); + break; + case 2: + vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], + at[1], VSTR_TYPE_FMT_END); + break; + case 3: + vstr_fmt_add(conf, handler->name, custom_fmt_cb, at[0], + at[1], at[2], VSTR_TYPE_FMT_END); + break; + } +} + +/** + * Management of thread-specific Vstr_conf objects + */ +#include <threading/thread_value.h> + +static thread_value_t *vstr_conf = NULL; + +static Vstr_conf *create_vstr_conf() +{ + int i; + Vstr_conf *conf = vstr_make_conf(); + vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '%'); + vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_TYPE_GRPALLOC_CACHE, + VSTR_TYPE_CNTL_CONF_GRPALLOC_CSTR); + vstr_cntl_conf(conf, VSTR_CNTL_CONF_SET_NUM_BUF_SZ, PRINTF_BUF_LEN); + for (i = 0; i < NUM_HANDLERS; ++i) + { + printf_hook_handler_t *handler = printf_hooks[i]; + if (handler) + { + vstr_fmt_add_handler(conf, handler); + } + } + return conf; +} + +static inline Vstr_conf *get_vstr_conf() +{ + Vstr_conf *conf = NULL; + if (vstr_conf) + { + conf = (Vstr_conf*)vstr_conf->get(vstr_conf); + if (!conf) + { + conf = create_vstr_conf(); + vstr_conf->set(vstr_conf, conf); + } + } + return conf; +} + +/** + * Described in header + */ +size_t vstr_print_in_hook(struct Vstr_base *base, size_t pos, const char *fmt, + ...) +{ + va_list args; + int written; + + va_start(args, fmt); + written = vstr_add_vfmt(base, pos, fmt, args); + va_end(args); + return written; +} + +/** + * Wrapper functions for printf and alike + */ +int vstr_wrapper_printf(const char *format, ...) +{ + int written; + va_list args; + va_start(args, format); + written = vstr_wrapper_vprintf(format, args); + va_end(args); + return written; +} +int vstr_wrapper_fprintf(FILE *stream, const char *format, ...) +{ + int written; + va_list args; + va_start(args, format); + written = vstr_wrapper_vfprintf(stream, format, args); + va_end(args); + return written; +} +int vstr_wrapper_sprintf(char *str, const char *format, ...) +{ + int written; + va_list args; + va_start(args, format); + written = vstr_wrapper_vsprintf(str, format, args); + va_end(args); + return written; +} +int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...) +{ + int written; + va_list args; + va_start(args, format); + written = vstr_wrapper_vsnprintf(str, size, format, args); + va_end(args); + return written; +} +int vstr_wrapper_asprintf(char **str, const char *format, ...) +{ + int written; + va_list args; + va_start(args, format); + written = vstr_wrapper_vasprintf(str, format, args); + va_end(args); + return written; +} +static inline int vstr_wrapper_vprintf_internal(Vstr_conf *conf, int fd, + const char *format, + va_list args) +{ + int written; + Vstr_base *s = vstr_make_base(conf); + vstr_add_vfmt(s, 0, format, args); + written = s->len; + while (s->len) + { + if (!vstr_sc_write_fd(s, 1, s->len, fd, NULL)) + { + if (errno != EAGAIN && errno != EINTR) + { + written -= s->len; + break; + } + } + } + vstr_free_base(s); + return written; +} +int vstr_wrapper_vprintf(const char *format, va_list args) +{ + Vstr_conf *conf = get_vstr_conf(); + if (conf) + { + return vstr_wrapper_vprintf_internal(conf, STDOUT_FILENO, format, args); + } + return vprintf(format, args); +} +int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list args) +{ + Vstr_conf *conf = get_vstr_conf(); + if (conf) + { + return vstr_wrapper_vprintf_internal(conf, fileno(stream), format, + args); + } + return vfprintf(stream, format, args); +} +static inline int vstr_wrapper_vsnprintf_internal(char *str, size_t size, + const char *format, + va_list args) +{ + Vstr_conf *conf = get_vstr_conf(); + if (conf) + { + int written; + Vstr_base *s = vstr_make_base(conf); + vstr_add_vfmt(s, 0, format, args); + written = s->len; + vstr_export_cstr_buf(s, 1, s->len, str, (size > 0) ? size : s->len + 1); + vstr_free_base(s); + return written; + } + return vsnprintf(str, size, format, args); +} +int vstr_wrapper_vsprintf(char *str, const char *format, va_list args) +{ + return vstr_wrapper_vsnprintf_internal(str, 0, format, args); +} +int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format, + va_list args) +{ + return (size > 0) ? vstr_wrapper_vsnprintf_internal(str, size, format, args) : 0; +} +int vstr_wrapper_vasprintf(char **str, const char *format, va_list args) +{ + size_t len = 100; + int written; + *str = malloc(len); + while (TRUE) + { + va_list ac; + va_copy(ac, args); + written = vstr_wrapper_vsnprintf_internal(*str, len, format, ac); + va_end(ac); + if (written < len) + { + break; + } + len = written + 1; + *str = realloc(*str, len); + } + return written; +} +#endif + +METHOD(printf_hook_t, add_handler, void, + private_printf_hook_t *this, char spec, + printf_hook_function_t hook, ...) +{ + int i = -1; + printf_hook_handler_t *handler; + printf_hook_argtype_t argtype; + va_list args; + + if (!IS_VALID_SPEC(spec)) + { + DBG1(DBG_LIB, "'%c' is not a valid printf hook specifier, " + "not registered!", spec); + return; + } + + handler = malloc_thing(printf_hook_handler_t); + handler->hook = hook; + + va_start(args, hook); + while ((argtype = va_arg(args, printf_hook_argtype_t)) != PRINTF_HOOK_ARGTYPE_END) + { + if (++i >= ARGS_MAX) + { + DBG1(DBG_LIB, "Too many arguments for printf hook with " + "specifier '%c', not registered!", spec); + va_end(args); + free(handler); + return; + } + handler->argtypes[i] = argtype; + } + va_end(args); + + handler->numargs = i + 1; + + if (handler->numargs > 0) + { +#if !defined(USE_VSTR) && \ + (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER)) +# ifdef HAVE_PRINTF_SPECIFIER + register_printf_specifier(spec, custom_print, custom_arginfo); +# else + register_printf_function(spec, custom_print, custom_arginfo); +# endif +#else + Vstr_conf *conf = get_vstr_conf(); + handler->name = malloc(2); + handler->name[0] = spec; + handler->name[1] = '\0'; + vstr_fmt_add_handler(conf, handler); +#endif + printf_hooks[SPEC_TO_INDEX(spec)] = handler; + } + else + { + free(handler); + } +} + +METHOD(printf_hook_t, destroy, void, + private_printf_hook_t *this) +{ + int i; +#ifdef USE_VSTR + Vstr_conf *conf = get_vstr_conf(); +#endif + + for (i = 0; i < NUM_HANDLERS; ++i) + { + printf_hook_handler_t *handler = printf_hooks[i]; + if (handler) + { +#ifdef USE_VSTR + vstr_fmt_del(conf, handler->name); + free(handler->name); +#endif + free(handler); + } + } + +#ifdef USE_VSTR + /* freeing the Vstr_conf of the main thread */ + vstr_conf->destroy(vstr_conf); + vstr_conf = NULL; + vstr_exit(); +#endif + free(this); +} + +/* + * see header file + */ +printf_hook_t *printf_hook_create() +{ + private_printf_hook_t *this; + + INIT(this, + .public = { + .add_handler = _add_handler, + .destroy = _destroy, + }, + ); + + memset(printf_hooks, 0, sizeof(printf_hooks)); + +#ifdef USE_VSTR + if (!vstr_init()) + { + DBG1(DBG_LIB, "failed to initialize Vstr library!"); + free(this); + return NULL; + } + vstr_conf = thread_value_create((thread_cleanup_t)vstr_free_conf); +#endif + + return &this->public; +} + diff --git a/src/libstrongswan/utils/printf_hook.h b/src/libstrongswan/utils/printf_hook.h new file mode 100644 index 000000000..1425910be --- /dev/null +++ b/src/libstrongswan/utils/printf_hook.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2006-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 printf_hook printf_hook + * @{ @ingroup utils + */ + +#ifndef PRINTF_HOOK_H_ +#define PRINTF_HOOK_H_ + +typedef struct printf_hook_t printf_hook_t; +typedef struct printf_hook_spec_t printf_hook_spec_t; +typedef struct printf_hook_data_t printf_hook_data_t; +typedef enum printf_hook_argtype_t printf_hook_argtype_t; + +#if !defined(USE_VSTR) && \ + !defined(HAVE_PRINTF_FUNCTION) && \ + !defined(HAVE_PRINTF_SPECIFIER) +/* assume newer glibc register_printf_specifier if none given */ +#define HAVE_PRINTF_SPECIFIER +#endif + +#if !defined(USE_VSTR) && \ + (defined(HAVE_PRINTF_FUNCTION) || defined(HAVE_PRINTF_SPECIFIER)) + +#include <stdio.h> +#include <printf.h> + +enum printf_hook_argtype_t { + PRINTF_HOOK_ARGTYPE_END = -1, + PRINTF_HOOK_ARGTYPE_INT = PA_INT, + PRINTF_HOOK_ARGTYPE_POINTER = PA_POINTER, +}; + +/** + * Data to pass to a printf hook. + */ +struct printf_hook_data_t { + + /** + * Output FILE stream + */ + FILE *stream;; +}; + +/** + * Helper macro to be used in printf hook callbacks. + */ +#define print_in_hook(data, fmt, ...) ({\ + ssize_t _written = fprintf(data->stream, fmt, ##__VA_ARGS__);\ + if (_written < 0)\ + {\ + _written = 0;\ + }\ + _written;\ +}) + +#else + +#include <vstr.h> + +enum printf_hook_argtype_t { + PRINTF_HOOK_ARGTYPE_END = VSTR_TYPE_FMT_END, + PRINTF_HOOK_ARGTYPE_INT = VSTR_TYPE_FMT_INT, + PRINTF_HOOK_ARGTYPE_POINTER = VSTR_TYPE_FMT_PTR_VOID, +}; + +/** + * Redefining printf and alike + */ +#include <stdio.h> +#include <stdarg.h> + +int vstr_wrapper_printf(const char *format, ...); +int vstr_wrapper_fprintf(FILE *stream, const char *format, ...); +int vstr_wrapper_sprintf(char *str, const char *format, ...); +int vstr_wrapper_snprintf(char *str, size_t size, const char *format, ...); +int vstr_wrapper_asprintf(char **str, const char *format, ...); + +int vstr_wrapper_vprintf(const char *format, va_list ap); +int vstr_wrapper_vfprintf(FILE *stream, const char *format, va_list ap); +int vstr_wrapper_vsprintf(char *str, const char *format, va_list ap); +int vstr_wrapper_vsnprintf(char *str, size_t size, const char *format, va_list ap); +int vstr_wrapper_vasprintf(char **str, const char *format, va_list ap); + +#ifdef printf +#undef printf +#endif +#ifdef fprintf +#undef fprintf +#endif +#ifdef sprintf +#undef sprintf +#endif +#ifdef snprintf +#undef snprintf +#endif +#ifdef asprintf +#undef asprintf +#endif +#ifdef vprintf +#undef vprintf +#endif +#ifdef vfprintf +#undef vfprintf +#endif +#ifdef vsprintf +#undef vsprintf +#endif +#ifdef vsnprintf +#undef vsnprintf +#endif +#ifdef vasprintf +#undef vasprintf +#endif + +#define printf vstr_wrapper_printf +#define fprintf vstr_wrapper_fprintf +#define sprintf vstr_wrapper_sprintf +#define snprintf vstr_wrapper_snprintf +#define asprintf vstr_wrapper_asprintf + +#define vprintf vstr_wrapper_vprintf +#define vfprintf vstr_wrapper_vfprintf +#define vsprintf vstr_wrapper_vsprintf +#define vsnprintf vstr_wrapper_vsnprintf +#define vasprintf vstr_wrapper_vasprintf + +/** + * Data to pass to a printf hook. + */ +struct printf_hook_data_t { + + /** + * Base to append printf to + */ + Vstr_base *base; + + /** + * Position in base to write to + */ + size_t pos; +}; + +/** + * Wrapper around vstr_add_vfmt(), avoids having to link all users of + * print_in_hook() against libvstr. + * + * @param base Vstr_string to add string to + * @param pos position to write to + * @param fmt format string + * @param ... arguments + * @return number of characters written + */ +size_t vstr_print_in_hook(struct Vstr_base *base, size_t pos, const char *fmt, + ...); + +/** + * Helper macro to be used in printf hook callbacks. + */ +#define print_in_hook(data, fmt, ...) ({\ + size_t _written; \ + _written = vstr_print_in_hook(data->base, data->pos, fmt, ##__VA_ARGS__);\ + data->pos += _written;\ + _written;\ +}) + +#endif + +/** + * Callback function type for printf hooks. + * + * @param data hook data, to pass to print_in_hook() + * @param spec format specifier + * @param args arguments array + * @return number of characters written + */ +typedef int (*printf_hook_function_t)(printf_hook_data_t *data, + printf_hook_spec_t *spec, + const void *const *args); + +/** + * Properties of the format specifier + */ +struct printf_hook_spec_t { + /** + * TRUE if a '#' was used in the format specifier + */ + int hash; + + /** + * TRUE if a '-' was used in the format specifier + */ + int minus; + + /** + * TRUE if a '+' was used in the format specifier + */ + int plus; + + /** + * The width as given in the format specifier. + */ + int width; +}; + +/** + * Printf handler management. + */ +struct printf_hook_t { + + /** + * Register a printf handler. + * + * @param spec printf hook format character + * @param hook hook function + * @param ... list of PRINTF_HOOK_ARGTYPE_*, MUST end with PRINTF_HOOK_ARGTYPE_END + */ + void (*add_handler)(printf_hook_t *this, char spec, + printf_hook_function_t hook, ...); + + /** + * Destroy a printf_hook instance. + */ + void (*destroy)(printf_hook_t *this); +}; + +/** + * Create a printf_hook instance. + */ +printf_hook_t *printf_hook_create(); + +#endif /** PRINTF_HOOK_H_ @}*/ diff --git a/src/libstrongswan/utils/settings.c b/src/libstrongswan/utils/settings.c new file mode 100644 index 000000000..809ca10ab --- /dev/null +++ b/src/libstrongswan/utils/settings.c @@ -0,0 +1,1248 @@ +/* + * 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. + */ + +#define _GNU_SOURCE +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <libgen.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/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; + + /** + * subsections, as section_t + */ + linked_list_t *sections; + + /** + * key value pairs, as kv_t + */ + linked_list_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), + .sections = linked_list_create(), + .kv = linked_list_create(), + ); + return this; +} + +/** + * destroy a section + */ +static void section_destroy(section_t *this) +{ + this->kv->destroy_function(this->kv, (void*)kv_destroy); + this->sections->destroy_function(this->sections, (void*)section_destroy); + free(this->name); + free(this); +} + +/** + * Purge contents of a section + */ +static void section_purge(section_t *this) +{ + this->kv->destroy_function(this->kv, (void*)kv_destroy); + this->kv = linked_list_create(); + this->sections->destroy_function(this->sections, (void*)section_destroy); + this->sections = linked_list_create(); +} + +/** + * callback to find a section by name + */ +static bool section_find(section_t *this, char *name) +{ + return streq(this->name, name); +} + +/** + * callback to find a kv pair by key + */ +static bool kv_find(kv_t *this, char *key) +{ + return streq(this->key, 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; + bool res; + char *pos; + + va_copy(copy, args); + while (start < key) + { + pos = strchr(start, '%'); + if (!pos) + { + start += strlen(start) + 1; + continue; + } + 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; + } + start = pos; + if (*start) + { + start++; + } + } + 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 (section->sections->find_first(section->sections, + (linked_list_match_t)section_find, + (void**)&found, buf) != SUCCESS) + { + if (ensure) + { + found = section_create(buf); + section->sections->insert_last(section->sections, found); + } + } + if (found && pos) + { + return find_section_buffered(found, start, pos, args, buf, len, ensure); + } + return found; +} + +/** + * Find a section by a given key (thread-safe). + */ +static section_t *find_section(private_settings_t *this, section_t *section, + char *key, va_list args) +{ + char buf[128], keybuf[512]; + section_t *found; + + if (snprintf(keybuf, sizeof(keybuf), "%s", key) >= sizeof(keybuf)) + { + return NULL; + } + this->lock->read_lock(this->lock); + found = find_section_buffered(section, keybuf, keybuf, args, buf, + sizeof(buf), FALSE); + this->lock->unlock(this->lock); + return found; +} + +/** + * Ensure that the section with the given key exists (thread-safe). + */ +static section_t *ensure_section(private_settings_t *this, section_t *section, + 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 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. + */ +static kv_t *find_value_buffered(section_t *section, char *start, char *key, + va_list args, char *buf, int len, bool ensure) +{ + char *pos; + kv_t *kv = NULL; + 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 (section->sections->find_first(section->sections, + (linked_list_match_t)section_find, + (void**)&found, buf) != SUCCESS) + { + if (!ensure) + { + return NULL; + } + found = section_create(buf); + section->sections->insert_last(section->sections, found); + } + return find_value_buffered(found, start, pos, args, buf, len, + ensure); + } + else + { + if (!print_key(buf, len, start, key, args)) + { + return NULL; + } + if (section->kv->find_first(section->kv, (linked_list_match_t)kv_find, + (void**)&kv, buf) != SUCCESS) + { + if (ensure) + { + kv = kv_create(buf, NULL); + section->kv->insert_last(section->kv, kv); + } + } + } + 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; +} + +/** + * Enumerate section names, not sections + */ +static bool section_filter(void *null, section_t **in, char **out) +{ + *out = (*in)->name; + return TRUE; +} + +METHOD(settings_t, create_section_enumerator, enumerator_t*, + private_settings_t *this, char *key, ...) +{ + section_t *section; + va_list args; + + va_start(args, key); + section = find_section(this, this->top, key, args); + va_end(args); + + if (!section) + { + return enumerator_create_empty(); + } + this->lock->read_lock(this->lock); + return enumerator_create_filter( + section->sections->create_enumerator(section->sections), + (void*)section_filter, this->lock, (void*)this->lock->unlock); +} + +/** + * Enumerate key and values, not kv_t entries + */ +static bool kv_filter(void *null, kv_t **in, char **key, + void *none, char **value) +{ + *key = (*in)->key; + *value = (*in)->value; + return TRUE; +} + +METHOD(settings_t, create_key_value_enumerator, enumerator_t*, + private_settings_t *this, char *key, ...) +{ + section_t *section; + va_list args; + + va_start(args, key); + section = find_section(this, this->top, key, args); + va_end(args); + + if (!section) + { + return enumerator_create_empty(); + } + this->lock->read_lock(this->lock); + return enumerator_create_filter( + section->kv->create_enumerator(section->kv), + (void*)kv_filter, this->lock, (void*)this->lock->unlock); +} + +/** + * 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 (section->sections->find_first(section->sections, + (linked_list_match_t)section_find, + (void**)&sub, key) != SUCCESS) + { + sub = section_create(key); + if (parse_section(contents, file, level, &inner, sub)) + { + section->sections->insert_last(section->sections, + sub); + 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 (section->kv->find_first(section->kv, + (linked_list_match_t)kv_find, + (void**)&kv, key) != SUCCESS) + { + kv = kv_create(key, value); + section->kv->insert_last(section->kv, kv); + } + 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) + { + DBG2(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 + 1); + text[len] = '\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)) + { + DBG2(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 = strdup(file); + dir = dirname(dir); + 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) + { + DBG2(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 = extension->sections->create_enumerator(extension->sections); + while (enumerator->enumerate(enumerator, (void**)&sec)) + { + section_t *found; + if (base->sections->find_first(base->sections, + (linked_list_match_t)section_find, (void**)&found, + sec->name) == SUCCESS) + { + section_extend(found, sec); + } + else + { + extension->sections->remove_at(extension->sections, enumerator); + base->sections->insert_last(base->sections, sec); + } + } + enumerator->destroy(enumerator); + + enumerator = extension->kv->create_enumerator(extension->kv); + while (enumerator->enumerate(enumerator, (void**)&kv)) + { + kv_t *found; + if (base->kv->find_first(base->kv, (linked_list_match_t)kv_find, + (void**)&found, kv->key) == SUCCESS) + { + found->value = kv->value; + } + else + { + extension->kv->remove_at(extension->kv, enumerator); + base->kv->insert_last(base->kv, kv); + } + } + 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, + .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 new file mode 100644 index 000000000..df0c534e9 --- /dev/null +++ b/src/libstrongswan/utils/settings.h @@ -0,0 +1,323 @@ +/* + * 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, ...); + + /** + * 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/utils.c b/src/libstrongswan/utils/utils.c new file mode 100644 index 000000000..30084cd81 --- /dev/null +++ b/src/libstrongswan/utils/utils.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2008-2012 Tobias Brunner + * Copyright (C) 2005-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. + */ + +#include "utils.h" + +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <stdint.h> +#include <limits.h> +#include <dirent.h> +#include <time.h> +#include <pthread.h> + +#include "collections/enumerator.h" +#include "utils/debug.h" + +ENUM(status_names, SUCCESS, NEED_MORE, + "SUCCESS", + "FAILED", + "OUT_OF_RES", + "ALREADY_DONE", + "NOT_SUPPORTED", + "INVALID_ARG", + "NOT_FOUND", + "PARSE_ERROR", + "VERIFY_ERROR", + "INVALID_STATE", + "DESTROY_ME", + "NEED_MORE", +); + +/** + * Described in header. + */ +void memxor(u_int8_t dst[], u_int8_t src[], size_t n) +{ + int m, i; + + /* byte wise XOR until dst aligned */ + for (i = 0; (uintptr_t)&dst[i] % sizeof(long) && i < n; i++) + { + dst[i] ^= src[i]; + } + /* try to use words if src shares an aligment with dst */ + switch (((uintptr_t)&src[i] % sizeof(long))) + { + case 0: + for (m = n - sizeof(long); i <= m; i += sizeof(long)) + { + *(long*)&dst[i] ^= *(long*)&src[i]; + } + break; + case sizeof(int): + for (m = n - sizeof(int); i <= m; i += sizeof(int)) + { + *(int*)&dst[i] ^= *(int*)&src[i]; + } + break; + case sizeof(short): + for (m = n - sizeof(short); i <= m; i += sizeof(short)) + { + *(short*)&dst[i] ^= *(short*)&src[i]; + } + break; + default: + break; + } + /* byte wise XOR of the rest */ + for (; i < n; i++) + { + dst[i] ^= src[i]; + } +} + +/** + * Described in header. + */ +void memwipe_noinline(void *ptr, size_t n) +{ + memwipe_inline(ptr, n); +} + +/** + * Described in header. + */ +void *memstr(const void *haystack, const char *needle, size_t n) +{ + unsigned const char *pos = haystack; + size_t l; + + if (!haystack || !needle || (l = strlen(needle)) == 0) + { + return NULL; + } + for (; n >= l; ++pos, --n) + { + if (memeq(pos, needle, l)) + { + return (void*)pos; + } + } + return NULL; +} + +/** + * Described in header. + */ +char* translate(char *str, const char *from, const char *to) +{ + char *pos = str; + if (strlen(from) != strlen(to)) + { + return str; + } + while (pos && *pos) + { + char *match; + if ((match = strchr(from, *pos)) != NULL) + { + *pos = to[match - from]; + } + pos++; + } + return str; +} + +/** + * Described in header. + */ +bool mkdir_p(const char *path, mode_t mode) +{ + int len; + char *pos, full[PATH_MAX]; + pos = full; + if (!path || *path == '\0') + { + return TRUE; + } + len = snprintf(full, sizeof(full)-1, "%s", path); + if (len < 0 || len >= sizeof(full)-1) + { + DBG1(DBG_LIB, "path string %s too long", path); + return FALSE; + } + /* ensure that the path ends with a '/' */ + if (full[len-1] != '/') + { + full[len++] = '/'; + full[len] = '\0'; + } + /* skip '/' at the beginning */ + while (*pos == '/') + { + pos++; + } + while ((pos = strchr(pos, '/'))) + { + *pos = '\0'; + if (access(full, F_OK) < 0) + { + if (mkdir(full, mode) < 0) + { + DBG1(DBG_LIB, "failed to create directory %s", full); + return FALSE; + } + } + *pos = '/'; + pos++; + } + return TRUE; +} + +ENUM(tty_color_names, TTY_RESET, TTY_BG_DEF, + "\e[0m", + "\e[1m", + "\e[4m", + "\e[5m", + "\e[30m", + "\e[31m", + "\e[32m", + "\e[33m", + "\e[34m", + "\e[35m", + "\e[36m", + "\e[37m", + "\e[39m", + "\e[40m", + "\e[41m", + "\e[42m", + "\e[43m", + "\e[44m", + "\e[45m", + "\e[46m", + "\e[47m", + "\e[49m", +); + +/** + * Get the escape string for a given TTY color, empty string on non-tty FILE + */ +char* tty_escape_get(int fd, tty_escape_t escape) +{ + if (!isatty(fd)) + { + return ""; + } + switch (escape) + { + case TTY_RESET: + case TTY_BOLD: + case TTY_UNDERLINE: + case TTY_BLINKING: + case TTY_FG_BLACK: + case TTY_FG_RED: + case TTY_FG_GREEN: + case TTY_FG_YELLOW: + case TTY_FG_BLUE: + case TTY_FG_MAGENTA: + case TTY_FG_CYAN: + case TTY_FG_WHITE: + case TTY_FG_DEF: + case TTY_BG_BLACK: + case TTY_BG_RED: + case TTY_BG_GREEN: + case TTY_BG_YELLOW: + case TTY_BG_BLUE: + case TTY_BG_MAGENTA: + case TTY_BG_CYAN: + case TTY_BG_WHITE: + case TTY_BG_DEF: + return enum_to_name(tty_color_names, escape); + /* warn if a excape code is missing */ + } + return ""; +} + +/** + * The size of the thread-specific error buffer + */ +#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 + */ +static pthread_once_t strerror_buf_key_once = PTHREAD_ONCE_INIT; + +/** + * Create the key used for the thread-specific error buffer + */ +static void create_strerror_buf_key() +{ + pthread_key_create(&strerror_buf_key, free); +} + +/** + * Retrieve the error buffer assigned to the current thread (or create it) + */ +static inline char *get_strerror_buf() +{ + char *buf; + + pthread_once(&strerror_buf_key_once, create_strerror_buf_key); + buf = pthread_getspecific(strerror_buf_key); + if (!buf) + { + buf = malloc(STRERROR_BUF_LEN); + pthread_setspecific(strerror_buf_key, buf); + } + return buf; +} + +#ifdef HAVE_STRERROR_R +/* + * Described in header. + */ +const char *safe_strerror(int errnum) +{ + char *buf = get_strerror_buf(), *msg; + +#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 + /* int version returns 0 on success */ + msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf; +#endif + return msg; +} +#else /* HAVE_STRERROR_R */ +/* we actually wan't to call strerror(3) below */ +#undef strerror +/* + * Described in header. + */ +const char *safe_strerror(int errnum) +{ + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + char *buf = get_strerror_buf(); + + /* 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; +} +#endif /* HAVE_STRERROR_R */ + + +#ifndef HAVE_CLOSEFROM +/** + * Described in header. + */ +void closefrom(int lowfd) +{ + char fd_dir[PATH_MAX]; + int maxfd, fd, len; + + /* try to close only open file descriptors on Linux... */ + len = snprintf(fd_dir, sizeof(fd_dir), "/proc/%u/fd", getpid()); + if (len > 0 && len < sizeof(fd_dir) && access(fd_dir, F_OK) == 0) + { + enumerator_t *enumerator = enumerator_create_directory(fd_dir); + if (enumerator) + { + char *rel; + while (enumerator->enumerate(enumerator, &rel, NULL, NULL)) + { + fd = atoi(rel); + if (fd >= lowfd) + { + close(fd); + } + } + enumerator->destroy(enumerator); + return; + } + } + + /* ...fall back to closing all fds otherwise */ + maxfd = (int)sysconf(_SC_OPEN_MAX); + if (maxfd < 0) + { + maxfd = 256; + } + for (fd = lowfd; fd < maxfd; fd++) + { + close(fd); + } +} +#endif /* HAVE_CLOSEFROM */ + +/** + * Return monotonic time + */ +time_t time_monotonic(timeval_t *tv) +{ +#if defined(HAVE_CLOCK_GETTIME) && \ + (defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \ + defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) + /* as we use time_monotonic() for condvar operations, we use the + * monotonic time source only if it is also supported by pthread. */ + timespec_t ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + if (tv) + { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + return ts.tv_sec; + } +#endif /* HAVE_CLOCK_GETTIME && (...) */ + /* Fallback to non-monotonic timestamps: + * On MAC OS X, creating monotonic timestamps is rather difficult. We + * could use mach_absolute_time() and catch sleep/wakeup notifications. + * We stick to the simpler (non-monotonic) gettimeofday() for now. + * But keep in mind: we need the same time source here as in condvar! */ + if (!tv) + { + return time(NULL); + } + if (gettimeofday(tv, NULL) != 0) + { /* should actually never fail if passed pointers are valid */ + return -1; + } + return tv->tv_sec; +} + +/** + * return null + */ +void *return_null() +{ + return NULL; +} + +/** + * returns TRUE + */ +bool return_true() +{ + return TRUE; +} + +/** + * returns FALSE + */ +bool return_false() +{ + return FALSE; +} + +/** + * returns FAILED + */ +status_t return_failed() +{ + return FAILED; +} + +/** + * returns SUCCESS + */ +status_t return_success() +{ + return SUCCESS; +} + +/** + * nop operation + */ +void nop() +{ +} + +#ifndef HAVE_GCC_ATOMIC_OPERATIONS + +/** + * We use a single mutex for all refcount variables. + */ +static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Increase refcount + */ +refcount_t ref_get(refcount_t *ref) +{ + refcount_t current; + + pthread_mutex_lock(&ref_mutex); + current = ++(*ref); + pthread_mutex_unlock(&ref_mutex); + + return current; +} + +/** + * Decrease refcount + */ +bool ref_put(refcount_t *ref) +{ + bool more_refs; + + pthread_mutex_lock(&ref_mutex); + more_refs = --(*ref) > 0; + pthread_mutex_unlock(&ref_mutex); + return !more_refs; +} + +/** + * Single mutex for all compare and swap operations. + */ +static pthread_mutex_t cas_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Compare and swap if equal to old value + */ +#define _cas_impl(name, type) \ +bool cas_##name(type *ptr, type oldval, type newval) \ +{ \ + bool swapped; \ + pthread_mutex_lock(&cas_mutex); \ + if ((swapped = (*ptr == oldval))) { *ptr = newval; } \ + pthread_mutex_unlock(&cas_mutex); \ + return swapped; \ +} + +_cas_impl(bool, bool) +_cas_impl(ptr, void*) + +#endif /* HAVE_GCC_ATOMIC_OPERATIONS */ + +/** + * Described in header. + */ +int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + static const char* months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + time_t *time = *((time_t**)(args[0])); + bool utc = *((bool*)(args[1]));; + struct tm t; + + if (*time == UNDEFINED_TIME) + { + return print_in_hook(data, "--- -- --:--:--%s----", + utc ? " UTC " : " "); + } + if (utc) + { + gmtime_r(time, &t); + } + else + { + localtime_r(time, &t); + } + 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, + t.tm_sec, utc ? " UTC " : " ", t.tm_year + 1900); +} + +/** + * Described in header. + */ +int time_delta_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args) +{ + char* unit = "second"; + time_t *arg1 = *((time_t**)(args[0])); + time_t *arg2 = *((time_t**)(args[1])); + u_int64_t delta = llabs(*arg1 - *arg2); + + if (delta > 2 * 60 * 60 * 24) + { + delta /= 60 * 60 * 24; + unit = "day"; + } + else if (delta > 2 * 60 * 60) + { + delta /= 60 * 60; + unit = "hour"; + } + else if (delta > 2 * 60) + { + delta /= 60; + unit = "minute"; + } + return print_in_hook(data, "%" PRIu64 " %s%s", delta, unit, + (delta == 1) ? "" : "s"); +} + +/** + * Number of bytes per line to dump raw data + */ +#define BYTES_PER_LINE 16 + +static char hexdig_upper[] = "0123456789ABCDEF"; + +/** + * Described in header. + */ +int mem_printf_hook(printf_hook_data_t *data, + printf_hook_spec_t *spec, const void *const *args) +{ + char *bytes = *((void**)(args[0])); + u_int len = *((int*)(args[1])); + + char buffer[BYTES_PER_LINE * 3]; + char ascii_buffer[BYTES_PER_LINE + 1]; + char *buffer_pos = buffer; + char *bytes_pos = bytes; + char *bytes_roof = bytes + len; + int line_start = 0; + int i = 0; + int written = 0; + + written += print_in_hook(data, "=> %u bytes @ %p", len, bytes); + + while (bytes_pos < bytes_roof) + { + *buffer_pos++ = hexdig_upper[(*bytes_pos >> 4) & 0xF]; + *buffer_pos++ = hexdig_upper[ *bytes_pos & 0xF]; + + ascii_buffer[i++] = + (*bytes_pos > 31 && *bytes_pos < 127) ? *bytes_pos : '.'; + + if (++bytes_pos == bytes_roof || i == BYTES_PER_LINE) + { + int padding = 3 * (BYTES_PER_LINE - i); + + while (padding--) + { + *buffer_pos++ = ' '; + } + *buffer_pos++ = '\0'; + ascii_buffer[i] = '\0'; + + written += print_in_hook(data, "\n%4d: %s %s", + line_start, buffer, ascii_buffer); + + buffer_pos = buffer; + line_start += BYTES_PER_LINE; + i = 0; + } + else + { + *buffer_pos++ = ' '; + } + } + return written; +} diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h new file mode 100644 index 000000000..d055f712d --- /dev/null +++ b/src/libstrongswan/utils/utils.h @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2008-2012 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 utils_i utils + * @{ @ingroup utils + */ + +#ifndef UTILS_H_ +#define UTILS_H_ + +#include <sys/types.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <string.h> + +#include "enum.h" + +/** + * strongSwan program return codes + */ +#define SS_RC_LIBSTRONGSWAN_INTEGRITY 64 +#define SS_RC_DAEMON_INTEGRITY 65 +#define SS_RC_INITIALIZATION_FAILED 66 + +#define SS_RC_FIRST SS_RC_LIBSTRONGSWAN_INTEGRITY +#define SS_RC_LAST SS_RC_INITIALIZATION_FAILED + +/** + * Number of bits in a byte + */ +#define BITS_PER_BYTE 8 + +/** + * Default length for various auxiliary text buffers + */ +#define BUF_LEN 512 + +/** + * General purpose boolean type. + */ +#ifdef HAVE_STDBOOL_H +# include <stdbool.h> +#else +# ifndef HAVE__BOOL +# define _Bool signed char +# endif /* HAVE__BOOL */ +# define bool _Bool +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +#endif /* HAVE_STDBOOL_H */ +#ifndef FALSE +# define FALSE false +#endif /* FALSE */ +#ifndef TRUE +# define TRUE true +#endif /* TRUE */ + +/** + * Helper function that compares two strings for equality + */ +static inline bool streq(const char *x, const char *y) +{ + return strcmp(x, y) == 0; +} + +/** + * Helper function that compares two strings for equality, length limited + */ +static inline bool strneq(const char *x, const char *y, size_t len) +{ + return strncmp(x, y, len) == 0; +} + +/** + * Helper function that checks if a string starts with a given prefix + */ +static inline bool strpfx(const char *x, const char *prefix) +{ + return strneq(x, prefix, strlen(prefix)); +} + +/** + * Helper function that compares two strings for equality ignoring case + */ +static inline bool strcaseeq(const char *x, const char *y) +{ + return strcasecmp(x, y) == 0; +} + +/** + * Helper function that compares two strings for equality ignoring case, length limited + */ +static inline bool strncaseeq(const char *x, const char *y, size_t len) +{ + return strncasecmp(x, y, len) == 0; +} + +/** + * NULL-safe strdup variant + */ +static inline char *strdupnull(const char *s) +{ + return s ? strdup(s) : NULL; +} + +/** + * Helper function that compares two binary blobs for equality + */ +static inline bool memeq(const void *x, const void *y, size_t len) +{ + return memcmp(x, y, len) == 0; +} + +/** + * Macro gives back larger of two values. + */ +#define max(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + _x > _y ? _x : _y; }) + + +/** + * Macro gives back smaller of two values. + */ +#define min(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + _x < _y ? _x : _y; }) + +/** + * Call destructor of an object, if object != NULL + */ +#define DESTROY_IF(obj) if (obj) (obj)->destroy(obj) + +/** + * Call offset destructor of an object, if object != NULL + */ +#define DESTROY_OFFSET_IF(obj, offset) if (obj) obj->destroy_offset(obj, offset); + +/** + * Call function destructor of an object, if object != NULL + */ +#define DESTROY_FUNCTION_IF(obj, fn) if (obj) obj->destroy_function(obj, fn); + +/** + * Debug macro to follow control flow + */ +#define POS printf("%s, line %d\n", __FILE__, __LINE__) + +/** + * Object allocation/initialization macro, using designated initializer. + */ +#define INIT(this, ...) { (this) = malloc(sizeof(*(this))); \ + *(this) = (typeof(*(this))){ __VA_ARGS__ }; } + +/** + * Method declaration/definition macro, providing private and public interface. + * + * Defines a method name with this as first parameter and a return value ret, + * and an alias for this method with a _ prefix, having the this argument + * safely casted to the public interface iface. + * _name is provided a function pointer, but will get optimized out by GCC. + */ +#define METHOD(iface, name, ret, this, ...) \ + static ret name(union {iface *_public; this;} \ + __attribute__((transparent_union)), ##__VA_ARGS__); \ + static typeof(name) *_##name = (typeof(name)*)name; \ + static ret name(this, ##__VA_ARGS__) + +/** + * Same as METHOD(), but is defined for two public interfaces. + */ +#define METHOD2(iface1, iface2, name, ret, this, ...) \ + static ret name(union {iface1 *_public1; iface2 *_public2; this;} \ + __attribute__((transparent_union)), ##__VA_ARGS__); \ + static typeof(name) *_##name = (typeof(name)*)name; \ + static ret name(this, ##__VA_ARGS__) + +/** + * 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.: + * BITFIELD2(u_int8_t, + * low: 4, + * high: 4, + * ) flags; + * The member defined first placed at bit 0. + */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define BITFIELD2(t, a, b,...) struct { t a; t b; __VA_ARGS__} +#define BITFIELD3(t, a, b, c,...) struct { t a; t b; t c; __VA_ARGS__} +#define BITFIELD4(t, a, b, c, d,...) struct { t a; t b; t c; t d; __VA_ARGS__} +#define BITFIELD5(t, a, b, c, d, e,...) struct { t a; t b; t c; t d; t e; __VA_ARGS__} +#elif BYTE_ORDER == BIG_ENDIAN +#define BITFIELD2(t, a, b,...) struct { t b; t a; __VA_ARGS__} +#define BITFIELD3(t, a, b, c,...) struct { t c; t b; t a; __VA_ARGS__} +#define BITFIELD4(t, a, b, c, d,...) struct { t d; t c; t b; t a; __VA_ARGS__} +#define BITFIELD5(t, a, b, c, d, e,...) struct { t e; t d; t c; t b; t a; __VA_ARGS__} +#endif + +/** + * Macro to allocate a sized type. + */ +#define malloc_thing(thing) ((thing*)malloc(sizeof(thing))) + +/** + * Get the number of elements in an array + */ +#define countof(array) (sizeof(array)/sizeof(array[0])) + +/** + * Ignore result of functions tagged with warn_unused_result attributes + */ +#define ignore_result(call) { if(call){}; } + +/** + * Assign a function as a class method + */ +#define ASSIGN(method, function) (method = (typeof(method))function) + +/** + * time_t not defined + */ +#define UNDEFINED_TIME 0 + +/** + * Maximum time since epoch causing wrap-around on Jan 19 03:14:07 UTC 2038 + */ +#define TIME_32_BIT_SIGNED_MAX 0x7fffffff + +/** + * define some missing fixed width int types on OpenSolaris. + * TODO: since the uintXX_t types are defined by the C99 standard we should + * probably use those anyway + */ +#ifdef __sun + #include <stdint.h> + typedef uint8_t u_int8_t; + typedef uint16_t u_int16_t; + typedef uint32_t u_int32_t; + typedef uint64_t u_int64_t; +#endif + +typedef enum status_t status_t; + +/** + * Return values of function calls. + */ +enum status_t { + /** + * Call succeeded. + */ + SUCCESS, + + /** + * Call failed. + */ + FAILED, + + /** + * Out of resources. + */ + OUT_OF_RES, + + /** + * The suggested operation is already done + */ + ALREADY_DONE, + + /** + * Not supported. + */ + NOT_SUPPORTED, + + /** + * One of the arguments is invalid. + */ + INVALID_ARG, + + /** + * Something could not be found. + */ + NOT_FOUND, + + /** + * Error while parsing. + */ + PARSE_ERROR, + + /** + * Error while verifying. + */ + VERIFY_ERROR, + + /** + * Object in invalid state. + */ + INVALID_STATE, + + /** + * Destroy object which called method belongs to. + */ + DESTROY_ME, + + /** + * Another call to the method is required. + */ + NEED_MORE, +}; + +/** + * enum_names for type status_t. + */ +extern enum_name_t *status_names; + +typedef enum tty_escape_t tty_escape_t; + +/** + * Excape codes for tty colors + */ +enum tty_escape_t { + /** text properties */ + TTY_RESET, + TTY_BOLD, + TTY_UNDERLINE, + TTY_BLINKING, + + /** foreground colors */ + TTY_FG_BLACK, + TTY_FG_RED, + TTY_FG_GREEN, + TTY_FG_YELLOW, + TTY_FG_BLUE, + TTY_FG_MAGENTA, + TTY_FG_CYAN, + TTY_FG_WHITE, + TTY_FG_DEF, + + /** background colors */ + TTY_BG_BLACK, + TTY_BG_RED, + TTY_BG_GREEN, + TTY_BG_YELLOW, + TTY_BG_BLUE, + TTY_BG_MAGENTA, + TTY_BG_CYAN, + TTY_BG_WHITE, + TTY_BG_DEF, +}; + +/** + * Get the escape string for a given TTY color, empty string on non-tty fd + */ +char* tty_escape_get(int fd, tty_escape_t escape); + +/** + * deprecated pluto style return value: + * error message, NULL for success + */ +typedef const char *err_t; + +/** + * Handle struct timeval like an own type. + */ +typedef struct timeval timeval_t; + +/** + * Handle struct timespec like an own type. + */ +typedef struct timespec timespec_t; + +/** + * Handle struct chunk_t like an own type. + */ +typedef struct sockaddr sockaddr_t; + +/** + * Same as memcpy, but XORs src into dst instead of copy + */ +void memxor(u_int8_t dest[], u_int8_t src[], size_t n); + +/** + * Safely overwrite n bytes of memory at ptr with zero, non-inlining variant. + */ +void memwipe_noinline(void *ptr, size_t n); + +/** + * Safely overwrite n bytes of memory at ptr with zero, inlining variant. + */ +static inline void memwipe_inline(void *ptr, size_t n) +{ + volatile char *c = (volatile char*)ptr; + size_t m, i; + + /* byte wise until long aligned */ + for (i = 0; (uintptr_t)&c[i] % sizeof(long) && i < n; i++) + { + c[i] = 0; + } + /* word wise */ + if (n >= sizeof(long)) + { + for (m = n - sizeof(long); i <= m; i += sizeof(long)) + { + *(volatile long*)&c[i] = 0; + } + } + /* byte wise of the rest */ + for (; i < n; i++) + { + c[i] = 0; + } +} + +/** + * Safely overwrite n bytes of memory at ptr with zero, auto-inlining variant. + */ +static inline void memwipe(void *ptr, size_t n) +{ + if (!ptr) + { + return; + } + if (__builtin_constant_p(n)) + { + memwipe_inline(ptr, n); + } + else + { + memwipe_noinline(ptr, n); + } +} + +/** + * A variant of strstr with the characteristics of memchr, where haystack is not + * a null-terminated string but simply a memory area of length n. + */ +void *memstr(const void *haystack, const char *needle, size_t n); + +/** + * Translates the characters in the given string, searching for characters + * in 'from' and mapping them to characters in 'to'. + * The two characters sets 'from' and 'to' must contain the same number of + * characters. + */ +char *translate(char *str, const char *from, const char *to); + +/** + * Creates a directory and all required parent directories. + * + * @param path path to the new directory + * @param mode permissions of the new directory/directories + * @return TRUE on success + */ +bool mkdir_p(const char *path, mode_t mode); + +/** + * Thread-safe wrapper around strerror and strerror_r. + * + * This is required because the first is not thread-safe (on some platforms) + * and the second uses two different signatures (POSIX/GNU) and is impractical + * to use anyway. + * + * @param errnum error code (i.e. errno) + * @return error message + */ +const char *safe_strerror(int errnum); + +/** + * Replace usages of strerror(3) with thread-safe variant. + */ +#define strerror(errnum) safe_strerror(errnum) + +#ifndef HAVE_CLOSEFROM +/** + * Close open file descriptors greater than or equal to lowfd. + * + * @param lowfd start closing file descriptoros from here + */ +void closefrom(int lowfd); +#endif + +/** + * Get a timestamp from a monotonic time source. + * + * While the time()/gettimeofday() functions are affected by leap seconds + * and system time changes, this function returns ever increasing monotonic + * time stamps. + * + * @param tv timeval struct receiving monotonic timestamps, or NULL + * @return monotonic timestamp in seconds + */ +time_t time_monotonic(timeval_t *tv); + +/** + * Add the given number of milliseconds to the given timeval struct + * + * @param tv timeval struct to modify + * @param ms number of milliseconds + */ +static inline void timeval_add_ms(timeval_t *tv, u_int ms) +{ + tv->tv_usec += ms * 1000; + while (tv->tv_usec >= 1000000 /* 1s */) + { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } +} + +/** + * returns null + */ +void *return_null(); + +/** + * No-Operation function + */ +void nop(); + +/** + * returns TRUE + */ +bool return_true(); + +/** + * returns FALSE + */ +bool return_false(); + +/** + * returns FAILED + */ +status_t return_failed(); + +/** + * returns SUCCESS + */ +status_t return_success(); + +/** + * Write a 16-bit host order value in network order to an unaligned address. + * + * @param host host order 16-bit value + * @param network unaligned address to write network order value to + */ +static inline void htoun16(void *network, u_int16_t host) +{ + char *unaligned = (char*)network; + + host = htons(host); + memcpy(unaligned, &host, sizeof(host)); +} + +/** + * Write a 32-bit host order value in network order to an unaligned address. + * + * @param host host order 32-bit value + * @param network unaligned address to write network order value to + */ +static inline void htoun32(void *network, u_int32_t host) +{ + char *unaligned = (char*)network; + + host = htonl(host); + memcpy((char*)unaligned, &host, sizeof(host)); +} + +/** + * Write a 64-bit host order value in network order to an unaligned address. + * + * @param host host order 64-bit value + * @param network unaligned address to write network order value to + */ +static inline void htoun64(void *network, u_int64_t host) +{ + char *unaligned = (char*)network; + +#ifdef be64toh + host = htobe64(host); + memcpy((char*)unaligned, &host, sizeof(host)); +#else + u_int32_t high_part, low_part; + + high_part = host >> 32; + high_part = htonl(high_part); + low_part = host & 0xFFFFFFFFLL; + low_part = htonl(low_part); + + memcpy(unaligned, &high_part, sizeof(high_part)); + unaligned += sizeof(high_part); + memcpy(unaligned, &low_part, sizeof(low_part)); +#endif +} + +/** + * Read a 16-bit value in network order from an unaligned address to host order. + * + * @param network unaligned address to read network order value from + * @return host order value + */ +static inline u_int16_t untoh16(void *network) +{ + char *unaligned = (char*)network; + u_int16_t tmp; + + memcpy(&tmp, unaligned, sizeof(tmp)); + return ntohs(tmp); +} + +/** + * Read a 32-bit value in network order from an unaligned address to host order. + * + * @param network unaligned address to read network order value from + * @return host order value + */ +static inline u_int32_t untoh32(void *network) +{ + char *unaligned = (char*)network; + u_int32_t tmp; + + memcpy(&tmp, unaligned, sizeof(tmp)); + return ntohl(tmp); +} + +/** + * Read a 64-bit value in network order from an unaligned address to host order. + * + * @param network unaligned address to read network order value from + * @return host order value + */ +static inline u_int64_t untoh64(void *network) +{ + char *unaligned = (char*)network; + +#ifdef be64toh + u_int64_t tmp; + + memcpy(&tmp, unaligned, sizeof(tmp)); + return be64toh(tmp); +#else + u_int32_t high_part, low_part; + + memcpy(&high_part, unaligned, sizeof(high_part)); + unaligned += sizeof(high_part); + memcpy(&low_part, unaligned, sizeof(low_part)); + + high_part = ntohl(high_part); + low_part = ntohl(low_part); + + return (((u_int64_t)high_part) << 32) + low_part; +#endif +} + +/** + * Round up size to be multiple of alignement + */ +static inline size_t round_up(size_t size, int alignement) +{ + int remainder; + + remainder = size % alignement; + if (remainder) + { + size += alignement - remainder; + } + return size; +} + +/** + * Round down size to be a multiple of alignement + */ +static inline size_t round_down(size_t size, int alignement) +{ + return size - (size % alignement); +} + +/** + * Special type to count references + */ +typedef u_int refcount_t; + +#ifdef HAVE_GCC_ATOMIC_OPERATIONS + +#define ref_get(ref) __sync_add_and_fetch(ref, 1) +#define ref_put(ref) (!__sync_sub_and_fetch(ref, 1)) + +#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 */ + +/** + * Get a new reference. + * + * Increments the reference counter atomic. + * + * @param ref pointer to ref counter + * @return new value of ref + */ +refcount_t ref_get(refcount_t *ref); + +/** + * Put back a unused reference. + * + * Decrements the reference counter atomic and + * says if more references available. + * + * @param ref pointer to ref counter + * @return TRUE if no more references counted + */ +bool ref_put(refcount_t *ref); + +/** + * Atomically replace value of ptr with newval if it currently equals oldval. + * + * @param ptr pointer to variable + * @param oldval old value of the variable + * @param newval new value set if possible + * @return TRUE if value equaled oldval and newval was written + */ +bool cas_bool(bool *ptr, bool oldval, bool newval); + +/** + * Atomically replace value of ptr with newval if it currently equals oldval. + * + * @param ptr pointer to variable + * @param oldval old value of the variable + * @param newval new value set if possible + * @return TRUE if value equaled oldval and newval was written + */ +bool cas_ptr(void **ptr, void *oldval, void *newval); + +#endif /* HAVE_GCC_ATOMIC_OPERATIONS */ + +/** + * printf hook for time_t. + * + * Arguments are: + * time_t* time, bool utc + */ +int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +/** + * printf hook for time_t deltas. + * + * Arguments are: + * time_t* begin, time_t* end + */ +int time_delta_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +/** + * printf hook for memory areas. + * + * Arguments are: + * u_char *ptr, u_int len + */ +int mem_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec, + const void *const *args); + +#endif /** UTILS_H_ @}*/ |
