Merge branch 'ipsec.conf-parser'

Replaces the ipsec.conf parser in starter.  The new parser is also based
on flex/bison but it simply returns key/value collections of all sections.
It already resolves also= and allows overriding options in all included
sections (not only %default), options set in included section can also
be cleared again (key=).  It provides other improvements too, like quoted
strings	(with escape sequences), unlimited includes and better
whitespace/comment handling.

Fixes #423.
Fixes #560.
This commit is contained in:
Tobias Brunner
2014-06-19 14:09:09 +02:00
28 changed files with 2519 additions and 1605 deletions
+1
View File
@@ -1701,6 +1701,7 @@ AC_CONFIG_FILES([
src/stroke/Makefile
src/ipsec/Makefile
src/starter/Makefile
src/starter/tests/Makefile
src/_updown/Makefile
src/_updown_espmark/Makefile
src/_copyright/Makefile
+1 -1
View File
@@ -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 \
@@ -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_ @}*/
+28 -11
View File
@@ -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,
@@ -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));
};
/**
+8 -2
View File
@@ -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
+3 -4
View File
@@ -1,5 +1,4 @@
starter
lexer.c
parser.h
parser.c
parser.output
parser/lexer.c
parser/parser.[ch]
parser/parser.output
+6 -5
View File
@@ -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 \
+19 -7
View File
@@ -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
-101
View File
@@ -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 <[email protected]>
o Version 0.2e -- 2004.10.14 -- Added support for change of interface address
by Stephan Scholz <[email protected]>
o Version 0.2s -- 2005-12-02 -- Ported to strongSwan
by Stephan Scholz <[email protected]>
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 <[email protected]>
THANKS
------
o Nathan Angelacos - include fix
+85 -273
View File
@@ -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);
*cp = strdupnull(value);
break;
}
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;
}
}
}
}
/* 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;
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;
}
}
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)
{
return FALSE;
if (*b1 != *b2)
{
return FALSE;
}
}
else
{
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;
+6 -11
View File
@@ -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_ */
+417 -608
View File
File diff suppressed because it is too large Load Diff
+4 -38
View File
@@ -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_ */
-55
View File
@@ -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_ */
+10 -2
View File
@@ -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_ */
-215
View File
@@ -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;
}
-272
View File
@@ -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);
}
}
+655
View File
@@ -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;
}
+120
View File
@@ -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_ @}*/
+205
View File
@@ -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);
}
+254
View File
@@ -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;
}
+27
View File
@@ -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",
+1
View File
@@ -0,0 +1 @@
starter_tests
+19
View File
@@ -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
+43
View File
@@ -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);
}
+16
View File
@@ -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)
+527
View File
@@ -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;
}