diff --git a/.travis.yml b/.travis.yml index d46dd307..6b3fc7b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ addons: packages: - libunbound-dev - libidn11-dev + - libyaml-dev - check - libevent-dev - libev-dev diff --git a/Makefile.in b/Makefile.in index 422f6d80..2a18e98d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -245,7 +245,7 @@ $(distdir): cp $(srcdir)/spec/example/*.[ch] $(distdir)/spec/example cp $(srcdir)/src/tools/Makefile.in $(distdir)/src/tools cp $(srcdir)/src/tools/*.[ch] $(distdir)/src/tools - cp $(srcdir)/stubby/stubby.conf.example $(distdir)/stubby + cp $(srcdir)/stubby/stubby.yml.example $(distdir)/stubby cp $(srcdir)/stubby/stubby-setdns-macos.sh $(distdir)/stubby cp $(srcdir)/stubby/src/stubby.c $(distdir)/stubby/src cp $(srcdir)/stubby/COPYING $(distdir)/stubby diff --git a/README.md b/README.md index b54c8f73..65af0d07 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ External dependencies are linked outside the getdns API build tree (we rely on c * [libunbound from NLnet Labs](https://unbound.net/) version 1.4.16 or later. * [libidn from the FSF](https://www.gnu.org/software/libidn/) version 1. (Note that the libidn version means the conversions between A-labels and U-labels may permit conversion of formally invalid labels under IDNA2008.) * [libssl and libcrypto from the OpenSSL Project](https://www.openssl.org/) version 0.9.7 or later. (Note: version 1.0.1 or later is required for TLS support, version 1.0.2 or later is required for TLS hostname authentication) +* [libyaml](http://pyyaml.org/wiki/LibYAML) version 0.1.6 or later. * Doxygen is used to generate documentation; while this is not technically necessary for the build it makes things a lot more pleasant. For example, to build on a recent version of Ubuntu, you would need the following packages: @@ -99,6 +100,7 @@ Note: If you only want to build stubby, then use the `--with-stubby` option when * getdns can be configured for stub resolution mode only with the `--enable-stub-only` option to configure. This removes the dependency on `libunbound`. * Currently getdns only offers two helper functions to deal with IDN: `getdns_convert_ulabel_to_alabel` and `getdns_convert_alabel_to_ulabel`. If you do not need these functions, getdns can be configured to compile without them with the `--without-libidn` option to configure. +* getdns can be configured to not support YAML configuration with the `--disable-yaml-config` option to configure. This removes the dependency on `libyaml`. * When both `--enable-stub-only` and `--without-libidn` options are used, getdns has only one dependency left, which is OpenSSL. ## Extensions and Event loop dependencies diff --git a/configure.ac b/configure.ac index 65898022..80d64aaa 100644 --- a/configure.ac +++ b/configure.ac @@ -578,6 +578,17 @@ case "$enable_stub_only" in ;; esac +my_with_yaml=1 +AC_ARG_ENABLE(yaml-config, AC_HELP_STRING([--disable-yaml-config], [Remove support for YAML configuration. Removes the libyaml dependency.])) +case "$enable_yaml_config" in + no) + my_with_yaml=0 + ;; + yes|*) + AC_DEFINE_UNQUOTED([USE_YAML_CONFIG], [1], [Define this to enable YAML config support.]) + ;; +esac + if test "$USE_WINSOCK" = 1; then AC_MSG_NOTICE([ Building on Windows ... YES! ]) AC_DEFINE_UNQUOTED([GETDNS_ON_WINDOWS], [1], [Define this to enable Windows build.]) @@ -619,6 +630,30 @@ else fi fi +if test $my_with_yaml = 1 +then + AC_ARG_WITH(libyaml, AS_HELP_STRING([--with-libyaml=pathname], + [path to libyaml (default: search /usr/local ..)]), + [], [withval="yes"]) + if test x_$withval = x_yes; then + for dir in /usr/local /opt/local /usr/pkg /usr/sfw; do + if test -f "$dir/include/yaml.h"; then + CFLAGS="$CFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + AC_MSG_NOTICE([Found libyaml in $dir]) + break + fi + done + else + if test x_$withval != x_no; then + CFLAGS="$CFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + else + my_with_yaml=0 + fi + fi +fi + if test $my_with_libunbound = 1 then # find libunbound @@ -659,6 +694,16 @@ then ]) fi +if test $my_with_yaml = 1 +then + AC_MSG_NOTICE([Checking for dependency libyaml]) + AC_CHECK_LIB([yaml], [yaml_parser_parse], [], [ + MISSING_DEPS="${MISSING_DEPS}${MISSING_SEP}libyaml" + MISSING_SEP=", " + found_all_libs=0 + ]) +fi + AC_ARG_ENABLE(unbound-event-api, AC_HELP_STRING([--disable-unbound-event-api], [Disable usage of libunbounds event API])) case "$enable_unbound_event_api" in no) diff --git a/src/Makefile.in b/src/Makefile.in index 22056063..9f496d40 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -92,6 +92,8 @@ UTIL_OBJ=rbtree.lo val_secalgo.lo lruhash.lo lookup3.lo locks.lo JSMN_OBJ=jsmn.lo +YAML_OBJ=convert_yaml_to_json.lo + EXTENSION_OBJ=$(DEFAULT_EVENTLOOP_OBJ) libevent.lo libev.lo NON_C99_OBJS=context.lo libuv.lo @@ -123,6 +125,9 @@ $(UTIL_OBJ): $(JSMN_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -DJSMN_GETDNS -c $(srcdir)/jsmn/$(@:.lo=.c) -o $@ +$(YAML_OBJ): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $(srcdir)/yaml/$(@:.lo=.c) -o $@ + $(EXTENSION_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -c $(srcdir)/extension/$(@:.lo=.c) -o $@ @@ -173,8 +178,8 @@ libgetdns_ext_ev.la: libgetdns.la libev.lo $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ libev.lo libgetdns.la $(LDFLAGS) $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/extension/libev.symbols -libgetdns.la: $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols +libgetdns.la: $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YAML_OBJ) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YAML_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: default cd test && $(MAKE) $@ @@ -188,14 +193,14 @@ stubby.lo: $(stubbysrcdir)/src/stubby.c stubby: stubby.lo libgetdns.la $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ stubby.lo $(LDFLAGS) libgetdns.la -install-stubby: stubby $(stubbysrcdir)/stubby.conf.example $(stubbysrcdir)/stubby-setdns-macos.sh +install-stubby: stubby $(stubbysrcdir)/stubby.yml.example $(stubbysrcdir)/stubby-setdns-macos.sh $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) $(LIBTOOL) --mode=install cp stubby $(DESTDIR)$(bindir) $(INSTALL) -m 755 -d $(DESTDIR)$(sbindir) $(INSTALL) -m 755 $(stubbysrcdir)/stubby-setdns-macos.sh $(DESTDIR)$(sbindir) $(INSTALL) -m 755 -d $(DESTDIR)$(stubbyconfdir) - test -f $(DESTDIR)$(stubbyconfdir)/stubby.conf || \ - $(INSTALL_DATA) $(stubbysrcdir)/stubby.conf.example $(DESTDIR)$(stubbyconfdir)/stubby.conf + test -f $(DESTDIR)$(stubbyconfdir)/stubby.yml || \ + $(INSTALL_DATA) $(stubbysrcdir)/stubby.yml.example $(DESTDIR)$(stubbyconfdir)/stubby.yml uninstall-stubby: $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/stubby @@ -226,13 +231,14 @@ Makefile: $(srcdir)/Makefile.in ../config.status depend: (cd $(srcdir) ; awk 'BEGIN{P=1}{if(P)print}/^# Dependencies/{P=0}' Makefile.in > Makefile.in.new ) - (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" -Iutil/auxiliary *.c gldns/*.c compat/*.c util/*.c jsmn/*.c extension/*.c| \ + (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" -Iutil/auxiliary *.c gldns/*.c compat/*.c util/*.c jsmn/*.c yaml/*.c extension/*.c| \ sed -e "s? $$blddir/? ?g" \ -e 's? gldns/? $$(srcdir)/gldns/?g' \ -e 's? compat/? $$(srcdir)/compat/?g' \ -e 's? util/auxiliary/util/? $$(srcdir)/util/auxiliary/util/?g' \ -e 's? util/? $$(srcdir)/util/?g' \ -e 's? jsmn/? $$(srcdir)/jsmn/?g' \ + -e 's? yaml/? $$(srcdir)/yaml/?g' \ -e 's? extension/? $$(srcdir)/extension/?g' \ -e 's? \([a-z_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ -e 's? \$$(srcdir)/config\.h? config.h?g' \ diff --git a/src/context.c b/src/context.c index 707016dc..d87756a6 100644 --- a/src/context.c +++ b/src/context.c @@ -3718,6 +3718,8 @@ _get_context_settings(getdns_context* context) || ( context->edns_maximum_udp_payload_size != -1 && getdns_dict_set_int(result, "edns_maximum_udp_payload_size", context->edns_maximum_udp_payload_size)) + || getdns_dict_set_int(result, "edns_client_subnet_private", + context->edns_client_subnet_private) || getdns_dict_set_int(result, "edns_extended_rcode", context->edns_extended_rcode) || getdns_dict_set_int(result, "edns_version", @@ -3733,7 +3735,9 @@ _get_context_settings(getdns_context* context) || getdns_dict_set_int(result, "tls_backoff_time", context->tls_backoff_time) || getdns_dict_set_int(result, "tls_connection_retries", - context->tls_connection_retries)) + context->tls_connection_retries) + || getdns_dict_set_int(result, "tls_query_padding_blocksize", + context->tls_query_padding_blocksize)) goto error; /* list fields */ diff --git a/src/convert.c b/src/convert.c index 180db8f5..c9244781 100644 --- a/src/convert.c +++ b/src/convert.c @@ -54,6 +54,7 @@ #include "dict.h" #include "list.h" #include "jsmn/jsmn.h" +#include "yaml/convert_yaml_to_json.h" #include "convert.h" #include "debug.h" @@ -1802,3 +1803,99 @@ getdns_str2int(const char *str, uint32_t *value) return GETDNS_RETURN_GOOD; } +getdns_return_t +getdns_yaml2dict(const char *str, getdns_dict **dict) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !dict) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2dict(jsonstr, dict); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) dict; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + +getdns_return_t +getdns_yaml2list(const char *str, getdns_list **list) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !list) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2list(jsonstr, list); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) list; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + +getdns_return_t +getdns_yaml2bindata(const char *str, getdns_bindata **bindata) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !bindata) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2bindata(jsonstr, bindata); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) bindata; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + +getdns_return_t +getdns_yaml2int(const char *str, uint32_t *value) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !value) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2int(jsonstr, value); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) value; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index c32a5c14..45f354cd 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -1685,6 +1685,98 @@ getdns_str2bindata(const char *str, getdns_bindata **bindata); getdns_return_t getdns_str2int(const char *str, uint32_t *value); +/** @} + */ + +/** + * \defgroup Uyaml2getdns_data Converting YAML input to getdns data structures + * @{ + */ + +/** + * Convert YAML text to a getdns_dict. + * + * @param str A textual representation of a getdns_dict. + * The format is similar, but not precisely YAML. + * - When str contains an IP or IPv6 address, it is converted + * to an getdns dict representation of that address. This may contain + * a port, tls_port, tsig spec or tls authentication name in the same + * way as may be given with the `getdns_query` tool. For example: + * `185.49.140.67:80#443` will result in the following getdns_dict: + * + * { address_type: "IPv4" + * , address_data: "185.49.140.67" + * , port: 80 + * , tls_port: 443 + * } + * @param dict The returned getdns_dict. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_yaml2dict(const char *str, getdns_dict **dict); + +/** + * Convert YAML text to a getdns_list. + * + * @param str A textual representation of a getdns_list. + * @param list The returned getdns_list. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_yaml2list(const char *str, getdns_list **list); + +/** + * Convert string text to a getdns_bindata. + * + * @param str A textual representation of a getdns_bindata + * The format is similar, but not precisely YAML. + * - Strings between double-quotes will be converted to bindata + * containers, but *without the trailing null byte*. + * For example: `{ suffix: [ "nlnetlabs.nl.", "nlnet.nl." ] }` + * - bindata representation of IP or IPv6 addresses may be + * given in their presentation format. For example: + * `{ dns_root_servers: [ 2001:7fd::1, 193.0.14.129 ] }` + * - Arbitrary binary data may be given with a `0x` prefix, + * or in base64 encoding. + * For example: + * + * { add_opt_parameters: + * { options: [ { option_code: 10 + * , option_data: 0xA9E4EC50C03F5D65 + * } ] + * } + * } + * + * - Wireformat domain name bindatas can be given with a trailing dot. + * For example: + * + * { upstream_recursive_servers: + * [ { address_data : 2a04:b900:0:100::37 + * , tsig_name : hmac-md5.tsigs.getdnsapi.net. + * , tsig_algorithm: hmac-md5.sig-alg.reg.int. + * , tsig_secret : 16G69OTeXW6xSQ== + * } ] + * } + * @param bindata The returned getdns_bindata. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_yaml2bindata(const char *str, getdns_bindata **bindata); + +/** + * Convert string text to a getdns 32 bits unsigned integer. + * + * @param str A textual representation of the integer. + * The format is similar, but not precisely YAML. + * - integer values may be given by the constant name. + * For example: `{ resolution_type: GETDNS_RESOLUTION_STUB }` + * or `{ specify_class: GETDNS_RRCLASS_CH }` + * @param value The returned integer. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_yaml2int(const char *str, uint32_t *value); + /** @} */ diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 00b26848..36262fce 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -159,5 +159,9 @@ getdns_wire2msg_dict_scan getdns_wire2rr_dict getdns_wire2rr_dict_buf getdns_wire2rr_dict_scan +getdns_yaml2bindata +getdns_yaml2dict +getdns_yaml2int +getdns_yaml2list plain_mem_funcs_user_arg priv_getdns_context_mf diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.Makefile b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.Makefile new file mode 100644 index 00000000..c297df6d --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.Makefile @@ -0,0 +1,15 @@ +builddir = @BUILDDIR@ +testname = @TPKG_NAME@ +LIBTOOL = $(builddir)/libtool + +CFLAGS=-I$(builddir)/src +LDLIBS=$(builddir)/src/libgetdns.la + +.SUFFIXES: .c .o .a .lo .h + +.c.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ + +$(testname): $(testname).lo + $(LIBTOOL) --tag=CC --mode=link $(CC) $(LDLIBS) $(LDFLAGS) -o $(testname) $(testname).lo + diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c new file mode 100644 index 00000000..128195c0 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include + +int main(int ac, char *av[]) +{ + FILE *f; + char *buf = NULL; + size_t len; + ssize_t bytes_read; + + f = fopen(av[1], "r"); + if (!f) { + fprintf(stderr, "Could not open %s", av[1]); + exit(EXIT_FAILURE); + } + + bytes_read = getdelim(&buf, &len, '\0', f); + fclose(f); + + if (bytes_read == -1) { + fprintf(stderr, "Could not read %s", av[1]); + exit(EXIT_FAILURE); + } + + buf = realloc(buf, bytes_read + 1); + if (!buf) { + fprintf(stderr, "Could not grow buffer"); + exit(EXIT_FAILURE); + } + buf[bytes_read] = '\0'; + + getdns_dict *dict = NULL; + getdns_list *list = NULL; + getdns_bindata *bindata = NULL; + getdns_return_t r; + + if (!(dict = getdns_dict_create())) { + fprintf(stderr, "Could not create dict"); + goto fail; + } + + r = getdns_yaml2dict(buf, &dict); + if (r) { + fprintf(stderr, "Error setting dict data: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + /* + * Now add a list, bindata and int to the dict by hand to check + * the other yaml2* functions work. + */ + if (!(list = getdns_list_create())) { + fprintf(stderr, "Could not create list"); + goto fail; + } + + r = getdns_yaml2list("[\"One\", \"two\", \"three\"]", &list); + if (r) { + fprintf(stderr, "Error setting list data: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_dict_set_list(dict, "List entry", list); + if (r) { + fprintf(stderr, "Error adding list to dict: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_yaml2bindata("2001:7fd::1", &bindata); + if (r) { + fprintf(stderr, "Error setting bindata: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_dict_set_bindata(dict, "Bindata entry", bindata); + if (r) { + fprintf(stderr, "Error adding list to dict: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + uint32_t intval; + r = getdns_yaml2int("32767", &intval); + if (r) { + fprintf(stderr, "Error setting int: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + if (intval != 32767) { + fprintf(stderr, "Error reading int: wrong value"); + goto fail; + } + + char *dict_str = getdns_pretty_print_dict(dict); + if (!dict_str) { + fprintf(stderr, "Could not convert dict to string"); + goto fail; + } + + printf("%s\n", dict_str); + free(dict_str); + getdns_dict_destroy(dict); + exit(EXIT_SUCCESS); + +fail: + if (dict) + getdns_dict_destroy(dict); + if (list) + getdns_list_destroy(list); + exit(EXIT_FAILURE); +} diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc new file mode 100644 index 00000000..e00f4468 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc @@ -0,0 +1,16 @@ +BaseName: 255-yaml-config +Version: 1.0 +Description: Test YAML configuration +CreationDate: Wed 13 Sep 2017 12:42:32 BST +Maintainer: Jim Hague +Category: +Component: +CmdDepends: +Depends: 200-stub-only-compile.tpkg +Help: +Pre: 255-yaml-config.pre +Post: +Test: 255-yaml-config.test +AuxFiles: +Passed: +Failure: diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good new file mode 100644 index 00000000..2703569c --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good @@ -0,0 +1,87 @@ +{ + "Bindata entry": , + "List entry": + [ + , + , + + ], + "dnssec_return_status": 1000, + "upstream_recursive_servers": + [ + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": + } + ] +} diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input new file mode 100644 index 00000000..68e4460f --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input @@ -0,0 +1,47 @@ +upstream_recursive_servers: +# IPv4 addresses +# The Surfnet/Sinodun servers + - address_data: 145.100.185.15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 145.100.185.16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= +# The getdnsapi.net server + - address_data: 185.49.141.37 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= +# The uncensored DNS servers (no SPKI available) + - address_data: 184.105.193.78 + tls_auth_name: "unicast.censurfridns.dk" +# IPv6 addresses +# The Surfnet/Sinodun servers + - address_data: 2001:610:1:40ba:145:100:185:15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 2001:610:1:40ba:145:100:185:16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= +# The getdnsapi.net server + - address_data: 2a04:b900:0:100::38 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= +# The uncensored DNS server (no SPKI available) + - address_data: 2620:ff:c000:0:1::64:25 + tls_auth_name: "unicast.censurfridns.dk" + +# Require DNSSEC validation. For releases earlier than 1.2 a trust anchor must +# be configured configured manually. This can be done with unbound-anchor. +dnssec_return_status: GETDNS_EXTENSION_TRUE diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre new file mode 100644 index 00000000..802443c2 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre @@ -0,0 +1,14 @@ +# #-- 255-yaml-config.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +( + grep '^CC=' "${BUILDDIR}/build-stub-only/src/Makefile" + grep '^LDFLAGS=' "${BUILDDIR}/build-stub-only/src/Makefile" + + BUILDDIR4SED=`echo "${BUILDDIR}/build-stub-only" | sed 's/\//\\\\\//g'` + sed -e "s/@BUILDDIR@/${BUILDDIR4SED}/g" \ + -e "s/@TPKG_NAME@/${TPKG_NAME}/g" "${TPKG_NAME}.Makefile" +) > Makefile diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test new file mode 100644 index 00000000..b3735fb4 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test @@ -0,0 +1,7 @@ +# #-- 255-yaml-config.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +make && "./${TPKG_NAME}" 255-yaml-config.input | tee out && diff out "${TPKG_NAME}.good" diff --git a/src/tools/getdns_query.c b/src/tools/getdns_query.c index 582f2c0c..aec56f44 100644 --- a/src/tools/getdns_query.c +++ b/src/tools/getdns_query.c @@ -215,7 +215,8 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-C\t\n"); fprintf(out, "\t\tRead settings from config file \n"); fprintf(out, "\t\tThe getdns context will be configured with these settings\n"); - fprintf(out, "\t\tThe file must be in json dict format.\n"); + fprintf(out, "\t\tThe file must be in YAML format (with extension of '.yml')\n"); + fprintf(out, "\t\tor JSON dict format (with extension '.conf')\n"); if (i_am_stubby) { fprintf(out, "\t\tBy default, configuration is first read from"); fprintf(out, "\n\t\t\"/etc/stubby.conf\" and then from \"$HOME/.stubby.conf\"\n"); @@ -472,16 +473,25 @@ done: return r; } -static void parse_config(const char *config_str) +static void parse_config(const char *config_str, int yaml_config) { getdns_dict *config_dict; getdns_list *list; getdns_return_t r; - if ((r = getdns_str2dict(config_str, &config_dict))) + if (yaml_config) { +#ifdef USE_YAML_CONFIG + r = getdns_yaml2dict(config_str, &config_dict); +#else + fprintf(stderr, "Support for YAML configuration files not available.\n"); + return; +#endif + } else { + r = getdns_str2dict(config_str, &config_dict); + } + if (r) fprintf(stderr, "Could not parse config file: %s\n", getdns_get_errorstr_by_id(r)); - else { if (!(r = getdns_dict_get_list( config_dict, "listen_addresses", &list))) { @@ -561,7 +571,7 @@ int parse_config_file(const char *fn, int report_open_failure) } config_file[config_file_sz] = 0; fclose(fh); - parse_config(config_file); + parse_config(config_file, strstr(fn, ".yml") != NULL); free(config_file); return GETDNS_RETURN_GOOD; } @@ -650,7 +660,7 @@ getdns_return_t parse_args(int argc, char **argv) } continue; } else if (arg[0] == '{') { - parse_config(arg); + parse_config(arg, 0); continue; } else if (arg[0] != '-') { @@ -1719,7 +1729,7 @@ main(int argc, char **argv) , "%s/.stubby.conf" , getenv("HOME") ); - (void) parse_config(default_stubby_config); + (void) parse_config(default_stubby_config, 0); (void) parse_config_file("/etc/stubby.conf", 0); if (n_chars > 0 && n_chars < (int)sizeof(home_stubby_conf_fn)){ (void) parse_config_file(home_stubby_conf_fn, 0); diff --git a/src/yaml/convert_yaml_to_json.c b/src/yaml/convert_yaml_to_json.c new file mode 100644 index 00000000..e62e971e --- /dev/null +++ b/src/yaml/convert_yaml_to_json.c @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2017, NLNet Labs, Verisign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "config.h" + +#ifdef USE_YAML_CONFIG + +#include + +#include "../gldns/gbuffer.h" +#include "convert_yaml_to_json.h" + +static int process_yaml_stream(yaml_parser_t *, yaml_event_t *, gldns_buffer *); + +static int process_yaml_document(yaml_parser_t *, yaml_event_t *, gldns_buffer *); + +static int process_yaml_mapping(yaml_parser_t *, yaml_event_t *, gldns_buffer *); + +static int process_yaml_sequence(yaml_parser_t *, yaml_event_t *, gldns_buffer *); + +static int process_yaml_value(yaml_parser_t *, yaml_event_t *, gldns_buffer *); + +static int output_scalar(yaml_event_t *, gldns_buffer *); + +static void report_parser_error(yaml_parser_t *); + +static char* event_type_string(yaml_event_type_t); + +/* public functions */ + +char * +yaml_string_to_json_string(const char *instr) +{ + assert(instr); + + gldns_buffer *buf; + char* json = NULL; + + buf = gldns_buffer_new(8192); + if (!buf) { + fprintf(stderr, "Could not assign buffer for json output"); + return NULL; + } + + yaml_parser_t parser; + yaml_event_t event; + + memset(&parser, 0, sizeof(parser)); + memset(&event, 0, sizeof(event)); + + if (!yaml_parser_initialize(&parser)) { + fprintf(stderr, "Could not initialize the parser object\n"); + goto return_error; + } + + /* Set the parser parameters. */ + yaml_parser_set_input_string(&parser, (const unsigned char *) instr, strlen(instr)); + + /* Get the first event. */ + if (!yaml_parser_parse(&parser, &event)) { + report_parser_error(&parser); + goto return_error; + } + + /* First event should be stream start. */ + if (event.type != YAML_STREAM_START_EVENT) { + fprintf(stderr, "Event error: wrong type of event: %d\n", event.type); + goto return_error; + } + + if (process_yaml_stream(&parser, &event, buf) != 0) { + goto return_error; + } + + yaml_event_delete(&event); + yaml_parser_delete(&parser); + + /* + * gldns_buffer_export() returns a pointer to the data and + * sets a flag to prevent it from being deleted by + * gldns_buffer_free() + */ + json = (char *) gldns_buffer_export(buf); + gldns_buffer_free(buf); + + return json; + +return_error: + + yaml_event_delete(&event); + yaml_parser_delete(&parser); + gldns_buffer_free(buf); + + return NULL; +} + +/* local functions */ + +int +process_yaml_stream(yaml_parser_t *parser, yaml_event_t *event, gldns_buffer *buf) +{ + assert(parser); + assert(event); + assert(buf); + + int done = 0; + + while (!done) + { + /* Delete the event that brought us here */ + yaml_event_delete(event); + + /* Get the next event. */ + if (!yaml_parser_parse(parser, event)) { + report_parser_error(parser); + return -1; + } + + switch (event->type) { + case YAML_STREAM_END_EVENT: + + done = 1; + break; + + case YAML_DOCUMENT_START_EVENT: + + if (process_yaml_document(parser, event, buf) != 0) { + return -1; + } + break; + + case YAML_STREAM_START_EVENT: + case YAML_DOCUMENT_END_EVENT: + case YAML_ALIAS_EVENT: + case YAML_SCALAR_EVENT: + case YAML_SEQUENCE_START_EVENT: + case YAML_SEQUENCE_END_EVENT: + case YAML_MAPPING_START_EVENT: + case YAML_MAPPING_END_EVENT: + + fprintf(stderr, + "Event error: %s. Expected YAML_DOCUMENT_START_EVENT or YAML_STREAM_END_EVENT.\n", + event_type_string(event->type)); + return -1; + + default: + /* NOTREACHED */ + break; + } + } + return 0; +} + +int +process_yaml_document(yaml_parser_t *parser, yaml_event_t *event, gldns_buffer *buf) +{ + assert(parser); + assert(event); + assert(buf); + + int done = 0; + + while (!done) + { + /* Delete the event that brought us here */ + yaml_event_delete(event); + + /* Get the next event. */ + if (!yaml_parser_parse(parser, event)) { + report_parser_error(parser); + return -1; + } + + switch (event->type) { + case YAML_DOCUMENT_END_EVENT: + + done = 1; + break; + + case YAML_MAPPING_START_EVENT: + + if (process_yaml_mapping(parser, event, buf) != 0) { + return -1; + } + break; + + case YAML_SEQUENCE_START_EVENT: + + if (process_yaml_sequence(parser, event, buf) != 0) { + return -1; + } + break; + + case YAML_SCALAR_EVENT: + + if (output_scalar(event, buf) != 0) { + fprintf(stderr, "Value error: Error outputting scalar\n"); + return -1; + } + break; + + case YAML_STREAM_START_EVENT: + case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_START_EVENT: + case YAML_ALIAS_EVENT: + case YAML_SEQUENCE_END_EVENT: + case YAML_MAPPING_END_EVENT: + + fprintf(stderr, + "Event error: %s. Expected YAML_MAPPING_START_EVENT or YAML_DOCUMENT_END_EVENT.\n", + event_type_string(event->type)); + return -1; + + default: + /* NOTREACHED */ + break; + } + } + return 0; +} + +int +process_yaml_mapping(yaml_parser_t *parser, yaml_event_t *event, gldns_buffer *buf) +{ + assert(parser); + assert(event); + assert(buf); + + int done = 0; + int members = 0; + + if ( gldns_buffer_printf(buf, "{ ") == -1 ) + return -1; + + while (!done) + { + /* Delete the event that brought us here */ + yaml_event_delete(event); + + /* Get the next event. */ + if (!yaml_parser_parse(parser, event)) { + report_parser_error(parser); + return -1; + } + + if (event->type == YAML_SCALAR_EVENT) { + if (members) + if (gldns_buffer_printf(buf, ", ") == -1) + return -1; + + if (output_scalar(event, buf) != 0) { + fprintf(stderr, "Mapping error: Error outputting key\n"); + return -1; + } + if (gldns_buffer_printf(buf, ": ") == -1) + return -1; + + members = 1; + } else if (event->type == YAML_MAPPING_END_EVENT) { + if (gldns_buffer_printf(buf, " }") == -1) + return -1; + done = 1; + continue; + } else { + fprintf(stderr, + "Event error: %s. Expected YAML_SCALAR_EVENT or YAML_MAPPING_END_EVENT.\n", + event_type_string(event->type)); + return -1; + } + + /* Delete the event that brought us here */ + yaml_event_delete(event); + + /* Get the next event. */ + if (!yaml_parser_parse(parser, event)) { + report_parser_error(parser); + return -1; + } + + switch (event->type) { + case YAML_SCALAR_EVENT: + case YAML_SEQUENCE_START_EVENT: + case YAML_MAPPING_START_EVENT: + if (process_yaml_value(parser, event, buf) != 0) { + return -1; + } + break; + + case YAML_STREAM_START_EVENT: + case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_START_EVENT: + case YAML_DOCUMENT_END_EVENT: + case YAML_ALIAS_EVENT: + case YAML_SEQUENCE_END_EVENT: + case YAML_MAPPING_END_EVENT: + fprintf(stderr, + "Event error: %s. Expected YAML_MAPPING_START_EVENT, YAML_SEQUENCE_START_EVENT or YAML_SCALAR_EVENT.\n", + event_type_string(event->type)); + return -1; + + default: + /* NOTREACHED */ + break; + } + } + return 0; +} + +int +process_yaml_sequence(yaml_parser_t *parser, yaml_event_t *event, gldns_buffer *buf) +{ + assert(parser); + assert(event); + assert(buf); + + int done = 0; + int elements = 0; + + if ( gldns_buffer_printf(buf, "[ ") == -1 ) + return -1; + + while (!done) + { + /* Delete the event that brought us here */ + yaml_event_delete(event); + + /* Get the next event. */ + if (!yaml_parser_parse(parser, event)) { + report_parser_error(parser); + return -1; + } + + switch (event->type) { + case YAML_SCALAR_EVENT: + case YAML_SEQUENCE_START_EVENT: + case YAML_MAPPING_START_EVENT: + + if (elements) + if (gldns_buffer_printf(buf, ", ") == -1) + return -1; + + if (process_yaml_value(parser, event, buf) != 0) + return -1; + + elements = 1; + break; + + case YAML_SEQUENCE_END_EVENT: + if (gldns_buffer_printf(buf, " ]") == -1) + return -1; + done = 1; + break; + + case YAML_STREAM_START_EVENT: + case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_START_EVENT: + case YAML_DOCUMENT_END_EVENT: + case YAML_ALIAS_EVENT: + case YAML_MAPPING_END_EVENT: + fprintf(stderr, + "Event error: %s. Expected YAML_MAPPING_START_EVENT, YAML_SEQUENCE_START_EVENT, YAML_SCALAR_EVENT or YAML_SEQUENCE_END_EVENT.\n", + event_type_string(event->type)); + return -1; + + default: + /* NOTREACHED */ + break; + } + } + return 0; +} + +int +process_yaml_value(yaml_parser_t *parser, yaml_event_t *event, gldns_buffer *buf) +{ + assert(parser); + assert(event); + assert(buf); + + switch (event->type) { + case YAML_SCALAR_EVENT: + if (output_scalar(event, buf) != 0) { + fprintf(stderr, "Value error: Error outputting scalar\n"); + return -1; + } + break; + + case YAML_SEQUENCE_START_EVENT: + if (process_yaml_sequence(parser, event, buf) != 0) { + return -1; + } + break; + + case YAML_MAPPING_START_EVENT: + if (process_yaml_mapping(parser, event, buf) != 0) { + return -1; + } + break; + + default: + fprintf(stderr, "Bug: calling process_yaml_value() in the wrong context"); + return -1; + } + return 0; +} + +int +output_scalar(yaml_event_t *event, gldns_buffer *buf) +{ + const char *fmt = "%s"; + + assert(event); + assert(buf); + assert(event->data.scalar.length > 0); + + if (event->data.scalar.style != YAML_PLAIN_SCALAR_STYLE) + fmt = "\"%s\""; + + if ( gldns_buffer_printf(buf, fmt, event->data.scalar.value) == -1 ) + return -1; + return 0; +} + +void report_parser_error(yaml_parser_t *parser) +{ + assert(parser); + + /* Display a parser error message. */ + switch (parser->error) { + case YAML_MEMORY_ERROR: + fprintf(stderr, "Memory error: Not enough memory for parsing\n"); + break; + + case YAML_READER_ERROR: + if (parser->problem_value != -1) { + fprintf(stderr, "Reader error: %s: #%X at %zu\n", parser->problem, + parser->problem_value, parser->problem_offset); + } else { + fprintf(stderr, "Reader error: %s at %zu\n", parser->problem, + parser->problem_offset); + } + break; + + case YAML_SCANNER_ERROR: + if (parser->context) { + fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n" + "%s at line %lu, column %lu\n", parser->context, + parser->context_mark.line+1, parser->context_mark.column+1, + parser->problem, parser->problem_mark.line+1, + parser->problem_mark.column+1); + } else { + fprintf(stderr, "Scanner error: %s at line %lu, column %lu\n", + parser->problem, parser->problem_mark.line+1, + parser->problem_mark.column+1); + } + break; + + case YAML_PARSER_ERROR: + if (parser->context) { + fprintf(stderr, "Parser error: %s at line %lu, column %lu\n" + "%s at line %lu, column %lu\n", parser->context, + parser->context_mark.line+1, parser->context_mark.column+1, + parser->problem, parser->problem_mark.line+1, + parser->problem_mark.column+1); + } else { + fprintf(stderr, "Parser error: %s at line %lu, column %lu\n", + parser->problem, parser->problem_mark.line+1, + parser->problem_mark.column+1); + } + break; + + default: + /* Couldn't happen. */ + fprintf(stderr, "Internal error\n"); + break; + } + return; +} + +/* TODO - improve this */ +char* +event_type_string(yaml_event_type_t type) +{ + switch (type) { + case YAML_STREAM_START_EVENT: + return "YAML_STREAM_START_EVENT"; + + case YAML_STREAM_END_EVENT: + return "YAML_STREAM_END_EVENT"; + + case YAML_DOCUMENT_START_EVENT: + return "YAML_DOCUMENT_START_EVENT"; + + case YAML_DOCUMENT_END_EVENT: + return "YAML_DOCUMENT_END_EVENT"; + + case YAML_ALIAS_EVENT: + return "YAML_ALIAS_EVENT"; + + case YAML_SCALAR_EVENT: + return "YAML_SCALAR_EVENT"; + + case YAML_SEQUENCE_START_EVENT: + return "YAML_SEQUENCE_START_EVENT"; + + case YAML_SEQUENCE_END_EVENT: + return "YAML_SEQUENCE_END_EVENT"; + + case YAML_MAPPING_START_EVENT: + return "YAML_MAPPING_START_EVENT"; + + case YAML_MAPPING_END_EVENT: + return "YAML_MAPPING_END_EVENT"; + + default: + /* NOTREACHED */ + return NULL; + } + return NULL; +} + +#endif /* USE_YAML_CONFIG */ diff --git a/src/yaml/convert_yaml_to_json.h b/src/yaml/convert_yaml_to_json.h new file mode 100644 index 00000000..75b652f6 --- /dev/null +++ b/src/yaml/convert_yaml_to_json.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, NLNet Labs, Verisign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the names of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CONVERT_YAML_TO_JSON_H +#define _CONVERT_YAML_TO_JSON_H + +#include + +/** + * read yaml-syntax data from the string and convert to json-syntax + * yaml syntax resitrictions imposed for getdns: + * the outer-most data structure must be a yaml mapping + * mapping keys must be yaml scalars + * plain scalars are output to the json string unchanged + * non-plain scalars (quoted, double-quoted, wrapped) are output double-quoted + * TODO Test on yaml data containing yaml tags (these are ignored at present) + * The code has only been tested on yaml data using indentation style, so it + * should be tested on other styles as well. + * @param instr the string carrying data in yaml syntax + * @return a string of data in json syntax on success + * @return NULL if there is a yaml syntax violation + * the outer-most structure in not a mapping + * a mapping key is complex (a mapping or sequence) + */ +char * yaml_string_to_json_string(const char *instr); + +#endif //_CONVERT_YAML_TO_JSON_H