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:
@@ -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
|
||||
|
||||
@@ -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_ @}*/
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
starter
|
||||
lexer.c
|
||||
parser.h
|
||||
parser.c
|
||||
parser.output
|
||||
parser/lexer.c
|
||||
parser/parser.[ch]
|
||||
parser/parser.output
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+4
-38
@@ -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_ */
|
||||
|
||||
@@ -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
@@ -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_ */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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, §ion))
|
||||
{
|
||||
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(§ion->also, ARRAY_HEAD, setting);
|
||||
return;
|
||||
}
|
||||
if (array_bsearch(section->settings, key, setting_find, &setting) == -1)
|
||||
{
|
||||
INIT(setting,
|
||||
.key = key,
|
||||
.value = value,
|
||||
);
|
||||
array_insert_create(§ion->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, ¤t);
|
||||
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(§ion->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, §ion))
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
starter_tests
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user