aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstrongswan/Makefile.am2
-rw-r--r--src/libstrongswan/collections/dictionary.h55
-rw-r--r--src/libstrongswan/collections/hashtable.c39
-rw-r--r--src/libstrongswan/collections/hashtable.h9
-rw-r--r--src/libstrongswan/tests/test_runner.c10
-rw-r--r--src/starter/.gitignore7
-rw-r--r--src/starter/Android.mk11
-rw-r--r--src/starter/Makefile.am26
-rw-r--r--src/starter/README101
-rw-r--r--src/starter/args.c356
-rw-r--r--src/starter/args.h17
-rw-r--r--src/starter/confread.c999
-rw-r--r--src/starter/confread.h42
-rw-r--r--src/starter/ipsec-parser.h55
-rw-r--r--src/starter/keywords.h12
-rw-r--r--src/starter/lexer.l215
-rw-r--r--src/starter/parser.y272
-rw-r--r--src/starter/parser/conf_parser.c655
-rw-r--r--src/starter/parser/conf_parser.h120
-rw-r--r--src/starter/parser/lexer.l205
-rw-r--r--src/starter/parser/parser.y254
-rw-r--r--src/starter/starter.c27
-rw-r--r--src/starter/tests/.gitignore1
-rw-r--r--src/starter/tests/Makefile.am19
-rw-r--r--src/starter/tests/starter_tests.c43
-rw-r--r--src/starter/tests/starter_tests.h16
-rw-r--r--src/starter/tests/suites/test_parser.c527
27 files changed, 2504 insertions, 1591 deletions
diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am
index d3bd84e10..3fb57de5a 100644
--- a/src/libstrongswan/Makefile.am
+++ b/src/libstrongswan/Makefile.am
@@ -59,7 +59,7 @@ nobase_strongswan_include_HEADERS = \
library.h \
asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \
collections/blocking_queue.h collections/enumerator.h collections/hashtable.h \
-collections/linked_list.h collections/array.h \
+collections/linked_list.h collections/array.h collections/dictionary.h \
crypto/crypters/crypter.h crypto/hashers/hasher.h crypto/mac.h \
crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \
crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \
diff --git a/src/libstrongswan/collections/dictionary.h b/src/libstrongswan/collections/dictionary.h
new file mode 100644
index 000000000..679e41d2d
--- /dev/null
+++ b/src/libstrongswan/collections/dictionary.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup dictionary dictionary
+ * @{ @ingroup collections
+ */
+
+#ifndef DICTIONARY_H_
+#define DICTIONARY_H_
+
+#include <collections/enumerator.h>
+
+typedef struct dictionary_t dictionary_t;
+
+/**
+ * Interface for read-only dictionaries.
+ */
+struct dictionary_t {
+
+ /**
+ * Create an enumerator over the key/value pairs in the dictionary.
+ *
+ * @return enumerator over (const void *key, void *value)
+ */
+ enumerator_t *(*create_enumerator)(dictionary_t *this);
+
+ /**
+ * Returns the value with the given key, if the dictionary 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)(dictionary_t *this, const void *key);
+
+ /**
+ * Destroys a dictionary object.
+ */
+ void (*destroy)(dictionary_t *this);
+};
+
+#endif /** DICTIONARY_H_ @}*/
diff --git a/src/libstrongswan/collections/hashtable.c b/src/libstrongswan/collections/hashtable.c
index ca31d8361..2b77a37cc 100644
--- a/src/libstrongswan/collections/hashtable.c
+++ b/src/libstrongswan/collections/hashtable.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2012 Tobias Brunner
+ * Copyright (C) 2008-2014 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -250,7 +250,7 @@ static void rehash(private_hashtable_t *this)
}
METHOD(hashtable_t, put, void*,
- private_hashtable_t *this, const void *key, void *value)
+ private_hashtable_t *this, const void *key, void *value)
{
void *old_value = NULL;
pair_t *pair;
@@ -309,19 +309,19 @@ static void *get_internal(private_hashtable_t *this, const void *key,
}
METHOD(hashtable_t, get, void*,
- private_hashtable_t *this, const void *key)
+ private_hashtable_t *this, const void *key)
{
return get_internal(this, key, this->equals);
}
METHOD(hashtable_t, get_match, void*,
- private_hashtable_t *this, const void *key, hashtable_equals_t match)
+ private_hashtable_t *this, const void *key, hashtable_equals_t match)
{
return get_internal(this, key, match);
}
METHOD(hashtable_t, remove_, void*,
- private_hashtable_t *this, const void *key)
+ private_hashtable_t *this, const void *key)
{
void *value = NULL;
pair_t *pair, *prev = NULL;
@@ -353,7 +353,7 @@ METHOD(hashtable_t, remove_, void*,
}
METHOD(hashtable_t, remove_at, void,
- private_hashtable_t *this, private_enumerator_t *enumerator)
+ private_hashtable_t *this, private_enumerator_t *enumerator)
{
if (enumerator->table == this && enumerator->current)
{
@@ -373,13 +373,13 @@ METHOD(hashtable_t, remove_at, void,
}
METHOD(hashtable_t, get_count, u_int,
- private_hashtable_t *this)
+ private_hashtable_t *this)
{
return this->count;
}
METHOD(enumerator_t, enumerate, bool,
- private_enumerator_t *this, const void **key, void **value)
+ private_enumerator_t *this, const void **key, void **value)
{
while (this->count && this->row < this->table->capacity)
{
@@ -411,7 +411,7 @@ METHOD(enumerator_t, enumerate, bool,
}
METHOD(hashtable_t, create_enumerator, enumerator_t*,
- private_hashtable_t *this)
+ private_hashtable_t *this)
{
private_enumerator_t *enumerator;
@@ -427,8 +427,8 @@ METHOD(hashtable_t, create_enumerator, enumerator_t*,
return &enumerator->enumerator;
}
-METHOD(hashtable_t, destroy, void,
- private_hashtable_t *this)
+static void destroy_internal(private_hashtable_t *this,
+ void (*fn)(void*,const void*))
{
pair_t *pair, *next;
u_int row;
@@ -438,6 +438,10 @@ METHOD(hashtable_t, destroy, void,
pair = this->table[row];
while (pair)
{
+ if (fn)
+ {
+ fn(pair->value, pair->key);
+ }
next = pair->next;
free(pair);
pair = next;
@@ -447,6 +451,18 @@ METHOD(hashtable_t, destroy, void,
free(this);
}
+METHOD(hashtable_t, destroy, void,
+ private_hashtable_t *this)
+{
+ destroy_internal(this, NULL);
+}
+
+METHOD(hashtable_t, destroy_function, void,
+ private_hashtable_t *this, void (*fn)(void*,const void*))
+{
+ destroy_internal(this, fn);
+}
+
/*
* Described in header.
*/
@@ -465,6 +481,7 @@ hashtable_t *hashtable_create(hashtable_hash_t hash, hashtable_equals_t equals,
.get_count = _get_count,
.create_enumerator = _create_enumerator,
.destroy = _destroy,
+ .destroy_function = _destroy_function,
},
.hash = hash,
.equals = equals,
diff --git a/src/libstrongswan/collections/hashtable.h b/src/libstrongswan/collections/hashtable.h
index 0a7ebeb65..f60564a42 100644
--- a/src/libstrongswan/collections/hashtable.h
+++ b/src/libstrongswan/collections/hashtable.h
@@ -156,6 +156,15 @@ struct hashtable_t {
* Destroys a hash table object.
*/
void (*destroy) (hashtable_t *this);
+
+ /**
+ * Destroys a hash table object and calls the given function for each
+ * item and its key in the hash table.
+ *
+ * @param function function to call on each item and key
+ */
+ void (*destroy_function)(hashtable_t *this,
+ void (*)(void *val, const void *key));
};
/**
diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c
index 443c0ae13..8f2e9855e 100644
--- a/src/libstrongswan/tests/test_runner.c
+++ b/src/libstrongswan/tests/test_runner.c
@@ -187,11 +187,17 @@ static bool call_fixture(test_case_t *tcase, bool up)
{
if (up)
{
- fixture->setup();
+ if (fixture->setup)
+ {
+ fixture->setup();
+ }
}
else
{
- fixture->teardown();
+ if (fixture->teardown)
+ {
+ fixture->teardown();
+ }
}
}
else
diff --git a/src/starter/.gitignore b/src/starter/.gitignore
index 6b880307d..a370ee554 100644
--- a/src/starter/.gitignore
+++ b/src/starter/.gitignore
@@ -1,5 +1,4 @@
starter
-lexer.c
-parser.h
-parser.c
-parser.output
+parser/lexer.c
+parser/parser.[ch]
+parser/parser.output \ No newline at end of file
diff --git a/src/starter/Android.mk b/src/starter/Android.mk
index 2d2ad25c3..c37fc1aa6 100644
--- a/src/starter/Android.mk
+++ b/src/starter/Android.mk
@@ -3,11 +3,11 @@ include $(CLEAR_VARS)
# copy-n-paste from Makefile.am (update for LEX/YACC)
starter_SOURCES := \
-parser.c lexer.c ipsec-parser.h netkey.c args.h netkey.h \
-starterstroke.c confread.c \
-starterstroke.h confread.h args.c \
-keywords.c files.h keywords.h cmp.c starter.c cmp.h invokecharon.c \
-invokecharon.h klips.c klips.h
+starter.c files.h \
+parser/parser.c parser/lexer.c parser/conf_parser.c parser/conf_parser.h \
+args.c args.h confread.c confread.h keywords.c keywords.h cmp.c cmp.h \
+invokecharon.c invokecharon.h starterstroke.c starterstroke.h \
+netkey.c netkey.h klips.c klips.h
LOCAL_SRC_FILES := $(filter %.c,$(starter_SOURCES))
@@ -16,6 +16,7 @@ LOCAL_SRC_FILES := $(filter %.c,$(starter_SOURCES))
LOCAL_C_INCLUDES += \
$(strongswan_PATH)/src/libhydra \
$(strongswan_PATH)/src/libstrongswan \
+ $(strongswan_PATH)/src/starter \
$(strongswan_PATH)/src/stroke
LOCAL_CFLAGS := $(strongswan_CFLAGS) -DSTART_CHARON \
diff --git a/src/starter/Makefile.am b/src/starter/Makefile.am
index 48110dd02..04e126d94 100644
--- a/src/starter/Makefile.am
+++ b/src/starter/Makefile.am
@@ -1,15 +1,22 @@
+SUBDIRS = . tests
+
ipsec_PROGRAMS = starter
starter_SOURCES = \
-parser.y lexer.l ipsec-parser.h netkey.c args.h netkey.h \
-starterstroke.c confread.c \
-starterstroke.h confread.h args.c \
-keywords.c files.h keywords.h cmp.c starter.c cmp.h invokecharon.c \
-invokecharon.h klips.c klips.h
+starter.c files.h \
+args.c args.h confread.c confread.h keywords.c keywords.h cmp.c cmp.h \
+invokecharon.c invokecharon.h starterstroke.c starterstroke.h \
+netkey.c netkey.h klips.c klips.h
+
+# parser is also used by tests
+noinst_LTLIBRARIES = libstarter.la
+libstarter_la_SOURCES = \
+parser/parser.y parser/lexer.l parser/conf_parser.c parser/conf_parser.h
AM_CPPFLAGS = \
-I${linux_headers} \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libhydra \
+ -I$(top_srcdir)/src/starter \
-I$(top_srcdir)/src/stroke \
-DIPSEC_DIR=\"${ipsecdir}\" \
-DIPSEC_CONFDIR=\"${sysconfdir}\" \
@@ -23,10 +30,15 @@ AM_CPPFLAGS = \
AM_YFLAGS = -v -d
-starter_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la $(top_builddir)/src/libhydra/libhydra.la $(SOCKLIB) $(PTHREADLIB)
+starter_LDADD = \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la \
+ $(top_builddir)/src/libhydra/libhydra.la \
+ libstarter.la \
+ $(SOCKLIB) $(PTHREADLIB)
+
EXTRA_DIST = keywords.txt ipsec.conf Android.mk
MAINTAINERCLEANFILES = keywords.c
-BUILT_SOURCES = parser.h
+BUILT_SOURCES = keywords.c parser/parser.h
if USE_CHARON
AM_CPPFLAGS += -DSTART_CHARON
diff --git a/src/starter/README b/src/starter/README
deleted file mode 100644
index 4aff64978..000000000
--- a/src/starter/README
+++ /dev/null
@@ -1,101 +0,0 @@
-
-IPsec Starter -- Version 0.2 [Contributed by Arkoon Network Security]
-============================ [ http://www.arkoon.net/]
-
-IPsec Starter is aimed to replace all the scripts which are used to
-start and stop strongSwan and to do that in a quicker and a smarter way.
-
-IPsec Starter can also reload the configuration file (kill --HUP or periodicaly)
-and apply the changes.
-
-Usage:
- starter [--debug] [--auto_update <x seconds>]
- --debug: enable debugging output
- --no_fork: all msg (including pluto) are sent to the console
- --auto_update: reload the config file (like kill -HUP) every x seconds
- and determine any configuration changes
-
-FEATURES
---------
-
-o Load modules of the native Linux 2.6 IPsec stack
-
-o Launch and monitor pluto
-
-o Add, initiate, route and del connections
-
-o Attach and detach interfaces according to config file
-
-o kill -HUP can be used to reload the config file. New connections will be
- added, old ones will be removed and modified ones will be reloaded.
- Interfaces/Klips/Pluto will be reloaded if necessary.
-
-o Full support of the %defaultroute wildcard parameter.
-
-o save own pid in /var/run/starter
-
-o Upon reloading, dynamic DNS addr will be resolved and reloaded. Use
- --auto_update to periodicaly check dynamic DNS changes.
-
-o kill -USR1 can be used to reload all connections (delete then add and
- route/initiate)
-
-o /var/run/dynip/xxxx can be used to use a virtual interface name in
- ipsec.conf. By example, when adsl can be ppp0, ppp1, ... :
- ipsec.conf: interfaces="ipsec0=adsl"
- And use /etc/ppp/ip-up to create /var/run/dynip/adsl
- /var/run/dynip/adsl: IP_PHYS=ppp0
-
-o %auto can be used to automaticaly name the connections
-
-o kill -TERM can be used to stop FS. pluto will be stopped.
-
-o Can be used to start strongSwan and load lots of connections in a few
- seconds.
-
-TODO
-----
-
-o handle wildcards in include lines -- use glob() fct
- ex: include /etc/ipsec.*.conf
-
-o handle duplicates keywords and sections
-
-o 'also' keyword not supported
-
-o manually keyed connections
-
-o IPv6
-
-o Documentation
-
-
-CHANGES
--------
-
-o Version 0.1 -- 2002.01.14 -- First public release
-
-o Version 0.2 -- 2002.09.04 -- Various enhancements
- FreeS/WAN 1.98b, x509 0.9.14, algo 0.8.0
-
-o Version 0.2d -- 2004.01.13 -- Adaptions for Openswan 1.0.0
- by Stephan Scholz <sscholz@astaro.com>
-
-o Version 0.2e -- 2004.10.14 -- Added support for change of interface address
- by Stephan Scholz <sscholz@astaro.com>
-
-o Version 0.2s -- 2005-12-02 -- Ported to strongSwan
- by Stephan Scholz <sscholz@astaro.com>
-
-o Version 0.2x -- 2006-01-02 -- Added missing strongSwan keywords
- Full support of the native Linux 2.6 IPsec stack
- Full support of %defaultroute
- Improved parsing of keywords using perfect hash
- function generated by gperf.
- by Andreas Steffen <andreas.steffen@hsr.ch>
-
-THANKS
-------
-
-o Nathan Angelacos - include fix
-
diff --git a/src/starter/args.c b/src/starter/args.c
index 0d662f400..0874cc7e5 100644
--- a/src/starter/args.c
+++ b/src/starter/args.c
@@ -1,6 +1,7 @@
-/* automatic handling of confread struct arguments
+/*
+ * Copyright (C) 2014 Tobias Brunner
* Copyright (C) 2006 Andreas Steffen
- * Hochschule fuer Technik Rapperswil, Switzerland
+ * 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
@@ -20,7 +21,6 @@
#include <library.h>
#include <utils/debug.h>
-#include "keywords.h"
#include "confread.h"
#include "args.h"
@@ -36,7 +36,6 @@ typedef enum {
ARG_UBIN,
ARG_PCNT,
ARG_STR,
- ARG_LST,
ARG_MISC
} arg_t;
@@ -219,123 +218,48 @@ static const token_info_t token_info[] =
{ ARG_MISC, 0, NULL /* KW_END_DEPRECATED */ },
};
-static void free_list(char **list)
-{
- char **s;
-
- for (s = list; *s; s++)
- {
- free(*s);
- }
- free(list);
-}
-
-char** new_list(char *value)
-{
- char *val, *b, *e, *end, **ret;
- int count;
-
- val = strdupnull(value);
- if (!val)
- {
- return NULL;
- }
- end = val + strlen(val);
- for (b = val, count = 0; b < end;)
- {
- for (e = b; ((*e != ' ') && (*e != '\0')); e++);
- *e = '\0';
- if (e != b)
- {
- count++;
- }
- b = e + 1;
- }
- if (count == 0)
- {
- free(val);
- return NULL;
- }
- ret = (char **)malloc((count+1) * sizeof(char *));
-
- for (b = val, count = 0; b < end; )
- {
- for (e = b; (*e != '\0'); e++);
- if (e != b)
- {
- ret[count++] = strdupnull(b);
- }
- b = e + 1;
- }
- ret[count] = NULL;
- free(val);
- return ret;
-}
-
-
/*
* assigns an argument value to a struct field
*/
-bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base,
- bool *assigned)
+bool assign_arg(kw_token_t token, kw_token_t first, char *key, char *value,
+ void *base, bool *assigned)
{
- char *p = base + token_info[token].offset;
+ char *p = (char*)base + token_info[token].offset;
const char **list = token_info[token].list;
-
int index = -1; /* used for enumeration arguments */
- seen_t *seen = (seen_t*)base; /* seen flags are at the top of the struct */
-
*assigned = FALSE;
- DBG3(DBG_APP, " %s=%s", kw->entry->name, kw->value);
-
- if (*seen & SEEN_KW(token, first))
- {
- DBG1(DBG_APP, "# duplicate '%s' option", kw->entry->name);
- return FALSE;
- }
-
- if (token == KW_ESP || token == KW_AH)
- {
- if (*seen & (SEEN_KW(KW_ESP, first) | SEEN_KW(KW_AH, first)))
- {
- DBG1(DBG_APP, "# can't have both 'ah' and 'esp' options");
- return FALSE;
- }
- }
-
- /* set flag that this argument has been seen */
- *seen |= SEEN_KW(token, first);
+ DBG3(DBG_APP, " %s=%s", key, value);
/* is there a keyword list? */
- if (list != NULL && token_info[token].type != ARG_LST)
+ if (list != NULL)
{
bool match = FALSE;
while (*list != NULL && !match)
{
index++;
- match = streq(kw->value, *list++);
+ match = streq(value, *list++);
}
if (!match)
{
- DBG1(DBG_APP, "# bad value: %s=%s", kw->entry->name, kw->value);
+ DBG1(DBG_APP, "# bad value: %s=%s", key, value);
return FALSE;
}
}
switch (token_info[token].type)
{
- case ARG_NONE:
- DBG1(DBG_APP, "# option '%s' not supported yet", kw->entry->name);
- return FALSE;
- case ARG_ENUM:
+ case ARG_NONE:
+ DBG1(DBG_APP, "# option '%s' not supported yet", key);
+ return FALSE;
+ case ARG_ENUM:
{
if (index < 0)
{
DBG1(DBG_APP, "# bad enumeration value: %s=%s (%d)",
- kw->entry->name, kw->value, index);
+ key, value, index);
return FALSE;
}
@@ -345,93 +269,86 @@ bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base,
*b = (index > 0);
}
else
- {
+ { /* FIXME: this is not entirely correct as the args are enums */
int *i = (int *)p;
*i = index;
}
+ break;
}
- break;
-
- case ARG_UINT:
+ case ARG_UINT:
{
char *endptr;
u_int *u = (u_int *)p;
- *u = strtoul(kw->value, &endptr, 10);
+ *u = strtoul(value, &endptr, 10);
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad integer value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
return FALSE;
}
+ break;
}
- break;
- case ARG_ULNG:
- case ARG_PCNT:
+ case ARG_ULNG:
+ case ARG_PCNT:
{
char *endptr;
unsigned long *l = (unsigned long *)p;
- *l = strtoul(kw->value, &endptr, 10);
+ *l = strtoul(value, &endptr, 10);
if (token_info[token].type == ARG_ULNG)
{
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad integer value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
return FALSE;
}
}
else
{
- if ((*endptr != '%') || (endptr[1] != '\0') || endptr == kw->value)
+ if ((*endptr != '%') || (endptr[1] != '\0') || endptr == value)
{
- DBG1(DBG_APP, "# bad percent value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad percent value: %s=%s", key, value);
return FALSE;
}
}
-
+ break;
}
- break;
- case ARG_ULLI:
+ case ARG_ULLI:
{
char *endptr;
unsigned long long *ll = (unsigned long long *)p;
- *ll = strtoull(kw->value, &endptr, 10);
+ *ll = strtoull(value, &endptr, 10);
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad integer value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
return FALSE;
}
+ break;
}
- break;
- case ARG_UBIN:
+ case ARG_UBIN:
{
char *endptr;
u_int *u = (u_int *)p;
- *u = strtoul(kw->value, &endptr, 2);
+ *u = strtoul(value, &endptr, 2);
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad binary value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad binary value: %s=%s", key, value);
return FALSE;
}
+ break;
}
- break;
- case ARG_TIME:
+ case ARG_TIME:
{
char *endptr;
time_t *t = (time_t *)p;
- *t = strtoul(kw->value, &endptr, 10);
+ *t = strtoul(value, &endptr, 10);
/* time in seconds? */
if (*endptr == '\0' || (*endptr == 's' && endptr[1] == '\0'))
@@ -456,60 +373,21 @@ bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base,
break;
}
}
- DBG1(DBG_APP, "# bad duration value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad duration value: %s=%s", key, value);
return FALSE;
}
- case ARG_STR:
+ case ARG_STR:
{
char **cp = (char **)p;
/* free any existing string */
free(*cp);
-
/* assign the new string */
- *cp = strdupnull(kw->value);
- }
- break;
- case ARG_LST:
- {
- char ***listp = (char ***)p;
-
- /* free any existing list */
- if (*listp != NULL)
- {
- free_list(*listp);
- }
- /* create a new list and assign values */
- *listp = new_list(kw->value);
-
- /* is there a keyword list? */
- if (list != NULL)
- {
- char ** lst;
-
- for (lst = *listp; lst && *lst; lst++)
- {
- bool match = FALSE;
-
- list = token_info[token].list;
-
- while (*list != NULL && !match)
- {
- match = streq(*lst, *list++);
- }
- if (!match)
- {
- DBG1(DBG_APP, "# bad value: %s=%s",
- kw->entry->name, *lst);
- return FALSE;
- }
- }
- }
+ *cp = strdupnull(value);
+ break;
}
- /* fall through */
- default:
- return TRUE;
+ default:
+ return TRUE;
}
*assigned = TRUE;
@@ -519,124 +397,69 @@ bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base,
/*
* frees all dynamically allocated arguments in a struct
*/
-void free_args(kw_token_t first, kw_token_t last, char *base)
+void free_args(kw_token_t first, kw_token_t last, void *base)
{
kw_token_t token;
for (token = first; token <= last; token++)
{
- char *p = base + token_info[token].offset;
+ char *p = (char*)base + token_info[token].offset;
switch (token_info[token].type)
{
- case ARG_STR:
+ case ARG_STR:
{
char **cp = (char **)p;
free(*cp);
*cp = NULL;
+ break;
}
- break;
- case ARG_LST:
- {
- char ***listp = (char ***)p;
-
- if (*listp != NULL)
- {
- free_list(*listp);
- *listp = NULL;
- }
- }
- break;
- default:
- break;
- }
- }
-}
-
-/*
- * clone all dynamically allocated arguments in a struct
- */
-void clone_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
-{
- kw_token_t token;
-
- for (token = first; token <= last; token++)
- {
- if (token_info[token].type == ARG_STR)
- {
- char **cp1 = (char **)(base1 + token_info[token].offset);
- char **cp2 = (char **)(base2 + token_info[token].offset);
-
- *cp1 = strdupnull(*cp2);
- }
- }
-}
-
-static bool cmp_list(char **list1, char **list2)
-{
- if ((list1 == NULL) && (list2 == NULL))
- {
- return TRUE;
- }
- if ((list1 == NULL) || (list2 == NULL))
- {
- return FALSE;
- }
-
- for ( ; *list1 && *list2; list1++, list2++)
- {
- if (strcmp(*list1,*list2) != 0)
- {
- return FALSE;
+ default:
+ break;
}
}
-
- if ((*list1 != NULL) || (*list2 != NULL))
- {
- return FALSE;
- }
-
- return TRUE;
}
/*
* compare all arguments in a struct
*/
-bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
+bool cmp_args(kw_token_t first, kw_token_t last, void *base1, void *base2)
{
kw_token_t token;
for (token = first; token <= last; token++)
{
- char *p1 = base1 + token_info[token].offset;
- char *p2 = base2 + token_info[token].offset;
+ char *p1 = (char*)base1 + token_info[token].offset;
+ char *p2 = (char*)base2 + token_info[token].offset;
switch (token_info[token].type)
{
- case ARG_ENUM:
- if (token_info[token].list == LST_bool)
+ case ARG_ENUM:
{
- bool *b1 = (bool *)p1;
- bool *b2 = (bool *)p2;
-
- if (*b1 != *b2)
+ if (token_info[token].list == LST_bool)
{
- return FALSE;
- }
- }
- else
- {
- int *i1 = (int *)p1;
- int *i2 = (int *)p2;
+ bool *b1 = (bool *)p1;
+ bool *b2 = (bool *)p2;
- if (*i1 != *i2)
+ if (*b1 != *b2)
+ {
+ return FALSE;
+ }
+ }
+ else
{
- return FALSE;
+ int *i1 = (int *)p1;
+ int *i2 = (int *)p2;
+
+ if (*i1 != *i2)
+ {
+ return FALSE;
+ }
}
+ break;
}
- break;
- case ARG_UINT:
+ case ARG_UINT:
{
u_int *u1 = (u_int *)p1;
u_int *u2 = (u_int *)p2;
@@ -645,10 +468,10 @@ bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
{
return FALSE;
}
+ break;
}
- break;
- case ARG_ULNG:
- case ARG_PCNT:
+ case ARG_ULNG:
+ case ARG_PCNT:
{
unsigned long *l1 = (unsigned long *)p1;
unsigned long *l2 = (unsigned long *)p2;
@@ -657,9 +480,9 @@ bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
{
return FALSE;
}
+ break;
}
- break;
- case ARG_ULLI:
+ case ARG_ULLI:
{
unsigned long long *ll1 = (unsigned long long *)p1;
unsigned long long *ll2 = (unsigned long long *)p2;
@@ -668,9 +491,9 @@ bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
{
return FALSE;
}
+ break;
}
- break;
- case ARG_TIME:
+ case ARG_TIME:
{
time_t *t1 = (time_t *)p1;
time_t *t2 = (time_t *)p2;
@@ -679,9 +502,9 @@ bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
{
return FALSE;
}
+ break;
}
- break;
- case ARG_STR:
+ case ARG_STR:
{
char **cp1 = (char **)p1;
char **cp2 = (char **)p2;
@@ -694,21 +517,10 @@ bool cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
{
return FALSE;
}
+ break;
}
- break;
- case ARG_LST:
- {
- char ***listp1 = (char ***)p1;
- char ***listp2 = (char ***)p2;
-
- if (!cmp_list(*listp1, *listp2))
- {
- return FALSE;
- }
- }
- break;
- default:
- break;
+ default:
+ break;
}
}
return TRUE;
diff --git a/src/starter/args.h b/src/starter/args.h
index f5c13e6ba..76c05de8c 100644
--- a/src/starter/args.h
+++ b/src/starter/args.h
@@ -1,6 +1,6 @@
-/* automatic handling of confread struct arguments
+/*
* Copyright (C) 2006 Andreas Steffen
- * Hochschule fuer Technik Rapperswil, Switzerland
+ * 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
@@ -17,16 +17,11 @@
#define _ARGS_H_
#include "keywords.h"
-#include "ipsec-parser.h"
-extern char **new_list(char *value);
-extern bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw
- , char *base, bool *assigned);
-extern void free_args(kw_token_t first, kw_token_t last, char *base);
-extern void clone_args(kw_token_t first, kw_token_t last, char *base1
- , char *base2);
-extern bool cmp_args(kw_token_t first, kw_token_t last, char *base1
- , char *base2);
+bool assign_arg(kw_token_t token, kw_token_t first, char *key, char *value,
+ void *base, bool *assigned);
+void free_args(kw_token_t first, kw_token_t last, void *base);
+bool cmp_args(kw_token_t first, kw_token_t last, void *base1, void *base2);
#endif /* _ARGS_H_ */
diff --git a/src/starter/confread.c b/src/starter/confread.c
index 0fac89542..17dca66a1 100644
--- a/src/starter/confread.c
+++ b/src/starter/confread.c
@@ -1,4 +1,7 @@
-/* strongSwan IPsec config file parser
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
*
* This program is free software; you can redistribute it and/or modify it
@@ -28,6 +31,7 @@
#include "confread.h"
#include "args.h"
#include "files.h"
+#include "parser/conf_parser.h"
#define IKE_LIFETIME_DEFAULT 10800 /* 3 hours */
#define IPSEC_LIFETIME_DEFAULT 3600 /* 1 hour */
@@ -41,6 +45,11 @@ static const char esp_defaults[] = "aes128-sha1,3des-sha1";
static const char firewall_defaults[] = IPSEC_SCRIPT " _updown iptables";
+/**
+ * Provided by GPERF
+ */
+extern kw_entry_t *in_word_set (char *str, unsigned int len);
+
static bool daemon_exists(char *daemon, char *path)
{
struct stat st;
@@ -55,24 +64,21 @@ static bool daemon_exists(char *daemon, char *path)
/**
* Process deprecated keywords
*/
-static bool is_deprecated(kw_token_t token, kw_list_t *kw, char *name)
+static bool is_deprecated(kw_token_t token, char *name, char *conn)
{
switch (token)
{
case KW_SETUP_DEPRECATED:
case KW_PKCS11_DEPRECATED:
- DBG1(DBG_APP, "# deprecated keyword '%s' in config setup",
- kw->entry->name);
+ DBG1(DBG_APP, "# deprecated keyword '%s' in config setup", name);
break;
case KW_CONN_DEPRECATED:
case KW_END_DEPRECATED:
case KW_PFS_DEPRECATED:
- DBG1(DBG_APP, "# deprecated keyword '%s' in conn '%s'",
- kw->entry->name, name);
+ DBG1(DBG_APP, "# deprecated keyword '%s' in conn '%s'", name, conn);
break;
case KW_CA_DEPRECATED:
- DBG1(DBG_APP, "# deprecated keyword '%s' in ca '%s'",
- kw->entry->name, name);
+ DBG1(DBG_APP, "# deprecated keyword '%s' in ca '%s'", name, conn);
break;
default:
return FALSE;
@@ -81,11 +87,11 @@ static bool is_deprecated(kw_token_t token, kw_list_t *kw, char *name)
switch (token)
{
case KW_PKCS11_DEPRECATED:
- DBG1(DBG_APP, " use the 'pkcs11' plugin instead", kw->entry->name);
+ DBG1(DBG_APP, " use the 'pkcs11' plugin instead");
break;
case KW_PFS_DEPRECATED:
DBG1(DBG_APP, " PFS is enabled by specifying a DH group in the "
- "'esp' cipher suite", kw->entry->name);
+ "'esp' cipher suite");
break;
default:
break;
@@ -93,101 +99,54 @@ static bool is_deprecated(kw_token_t token, kw_list_t *kw, char *name)
return TRUE;
}
-static void default_values(starter_config_t *cfg)
-{
- if (cfg == NULL)
- return;
-
- memset(cfg, 0, sizeof(struct starter_config));
-
- /* is there enough space for all seen flags? */
- assert(KW_SETUP_LAST - KW_SETUP_FIRST <
- sizeof(cfg->setup.seen) * BITS_PER_BYTE);
- assert(KW_CONN_LAST - KW_CONN_FIRST <
- sizeof(cfg->conn_default.seen) * BITS_PER_BYTE);
- assert(KW_END_LAST - KW_END_FIRST <
- sizeof(cfg->conn_default.right.seen) * BITS_PER_BYTE);
- assert(KW_CA_LAST - KW_CA_FIRST <
- sizeof(cfg->ca_default.seen) * BITS_PER_BYTE);
-
- cfg->setup.seen = SEEN_NONE;
- cfg->setup.uniqueids = TRUE;
-
-#ifdef START_CHARON
- cfg->setup.charonstart = TRUE;
-#endif
-
- cfg->conn_default.seen = SEEN_NONE;
- cfg->conn_default.startup = STARTUP_NO;
- cfg->conn_default.state = STATE_IGNORE;
- cfg->conn_default.mode = MODE_TUNNEL;
- cfg->conn_default.options = SA_OPTION_MOBIKE;
-
- cfg->conn_default.ike = strdupnull(ike_defaults);
- cfg->conn_default.esp = strdupnull(esp_defaults);
- cfg->conn_default.sa_ike_life_seconds = IKE_LIFETIME_DEFAULT;
- cfg->conn_default.sa_ipsec_life_seconds = IPSEC_LIFETIME_DEFAULT;
- cfg->conn_default.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
- cfg->conn_default.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
- cfg->conn_default.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
- cfg->conn_default.install_policy = TRUE;
- cfg->conn_default.dpd_delay = 30; /* seconds */
- cfg->conn_default.dpd_timeout = 150; /* seconds */
- cfg->conn_default.replay_window = SA_REPLAY_WINDOW_DEFAULT;
-
- cfg->conn_default.left.seen = SEEN_NONE;
- cfg->conn_default.right.seen = SEEN_NONE;
-
- cfg->conn_default.left.sendcert = CERT_SEND_IF_ASKED;
- cfg->conn_default.right.sendcert = CERT_SEND_IF_ASKED;
-
- cfg->conn_default.left.ikeport = 500;
- cfg->conn_default.right.ikeport = 500;
-
- cfg->conn_default.left.to_port = 0xffff;
- cfg->conn_default.right.to_port = 0xffff;
-
- cfg->ca_default.seen = SEEN_NONE;
-}
-
-#define KW_SA_OPTION_FLAG(sy, sn, fl) \
- if (streq(kw->value, sy)) { conn->options |= fl; } \
- else if (streq(kw->value, sn)) { conn->options &= ~fl; } \
- else { DBG1(DBG_APP, "# bad option value: %s=%s", kw->entry->name, kw->value); cfg->err++; }
-
-static void load_setup(starter_config_t *cfg, config_parsed_t *cfgp)
+/*
+ * parse config setup section
+ */
+static void load_setup(starter_config_t *cfg, conf_parser_t *parser)
{
- kw_list_t *kw;
+ enumerator_t *enumerator;
+ dictionary_t *dict;
+ kw_entry_t *entry;
+ char *key, *value;
DBG2(DBG_APP, "Loading config setup");
-
- for (kw = cfgp->config_setup; kw; kw = kw->next)
+ dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, NULL);
+ if (!dict)
+ {
+ return;
+ }
+ enumerator = dict->create_enumerator(dict);
+ while (enumerator->enumerate(enumerator, &key, &value))
{
bool assigned = FALSE;
- kw_token_t token = kw->entry->token;
-
- if ((int)token < KW_SETUP_FIRST || token > KW_SETUP_LAST)
+ entry = in_word_set(key, strlen(key));
+ if (!entry)
+ {
+ DBG1(DBG_APP, "# unknown keyword '%s'", key);
+ cfg->non_fatal_err++;
+ continue;
+ }
+ if ((int)entry->token < KW_SETUP_FIRST || entry->token > KW_SETUP_LAST)
{
- DBG1(DBG_APP, "# unsupported keyword '%s' in config setup",
- kw->entry->name);
+ DBG1(DBG_APP, "# unsupported keyword '%s' in config setup", key);
cfg->err++;
continue;
}
-
- if (is_deprecated(token, kw, ""))
+ if (is_deprecated(entry->token, key, ""))
{
cfg->non_fatal_err++;
continue;
}
-
- if (!assign_arg(token, KW_SETUP_FIRST, kw, (char *)cfg, &assigned))
+ if (!assign_arg(entry->token, KW_SETUP_FIRST, key, value, cfg,
+ &assigned))
{
DBG1(DBG_APP, " bad argument value in config setup");
cfg->err++;
- continue;
}
}
+ enumerator->destroy(enumerator);
+ dict->destroy(dict);
/* verify the executables are actually available */
#ifdef START_CHARON
@@ -198,292 +157,321 @@ static void load_setup(starter_config_t *cfg, config_parsed_t *cfgp)
#endif
}
+/*
+ * parse a ca section
+ */
+static void load_ca(starter_ca_t *ca, starter_config_t *cfg,
+ conf_parser_t *parser)
+{
+ enumerator_t *enumerator;
+ dictionary_t *dict;
+ kw_entry_t *entry;
+ kw_token_t token;
+ char *key, *value;
+
+ DBG2(DBG_APP, "Loading ca '%s'", ca->name);
+ dict = parser->get_section(parser, CONF_PARSER_CA, ca->name);
+ if (!dict)
+ {
+ return;
+ }
+ enumerator = dict->create_enumerator(dict);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ bool assigned = FALSE;
+
+ entry = in_word_set(key, strlen(key));
+ if (!entry)
+ {
+ DBG1(DBG_APP, "# unknown keyword '%s'", key);
+ cfg->non_fatal_err++;
+ continue;
+ }
+ token = entry->token;
+ if (token == KW_AUTO)
+ {
+ token = KW_CA_SETUP;
+ }
+ if (token < KW_CA_FIRST || token > KW_CA_LAST)
+ {
+ DBG1(DBG_APP, "# unsupported keyword '%s' in ca '%s'",
+ key, ca->name);
+ cfg->err++;
+ continue;
+ }
+ if (is_deprecated(token, key, ca->name))
+ {
+ cfg->non_fatal_err++;
+ continue;
+ }
+ if (!assign_arg(token, KW_CA_FIRST, key, value, ca, &assigned))
+ {
+ DBG1(DBG_APP, " bad argument value in ca '%s'", ca->name);
+ cfg->err++;
+ }
+ }
+ enumerator->destroy(enumerator);
+ dict->destroy(dict);
+
+ /* treat 'route' and 'start' as 'add' */
+ if (ca->startup != STARTUP_NO)
+ {
+ ca->startup = STARTUP_ADD;
+ }
+}
+
+/*
+ * set some default values
+ */
+static void conn_defaults(starter_conn_t *conn)
+{
+ conn->startup = STARTUP_NO;
+ conn->state = STATE_IGNORE;
+ conn->mode = MODE_TUNNEL;
+ conn->options = SA_OPTION_MOBIKE;
+
+ conn->ike = strdupnull(ike_defaults);
+ /* esp defaults are set after parsing the conn section */
+ conn->sa_ike_life_seconds = IKE_LIFETIME_DEFAULT;
+ conn->sa_ipsec_life_seconds = IPSEC_LIFETIME_DEFAULT;
+ conn->sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
+ conn->sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
+ conn->sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
+ conn->install_policy = TRUE;
+ conn->dpd_delay = 30; /* seconds */
+ conn->dpd_timeout = 150; /* seconds */
+ conn->replay_window = SA_REPLAY_WINDOW_DEFAULT;
+
+ conn->left.sendcert = CERT_SEND_IF_ASKED;
+ conn->right.sendcert = CERT_SEND_IF_ASKED;
+
+ conn->left.ikeport = 500;
+ conn->right.ikeport = 500;
+
+ conn->left.to_port = 0xffff;
+ conn->right.to_port = 0xffff;
+}
+
+/*
+ * parse left|right specific options
+ */
static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token,
- kw_list_t *kw, char *conn_name, starter_config_t *cfg)
+ char *key, char *value, starter_config_t *cfg)
{
bool assigned = FALSE;
- char *name = kw->entry->name;
- char *value = kw->value;
-
- if (is_deprecated(token, kw, conn_name))
+ if (is_deprecated(token, key, conn->name))
{
cfg->non_fatal_err++;
return;
}
- if (!assign_arg(token, KW_END_FIRST, kw, (char *)end, &assigned))
+ if (!assign_arg(token, KW_END_FIRST, key, value, end, &assigned))
+ {
goto err;
+ }
/* post processing of some keywords that were assigned automatically */
switch (token)
{
- case KW_HOST:
- if (value && strlen(value) > 0 && value[0] == '%')
- {
- if (streq(value, "%defaultroute"))
+ case KW_HOST:
+ if (value && strlen(value) > 0 && value[0] == '%')
{
- value = "%any";
+ if (streq(value, "%defaultroute"))
+ {
+ value = "%any";
+ }
+ if (!streq(value, "%any") && !streq(value, "%any4") &&
+ !streq(value, "%any6"))
+ { /* allow_any prefix */
+ end->allow_any = TRUE;
+ value++;
+ }
}
- if (!streq(value, "%any") && !streq(value, "%any4") &&
- !streq(value, "%any6"))
- { /* allow_any prefix */
- end->allow_any = TRUE;
- value++;
+ free(end->host);
+ end->host = strdupnull(value);
+ break;
+ case KW_SOURCEIP:
+ conn->mode = MODE_TUNNEL;
+ conn->proxy_mode = FALSE;
+ break;
+ case KW_SENDCERT:
+ if (end->sendcert == CERT_YES_SEND)
+ {
+ end->sendcert = CERT_ALWAYS_SEND;
}
- }
- free(end->host);
- end->host = strdupnull(value);
- break;
- case KW_SOURCEIP:
- conn->mode = MODE_TUNNEL;
- conn->proxy_mode = FALSE;
- break;
- case KW_SENDCERT:
- if (end->sendcert == CERT_YES_SEND)
- {
- end->sendcert = CERT_ALWAYS_SEND;
- }
- else if (end->sendcert == CERT_NO_SEND)
- {
- end->sendcert = CERT_NEVER_SEND;
- }
- break;
- default:
- break;
+ else if (end->sendcert == CERT_NO_SEND)
+ {
+ end->sendcert = CERT_NEVER_SEND;
+ }
+ break;
+ default:
+ break;
}
if (assigned)
+ {
return;
+ }
/* individual processing of keywords that were not assigned automatically */
switch (token)
{
- case KW_PROTOPORT:
- {
- struct protoent *proto;
- struct servent *svc;
- char *sep, *port = "", *endptr;
- long int p;
-
- sep = strchr(value, '/');
- if (sep)
- { /* protocol/port */
- *sep = '\0';
- port = sep + 1;
- }
+ case KW_PROTOPORT:
+ {
+ struct protoent *proto;
+ struct servent *svc;
+ char *sep, *port = "", *endptr;
+ long int p;
+
+ sep = strchr(value, '/');
+ if (sep)
+ { /* protocol/port */
+ *sep = '\0';
+ port = sep + 1;
+ }
- if (streq(value, "%any"))
- {
- end->protocol = 0;
- }
- else
- {
- proto = getprotobyname(value);
- if (proto)
+ if (streq(value, "%any"))
{
- end->protocol = proto->p_proto;
+ end->protocol = 0;
}
else
{
- p = strtol(value, &endptr, 0);
- if ((*value && *endptr) || p < 0 || p > 0xff)
+ proto = getprotobyname(value);
+ if (proto)
{
- DBG1(DBG_APP, "# bad protocol: %s=%s", name, value);
- goto err;
+ end->protocol = proto->p_proto;
+ }
+ else
+ {
+ p = strtol(value, &endptr, 0);
+ if ((*value && *endptr) || p < 0 || p > 0xff)
+ {
+ DBG1(DBG_APP, "# bad protocol: %s=%s", key, value);
+ goto err;
+ }
+ end->protocol = (u_int8_t)p;
}
- end->protocol = (u_int8_t)p;
}
- }
- if (streq(port, "%any"))
- {
- end->from_port = 0;
- end->to_port = 0xffff;
- }
- else if (streq(port, "%opaque"))
- {
- end->from_port = 0xffff;
- end->to_port = 0;
- }
- else if (*port)
- {
- svc = getservbyname(port, NULL);
- if (svc)
+ if (streq(port, "%any"))
{
- end->from_port = end->to_port = ntohs(svc->s_port);
+ end->from_port = 0;
+ end->to_port = 0xffff;
}
- else
+ else if (streq(port, "%opaque"))
{
- p = strtol(port, &endptr, 0);
- if (p < 0 || p > 0xffff)
+ end->from_port = 0xffff;
+ end->to_port = 0;
+ }
+ else if (*port)
+ {
+ svc = getservbyname(port, NULL);
+ if (svc)
{
- DBG1(DBG_APP, "# bad port: %s=%s", name, port);
- goto err;
+ end->from_port = end->to_port = ntohs(svc->s_port);
}
- end->from_port = p;
- if (*endptr == '-')
+ else
{
- port = endptr + 1;
p = strtol(port, &endptr, 0);
if (p < 0 || p > 0xffff)
{
- DBG1(DBG_APP, "# bad port: %s=%s", name, port);
+ DBG1(DBG_APP, "# bad port: %s=%s", key, port);
+ goto err;
+ }
+ end->from_port = p;
+ if (*endptr == '-')
+ {
+ port = endptr + 1;
+ p = strtol(port, &endptr, 0);
+ if (p < 0 || p > 0xffff)
+ {
+ DBG1(DBG_APP, "# bad port: %s=%s", key, port);
+ goto err;
+ }
+ }
+ end->to_port = p;
+ if (*endptr)
+ {
+ DBG1(DBG_APP, "# bad port: %s=%s", key, port);
goto err;
}
- }
- end->to_port = p;
- if (*endptr)
- {
- DBG1(DBG_APP, "# bad port: %s=%s", name, port);
- goto err;
}
}
+ if (sep)
+ { /* restore the original text in case also= is used */
+ *sep = '/';
+ }
+ break;
}
- if (sep)
- { /* restore the original text in case also= is used */
- *sep = '/';
- }
- break;
- }
- default:
- break;
+ default:
+ break;
}
return;
err:
- DBG1(DBG_APP, " bad argument value in conn '%s'", conn_name);
+ DBG1(DBG_APP, " bad argument value in conn '%s'", conn->name);
cfg->err++;
}
/*
- * handles left|rightfirewall and left|rightupdown parameters
+ * macro to handle simple flags
*/
-static void handle_firewall(const char *label, starter_end_t *end,
- starter_config_t *cfg)
-{
- if (end->firewall && (end->seen & SEEN_KW(KW_FIREWALL, KW_END_FIRST)))
- {
- if (end->updown != NULL)
- {
- DBG1(DBG_APP, "# cannot have both %sfirewall and %supdown", label,
- label);
- cfg->err++;
- }
- else
- {
- end->updown = strdupnull(firewall_defaults);
- end->firewall = FALSE;
- }
- }
-}
+#define KW_SA_OPTION_FLAG(sy, sn, fl) \
+ if (streq(value, sy)) { conn->options |= fl; } \
+ else if (streq(value, sn)) { conn->options &= ~fl; } \
+ else { DBG1(DBG_APP, "# bad option value: %s=%s", key, value); cfg->err++; }
/*
- * parse a conn section
+ * parse settings not handled by the simple argument parser
*/
-static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg)
+static void handle_keyword(kw_token_t token, starter_conn_t *conn, char *key,
+ char *value, starter_config_t *cfg)
{
- char *conn_name = (conn->name == NULL)? "%default":conn->name;
-
- for ( ; kw; kw = kw->next)
+ if ((token == KW_ESP && conn->ah) || (token == KW_AH && conn->esp))
+ {
+ DBG1(DBG_APP, "# can't have both 'ah' and 'esp' options");
+ cfg->err++;
+ return;
+ }
+ switch (token)
{
- bool assigned = FALSE;
-
- kw_token_t token = kw->entry->token;
-
- if (token >= KW_LEFT_FIRST && token <= KW_LEFT_LAST)
- {
- kw_end(conn, &conn->left, token - KW_LEFT_FIRST + KW_END_FIRST
- , kw, conn_name, cfg);
- continue;
- }
- else if (token >= KW_RIGHT_FIRST && token <= KW_RIGHT_LAST)
- {
- kw_end(conn, &conn->right, token - KW_RIGHT_FIRST + KW_END_FIRST
- , kw, conn_name, cfg);
- continue;
- }
-
- if (token == KW_AUTO)
- {
- token = KW_CONN_SETUP;
- }
- else if (token == KW_ALSO)
- {
- if (cfg->parse_also)
- {
- also_t *also = malloc_thing(also_t);
-
- also->name = strdupnull(kw->value);
- also->next = conn->also;
- conn->also = also;
-
- DBG2(DBG_APP, " also=%s", kw->value);
- }
- continue;
- }
-
- if (token < KW_CONN_FIRST || token > KW_CONN_LAST)
- {
- DBG1(DBG_APP, "# unsupported keyword '%s' in conn '%s'",
- kw->entry->name, conn_name);
- cfg->err++;
- continue;
- }
-
- if (is_deprecated(token, kw, conn_name))
- {
- cfg->non_fatal_err++;
- continue;
- }
-
- if (!assign_arg(token, KW_CONN_FIRST, kw, (char *)conn, &assigned))
- {
- DBG1(DBG_APP, " bad argument value in conn '%s'", conn_name);
- cfg->err++;
- continue;
- }
-
- if (assigned)
- continue;
-
- switch (token)
- {
case KW_TYPE:
+ {
conn->mode = MODE_TRANSPORT;
conn->proxy_mode = FALSE;
- if (streq(kw->value, "tunnel"))
+ if (streq(value, "tunnel"))
{
conn->mode = MODE_TUNNEL;
}
- else if (streq(kw->value, "beet"))
+ else if (streq(value, "beet"))
{
conn->mode = MODE_BEET;
}
- else if (streq(kw->value, "transport_proxy"))
+ else if (streq(value, "transport_proxy"))
{
conn->mode = MODE_TRANSPORT;
conn->proxy_mode = TRUE;
}
- else if (streq(kw->value, "passthrough") || streq(kw->value, "pass"))
+ else if (streq(value, "passthrough") || streq(value, "pass"))
{
conn->mode = MODE_PASS;
}
- else if (streq(kw->value, "drop") || streq(kw->value, "reject"))
+ else if (streq(value, "drop") || streq(value, "reject"))
{
conn->mode = MODE_DROP;
}
- else if (!streq(kw->value, "transport"))
+ else if (!streq(value, "transport"))
{
- DBG1(DBG_APP, "# bad policy value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad policy value: %s=%s", key, value);
cfg->err++;
}
break;
+ }
case KW_COMPRESS:
KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_COMPRESS)
break;
case KW_MARK:
- if (!mark_from_string(kw->value, &conn->mark_in))
+ if (!mark_from_string(value, &conn->mark_in))
{
cfg->err++;
break;
@@ -491,19 +479,19 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
conn->mark_out = conn->mark_in;
break;
case KW_MARK_IN:
- if (!mark_from_string(kw->value, &conn->mark_in))
+ if (!mark_from_string(value, &conn->mark_in))
{
cfg->err++;
}
break;
case KW_MARK_OUT:
- if (!mark_from_string(kw->value, &conn->mark_out))
+ if (!mark_from_string(value, &conn->mark_out))
{
cfg->err++;
}
break;
case KW_TFC:
- if (streq(kw->value, "%mtu"))
+ if (streq(value, "%mtu"))
{
conn->tfc = -1;
}
@@ -511,17 +499,16 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
{
char *endptr;
- conn->tfc = strtoul(kw->value, &endptr, 10);
+ conn->tfc = strtoul(value, &endptr, 10);
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad integer value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
cfg->err++;
}
}
break;
case KW_KEYINGTRIES:
- if (streq(kw->value, "%forever"))
+ if (streq(value, "%forever"))
{
conn->sa_keying_tries = 0;
}
@@ -529,11 +516,10 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
{
char *endptr;
- conn->sa_keying_tries = strtoul(kw->value, &endptr, 10);
+ conn->sa_keying_tries = strtoul(value, &endptr, 10);
if (*endptr != '\0')
{
- DBG1(DBG_APP, "# bad integer value: %s=%s", kw->entry->name,
- kw->value);
+ DBG1(DBG_APP, "# bad integer value: %s=%s", key, value);
cfg->err++;
}
}
@@ -558,219 +544,120 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg
break;
default:
break;
- }
}
-
- handle_firewall("left", &conn->left, cfg);
- handle_firewall("right", &conn->right, cfg);
}
/*
- * initialize a conn object with the default conn
+ * handles left|rightfirewall and left|rightupdown parameters
*/
-static void conn_default(char *name, starter_conn_t *conn, starter_conn_t *def)
+static void handle_firewall(const char *label, starter_end_t *end,
+ starter_config_t *cfg)
{
- memcpy(conn, def, sizeof(starter_conn_t));
- conn->name = strdupnull(name);
-
- clone_args(KW_CONN_FIRST, KW_CONN_LAST, (char *)conn, (char *)def);
- clone_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->left, (char *)&def->left);
- clone_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->right, (char *)&def->right);
+ if (end->firewall)
+ {
+ if (end->updown != NULL)
+ {
+ DBG1(DBG_APP, "# cannot have both %sfirewall and %supdown", label,
+ label);
+ cfg->err++;
+ }
+ else
+ {
+ end->updown = strdupnull(firewall_defaults);
+ end->firewall = FALSE;
+ }
+ }
}
/*
- * parse a ca section
+ * parse a conn section
*/
-static void load_ca(starter_ca_t *ca, kw_list_t *kw, starter_config_t *cfg)
+static void load_conn(starter_conn_t *conn, starter_config_t *cfg,
+ conf_parser_t *parser)
{
- char *ca_name = (ca->name == NULL)? "%default":ca->name;
-
- for ( ; kw; kw = kw->next)
+ enumerator_t *enumerator;
+ dictionary_t *dict;
+ kw_entry_t *entry;
+ kw_token_t token;
+ char *key, *value;
+
+ DBG2(DBG_APP, "Loading conn '%s'", conn->name);
+ dict = parser->get_section(parser, CONF_PARSER_CONN, conn->name);
+ if (!dict)
+ {
+ return;
+ }
+ enumerator = dict->create_enumerator(dict);
+ while (enumerator->enumerate(enumerator, &key, &value))
{
bool assigned = FALSE;
- kw_token_t token = kw->entry->token;
-
- if (token == KW_AUTO)
+ entry = in_word_set(key, strlen(key));
+ if (!entry)
{
- token = KW_CA_SETUP;
- }
- else if (token == KW_ALSO)
- {
- if (cfg->parse_also)
- {
- also_t *also = malloc_thing(also_t);
-
- also->name = strdupnull(kw->value);
- also->next = ca->also;
- ca->also = also;
-
- DBG2(DBG_APP, " also=%s", kw->value);
- }
+ DBG1(DBG_APP, "# unknown keyword '%s'", key);
+ cfg->non_fatal_err++;
continue;
}
-
- if (token < KW_CA_FIRST || token > KW_CA_LAST)
+ token = entry->token;
+ if (token >= KW_LEFT_FIRST && token <= KW_LEFT_LAST)
{
- DBG1(DBG_APP, "# unsupported keyword '%s' in ca '%s'",
- kw->entry->name, ca_name);
- cfg->err++;
+ kw_end(conn, &conn->left, token - KW_LEFT_FIRST + KW_END_FIRST,
+ key, value, cfg);
continue;
}
-
- if (is_deprecated(token, kw, ca_name))
+ else if (token >= KW_RIGHT_FIRST && token <= KW_RIGHT_LAST)
{
- cfg->non_fatal_err++;
+ kw_end(conn, &conn->right, token - KW_RIGHT_FIRST + KW_END_FIRST,
+ key, value, cfg);
continue;
}
-
- if (!assign_arg(token, KW_CA_FIRST, kw, (char *)ca, &assigned))
- {
- DBG1(DBG_APP, " bad argument value in ca '%s'", ca_name);
- cfg->err++;
- }
- }
-
- /* treat 'route' and 'start' as 'add' */
- if (ca->startup != STARTUP_NO)
- ca->startup = STARTUP_ADD;
-}
-
-/*
- * initialize a ca object with the default ca
- */
-static void ca_default(char *name, starter_ca_t *ca, starter_ca_t *def)
-{
- memcpy(ca, def, sizeof(starter_ca_t));
- ca->name = strdupnull(name);
-
- clone_args(KW_CA_FIRST, KW_CA_LAST, (char *)ca, (char *)def);
-}
-
-static kw_list_t* find_also_conn(const char* name, starter_conn_t *conn,
- starter_config_t *cfg);
-
-static void load_also_conns(starter_conn_t *conn, also_t *also,
- starter_config_t *cfg)
-{
- while (also != NULL)
- {
- kw_list_t *kw = find_also_conn(also->name, conn, cfg);
-
- if (kw == NULL)
+ if (token == KW_AUTO)
{
- DBG1(DBG_APP, " conn '%s' cannot include '%s'", conn->name,
- also->name);
+ token = KW_CONN_SETUP;
}
- else
+ if (token < KW_CONN_FIRST || token > KW_CONN_LAST)
{
- DBG2(DBG_APP, "conn '%s' includes '%s'", conn->name, also->name);
- /* only load if no error occurred in the first round */
- if (cfg->err == 0)
- load_conn(conn, kw, cfg);
+ DBG1(DBG_APP, "# unsupported keyword '%s' in conn '%s'",
+ key, conn->name);
+ cfg->err++;
+ continue;
}
- also = also->next;
- }
-}
-
-/*
- * find a conn included by also
- */
-static kw_list_t* find_also_conn(const char* name, starter_conn_t *conn,
- starter_config_t *cfg)
-{
- starter_conn_t *c = cfg->conn_first;
-
- while (c != NULL)
- {
- if (streq(name, c->name))
+ if (is_deprecated(token, key, conn->name))
{
- if (conn->visit == c->visit)
- {
- DBG1(DBG_APP, "# detected also loop");
- cfg->err++;
- return NULL;
- }
- c->visit = conn->visit;
- load_also_conns(conn, c->also, cfg);
- return c->kw;
+ cfg->non_fatal_err++;
+ continue;
}
- c = c->next;
- }
-
- DBG1(DBG_APP, "# also '%s' not found", name);
- cfg->err++;
- return NULL;
-}
-
-static kw_list_t* find_also_ca(const char* name, starter_ca_t *ca,
- starter_config_t *cfg);
-
-static void load_also_cas(starter_ca_t *ca, also_t *also, starter_config_t *cfg)
-{
- while (also != NULL)
- {
- kw_list_t *kw = find_also_ca(also->name, ca, cfg);
-
- if (kw == NULL)
+ if (!assign_arg(token, KW_CONN_FIRST, key, value, conn,
+ &assigned))
{
- DBG1(DBG_APP, " ca '%s' cannot include '%s'", ca->name,
- also->name);
+ DBG1(DBG_APP, " bad argument value in conn '%s'", conn->name);
+ cfg->err++;
+ continue;
}
- else
+ if (!assigned)
{
- DBG2(DBG_APP, "ca '%s' includes '%s'", ca->name, also->name);
- /* only load if no error occurred in the first round */
- if (cfg->err == 0)
- load_ca(ca, kw, cfg);
+ handle_keyword(token, conn, key, value, cfg);
}
- also = also->next;
}
-}
+ enumerator->destroy(enumerator);
+ dict->destroy(dict);
-/*
- * find a ca included by also
- */
-static kw_list_t* find_also_ca(const char* name, starter_ca_t *ca,
- starter_config_t *cfg)
-{
- starter_ca_t *c = cfg->ca_first;
+ handle_firewall("left", &conn->left, cfg);
+ handle_firewall("right", &conn->right, cfg);
- while (c != NULL)
+ if (!conn->esp && !conn->ah)
{
- if (streq(name, c->name))
- {
- if (ca->visit == c->visit)
- {
- DBG1(DBG_APP, "# detected also loop");
- cfg->err++;
- return NULL;
- }
- c->visit = ca->visit;
- load_also_cas(ca, c->also, cfg);
- return c->kw;
- }
- c = c->next;
+ conn->esp = strdupnull(esp_defaults);
}
-
- DBG1(DBG_APP, "# also '%s' not found", name);
- cfg->err++;
- return NULL;
}
/*
- * free the memory used by also_t objects
+ * free the memory used by a starter_ca_t object
*/
-static void free_also(also_t *head)
+static void confread_free_ca(starter_ca_t *ca)
{
- while (head != NULL)
- {
- also_t *also = head;
-
- head = also->next;
- free(also->name);
- free(also);
- }
+ free_args(KW_CA_NAME, KW_CA_LAST, (char *)ca);
}
/*
@@ -781,17 +668,6 @@ static void confread_free_conn(starter_conn_t *conn)
free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->left);
free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->right);
free_args(KW_CONN_NAME, KW_CONN_LAST, (char *)conn);
- free_also(conn->also);
-}
-
-/*
- * free the memory used by a starter_ca_t object
- */
-static void
-confread_free_ca(starter_ca_t *ca)
-{
- free_args(KW_CA_NAME, KW_CA_LAST, (char *)ca);
- free_also(ca->also);
}
/*
@@ -804,8 +680,6 @@ void confread_free(starter_config_t *cfg)
free_args(KW_SETUP_FIRST, KW_SETUP_LAST, (char *)cfg);
- confread_free_conn(&cfg->conn_default);
-
while (conn != NULL)
{
starter_conn_t *conn_aux = conn;
@@ -815,8 +689,6 @@ void confread_free(starter_config_t *cfg)
free(conn_aux);
}
- confread_free_ca(&cfg->ca_default);
-
while (ca != NULL)
{
starter_ca_t *ca_aux = ca;
@@ -834,170 +706,108 @@ void confread_free(starter_config_t *cfg)
*/
starter_config_t* confread_load(const char *file)
{
+ conf_parser_t *parser;
starter_config_t *cfg = NULL;
- config_parsed_t *cfgp;
- section_list_t *sconn, *sca;
- starter_conn_t *conn;
- starter_ca_t *ca;
-
+ enumerator_t *enumerator;
u_int total_err;
- u_int visit = 0;
+ char *name;
- /* load IPSec configuration file */
- cfgp = parser_load_conf(file);
- if (!cfgp)
+ parser = conf_parser_create(file);
+ if (!parser->parse(parser))
{
+ parser->destroy(parser);
return NULL;
}
- cfg = malloc_thing(starter_config_t);
-
- /* set default values */
- default_values(cfg);
- /* load config setup section */
- load_setup(cfg, cfgp);
-
- /* in the first round parse also statements */
- cfg->parse_also = TRUE;
+ INIT(cfg,
+ .setup = {
+ .uniqueids = TRUE,
- /* find %default ca section */
- for (sca = cfgp->ca_first; sca; sca = sca->next)
- {
- if (streq(sca->name, "%default"))
- {
- DBG2(DBG_APP, "Loading ca %%default");
- load_ca(&cfg->ca_default, sca->kw, cfg);
}
- }
+ );
+#ifdef START_CHARON
+ cfg->setup.charonstart = TRUE;
+#endif
- /* parameters defined in ca %default sections can be overloads */
- cfg->ca_default.seen = SEEN_NONE;
+ /* load config setup section */
+ load_setup(cfg, parser);
- /* load other ca sections */
- for (sca = cfgp->ca_first; sca; sca = sca->next)
+ /* load ca sections */
+ enumerator = parser->get_sections(parser, CONF_PARSER_CA);
+ while (enumerator->enumerate(enumerator, &name))
{
- u_int previous_err;
+ u_int previous_err = cfg->err;
+ starter_ca_t *ca;
- /* skip %default ca section */
- if (streq(sca->name, "%default"))
- continue;
-
- DBG2(DBG_APP, "Loading ca '%s'", sca->name);
- ca = malloc_thing(starter_ca_t);
-
- ca_default(sca->name, ca, &cfg->ca_default);
- ca->kw = sca->kw;
- ca->next = NULL;
+ INIT(ca,
+ .name = strdup(name),
+ );
+ load_ca(ca, cfg, parser);
- previous_err = cfg->err;
- load_ca(ca, ca->kw, cfg);
if (cfg->err > previous_err)
{
- /* errors occurred - free the ca */
confread_free_ca(ca);
cfg->non_fatal_err += cfg->err - previous_err;
cfg->err = previous_err;
}
else
{
- /* success - insert the ca into the chained list */
if (cfg->ca_last)
+ {
cfg->ca_last->next = ca;
+ }
cfg->ca_last = ca;
if (!cfg->ca_first)
+ {
cfg->ca_first = ca;
+ }
+ if (ca->startup != STARTUP_NO)
+ {
+ ca->state = STATE_TO_ADD;
+ }
}
}
+ enumerator->destroy(enumerator);
- for (ca = cfg->ca_first; ca; ca = ca->next)
- {
- also_t *also = ca->also;
-
- while (also != NULL)
- {
- kw_list_t *kw = find_also_ca(also->name, cfg->ca_first, cfg);
-
- load_ca(ca, kw, cfg);
- also = also->next;
- }
-
- if (ca->startup != STARTUP_NO)
- ca->state = STATE_TO_ADD;
- }
-
- /* find %default conn sections */
- for (sconn = cfgp->conn_first; sconn; sconn = sconn->next)
- {
- if (streq(sconn->name, "%default"))
- {
- DBG2(DBG_APP, "Loading conn %%default");
- load_conn(&cfg->conn_default, sconn->kw, cfg);
- }
- }
-
- /* parameters defined in conn %default sections can be overloaded */
- cfg->conn_default.seen = SEEN_NONE;
- cfg->conn_default.right.seen = SEEN_NONE;
- cfg->conn_default.left.seen = SEEN_NONE;
-
- /* load other conn sections */
- for (sconn = cfgp->conn_first; sconn; sconn = sconn->next)
+ /* load conn sections */
+ enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
+ while (enumerator->enumerate(enumerator, &name))
{
- u_int previous_err;
-
- /* skip %default conn section */
- if (streq(sconn->name, "%default"))
- continue;
-
- DBG2(DBG_APP, "Loading conn '%s'", sconn->name);
- conn = malloc_thing(starter_conn_t);
+ u_int previous_err = cfg->err;
+ starter_conn_t *conn;
- conn_default(sconn->name, conn, &cfg->conn_default);
- conn->kw = sconn->kw;
- conn->next = NULL;
+ INIT(conn,
+ .name = strdup(name),
+ );
+ conn_defaults(conn);
+ load_conn(conn, cfg, parser);
- previous_err = cfg->err;
- load_conn(conn, conn->kw, cfg);
if (cfg->err > previous_err)
{
- /* error occurred - free the conn */
confread_free_conn(conn);
cfg->non_fatal_err += cfg->err - previous_err;
cfg->err = previous_err;
}
else
{
- /* success - insert the conn into the chained list */
if (cfg->conn_last)
+ {
cfg->conn_last->next = conn;
+ }
cfg->conn_last = conn;
if (!cfg->conn_first)
+ {
cfg->conn_first = conn;
+ }
+ if (conn->startup != STARTUP_NO)
+ {
+ conn->state = STATE_TO_ADD;
+ }
}
}
+ enumerator->destroy(enumerator);
- /* in the second round do not parse also statements */
- cfg->parse_also = FALSE;
-
- for (ca = cfg->ca_first; ca; ca = ca->next)
- {
- ca->visit = ++visit;
- load_also_cas(ca, ca->also, cfg);
-
- if (ca->startup != STARTUP_NO)
- ca->state = STATE_TO_ADD;
- }
-
- for (conn = cfg->conn_first; conn; conn = conn->next)
- {
- conn->visit = ++visit;
- load_also_conns(conn, conn->also, cfg);
-
- if (conn->startup != STARTUP_NO)
- conn->state = STATE_TO_ADD;
- }
-
- parser_free_conf(cfgp);
+ parser->destroy(parser);
total_err = cfg->err + cfg->non_fatal_err;
if (total_err > 0)
@@ -1005,6 +815,5 @@ starter_config_t* confread_load(const char *file)
DBG1(DBG_APP, "### %d parsing error%s (%d fatal) ###",
total_err, (total_err > 1)?"s":"", cfg->err);
}
-
return cfg;
}
diff --git a/src/starter/confread.h b/src/starter/confread.h
index a32f8cba4..0eea98849 100644
--- a/src/starter/confread.h
+++ b/src/starter/confread.h
@@ -18,13 +18,6 @@
#include <kernel/kernel_ipsec.h>
-#include "ipsec-parser.h"
-
-/** to mark seen keywords */
-typedef u_int64_t seen_t;
-#define SEEN_NONE 0;
-#define SEEN_KW(kw, base) ((seen_t)1 << ((kw) - (base)))
-
typedef enum {
STARTUP_NO,
STARTUP_ADD,
@@ -92,7 +85,6 @@ typedef enum {
typedef struct starter_end starter_end_t;
struct starter_end {
- seen_t seen;
char *auth;
char *auth2;
char *id;
@@ -121,22 +113,10 @@ struct starter_end {
char *dns;
};
-typedef struct also also_t;
-
-struct also {
- char *name;
- bool included;
- also_t *next;
-};
-
typedef struct starter_conn starter_conn_t;
struct starter_conn {
- seen_t seen;
char *name;
- also_t *also;
- kw_list_t *kw;
- u_int visit;
startup_t startup;
starter_state_t state;
@@ -193,11 +173,7 @@ struct starter_conn {
typedef struct starter_ca starter_ca_t;
struct starter_ca {
- seen_t seen;
char *name;
- also_t *also;
- kw_list_t *kw;
- u_int visit;
startup_t startup;
starter_state_t state;
@@ -217,7 +193,6 @@ typedef struct starter_config starter_config_t;
struct starter_config {
struct {
- seen_t seen;
bool charonstart;
char *charondebug;
bool uniqueids;
@@ -229,23 +204,14 @@ struct starter_config {
u_int err;
u_int non_fatal_err;
- /* do we parse also statements */
- bool parse_also;
-
- /* ca %default */
- starter_ca_t ca_default;
-
- /* connections list (without %default) */
+ /* connections list */
starter_ca_t *ca_first, *ca_last;
- /* conn %default */
- starter_conn_t conn_default;
-
- /* connections list (without %default) */
+ /* connections list */
starter_conn_t *conn_first, *conn_last;
};
-extern starter_config_t *confread_load(const char *file);
-extern void confread_free(starter_config_t *cfg);
+starter_config_t *confread_load(const char *file);
+void confread_free(starter_config_t *cfg);
#endif /* _IPSEC_CONFREAD_H_ */
diff --git a/src/starter/ipsec-parser.h b/src/starter/ipsec-parser.h
deleted file mode 100644
index 1c6cf20ef..000000000
--- a/src/starter/ipsec-parser.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* strongSwan config file parser
- * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
- *
- * 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.
- */
-
-#ifndef _IPSEC_PARSER_H_
-#define _IPSEC_PARSER_H_
-
-#include "keywords.h"
-
-typedef struct kw_entry kw_entry_t;
-
-struct kw_entry {
- char *name;
- kw_token_t token;
-};
-
-typedef struct kw_list kw_list_t;
-
-struct kw_list {
- kw_entry_t *entry;
- char *value;
- kw_list_t *next;
-};
-
-typedef struct section_list section_list_t;
-
-struct section_list {
- char *name;
- kw_list_t *kw;
- section_list_t *next;
-};
-
-typedef struct config_parsed config_parsed_t;
-
-struct config_parsed {
- kw_list_t *config_setup;
- section_list_t *conn_first, *conn_last;
- section_list_t *ca_first, *ca_last;
-};
-
-config_parsed_t *parser_load_conf (const char *file);
-void parser_free_conf (config_parsed_t *cfg);
-
-#endif /* _IPSEC_PARSER_H_ */
-
diff --git a/src/starter/keywords.h b/src/starter/keywords.h
index 5b6b28bf8..94af493f8 100644
--- a/src/starter/keywords.h
+++ b/src/starter/keywords.h
@@ -16,7 +16,10 @@
#ifndef _KEYWORDS_H_
#define _KEYWORDS_H_
-typedef enum {
+typedef enum kw_token_t kw_token_t;
+typedef struct kw_entry_t kw_entry_t;
+
+enum kw_token_t {
/* config setup keywords */
KW_CHARONDEBUG,
KW_UNIQUEIDS,
@@ -185,6 +188,11 @@ typedef enum {
KW_ALSO,
KW_AUTO,
-} kw_token_t;
+};
+
+struct kw_entry_t {
+ char *name;
+ kw_token_t token;
+};
#endif /* _KEYWORDS_H_ */
diff --git a/src/starter/lexer.l b/src/starter/lexer.l
deleted file mode 100644
index 734776a74..000000000
--- a/src/starter/lexer.l
+++ /dev/null
@@ -1,215 +0,0 @@
-%option noinput
-%option nounput
-%{
-/* FreeS/WAN config file parser (parser.l)
- * Copyright (C) 2001 Mathieu Lafon - Arkoon Network Security
- *
- * 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 <string.h>
-#include <stdlib.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif
-
-#include "parser.h"
-
-#define MAX_INCLUDE_DEPTH 20
-
-extern void yyerror(const char *);
-extern int yylex(void);
-extern int yylex_destroy(void);
-
-static struct {
- int stack_ptr;
- YY_BUFFER_STATE stack[MAX_INCLUDE_DEPTH];
- FILE *file[MAX_INCLUDE_DEPTH];
- unsigned int line[MAX_INCLUDE_DEPTH];
- char *filename[MAX_INCLUDE_DEPTH];
-} __parser_y_private;
-
-void _parser_y_error(char *b, int size, const char *s);
-void _parser_y_init (const char *f);
-void _parser_y_fini (void);
-int _parser_y_include (const char *filename);
-
-void _parser_y_error(char *b, int size, const char *s)
-{
- extern char *yytext; // was: char yytext[];
-
- snprintf(b, size, "%s:%d: %s [%s]",
- __parser_y_private.filename[__parser_y_private.stack_ptr],
- __parser_y_private.line[__parser_y_private.stack_ptr],
- s, yytext);
-}
-
-void _parser_y_init (const char *f)
-{
- memset(&__parser_y_private, 0, sizeof(__parser_y_private));
- __parser_y_private.line[0] = 1;
- __parser_y_private.filename[0] = strdup(f);
-}
-
-void _parser_y_fini (void)
-{
- unsigned int i;
-
- for (i = 0; i < MAX_INCLUDE_DEPTH; i++)
- {
- if (__parser_y_private.filename[i])
- free(__parser_y_private.filename[i]);
- if (__parser_y_private.file[i])
- fclose(__parser_y_private.file[i]);
- }
- memset(&__parser_y_private, 0, sizeof(__parser_y_private));
- yylex_destroy();
-}
-
-/**
- * parse the file located at filename
- */
-int include_file(char *filename)
-{
- unsigned int p = __parser_y_private.stack_ptr + 1;
- FILE *f;
-
- if (p >= MAX_INCLUDE_DEPTH)
- {
- yyerror("max inclusion depth reached");
- return 1;
- }
-
- f = fopen(filename, "r");
- if (!f)
- {
- yyerror("can't open include filename");
- return 0; /* ignore this error */
- }
-
- __parser_y_private.stack_ptr++;
- __parser_y_private.file[p] = f;
- __parser_y_private.stack[p] = YY_CURRENT_BUFFER;
- __parser_y_private.line[p] = 1;
- __parser_y_private.filename[p] = strdup(filename);
-
- yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
- return 0;
-}
-
-int _parser_y_include (const char *filename)
-{
- int ret = 0;
-#ifdef HAVE_GLOB_H
- {
- glob_t files;
- int i;
-
- ret = glob(filename, GLOB_ERR, NULL, &files);
- if (ret)
- {
- const char *err;
-
- switch (ret)
- {
- case GLOB_NOSPACE:
- err = "include files ran out of memory";
- break;
- case GLOB_ABORTED:
- err = "include files aborted due to read error";
- break;
- case GLOB_NOMATCH:
- err = "include files found no matches";
- break;
- default:
- err = "unknown include files error";
- }
- globfree(&files);
- yyerror(err);
- return 1;
- }
-
- for (i = 0; i < files.gl_pathc; i++)
- {
- if ((ret = include_file(files.gl_pathv[i])))
- {
- break;
- }
- }
- globfree(&files);
- }
-#else /* HAVE_GLOB_H */
- /* if glob(3) is not available, try to load pattern directly */
- ret = include_file(filename);
-#endif /* HAVE_GLOB_H */
- return ret;
-}
-
-%}
-
-%%
-
-<<EOF>> {
- if (__parser_y_private.filename[__parser_y_private.stack_ptr]) {
- free(__parser_y_private.filename[__parser_y_private.stack_ptr]);
- __parser_y_private.filename[__parser_y_private.stack_ptr] = NULL;
- }
- if (__parser_y_private.file[__parser_y_private.stack_ptr]) {
- fclose(__parser_y_private.file[__parser_y_private.stack_ptr]);
- __parser_y_private.file[__parser_y_private.stack_ptr] = NULL;
- yy_delete_buffer (YY_CURRENT_BUFFER);
- yy_switch_to_buffer
- (__parser_y_private.stack[__parser_y_private.stack_ptr]);
- }
- if (--__parser_y_private.stack_ptr < 0) {
- yyterminate();
- }
-}
-
-^[\t ]+ return FIRST_SPACES;
-
-[\t ]+ /* ignore spaces in line */ ;
-
-= return EQUAL;
-
-\n|#.*\n {
- __parser_y_private.line[__parser_y_private.stack_ptr]++;
- return EOL;
- }
-
-config return CONFIG;
-setup return SETUP;
-conn return CONN;
-ca return CA;
-include return INCLUDE;
-version return FILE_VERSION;
-
-[^\"= \t\n]+ {
- yylval.s = strdup(yytext);
- return STRING;
- }
-
-\"[^\"\n]*\" {
- yylval.s = strdup(yytext+1);
- if (yylval.s) yylval.s[strlen(yylval.s)-1]='\0';
- return STRING;
- }
-
-. yyerror(yytext);
-
-%%
-
-int yywrap(void)
-{
- return 1;
-}
-
diff --git a/src/starter/parser.y b/src/starter/parser.y
deleted file mode 100644
index 2cf0501f4..000000000
--- a/src/starter/parser.y
+++ /dev/null
@@ -1,272 +0,0 @@
-%{
-/* strongSwan config file parser (parser.y)
- * Copyright (C) 2001 Mathieu Lafon - Arkoon Network Security
- *
- * 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 <stdlib.h>
-#include <string.h>
-
-#include <library.h>
-#include <utils/debug.h>
-
-#include "ipsec-parser.h"
-
-#define YYERROR_VERBOSE
-#define ERRSTRING_LEN 256
-
-/**
- * Bison
- */
-static char parser_errstring[ERRSTRING_LEN+1];
-
-extern void yyerror(const char *s);
-extern int yylex (void);
-extern void _parser_y_error(char *b, int size, const char *s);
-
-/**
- * Static Globals
- */
-static int _save_errors_;
-static config_parsed_t *_parser_cfg;
-static kw_list_t **_parser_kw, *_parser_kw_last;
-static char errbuf[ERRSTRING_LEN+1];
-
-/**
- * Gperf
- */
-extern kw_entry_t *in_word_set (char *str, unsigned int len);
-
-%}
-
-%union { char *s; };
-%token EQUAL FIRST_SPACES EOL CONFIG SETUP CONN CA INCLUDE FILE_VERSION
-%token <s> STRING
-
-%%
-
-/*
- * Config file
- */
-
-config_file:
- config_file section_or_include
- | /* NULL */
- ;
-
-section_or_include:
- FILE_VERSION STRING EOL
- {
- free($2);
- }
- | CONFIG SETUP EOL
- {
- _parser_kw = &(_parser_cfg->config_setup);
- _parser_kw_last = NULL;
- } kw_section
- | CONN STRING EOL
- {
- section_list_t *section = malloc_thing(section_list_t);
-
- section->name = strdupnull($2);
- section->kw = NULL;
- section->next = NULL;
- _parser_kw = &(section->kw);
- if (!_parser_cfg->conn_first)
- _parser_cfg->conn_first = section;
- if (_parser_cfg->conn_last)
- _parser_cfg->conn_last->next = section;
- _parser_cfg->conn_last = section;
- _parser_kw_last = NULL;
- free($2);
- } kw_section
- | CA STRING EOL
- {
- section_list_t *section = malloc_thing(section_list_t);
- section->name = strdupnull($2);
- section->kw = NULL;
- section->next = NULL;
- _parser_kw = &(section->kw);
- if (!_parser_cfg->ca_first)
- _parser_cfg->ca_first = section;
- if (_parser_cfg->ca_last)
- _parser_cfg->ca_last->next = section;
- _parser_cfg->ca_last = section;
- _parser_kw_last = NULL;
- free($2);
- } kw_section
- | INCLUDE STRING
- {
- extern void _parser_y_include (const char *f);
- _parser_y_include($2);
- free($2);
- } EOL
- | EOL
- ;
-
-kw_section:
- FIRST_SPACES statement_kw EOL kw_section
- |
- ;
-
-statement_kw:
- STRING EQUAL STRING
- {
- kw_list_t *new;
- kw_entry_t *entry = in_word_set($1, strlen($1));
-
- if (entry == NULL)
- {
- snprintf(errbuf, ERRSTRING_LEN, "unknown keyword '%s'", $1);
- yyerror(errbuf);
- }
- else if (_parser_kw)
- {
- new = (kw_list_t *)malloc_thing(kw_list_t);
- new->entry = entry;
- new->value = strdupnull($3);
- new->next = NULL;
- if (_parser_kw_last)
- _parser_kw_last->next = new;
- _parser_kw_last = new;
- if (!*_parser_kw)
- *_parser_kw = new;
- }
- free($1);
- free($3);
- }
- | STRING EQUAL
- {
- free($1);
- }
- |
- ;
-
-%%
-
-void yyerror(const char *s)
-{
- if (_save_errors_)
- _parser_y_error(parser_errstring, ERRSTRING_LEN, s);
-}
-
-config_parsed_t *parser_load_conf(const char *file)
-{
- config_parsed_t *cfg = NULL;
- int err = 0;
- FILE *f;
-
- extern void _parser_y_init(const char *f);
- extern void _parser_y_fini(void);
- extern FILE *yyin;
-
- memset(parser_errstring, 0, ERRSTRING_LEN+1);
-
- cfg = (config_parsed_t *)malloc_thing(config_parsed_t);
- if (cfg)
- {
- memset(cfg, 0, sizeof(config_parsed_t));
- f = fopen(file, "r");
- if (f)
- {
- yyin = f;
- _parser_y_init(file);
- _save_errors_ = 1;
- _parser_cfg = cfg;
-
- if (yyparse() !=0 )
- {
- if (parser_errstring[0] == '\0')
- {
- snprintf(parser_errstring, ERRSTRING_LEN, "Unknown error...");
- }
- _save_errors_ = 0;
- while (yyparse() != 0);
- err++;
- }
- else if (parser_errstring[0] != '\0')
- {
- err++;
- }
- else
- {
- /**
- * Config valid
- */
- }
-
- fclose(f);
- }
- else
- {
- snprintf(parser_errstring, ERRSTRING_LEN, "can't load file '%s'", file);
- err++;
- }
- }
- else
- {
- snprintf(parser_errstring, ERRSTRING_LEN, "can't allocate memory");
- err++;
- }
-
- if (err)
- {
- DBG1(DBG_APP, "%s", parser_errstring);
-
- if (cfg)
- parser_free_conf(cfg);
- cfg = NULL;
- }
-
- _parser_y_fini();
- return cfg;
-}
-
-static void parser_free_kwlist(kw_list_t *list)
-{
- kw_list_t *elt;
-
- while (list)
- {
- elt = list;
- list = list->next;
- free(elt->value);
- free(elt);
- }
-}
-
-void parser_free_conf(config_parsed_t *cfg)
-{
- section_list_t *sec;
- if (cfg)
- {
- parser_free_kwlist(cfg->config_setup);
- while (cfg->conn_first)
- {
- sec = cfg->conn_first;
- cfg->conn_first = cfg->conn_first->next;
- free(sec->name);
- parser_free_kwlist(sec->kw);
- free(sec);
- }
- while (cfg->ca_first)
- {
- sec = cfg->ca_first;
- cfg->ca_first = cfg->ca_first->next;
- free(sec->name);
- parser_free_kwlist(sec->kw);
- free(sec);
- }
- free(cfg);
- }
-}
diff --git a/src/starter/parser/conf_parser.c b/src/starter/parser/conf_parser.c
new file mode 100644
index 000000000..119e37961
--- /dev/null
+++ b/src/starter/parser/conf_parser.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2013-2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "conf_parser.h"
+
+#include <collections/array.h>
+#include <collections/hashtable.h>
+
+/**
+ * Provided by the generated parser
+ */
+bool conf_parser_parse_file(conf_parser_t *this, char *file);
+
+typedef struct private_conf_parser_t private_conf_parser_t;
+typedef struct section_t section_t;
+
+/**
+ * Private data
+ */
+struct private_conf_parser_t {
+
+ /**
+ * Public interface
+ */
+ conf_parser_t public;
+
+ /**
+ * Path to config file
+ */
+ char *file;
+
+ /**
+ * TRUE while parsing the config file
+ */
+ bool parsing;
+
+ /**
+ * Hashtable for ca sections, as section_t
+ */
+ hashtable_t *cas;
+
+ /**
+ * Hashtable for conn sections, as section_t
+ */
+ hashtable_t *conns;
+
+ /**
+ * Array to keep track of the order of conn sections, as section_t
+ */
+ array_t *conns_order;
+
+ /**
+ * config setup section
+ */
+ section_t *config_setup;
+
+ /**
+ * Pointer to the current section (the one added last) during parsing
+ */
+ section_t *current_section;
+
+ /**
+ * Refcount for this parser instance (also used by dictionaries)
+ */
+ refcount_t ref;
+};
+
+typedef struct {
+ /** Key of the setting */
+ char *key;
+ /** Value of the setting */
+ char *value;
+} setting_t;
+
+int setting_find(const void *a, const void *b)
+{
+ const char *key = a;
+ const setting_t *setting = b;
+ return strcmp(key, setting->key);
+}
+
+int setting_sort(const void *a, const void *b, void *user)
+{
+ const setting_t *sa = a, *sb = b;
+ return strcmp(sa->key, sb->key);
+}
+
+static void setting_destroy(setting_t *this)
+{
+ free(this->key);
+ free(this->value);
+ free(this);
+}
+
+struct section_t {
+ /** Name of the section */
+ char *name;
+ /** Sorted array of settings, as setting_t */
+ array_t *settings;
+ /** Array of also= settings (in reversed order, i.e. most important to least
+ * important), as setting_t */
+ array_t *also;
+ /** Array of linearized parent objects derived from also= settings, in their
+ * order of importance (most to least, i.e. %default) as section_t
+ * NULL if not yet determined */
+ array_t *parents;
+};
+
+static section_t *section_create(char *name)
+{
+ section_t *this;
+
+ INIT(this,
+ .name = name,
+ );
+ return this;
+}
+
+static void section_destroy(section_t *this)
+{
+ array_destroy_function(this->settings, (void*)setting_destroy, NULL);
+ array_destroy_function(this->also, (void*)setting_destroy, NULL);
+ array_destroy(this->parents);
+ free(this->name);
+ free(this);
+}
+
+typedef struct {
+ /** Public interface */
+ dictionary_t public;
+ /** Parser object */
+ private_conf_parser_t *parser;
+ /** Section object */
+ section_t *section;
+} section_dictionary_t;
+
+typedef struct {
+ /** Public interface */
+ enumerator_t public;
+ /** Current settings enumerator */
+ enumerator_t *settings;
+ /** Enumerator into parent list */
+ enumerator_t *parents;
+ /** Hashtable to keep track of already enumerated settings */
+ hashtable_t *seen;
+} dictionary_enumerator_t;
+
+METHOD(enumerator_t, dictionary_enumerate, bool,
+ dictionary_enumerator_t *this, char **key, char **value)
+{
+ setting_t *setting;
+ section_t *parent;
+
+ while (TRUE)
+ {
+ if (this->settings &&
+ this->settings->enumerate(this->settings, &setting))
+ {
+ if (this->seen->get(this->seen, setting->key))
+ {
+ continue;
+ }
+ this->seen->put(this->seen, setting->key, setting->key);
+ if (!setting->value)
+ {
+ continue;
+ }
+ if (key)
+ {
+ *key = setting->key;
+ }
+ if (value)
+ {
+ *value = setting->value;
+ }
+ return TRUE;
+ }
+ DESTROY_IF(this->settings);
+ this->settings = NULL;
+ if (this->parents &&
+ this->parents->enumerate(this->parents, &parent))
+ {
+ if (parent->settings)
+ {
+ this->settings = array_create_enumerator(parent->settings);
+ }
+ continue;
+ }
+ DESTROY_IF(this->parents);
+ this->parents = NULL;
+ break;
+ }
+ return FALSE;
+}
+
+METHOD(enumerator_t, dictionary_enumerator_destroy, void,
+ dictionary_enumerator_t *this)
+{
+ DESTROY_IF(this->settings);
+ DESTROY_IF(this->parents);
+ this->seen->destroy(this->seen);
+ free(this);
+}
+
+METHOD(dictionary_t, dictionary_create_enumerator, enumerator_t*,
+ section_dictionary_t *this)
+{
+ dictionary_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_dictionary_enumerate,
+ .destroy = _dictionary_enumerator_destroy,
+ },
+ .seen = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8),
+ );
+ if (this->section->settings)
+ {
+ enumerator->settings = array_create_enumerator(this->section->settings);
+ }
+ if (this->section->parents)
+ {
+ enumerator->parents = array_create_enumerator(this->section->parents);
+ }
+ return &enumerator->public;
+}
+
+METHOD(dictionary_t, dictionary_get, void*,
+ section_dictionary_t *this, const void *key)
+{
+ enumerator_t *parents;
+ section_t *section;
+ setting_t *setting;
+ char *value = NULL;
+
+ section = this->section;
+ if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
+ {
+ return setting->value;
+ }
+ parents = array_create_enumerator(section->parents);
+ while (parents->enumerate(parents, &section))
+ {
+ if (array_bsearch(section->settings, key, setting_find, &setting) != -1)
+ {
+ value = setting->value;
+ break;
+ }
+ }
+ parents->destroy(parents);
+ return value;
+}
+
+METHOD(dictionary_t, dictionary_destroy, void,
+ section_dictionary_t *this)
+{
+ this->parser->public.destroy(&this->parser->public);
+ free(this);
+}
+
+static dictionary_t *section_dictionary_create(private_conf_parser_t *parser,
+ section_t *section)
+{
+ section_dictionary_t *this;
+
+ INIT(this,
+ .public = {
+ .create_enumerator = _dictionary_create_enumerator,
+ .get = _dictionary_get,
+ .destroy = _dictionary_destroy,
+ },
+ .parser = parser,
+ .section = section,
+ );
+
+ ref_get(&parser->ref);
+
+ return &this->public;
+}
+
+static bool conn_filter(void *unused, section_t **section, char **name)
+{
+ *name = (*section)->name;
+ return TRUE;
+}
+
+static bool ca_filter(void *unused, void *key, char **name, section_t **section)
+{
+ *name = (*section)->name;
+ return TRUE;
+}
+
+METHOD(conf_parser_t, get_sections, enumerator_t*,
+ private_conf_parser_t *this, conf_parser_section_t type)
+{
+ switch (type)
+ {
+ case CONF_PARSER_CONN:
+ return enumerator_create_filter(
+ array_create_enumerator(this->conns_order),
+ (void*)conn_filter, NULL, NULL);
+ case CONF_PARSER_CA:
+ return enumerator_create_filter(
+ this->cas->create_enumerator(this->cas),
+ (void*)ca_filter, NULL, NULL);
+ case CONF_PARSER_CONFIG_SETUP:
+ default:
+ return enumerator_create_empty();
+ }
+}
+
+METHOD(conf_parser_t, get_section, dictionary_t*,
+ private_conf_parser_t *this, conf_parser_section_t type, char *name)
+{
+ section_t *section = NULL;
+
+ switch (type)
+ {
+ case CONF_PARSER_CONFIG_SETUP:
+ section = this->config_setup;
+ break;
+ case CONF_PARSER_CONN:
+ section = this->conns->get(this->conns, name);
+ break;
+ case CONF_PARSER_CA:
+ section = this->cas->get(this->cas, name);
+ break;
+ default:
+ break;
+ }
+ return section ? section_dictionary_create(this, section) : NULL;
+}
+
+METHOD(conf_parser_t, add_section, bool,
+ private_conf_parser_t *this, conf_parser_section_t type, char *name)
+{
+ hashtable_t *sections = this->conns;
+ array_t *order = this->conns_order;
+ section_t *section = NULL;
+ bool exists = FALSE;
+
+ if (!this->parsing)
+ {
+ free(name);
+ return exists;
+ }
+ switch (type)
+ {
+ case CONF_PARSER_CONFIG_SETUP:
+ section = this->config_setup;
+ /* we don't expect a name, but just in case */
+ free(name);
+ break;
+ case CONF_PARSER_CA:
+ sections = this->cas;
+ order = NULL;
+ /* fall-through */
+ case CONF_PARSER_CONN:
+ section = sections->get(sections, name);
+ if (!section)
+ {
+ section = section_create(name);
+ sections->put(sections, name, section);
+ if (order)
+ {
+ array_insert(order, ARRAY_TAIL, section);
+ }
+ }
+ else
+ {
+ exists = TRUE;
+ free(name);
+ }
+ break;
+
+ }
+ this->current_section = section;
+ return exists;
+}
+
+METHOD(conf_parser_t, add_setting, void,
+ private_conf_parser_t *this, char *key, char *value)
+{
+ section_t *section = this->current_section;
+ setting_t *setting;
+
+ if (!this->parsing || !this->current_section)
+ {
+ free(key);
+ free(value);
+ return;
+ }
+ if (streq(key, "also"))
+ {
+ if (!value || !strlen(value) || streq(value, "%default"))
+ { /* we require a name, but all sections inherit from %default */
+ free(key);
+ free(value);
+ return;
+ }
+ INIT(setting,
+ .key = key,
+ .value = value,
+ );
+ array_insert_create(&section->also, ARRAY_HEAD, setting);
+ return;
+ }
+ if (array_bsearch(section->settings, key, setting_find, &setting) == -1)
+ {
+ INIT(setting,
+ .key = key,
+ .value = value,
+ );
+ array_insert_create(&section->settings, ARRAY_TAIL, setting);
+ array_sort(section->settings, setting_sort, NULL);
+ }
+ else
+ {
+ free(setting->value);
+ setting->value = value;
+ free(key);
+ }
+}
+
+/**
+ * Check if the given section is contained in the given array. The search
+ * starts at the given index.
+ */
+static bool is_contained_in(array_t *arr, section_t *section)
+{
+ section_t *current;
+ int i;
+
+ for (i = 0; i < array_count(arr); i++)
+ {
+ array_get(arr, i, &current);
+ if (streq(section->name, current->name))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * This algorithm to linearize sections uses a bottom-first depth-first
+ * semantic, with an additional elimination step that removes all but the
+ * last occurrence of each section.
+ *
+ * Consider this configuration:
+ *
+ * conn A
+ * conn B
+ * also=A
+ * conn C
+ * also=A
+ * conn D
+ * also=C
+ * also=B
+ *
+ * The linearization would yield D B A C A, which gets reduced to D B C A.
+ *
+ * Ambiguous configurations are handled pragmatically.
+ *
+ * Consider the following configuration:
+ *
+ * conn A
+ * conn B
+ * conn C
+ * also=A
+ * also=B
+ * conn D
+ * also=B
+ * also=A
+ * conn E
+ * also=C
+ * also=D
+ *
+ * It is ambiguous because D and C include the same two sections but in
+ * a different order.
+ *
+ * The linearization would yield E D A B C B A which gets reduced to E D C B A.
+ */
+static bool resolve_also_single(hashtable_t *sections,
+ section_t *section, section_t *def, array_t *stack)
+{
+ enumerator_t *enumerator;
+ array_t *parents;
+ section_t *parent, *grandparent;
+ setting_t *also;
+ bool success = TRUE;
+ int i;
+
+ array_insert(stack, ARRAY_HEAD, section);
+ parents = array_create(0, 0);
+ enumerator = array_create_enumerator(section->also);
+ while (enumerator->enumerate(enumerator, &also))
+ {
+ parent = sections->get(sections, also->value);
+ if (!parent || is_contained_in(stack, parent))
+ {
+ if (!parent)
+ {
+ DBG1(DBG_CFG, "section '%s' referenced in section '%s' not "
+ "found", also->value, section->name);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "section '%s' referenced in section '%s' causes "
+ "a loop", parent->name, section->name);
+ }
+ array_remove_at(section->also, enumerator);
+ setting_destroy(also);
+ success = FALSE;
+ continue;
+ }
+ if (!parent->parents)
+ {
+ if (!resolve_also_single(sections, parent, def, stack))
+ {
+ success = FALSE;
+ continue;
+ }
+ }
+ /* add the grandparents and the parent to the list */
+ array_insert(parents, ARRAY_TAIL, parent);
+ for (i = 0; i < array_count(parent->parents); i++)
+ {
+ array_get(parent->parents, i, &grandparent);
+ array_insert(parents, ARRAY_TAIL, grandparent);
+ }
+ }
+ enumerator->destroy(enumerator);
+ array_remove(stack, ARRAY_HEAD, NULL);
+
+ if (success && def && !array_count(parents))
+ {
+ array_insert(parents, ARRAY_TAIL, def);
+ }
+
+ while (success && array_remove(parents, ARRAY_HEAD, &parent))
+ {
+ if (!is_contained_in(parents, parent))
+ { /* last occurrence of this section */
+ array_insert_create(&section->parents, ARRAY_TAIL, parent);
+ }
+ }
+ array_destroy(parents);
+ return success;
+}
+
+/**
+ * Resolve also= statements. The functions returns TRUE if everything is fine,
+ * or FALSE if either a referenced section does not exist, or if the section
+ * inheritance can't be determined properly (e.g. if there are loops or if a
+ * section inherits from multiple sections - perhaps over several levels - in
+ * an ambiguous way).
+ */
+static bool resolve_also(hashtable_t *sections)
+{
+ enumerator_t *enumerator;
+ section_t *def, *section;
+ array_t *stack;
+ bool success = TRUE;
+
+ stack = array_create(0, 0);
+
+ def = sections->get(sections, "%default");
+ if (def)
+ { /* the default section is the only one with an empty parents list */
+ def->parents = array_create(0, 0);
+ }
+ enumerator = sections->create_enumerator(sections);
+ while (enumerator->enumerate(enumerator, NULL, &section))
+ {
+ if (section->parents)
+ { /* already determined */
+ continue;
+ }
+ success = resolve_also_single(sections, section, def, stack) && success;
+ }
+ enumerator->destroy(enumerator);
+ array_destroy(stack);
+ return success;
+}
+
+METHOD(conf_parser_t, parse, bool,
+ private_conf_parser_t *this)
+{
+ bool success;
+
+ if (!this->file)
+ { /* no file, lets assume this is OK */
+ return TRUE;
+ }
+ this->parsing = TRUE;
+ success = conf_parser_parse_file(&this->public, this->file);
+ this->parsing = FALSE;
+ return success && resolve_also(this->conns) && resolve_also(this->cas);
+}
+
+METHOD(conf_parser_t, destroy, void,
+ private_conf_parser_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ this->cas->destroy_function(this->cas, (void*)section_destroy);
+ this->conns->destroy_function(this->conns, (void*)section_destroy);
+ section_destroy(this->config_setup);
+ array_destroy(this->conns_order);
+ free(this->file);
+ free(this);
+ }
+}
+
+/*
+ * Described in header
+ */
+conf_parser_t *conf_parser_create(const char *file)
+{
+ private_conf_parser_t *this;
+
+ INIT(this,
+ .public = {
+ .parse = _parse,
+ .get_sections = _get_sections,
+ .get_section = _get_section,
+ .add_section = _add_section,
+ .add_setting = _add_setting,
+ .destroy = _destroy,
+ },
+ .file = strdupnull(file),
+ .cas = hashtable_create(hashtable_hash_str,
+ hashtable_equals_str, 8),
+ .conns = hashtable_create(hashtable_hash_str,
+ hashtable_equals_str, 8),
+ .conns_order = array_create(0, 0),
+ .config_setup = section_create(NULL),
+ .ref = 1,
+ );
+
+ return &this->public;
+}
diff --git a/src/starter/parser/conf_parser.h b/src/starter/parser/conf_parser.h
new file mode 100644
index 000000000..a55b7cb53
--- /dev/null
+++ b/src/starter/parser/conf_parser.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013-2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup conf_parser conf_parser
+ * @{ @ingroup starter
+ */
+
+#ifndef CONF_PARSER_H_
+#define CONF_PARSER_H_
+
+#include <library.h>
+#include <collections/dictionary.h>
+
+typedef enum conf_parser_section_t conf_parser_section_t;
+typedef struct conf_parser_t conf_parser_t;
+
+/**
+ * Type of section
+ */
+enum conf_parser_section_t {
+ /**
+ * config setup
+ */
+ CONF_PARSER_CONFIG_SETUP,
+
+ /**
+ * conn <name>
+ */
+ CONF_PARSER_CONN,
+
+ /**
+ * ca <name>
+ */
+ CONF_PARSER_CA,
+};
+
+/**
+ * Parser for ipsec.conf
+ */
+struct conf_parser_t {
+
+ /**
+ * Parse the config file.
+ *
+ * @return TRUE if config file was parsed successfully
+ */
+ bool (*parse)(conf_parser_t *this);
+
+ /**
+ * Get the names of all sections of the given type.
+ *
+ * @note Returns an empty enumerator for the config setup section.
+ *
+ * @return enumerator over char*
+ */
+ enumerator_t *(*get_sections)(conf_parser_t *this,
+ conf_parser_section_t type);
+
+ /**
+ * Get the section with the given type and name.
+ *
+ * @note The name is ignored for the config setup section.
+ *
+ * @return dictionary with settings
+ */
+ dictionary_t *(*get_section)(conf_parser_t *this,
+ conf_parser_section_t type, char *name);
+
+ /**
+ * Add a section while parsing.
+ *
+ * @note This method can only be called while parsing the config file.
+ *
+ * @param type type of section to add
+ * @param name name of the section, if applicable (gets adopted)
+ * @return TRUE if the section already existed (settings get added)
+ */
+ bool (*add_section)(conf_parser_t *this, conf_parser_section_t type,
+ char *name);
+
+ /**
+ * Add a key/value pair to the latest section.
+ *
+ * @note This method can only be called while parsing the config file.
+ *
+ * @param name key string (gets adopted)
+ * @param value optional value string (gets adopted), if no value is
+ * specified the key is set empty
+ */
+ void (*add_setting)(conf_parser_t *this, char *key, char *value);
+
+
+ /**
+ * Destroy a conf_parser_t instance.
+ */
+ void (*destroy)(conf_parser_t *this);
+};
+
+/**
+ * Create a conf_parser_t instance.
+ *
+ * @param file ipsec.conf file to parse (gets copied)
+ * @return conf_parser_t instance
+ */
+conf_parser_t *conf_parser_create(const char *file);
+
+#endif /** CONF_PARSER_H_ @}*/ \ No newline at end of file
diff --git a/src/starter/parser/lexer.l b/src/starter/parser/lexer.l
new file mode 100644
index 000000000..a88cbe809
--- /dev/null
+++ b/src/starter/parser/lexer.l
@@ -0,0 +1,205 @@
+%{
+/*
+ * Copyright (C) 2013-2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <utils/parser_helper.h>
+#include <parser/conf_parser.h>
+
+#include "parser.h"
+
+bool conf_parser_open_next_file(parser_helper_t *ctx);
+
+static void include_files(parser_helper_t *ctx);
+
+%}
+%option debug
+%option warn
+
+/* use start conditions stack */
+%option stack
+
+/* do not declare unneded functions */
+%option noinput noyywrap
+
+/* don't use global variables, and interact properly with bison */
+%option reentrant bison-bridge
+
+/* maintain the line number */
+%option yylineno
+
+/* don't generate a default rule */
+%option nodefault
+
+/* prefix function/variable declarations */
+%option prefix="conf_parser_"
+/* don't change the name of the output file otherwise autotools has issues */
+%option outfile="lex.yy.c"
+
+/* type of our extra data */
+%option extra-type="parser_helper_t*"
+
+/* state used to scan include file patterns */
+%x inc
+/* state used to scan quoted strings */
+%x str
+
+%%
+
+^[\t ]*"version"[^\n]*$ /* eat legacy version delcaration */
+^[\t ]+ return SPACES;
+[\t ]+ /* eat other whitespace */
+[\t ]*#[^\n]* /* eat comments */
+
+\n return NEWLINE;
+
+"=" return EQ;
+^"config setup" return CONFIG_SETUP;
+^"conn" return CONN;
+^"ca" return CA;
+
+"include"[\t ]+/[^=] {
+ yyextra->string_init(yyextra);
+ yy_push_state(inc, yyscanner);
+}
+
+"\"" {
+ yyextra->string_init(yyextra);
+ yy_push_state(str, yyscanner);
+}
+
+(@#)?[^\"#= \t\n]+ {
+ yylval->s = strdup(yytext);
+ return STRING;
+}
+
+<inc>{
+ /* we allow all characters except # and spaces, they can be escaped */
+ <<EOF>> |
+ [#\n\t ] {
+ if (*yytext)
+ {
+ switch (yytext[0])
+ {
+ case '\n':
+ /* put the newline back to fix the line numbers */
+ unput('\n');
+ yy_set_bol(0);
+ break;
+ case '#':
+ /* comments are parsed outside of this start condition */
+ unput(yytext[0]);
+ break;
+ }
+ }
+ include_files(yyextra);
+ yy_pop_state(yyscanner);
+ }
+ "\"" { /* string include */
+ yy_push_state(str, yyscanner);
+ }
+ \\ {
+ yyextra->string_add(yyextra, yytext);
+ }
+ \\["#} ] {
+ yyextra->string_add(yyextra, yytext+1);
+ }
+ [^"\\#\n\t ]+ {
+ yyextra->string_add(yyextra, yytext);
+ }
+}
+
+<str>{
+ "\"" |
+ <<EOF>> |
+ \n |
+ \\ {
+ if (!streq(yytext, "\""))
+ {
+ if (streq(yytext, "\n"))
+ { /* put the newline back to fix the line numbers */
+ unput('\n');
+ yy_set_bol(0);
+ }
+ PARSER_DBG1(yyextra, "unterminated string detected");
+ }
+ if (yy_top_state(yyscanner) == inc)
+ { /* string include */
+ include_files(yyextra);
+ yy_pop_state(yyscanner);
+ yy_pop_state(yyscanner);
+ }
+ else
+ {
+ yy_pop_state(yyscanner);
+ yylval->s = yyextra->string_get(yyextra);
+ return STRING;
+ }
+ }
+ \\n yyextra->string_add(yyextra, "\n");
+ \\r yyextra->string_add(yyextra, "\r");
+ \\t yyextra->string_add(yyextra, "\t");
+ \\b yyextra->string_add(yyextra, "\b");
+ \\f yyextra->string_add(yyextra, "\f");
+ \\(.|\n) {
+ yyextra->string_add(yyextra, yytext+1);
+ }
+ [^\\\n"]+ {
+ yyextra->string_add(yyextra, yytext);
+ }
+}
+
+<<EOF>> {
+ conf_parser_pop_buffer_state(yyscanner);
+ if (!conf_parser_open_next_file(yyextra) && !YY_CURRENT_BUFFER)
+ {
+ yyterminate();
+ }
+}
+
+%%
+
+/**
+ * Open the next file, if any is queued and readable, otherwise returns FALSE.
+ */
+bool conf_parser_open_next_file(parser_helper_t *ctx)
+{
+ FILE *file;
+
+ file = ctx->file_next(ctx);
+ if (!file)
+ {
+ return FALSE;
+ }
+
+ conf_parser_set_in(file, ctx->scanner);
+ conf_parser_push_buffer_state(
+ conf_parser__create_buffer(file, YY_BUF_SIZE,
+ ctx->scanner), ctx->scanner);
+ return TRUE;
+}
+
+/**
+ * Assumes that the file pattern to include is currently stored as string on
+ * the helper object.
+ */
+static void include_files(parser_helper_t *ctx)
+{
+ char *pattern = ctx->string_get(ctx);
+
+ ctx->file_include(ctx, pattern);
+ free(pattern);
+
+ conf_parser_open_next_file(ctx);
+}
diff --git a/src/starter/parser/parser.y b/src/starter/parser/parser.y
new file mode 100644
index 000000000..54dedc12b
--- /dev/null
+++ b/src/starter/parser/parser.y
@@ -0,0 +1,254 @@
+%{
+/*
+ * Copyright (C) 2013-2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE /* for asprintf() */
+#include <stdio.h>
+
+#include <utils/parser_helper.h>
+#include <settings/settings_types.h>
+#include <parser/conf_parser.h>
+
+#include "parser.h"
+
+#define YYDEBUG 1
+
+/**
+ * Defined by the lexer
+ */
+int conf_parser_lex(YYSTYPE *lvalp, void *scanner);
+int conf_parser_lex_init_extra(parser_helper_t *extra, void *scanner);
+int conf_parser_lex_destroy(void *scanner);
+int conf_parser_set_in(FILE *in, void *scanner);
+void conf_parser_set_debug(int debug, void *scanner);
+char *conf_parser_get_text(void *scanner);
+int conf_parser_get_leng(void *scanner);
+int conf_parser_get_lineno(void *scanner);
+/* Custom functions in lexer */
+bool conf_parser_open_next_file(parser_helper_t *ctx);
+
+/**
+ * Forward declaration
+ */
+static void conf_parser_error(parser_helper_t *ctx, const char *s);
+
+/**
+ * Make sure to call lexer with the proper context
+ */
+#undef yylex
+static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
+{
+ return conf_parser_lex(lvalp, ctx->scanner);
+}
+
+%}
+%debug
+
+/* generate verbose error messages */
+%error-verbose
+/* generate a reentrant parser */
+%define api.pure
+/* prefix function/variable declarations */
+%name-prefix "conf_parser_"
+
+/* interact properly with the reentrant lexer */
+%lex-param {parser_helper_t *ctx}
+%parse-param {parser_helper_t *ctx}
+
+/* types for terminal symbols... */
+%union {
+ char *s;
+ conf_parser_section_t t;
+}
+%token <s> STRING
+%token EQ SPACES NEWLINE CONFIG_SETUP CONN CA
+
+/* ...and other symbols */
+%type <t> section_type
+%type <s> section_name value
+
+/* make the equal sign left associative */
+%left EQ
+
+/* properly destroy STRING tokens, which are strdup()ed, on errors */
+%destructor { free($$); } STRING section_name value
+
+/* there are two shift/reduce conflicts because we allow empty lines (and lines
+ * with spaces) within settings and anywhere else (i.e. in the beginning) */
+//%expect 2
+
+%%
+
+/**
+ * ipsec.conf grammar rules
+ */
+statements:
+ /* empty */
+ | statements NEWLINE
+ | statements statement
+ ;
+
+statement:
+ section
+ | SPACES setting
+ ;
+
+section:
+ section_type section_name
+ {
+ if ($1 != CONF_PARSER_CONFIG_SETUP && (!$2 || !strlen($2)))
+ {
+ PARSER_DBG1(ctx, "section name missing");
+ free($2);
+ YYERROR;
+ }
+ conf_parser_t *parser = (conf_parser_t*)ctx->context;
+ parser->add_section(parser, $1, $2);
+ }
+ ;
+
+section_type:
+ CONFIG_SETUP
+ {
+ $$ = CONF_PARSER_CONFIG_SETUP;
+ }
+ |
+ CONN
+ {
+ $$ = CONF_PARSER_CONN;
+ }
+ |
+ CA
+ {
+ $$ = CONF_PARSER_CA;
+ }
+ ;
+
+section_name:
+ /* empty */
+ {
+ $$ = NULL;
+ }
+ | STRING
+ {
+ $$ = $1;
+ }
+ ;
+
+setting:
+ /* empty */
+ |
+ STRING EQ value
+ {
+ if (!strlen($1))
+ {
+ PARSER_DBG1(ctx, "setting name can't be empty");
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ conf_parser_t *parser = (conf_parser_t*)ctx->context;
+ parser->add_setting(parser, $1, $value);
+ }
+ |
+ STRING EQ
+ {
+ if (!strlen($1))
+ {
+ PARSER_DBG1(ctx, "setting name can't be empty");
+ free($1);
+ YYERROR;
+ }
+ conf_parser_t *parser = (conf_parser_t*)ctx->context;
+ parser->add_setting(parser, $1, NULL);
+ }
+ |
+ STRING
+ {
+ PARSER_DBG1(ctx, "missing value for setting '%s'", $1);
+ free($1);
+ YYERROR;
+ }
+ ;
+
+value:
+ STRING
+ | value STRING
+ { /* just put a single space between them, use strings for more */
+ if (asprintf(&$$, "%s %s", $1, $2) < 0)
+ {
+ free($1);
+ free($2);
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ ;
+
+%%
+
+/**
+ * Referenced by the generated parser
+ */
+static void conf_parser_error(parser_helper_t *ctx, const char *s)
+{
+ char *text = conf_parser_get_text(ctx->scanner);
+ int len = conf_parser_get_leng(ctx->scanner);
+
+ if (len && text[len-1] == '\n')
+ { /* cut off newline at the end to avoid muti-line log messages */
+ len--;
+ }
+ PARSER_DBG1(ctx, "%s [%.*s]", s, (int)len, text);
+}
+
+/**
+ * Parse the given file
+ */
+bool conf_parser_parse_file(conf_parser_t *this, char *name)
+{
+ parser_helper_t *helper;
+ bool success = FALSE;
+
+ helper = parser_helper_create(this);
+ helper->get_lineno = conf_parser_get_lineno;
+ if (conf_parser_lex_init_extra(helper, &helper->scanner) != 0)
+ {
+ helper->destroy(helper);
+ return FALSE;
+ }
+ helper->file_include(helper, name);
+ if (!conf_parser_open_next_file(helper))
+ {
+ DBG1(DBG_CFG, "failed to open config file '%s'", name);
+ }
+ else
+ {
+ if (getenv("DEBUG_CONF_PARSER"))
+ {
+ yydebug = 1;
+ conf_parser_set_debug(1, helper->scanner);
+ }
+ success = yyparse(helper) == 0;
+ if (!success)
+ {
+ DBG1(DBG_CFG, "invalid config file '%s'", name);
+ }
+ }
+ conf_parser_lex_destroy(helper->scanner);
+ helper->destroy(helper);
+ return success;
+}
diff --git a/src/starter/starter.c b/src/starter/starter.c
index 33916c95c..ef5780843 100644
--- a/src/starter/starter.c
+++ b/src/starter/starter.c
@@ -418,6 +418,7 @@ int main (int argc, char **argv)
bool no_fork = FALSE;
bool attach_gdb = FALSE;
bool load_warning = FALSE;
+ bool conftest = FALSE;
library_init(NULL, "starter");
atexit(library_deinit);
@@ -467,6 +468,10 @@ int main (int argc, char **argv)
{
config_file = argv[++i];
}
+ else if (streq(argv[i], "--conftest"))
+ {
+ conftest = TRUE;
+ }
else
{
usage(argv[0]);
@@ -485,6 +490,28 @@ int main (int argc, char **argv)
init_log("ipsec_starter");
+ if (conftest)
+ {
+ int status = LSB_RC_SUCCESS;
+
+ cfg = confread_load(config_file);
+ if (cfg == NULL || cfg->err > 0)
+ {
+ DBG1(DBG_APP, "config invalid!");
+ status = LSB_RC_INVALID_ARGUMENT;
+ }
+ else
+ {
+ DBG1(DBG_APP, "config OK");
+ }
+ if (cfg)
+ {
+ confread_free(cfg);
+ }
+ cleanup();
+ exit(status);
+ }
+
DBG1(DBG_APP, "Starting %sSwan "VERSION" IPsec [starter]...",
lib->settings->get_bool(lib->settings,
"charon.i_dont_care_about_security_and_use_aggressive_mode_psk",
diff --git a/src/starter/tests/.gitignore b/src/starter/tests/.gitignore
new file mode 100644
index 000000000..c4c6471e8
--- /dev/null
+++ b/src/starter/tests/.gitignore
@@ -0,0 +1 @@
+starter_tests \ No newline at end of file
diff --git a/src/starter/tests/Makefile.am b/src/starter/tests/Makefile.am
new file mode 100644
index 000000000..f84327b6b
--- /dev/null
+++ b/src/starter/tests/Makefile.am
@@ -0,0 +1,19 @@
+TESTS = starter_tests
+
+check_PROGRAMS = $(TESTS)
+
+starter_tests_SOURCES = \
+ suites/test_parser.c \
+ starter_tests.h starter_tests.c
+
+starter_tests_CFLAGS = \
+ -I$(top_srcdir)/src/libstrongswan \
+ -I$(top_srcdir)/src/libstrongswan/tests \
+ -I$(top_srcdir)/src/starter \
+ @COVERAGE_CFLAGS@
+
+starter_tests_LDFLAGS = @COVERAGE_LDFLAGS@
+starter_tests_LDADD = \
+ $(top_builddir)/src/libstrongswan/libstrongswan.la \
+ $(top_builddir)/src/libstrongswan/tests/libtest.la \
+ ../libstarter.la
diff --git a/src/starter/tests/starter_tests.c b/src/starter/tests/starter_tests.c
new file mode 100644
index 000000000..4194c5256
--- /dev/null
+++ b/src/starter/tests/starter_tests.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <test_runner.h>
+
+/* declare test suite constructors */
+#define TEST_SUITE(x) test_suite_t* x();
+#include "starter_tests.h"
+#undef TEST_SUITE
+
+static test_configuration_t tests[] = {
+#define TEST_SUITE(x) \
+ { .suite = x, },
+#include "starter_tests.h"
+ { .suite = NULL, }
+};
+
+static bool test_runner_init(bool init)
+{
+ if (!init)
+ {
+ lib->processor->set_threads(lib->processor, 0);
+ lib->processor->cancel(lib->processor);
+ }
+ return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_runner_run("stroke", tests, test_runner_init);
+}
diff --git a/src/starter/tests/starter_tests.h b/src/starter/tests/starter_tests.h
new file mode 100644
index 000000000..3486597a0
--- /dev/null
+++ b/src/starter/tests/starter_tests.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+TEST_SUITE(parser_suite_create)
diff --git a/src/starter/tests/suites/test_parser.c b/src/starter/tests/suites/test_parser.c
new file mode 100644
index 000000000..684bc2a27
--- /dev/null
+++ b/src/starter/tests/suites/test_parser.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <unistd.h>
+
+#include <test_suite.h>
+
+#include "../../parser/conf_parser.h"
+
+static char *path = "/tmp/strongswan-starter-parser-test";
+static conf_parser_t *parser;
+
+static void create_parser(chunk_t contents)
+{
+ ck_assert(chunk_write(contents, path, 0022, TRUE));
+ parser = conf_parser_create(path);
+}
+
+START_TEARDOWN(teardown_parser)
+{
+ parser->destroy(parser);
+ unlink(path);
+}
+END_TEARDOWN
+
+START_TEST(test_get_sections_config_setup)
+{
+ enumerator_t *enumerator;
+
+ create_parser(chunk_from_str(""));
+ ck_assert(parser->parse(parser));
+ enumerator = parser->get_sections(parser, CONF_PARSER_CONFIG_SETUP);
+ ck_assert(enumerator);
+ ck_assert(!enumerator->enumerate(enumerator, NULL));
+ enumerator->destroy(enumerator);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+ ck_assert(parser->parse(parser));
+ enumerator = parser->get_sections(parser, CONF_PARSER_CONFIG_SETUP);
+ ck_assert(enumerator);
+ ck_assert(!enumerator->enumerate(enumerator, NULL));
+ enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_get_sections_conn)
+{
+ enumerator_t *enumerator;
+ char *name;
+
+ create_parser(chunk_from_str(""));
+ ck_assert(parser->parse(parser));
+ enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
+ ck_assert(enumerator);
+ ck_assert(!enumerator->enumerate(enumerator, NULL));
+ enumerator->destroy(enumerator);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str(
+ "conn foo\n"
+ "conn bar\n"
+ "conn foo\n"));
+ ck_assert(parser->parse(parser));
+ enumerator = parser->get_sections(parser, CONF_PARSER_CONN);
+ ck_assert(enumerator);
+ ck_assert(enumerator->enumerate(enumerator, &name));
+ ck_assert_str_eq("foo", name);
+ ck_assert(enumerator->enumerate(enumerator, &name));
+ ck_assert_str_eq("bar", name);
+ ck_assert(!enumerator->enumerate(enumerator, &name));
+ enumerator->destroy(enumerator);
+}
+END_TEST
+
+START_TEST(test_get_section_config_setup)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(""));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, "foo");
+ ck_assert(dict);
+ dict->destroy(dict);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, NULL);
+ ck_assert(dict);
+ dict->destroy(dict);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str("config setup\n\tfoo=bar"));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONFIG_SETUP, "foo");
+ ck_assert(dict);
+ dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_get_section_conn)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(""));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(!dict);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str("conn foo\n"));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "bar"));
+ ck_assert(dict);
+ dict->destroy(dict);
+ parser->destroy(parser);
+
+ create_parser(chunk_from_str("conn foo\n\tfoo=bar"));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(dict);
+ dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_enumerate_values)
+{
+ enumerator_t *enumerator;
+ dictionary_t *dict;
+ char *key, *value;
+ int i;
+
+ create_parser(chunk_from_str(
+ "conn foo\n"
+ " foo=bar\n"
+ " bar=baz"));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(dict);
+ ck_assert_str_eq("bar", dict->get(dict, "foo"));
+ ck_assert_str_eq("baz", dict->get(dict, "bar"));
+ enumerator = dict->create_enumerator(dict);
+ for (i = 0; enumerator->enumerate(enumerator, &key, &value); i++)
+ {
+ if ((streq(key, "foo") && !streq(value, "bar")) ||
+ (streq(key, "bar") && !streq(value, "baz")))
+ {
+ fail("unexpected setting %s=%s", key, value);
+ }
+ }
+ enumerator->destroy(enumerator);
+ ck_assert_int_eq(i, 2);
+ dict->destroy(dict);
+}
+END_TEST
+
+#define extensibility_config(section) \
+ section "\n" \
+ " foo=bar\n" \
+ " dup=one\n" \
+ " dup=two\n" \
+ "\n" \
+ " nope=val\n" \
+ "\n" \
+ section "\n" \
+ " foo=baz\n" \
+ section "\n" \
+ " answer=42\n" \
+ " nope=\n"
+
+static struct {
+ char *conf;
+ conf_parser_section_t type;
+ char *name;
+} extensibility_data[] = {
+ { extensibility_config("config setup"), CONF_PARSER_CONFIG_SETUP, NULL },
+ { extensibility_config("ca ca-foo"), CONF_PARSER_CA, "ca-foo" },
+ { extensibility_config("conn conn-foo"), CONF_PARSER_CONN, "conn-foo" },
+};
+
+START_TEST(test_extensibility)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(extensibility_data[_i].conf));
+ ck_assert(parser->parse(parser));
+
+ dict = parser->get_section(parser, extensibility_data[_i].type,
+ extensibility_data[_i].name);
+ ck_assert(dict);
+ ck_assert_str_eq("baz", dict->get(dict, "foo"));
+ ck_assert_str_eq("two", dict->get(dict, "dup"));
+ ck_assert_str_eq("42", dict->get(dict, "answer"));
+ ck_assert(!dict->get(dict, "nope"));
+ ck_assert(!dict->get(dict, "anything"));
+ dict->destroy(dict);
+}
+END_TEST
+
+static struct {
+ char *conf;
+ bool check_section;
+ char *value;
+} comments_data[] = {
+ { "# conn foo", FALSE, NULL },
+ { "# conn foo\n", FALSE, NULL },
+ { "conn foo # asdf", TRUE, NULL },
+ { "conn foo # asdf", TRUE, NULL },
+ { "conn foo# asdf\n", TRUE, NULL },
+ { "conn foo # asdf\n\tkey=val", TRUE, "val" },
+ { "conn foo # asdf\n#\tkey=val", TRUE, NULL },
+ { "conn foo # asdf\n\t#key=val", TRUE, NULL },
+ { "conn foo # asdf\n\tkey=@#keyid", TRUE, "@#keyid" },
+ { "conn foo # asdf\n\tkey=\"@#keyid\"", TRUE, "@#keyid" },
+ { "conn foo # asdf\n\tkey=asdf@#keyid", TRUE, "asdf@" },
+ { "conn foo # asdf\n\tkey=#val", TRUE, NULL },
+ { "conn foo # asdf\n\tkey=val#asdf", TRUE, "val" },
+ { "conn foo # asdf\n\tkey=\"val#asdf\"", TRUE, "val#asdf" },
+ { "conn foo # asdf\n\tkey=val # asdf\n", TRUE, "val" },
+ { "conn foo # asdf\n# asdf\n\tkey=val\n", TRUE, "val" },
+ { "conn foo # asdf\n\t# asdf\n\tkey=val\n", TRUE, "val" },
+};
+
+START_TEST(test_comments)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(comments_data[_i].conf));
+ ck_assert(parser->parse(parser));
+ if (comments_data[_i].check_section)
+ {
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(dict);
+ if (comments_data[_i].value)
+ {
+ ck_assert_str_eq(comments_data[_i].value, dict->get(dict, "key"));
+ }
+ else
+ {
+ ck_assert(!dict->get(dict, "key"));
+ }
+ dict->destroy(dict);
+ }
+ else
+ {
+ ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "foo"));
+ }
+}
+END_TEST
+
+static struct {
+ char *conf;
+ bool check_section;
+ char *value;
+} whitespace_data[] = {
+ { "conn foo ", FALSE, NULL },
+ { "conn foo", FALSE, NULL },
+ { "conn foo\n", FALSE, NULL },
+ { "conn foo \n", FALSE, NULL },
+ { "conn foo\n ", FALSE, NULL },
+ { "conn foo\n \n", FALSE, NULL },
+ { "conn foo\nconn bar", TRUE, NULL },
+ { "conn foo\n \nconn bar", TRUE, NULL },
+ { "conn foo\n key=val", FALSE, "val" },
+ { "conn foo\n\tkey=val", FALSE, "val" },
+ { "conn foo\n\t \tkey=val", FALSE, "val" },
+ { "conn foo\n\tkey = val ", FALSE, "val" },
+};
+
+START_TEST(test_whitespace)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(whitespace_data[_i].conf));
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(dict);
+ if (whitespace_data[_i].value)
+ {
+ ck_assert_str_eq(whitespace_data[_i].value, dict->get(dict, "key"));
+ }
+ else
+ {
+ ck_assert(!dict->get(dict, "key"));
+ }
+ dict->destroy(dict);
+ if (whitespace_data[_i].check_section)
+ {
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "bar");
+ ck_assert(dict);
+ dict->destroy(dict);
+ }
+ else
+ {
+ ck_assert(!parser->get_section(parser, CONF_PARSER_CONN, "bar"));
+ }
+}
+END_TEST
+
+static struct {
+ bool valid;
+ char *conf;
+ char *section;
+ char *value;
+} strings_data[] = {
+ { FALSE, "\"conn foo\"", NULL, NULL },
+ { TRUE, "conn \"foo\"", "foo", NULL },
+ { FALSE, "conn foo bar", NULL, NULL },
+ { TRUE, "conn \"foo bar\"", "foo bar", NULL },
+ { TRUE, "conn \"#foo\"", "#foo", NULL },
+ { FALSE, "conn foo\n\t\"key=val\"", "foo", NULL },
+ { TRUE, "conn foo\n\t\"key\"=val", "foo", "val" },
+ { TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" },
+ { TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" },
+ { TRUE, "conn foo\n\tkey=\"val ue\"", "foo", "val ue" },
+ { TRUE, "conn foo\n\tkey=\"val\\nue\"", "foo", "val\nue" },
+};
+
+START_TEST(test_strings)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(strings_data[_i].conf));
+ ck_assert(parser->parse(parser) == strings_data[_i].valid);
+ if (strings_data[_i].section)
+ {
+ dict = parser->get_section(parser, CONF_PARSER_CONN,
+ strings_data[_i].section);
+ ck_assert(dict);
+ if (strings_data[_i].value)
+ {
+ ck_assert_str_eq(strings_data[_i].value, dict->get(dict, "key"));
+ }
+ else
+ {
+ ck_assert(!dict->get(dict, "key"));
+ }
+ dict->destroy(dict);
+ }
+}
+END_TEST
+
+START_TEST(test_refcounting)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(
+ "conn foo\n"
+ " key=val"));
+
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "foo");
+ ck_assert(dict);
+ ck_assert_str_eq("val", dict->get(dict, "key"));
+ parser->destroy(parser);
+ ck_assert_str_eq("val", dict->get(dict, "key"));
+ dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_also)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(
+ "conn A\n"
+ " key=vala\n"
+ " keya=val1\n"
+ " unset=set\n"
+ "conn B\n"
+ " also=A\n"
+ " key=valb\n"
+ " keyb=val2\n"
+ " unset=\n"
+ "conn C\n"
+ " keyc=val3\n"
+ " unset=set again\n"
+ " also=B\n"
+ "conn D\n"
+ " keyd=val4\n"
+ " also=A\n"
+ " also=B\n"
+ "conn E\n"
+ " keye=val5\n"
+ " also=B\n"
+ " also=A\n"
+ ""));
+
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "B");
+ ck_assert(dict);
+ ck_assert_str_eq("valb", dict->get(dict, "key"));
+ ck_assert_str_eq("val1", dict->get(dict, "keya"));
+ ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+ ck_assert(!dict->get(dict, "unset"));
+ dict->destroy(dict);
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "C");
+ ck_assert(dict);
+ ck_assert_str_eq("valb", dict->get(dict, "key"));
+ ck_assert_str_eq("val1", dict->get(dict, "keya"));
+ ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+ ck_assert_str_eq("val3", dict->get(dict, "keyc"));
+ ck_assert_str_eq("set again", dict->get(dict, "unset"));
+ dict->destroy(dict);
+ /* since B includes A too the inclusion in D and E has no effect */
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "D");
+ ck_assert(dict);
+ ck_assert_str_eq("valb", dict->get(dict, "key"));
+ ck_assert_str_eq("val1", dict->get(dict, "keya"));
+ ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+ ck_assert(!dict->get(dict, "keyc"));
+ ck_assert_str_eq("val4", dict->get(dict, "keyd"));
+ ck_assert(!dict->get(dict, "unset"));
+ dict->destroy(dict);
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "E");
+ ck_assert(dict);
+ ck_assert_str_eq("valb", dict->get(dict, "key"));
+ ck_assert_str_eq("val1", dict->get(dict, "keya"));
+ ck_assert_str_eq("val2", dict->get(dict, "keyb"));
+ ck_assert(!dict->get(dict, "keyc"));
+ ck_assert(!dict->get(dict, "keyd"));
+ ck_assert_str_eq("val5", dict->get(dict, "keye"));
+ ck_assert(!dict->get(dict, "unset"));
+ dict->destroy(dict);
+}
+END_TEST
+
+START_TEST(test_ambiguous)
+{
+ dictionary_t *dict;
+
+ create_parser(chunk_from_str(
+ "conn A\n"
+ " key=vala\n"
+ "conn B\n"
+ " key=valb\n"
+ "conn C\n"
+ " also=A\n"
+ " also=B\n"
+ "conn D\n"
+ " also=B\n"
+ " also=A\n"
+ "conn E\n"
+ " also=C\n"
+ " also=D\n"
+ "conn F\n"
+ " also=D\n"
+ " also=C\n"));
+
+ ck_assert(parser->parse(parser));
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "E");
+ ck_assert(dict);
+ ck_assert_str_eq("valb", dict->get(dict, "key"));
+ dict->destroy(dict);
+ dict = parser->get_section(parser, CONF_PARSER_CONN, "F");
+ ck_assert(dict);
+ ck_assert_str_eq("vala", dict->get(dict, "key"));
+ dict->destroy(dict);
+}
+END_TEST
+
+Suite *parser_suite_create()
+{
+ Suite *s;
+ TCase *tc;
+
+ s = suite_create("ipsec.conf parser");
+
+ tc = tcase_create("get_section(s)");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_test(tc, test_get_sections_config_setup);
+ tcase_add_test(tc, test_get_sections_conn);
+ tcase_add_test(tc, test_get_section_config_setup);
+ tcase_add_test(tc, test_get_section_conn);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("enumerate settings");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_test(tc, test_enumerate_values);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("extensibility");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_loop_test(tc, test_extensibility, 0, countof(extensibility_data));
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("comments");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_loop_test(tc, test_comments, 0, countof(comments_data));
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("whitespace");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_loop_test(tc, test_whitespace, 0, countof(whitespace_data));
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("strings");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_loop_test(tc, test_strings, 0, countof(strings_data));
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("refcounting");
+ tcase_add_test(tc, test_refcounting);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("also=");
+ tcase_add_checked_fixture(tc, NULL, teardown_parser);
+ tcase_add_test(tc, test_also);
+ tcase_add_test(tc, test_ambiguous);
+ suite_add_tcase(s, tc);
+
+ return s;
+}