summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2016-03-27 19:27:30 +0200
committerLinus Nordberg <linus@nordu.net>2016-03-27 19:44:25 +0200
commit56d70baa79ae5907b11445364bbea9b31ee4cd20 (patch)
treeef0403255a04de75736da82385d282a0198c52c2
parent9f723f6f1d79c1be460ece10555945346045b175 (diff)
WIP
-rw-r--r--Makefile9
-rw-r--r--c_src/Makefile17
-rw-r--r--c_src/dnssec.c138
-rw-r--r--c_src/erlport.c119
-rw-r--r--c_src/erlport.h18
-rw-r--r--c_src/net_read_write.c93
-rw-r--r--c_src/net_read_write.h10
-rw-r--r--ebin/catlfish.app4
-rw-r--r--src/catlfish.erl4
-rw-r--r--src/dnssecport.erl130
-rw-r--r--src/v1.erl55
-rwxr-xr-xtest/check.erl7
-rw-r--r--tools/dnssec/Makefile12
-rw-r--r--tools/dnssec/README.md3
-rw-r--r--tools/dnssec/dns-net2wire.c1852
-rw-r--r--tools/dnssec/dns-wire2text.c149
-rw-r--r--tools/dnssec/net2wire.c301
-rwxr-xr-xtools/submitcert.py19
18 files changed, 2902 insertions, 38 deletions
diff --git a/Makefile b/Makefile
index 5435c43..58dc408 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,14 @@ INSTDIR=$(PREFIX)/catlfish
SOFTHSM=/usr/local/bin/softhsm2-util
build all:
+ (cd c_src && make all)
+ mkdir -p priv
+ cp c_src/dnssecport priv/
./make.erl
clean:
+ (cd c_src && make clean)
+ -rm priv/dnssecport
-rm ebin/*.beam
release: all
@@ -232,7 +237,9 @@ dialyze: build
dialyzer ebin
tags:
- find . -name \*.?rl -o -name \*.py | etags -
+ etags src/*.[he]rl
+ etags --append c_src/*.[ch]
+ etags --append tools/*.py
# Unit testing.
check: all
diff --git a/c_src/Makefile b/c_src/Makefile
new file mode 100644
index 0000000..ae9c73e
--- /dev/null
+++ b/c_src/Makefile
@@ -0,0 +1,17 @@
+CC = gcc
+#CFLAGS = -Wall -Werror -std=gnu99
+CFLAGS = -Wall -Werror -std=gnu99 -DTEST
+LDFLAGS =
+
+PORTS = dnssecport
+
+common_OBJS = erlport.o net_read_write.o
+dnssecport_OBJS = dnssec.o dnssec_test.o $(common_OBJS)
+
+all: $(PORTS)
+
+clean:
+ rm -f $(common_OBJS) $(dnssecport_OBJS) $(PORTS)
+
+dnssecport: $(dnssecport_OBJS)
+ $(CC) -o $@ $^ $(LDFLAGS) -ldl -lgetdns
diff --git a/c_src/dnssec.c b/c_src/dnssec.c
new file mode 100644
index 0000000..693d645
--- /dev/null
+++ b/c_src/dnssec.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016, NORDUnet A/S.
+ * See LICENSE for licensing information.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <getopt.h>
+#include <getdns/getdns.h>
+#include "erlport.h"
+#include "dnssec_test.h"
+
+static char *testmode = NULL;
+
+/* getdns/src/convert.c */
+getdns_return_t getdns_wire2rr_dict(const uint8_t *wire, size_t wire_len,
+ getdns_dict **rr_dict);
+
+#if !defined(TEST)
+static getdns_return_t
+validate(const unsigned char *records,
+ size_t records_len,
+ getdns_list *trust_anchors)
+{
+ getdns_return_t r = GETDNS_DNSSEC_INDETERMINATE;
+ getdns_list *to_validate = getdns_list_create();
+ getdns_list *support_records = getdns_list_create();
+ getdns_dict *records_dict = NULL;
+
+ if (to_validate == NULL || support_records == NULL)
+ {
+ r = GETDNS_RETURN_MEMORY_ERROR;
+ goto out;
+ }
+
+ /* TODO: figure out if we get _all_ RRs in records here bc i have
+ the feeling that we're not supposed to mix RR types in the same
+ dict; maybe this will help some:
+ https://getdnsapi.net/pipermail/users/2015-May/000032.html
+ */
+ r = getdns_wire2rr_dict(records, records_len, &records_dict);
+ if (r)
+ goto out;
+
+ /*
+ to_validate: one dict with the DS and one with a RRSIG for that DS
+ support_records: DS and DNSKEY dicts with accompanying RRSIG's
+ trust_anchors: DNSKEY (or DS)
+ */
+ r = getdns_list_set_dict(to_validate, i, records_dict);
+ if (r)
+ goto out;
+
+ r = getdns_validate_dnssec(to_validate,
+ support_records,
+ trust_anchors);
+
+out:
+ if (to_validate)
+ getdns_list_destroy(to_validate);
+ if (support_records)
+ getdns_list_destroy(support_records);
+
+ return r;
+}
+#endif /* !TEST */
+
+static void
+loop(getdns_list *trust_anchors)
+{
+ getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR;
+ unsigned char buf[65536];
+ ssize_t len;
+
+ while ((len = read_command(buf, sizeof(buf), 4)) > 0)
+ {
+ unsigned char *reply = NULL;
+
+#if !defined(TEST)
+ r = validate(buf, len, trust_anchors);
+#else
+ r = test_validate(buf, len, trust_anchors, testmode);
+#endif
+
+ switch (r)
+ {
+ case GETDNS_RETURN_GOOD:
+ reply = (unsigned char *) "valid";
+ break;
+ default:
+ fprintf(stderr, "error %d\n", r); /* DEBUG */
+ reply = (unsigned char *) "err";
+ }
+
+ write_reply(reply, strlen((const char *) reply), 4);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ getdns_list *trust_anchors = NULL;
+ time_t trust_anchor_date;
+
+ while (1) {
+ static struct option long_options[] = {
+ {"testmode", required_argument, NULL, 't'},
+ {0, 0, 0, 0}};
+
+ c = getopt_long(argc, argv, "", long_options, NULL);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+#if defined(TEST)
+ case 't':
+ testmode = optarg;
+ break;
+#endif
+ default:
+ fprintf(stderr, "dnssecport: bad option: %s", argv[optind]);
+ return -1;
+ }
+ }
+
+ if (optind < argc) /* Using getdns trust anchor. */
+ {
+ trust_anchors = getdns_root_trust_anchor(&trust_anchor_date);
+ }
+
+ loop(trust_anchors);
+
+ return 0;
+}
diff --git a/c_src/erlport.c b/c_src/erlport.c
new file mode 100644
index 0000000..372f98d
--- /dev/null
+++ b/c_src/erlport.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2014-2015, NORDUnet A/S.
+ * See LICENSE for licensing information.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "net_read_write.h"
+#include "erlport.h"
+
+static ssize_t
+read_length(size_t length_size)
+{
+ unsigned char buf[4];
+
+ if (length_size != 2 && length_size != 4) {
+ return -1;
+ }
+
+ if (length_size > sizeof(buf)) {
+ return -1;
+ }
+
+ ssize_t ret;
+
+ ret = net_read(0, (char *)buf, length_size);
+
+ if (ret != (ssize_t) length_size) {
+ return -1;
+ }
+
+ if (length_size == 2) {
+ return (ssize_t)(((unsigned long)buf[0] << 8) | (unsigned long)buf[1]);
+ } else {
+ return (ssize_t)(((unsigned long)buf[0] << 24) |
+ ((unsigned long)buf[1] << 16) |
+ ((unsigned long)buf[2] << 8) |
+ (unsigned long)buf[3]);
+ }
+}
+
+ssize_t
+read_command(unsigned char *buf, size_t maxlen, size_t length_size)
+{
+ ssize_t len;
+
+ len = read_length(length_size);
+
+ if (len < 0) {
+ return -1;
+ }
+
+ if (len > (ssize_t) maxlen) {
+ return -1;
+ }
+ return net_read(0, (char *)buf, (size_t)len);
+}
+
+static int
+write_length(size_t len, size_t length_size)
+{
+ unsigned char buf[4];
+
+ if (length_size != 2 && length_size != 4) {
+ return -1;
+ }
+
+ if (length_size == 2) {
+ buf[0] = (len >> 8) & 0xff;
+ buf[1] = len & 0xff;
+ } else {
+ buf[0] = (len >> 24) & 0xff;
+ buf[1] = (len >> 16) & 0xff;
+ buf[2] = (len >> 8) & 0xff;
+ buf[3] = len & 0xff;
+ }
+
+ ssize_t ret;
+
+ ret = net_write(1, (char *)buf, length_size);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (ret != (ssize_t) length_size) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+write_reply(unsigned char *msg, size_t len, size_t length_size)
+{
+ ssize_t ret;
+
+ ret = write_length(len, length_size);
+ if (ret < 0) {
+ return -1;
+ }
+ ret = net_write(1, (char *)msg, len);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+write_status(char *msg)
+{
+ return write_reply((unsigned char *)msg, strlen(msg), 2);
+}
diff --git a/c_src/erlport.h b/c_src/erlport.h
new file mode 100644
index 0000000..58b2591
--- /dev/null
+++ b/c_src/erlport.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2014-2015, NORDUnet A/S.
+ * See LICENSE for licensing information.
+ */
+
+#ifndef ERLPORT_H
+#define ERLPORT_H
+
+ssize_t
+read_command(unsigned char *buf, size_t maxlen, size_t length_size);
+
+int
+write_reply(unsigned char *msg, size_t len, size_t length_size);
+
+int
+write_status(char *msg);
+
+#endif
diff --git a/c_src/net_read_write.c b/c_src/net_read_write.c
new file mode 100644
index 0000000..f8f14f0
--- /dev/null
+++ b/c_src/net_read_write.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 THE INSTITUTE OR CONTRIBUTORS 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 <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "net_read_write.h"
+
+/*
+ * Like read but never return partial data.
+ */
+
+ssize_t
+net_read (int fd, void *buf, size_t nbytes)
+{
+ char *cbuf = (char *)buf;
+ ssize_t count;
+ size_t rem = nbytes;
+
+ while (rem > 0) {
+ count = read (fd, cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ } else if (count == 0) {
+ return count;
+ }
+ cbuf += (size_t) count;
+ rem -= (size_t) count;
+ }
+ return (ssize_t)nbytes;
+}
+
+/*
+ * Like write but never return partial data.
+ */
+
+ssize_t
+net_write (int fd, const void *buf, size_t nbytes)
+{
+ const char *cbuf = (const char *)buf;
+ ssize_t count;
+ size_t rem = nbytes;
+
+ while (rem > 0) {
+ count = write (fd, cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ }
+ cbuf += (size_t)count;
+ rem -= (size_t)count;
+ }
+ return (ssize_t)nbytes;
+}
diff --git a/c_src/net_read_write.h b/c_src/net_read_write.h
new file mode 100644
index 0000000..80b92b3
--- /dev/null
+++ b/c_src/net_read_write.h
@@ -0,0 +1,10 @@
+#ifndef NET_READ_WRITE_H
+#define NET_READ_WRITE_H
+
+ssize_t
+net_read (int, void *, size_t);
+
+ssize_t
+net_write (int, const void *, size_t);
+
+#endif
diff --git a/ebin/catlfish.app b/ebin/catlfish.app
index af48ae3..aa833bc 100644
--- a/ebin/catlfish.app
+++ b/ebin/catlfish.app
@@ -1,4 +1,4 @@
-%%% Copyright (c) 2014-2015, NORDUnet A/S.
+%%% Copyright (c) 2014-2016, NORDUnet A/S.
%%% See LICENSE for licensing information.
%% Application resource file for catlfish.
@@ -6,6 +6,6 @@
{application, catlfish,
[{description, "catlfish -- Certificate Transparency Log Server"},
{vsn, "0.9.0-dev"},
- {modules, [catlfish, catlfish_app, catlfish_sup, catlfish_web, v1, x509]},
+ {modules, [catlfish, catlfish_app, catlfish_sup, catlfish_web, v1, ratelimit]},
{applications, [kernel, stdlib, plop, lager, mochiweb, idna, asn1, crypto]},
{mod, {catlfish_app, []}}]}.
diff --git a/src/catlfish.erl b/src/catlfish.erl
index e3b5939..711deaa 100644
--- a/src/catlfish.erl
+++ b/src/catlfish.erl
@@ -286,7 +286,7 @@ entryhash_from_entry(PackedEntry) ->
crypto:hash(sha256, [Cert | Chain]).
%% Private functions.
--spec pack_entry(normal|precert, binary(), binary(), [binary()]) -> binary().
+-spec pack_entry(normal|precert, binary(), binary(), [binary()]) -> list().
pack_entry(Type, MTLText, EndEntityCert, CertChain) ->
[{<<"MTL1">>, MTLText},
{case Type of
@@ -297,7 +297,7 @@ pack_entry(Type, MTLText, EndEntityCert, CertChain) ->
list_to_binary(
[tlv:encode(<<"X509">>, E) || E <- CertChain])}].
--spec unpack_entry(binary()) -> {normal|precert, binary(), binary(), [binary()]}.
+-spec unpack_entry(list()) -> {normal|precert, binary(), binary(), [binary()]}.
unpack_entry(Entry) ->
[{<<"MTL1">>, MTLText}|Rest1] = Entry,
[{EECType, EndEntityCert}|Rest2] = Rest1,
diff --git a/src/dnssecport.erl b/src/dnssecport.erl
new file mode 100644
index 0000000..804e727
--- /dev/null
+++ b/src/dnssecport.erl
@@ -0,0 +1,130 @@
+%%% Copyright (c) 2016, NORDUnet A/S.
+%%% See LICENSE for licensing information.
+
+-module(dnssecport).
+-behaviour(gen_server).
+-export([start_link/0, stop/0]).
+-export([validate/2]).
+%% gen_server callbacks.
+-export([init/1, handle_call/3, terminate/2, handle_cast/2, handle_info/2,
+ code_change/3]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE,
+ [code:priv_dir(catlfish) ++ "/dnssecport"], []).
+
+stop() ->
+ gen_server:call(?MODULE, stop).
+
+validate(ValidateRR, SupportRRs) ->
+ gen_server:call(?MODULE, {validate, [ValidateRR, SupportRRs]}).
+
+-record(state, {port :: port()}).
+
+init(Program) ->
+ lager:debug("starting dnssec service"),
+ Port = create_port(Program, []), % TODO: Pass path to dir with trust root.
+ {ok, #state{port = Port}}.
+
+handle_call(stop, _From, State) ->
+ lager:debug("dnssec stop request received"),
+ stop_port(State);
+handle_call({validate, [ValidateRR, SupportRRs]}, _From, State) ->
+ lager:debug("dnssec validate incoming request: ~p", [ValidateRR]),
+ case State#state.port of
+ undefined ->
+ {error, noport};
+ Port when is_port(Port) ->
+ S = list_to_binary(SupportRRs),
+ Port ! {self(), {command, <<ValidateRR/binary, S/binary>>}},
+ receive
+ {Port, {data, Response}} ->
+ lager:debug("received response from dnssec port: ~p",
+ [Response]),
+ case Response of
+ "valid" ->
+ {reply, ok, State};
+ Err ->
+ {reply, {error, Err}, State}
+ end;
+ {Port, {exit_status, ExitStatus}} ->
+ lager:error("dnssec port ~p exiting with status ~p",
+ [Port, ExitStatus]),
+ {stop, portexit, State#state{port = undefined}}
+ after
+ 3000 ->
+ lager:error("dnssec port timeout"),
+ {stop, timeout, State}
+ end
+ end.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+handle_cast(_Request, State) ->
+ {noreply, State}.
+
+terminate(Reason, _State) ->
+ lager:info("dnssec port terminating: ~p", [Reason]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%
+create_port(Program, Args) ->
+ open_port({spawn_executable, Program},
+ [{args, Args},
+ exit_status, % Let us know if process dies.
+ {packet, 4}]).
+
+stop_port(State) ->
+ Port = State#state.port,
+ Port ! {self(), close},
+ receive
+ {Port, closed} ->
+ {stop, closed, State#state{port = undefined}};
+ {Port, Msg} ->
+ lager:debug("message received from dying port: ~p", [Msg]),
+ {stop, unknown, State#state{port = undefined}}
+ after
+ 2000 ->
+ lager:error("dnssec port ~p refuses to die", [Port]),
+ {stop, timeout, State}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Unit tests.
+start_test_port(TestType) ->
+ Port = create_port("priv/dnssecport", ["--testmode", atom_to_list(TestType)]),
+ ?debugFmt("Port: ~p", [Port]),
+ Port.
+stop_test_port(Port) ->
+ {stop, closed, _State} = stop_port(#state{port = Port}),
+ ok.
+
+err_test_() ->
+ {setup,
+ fun() -> start_test_port(err) end,
+ fun(Port) -> stop_test_port(Port) end,
+ fun(Port) ->
+ R = handle_call({validate, [<<"invalid-DS">>, []]},
+ self(), #state{port = Port}),
+ [
+ ?_assertMatch({reply, {error, "err"}, _State}, R)
+ ]
+ end}.
+
+ok_test_() ->
+ {setup,
+ fun() -> start_test_port(ok) end,
+ fun(Port) -> stop_test_port(Port) end,
+ fun(Port) ->
+ R = handle_call({validate, [<<"invalid-DS">>, []]},
+ self(), #state{port = Port}),
+ [
+ ?_assertMatch({reply, ok, _State}, R)
+ ]
+ end}.
diff --git a/src/v1.erl b/src/v1.erl
index 7b7f6bf..86cd799 100644
--- a/src/v1.erl
+++ b/src/v1.erl
@@ -1,4 +1,4 @@
-%%% Copyright (c) 2014-2015, NORDUnet A/S.
+%%% Copyright (c) 2014-2016, NORDUnet A/S.
%%% See LICENSE for licensing information.
%%% @doc Certificate Transparency (RFC 6962)
@@ -7,7 +7,7 @@
%% API (URL)
-export([request/4]).
--define(APPURL_CT_V1, "open/gaol/v1").
+-define(APPURL_CT_V1, "dt/v1").
check_valid_sth() ->
case plop:sth() of
@@ -30,9 +30,9 @@ check_valid_sth() ->
end.
%% Public functions, i.e. part of URL.
-request(post, ?APPURL_CT_V1, "add-blob", Input) ->
+request(post, ?APPURL_CT_V1, "add-ds-rr", Input) ->
check_valid_sth(),
- add_blob(Input);
+ add_ds(Input);
request(get, ?APPURL_CT_V1, "get-sth", _Query) ->
check_valid_sth(),
@@ -147,29 +147,36 @@ internalerror(Text) ->
"~s~n" ++
"</body></html>~n", [Text])}.
--spec add_blob(any()) -> any().
-add_blob(Input) ->
+-spec add_ds(any()) -> any().
+add_ds(Input) ->
case (catch mochijson2:decode(Input)) of
{error, E} ->
- err400("add-blob: bad input:", E);
- {struct, [{<<"blob">>, Blob}]} ->
- case (catch base64:decode(Blob)) of
- {'EXIT', _} ->
- err400("add-blob: invalid base64-encoded blob", Blob);
- DecodedBlob ->
- add_blob_helper(DecodedBlob,
- application:get_env(catlfish,
- max_submit_size,
- 0))
+ err400("add-ds-rr: bad input:", E);
+ {struct, [{<<"chain">>, List}]} ->
+ case decode_chain(List) of
+ {invalid, ErrText} ->
+ err400(io:format("add-ds-rr: ~p", [ErrText]), List);
+ [DSRR, DSRRSIG | SupportRRs] ->
+ add_ds_helper(DSRR, DSRRSIG, SupportRRs);
+ _ ->
+ err400("add-ds-rr: missing one or more entries", List)
end;
_ ->
- err400("add-blob: missing input: blob", Input)
+ err400("add-ds-rr: missing input: chain", Input)
end.
-add_blob_helper(Blob, MaxSize) when MaxSize == 0 ->
- success(catlfish:add_chain(Blob, [], normal));
-add_blob_helper(Blob, MaxSize) when erlang:size(Blob) =< MaxSize ->
- add_blob_helper(Blob, 0);
-add_blob_helper(Blob, MaxSize) ->
- err400(io_lib:format("add-blob: blob too large (~p > ~p)",
- [erlang:size(Blob), MaxSize]), Blob).
+decode_chain(List) ->
+ case (catch [base64:decode(X) || X <- List]) of
+ {'EXIT', _} ->
+ {invalid, "invalid base64-encoding"};
+ L ->
+ L
+ end.
+
+add_ds_helper(DSRR, DSRRSIG, Support) ->
+ case dnssecport:dnssec_validate([DSRR, DSRRSIG], Support) of
+ ok ->
+ success(catlfish:add_chain(DSRR, [DSRRSIG | Support], normal));
+ _ ->
+ err400("add-ds-rr: invalid DS record", [DSRR, DSRRSIG | Support])
+ end.
diff --git a/test/check.erl b/test/check.erl
index b538346..2cb388c 100755
--- a/test/check.erl
+++ b/test/check.erl
@@ -1,11 +1,12 @@
#! /usr/bin/env escript
%% -*- erlang -*- mode
-%%! -pa ebin -pa ../lager/ebin -pa ../lager/deps/goldrush/ebin -pa ../mochiweb/ebin -config test/config/check.config
+%%! -pa ebin -pa ../lager/ebin -pa ../lager/deps/goldrush/ebin -pa ../mochiweb/ebin -s lager -config test/config/check.config
-%% To enable logging, pass `-s lager' by adding it to the line above.
+%% To enable logging, pass `-s lager' to erlang by adding it to the
+%% %%!-line above.
%% Tweak the amount of logging by changing `lager_console_backend' in
%% config/check.config.
main(_) ->
- ok = x509:test(),
+ ok = dnssecport:test().
ok = catlfish:test().
diff --git a/tools/dnssec/Makefile b/tools/dnssec/Makefile
new file mode 100644
index 0000000..7009220
--- /dev/null
+++ b/tools/dnssec/Makefile
@@ -0,0 +1,12 @@
+CFLAGS = -Wall -g -std=c99
+CFLAGS_PEDANTIC = -pedantic -Werror -Wextra
+
+all: dns-net2wire dns-wire2text
+
+dns-net2wire: dns-net2wire.c
+ $(CC) $(CFLAGS) -o $@ -lgetdns -lgetdns_ext_event -levent $<
+dns-wire2text: dns-wire2text.c
+ $(CC) $(CFLAGS) $(CFLAGS_PEDANTIC) -o $@ -lgetdns -lgetdns_ext_event -levent $<
+
+net2wire: net2wire.c
+ $(CC) $(CFLAGS) -I ~/usr/include -L ~/usr/lib -o $@ -lgetdns -lgetdns_ext_event -levent $<
diff --git a/tools/dnssec/README.md b/tools/dnssec/README.md
new file mode 100644
index 0000000..e6f2985
--- /dev/null
+++ b/tools/dnssec/README.md
@@ -0,0 +1,3 @@
+This directory contains tools for DNSSEC Transparency.
+
+- dns-wire2text reads RR's and prints them in presentation format
diff --git a/tools/dnssec/dns-net2wire.c b/tools/dnssec/dns-net2wire.c
new file mode 100644
index 0000000..c193139
--- /dev/null
+++ b/tools/dnssec/dns-net2wire.c
@@ -0,0 +1,1852 @@
+/*
+ Based on getdns/src/test/getdns_query.c (commit 60be402).
+ For license information, see end of this file.
+
+ Example usage:
+ $ dns-net2wire -q +dnssec_return_validation_chain +dnssec_return_status +dnssec_return_only_secure dfri.se
+ */
+
+#define _POSIX_SOURCE
+
+//#include "config.h"
+//#include "debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <assert.h>
+#include <arpa/inet.h>
+#include <getdns/getdns.h>
+#include <getdns/getdns_extra.h>
+
+#define DEBUG_SCHED(...)
+
+#define MAX_TIMEOUTS FD_SETSIZE
+
+#define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\""
+
+/* Eventloop based on select */
+typedef struct my_eventloop {
+ getdns_eventloop base;
+ getdns_eventloop_event *fd_events[FD_SETSIZE];
+ uint64_t fd_timeout_times[FD_SETSIZE];
+ getdns_eventloop_event *timeout_events[MAX_TIMEOUTS];
+ uint64_t timeout_times[MAX_TIMEOUTS];
+} my_eventloop;
+
+static uint64_t get_now_plus(uint64_t amount)
+{
+ struct timeval tv;
+ uint64_t now;
+
+ if (gettimeofday(&tv, NULL)) {
+ perror("gettimeofday() failed");
+ exit(EXIT_FAILURE);
+ }
+ now = tv.tv_sec * 1000000 + tv.tv_usec;
+
+ return (now + amount * 1000) >= now ? now + amount * 1000 : -1;
+}
+
+getdns_return_t
+my_eventloop_schedule(getdns_eventloop *loop,
+ int fd, uint64_t timeout, getdns_eventloop_event *event)
+{
+ my_eventloop *my_loop = (my_eventloop *)loop;
+ size_t i;
+
+ assert(loop);
+ assert(event);
+ assert(fd < FD_SETSIZE);
+
+ DEBUG_SCHED( "%s(loop: %p, fd: %d, timeout: %"PRIu64", event: %p)\n"
+ , __FUNCTION__, loop, fd, timeout, event);
+ if (fd >= 0 && (event->read_cb || event->write_cb)) {
+ assert(my_loop->fd_events[fd] == NULL);
+
+ my_loop->fd_events[fd] = event;
+ my_loop->fd_timeout_times[fd] = get_now_plus(timeout);
+ event->ev = (void *) (intptr_t) fd + 1;
+
+ DEBUG_SCHED( "scheduled read/write at %d\n", fd);
+ return GETDNS_RETURN_GOOD;
+ }
+
+ assert(event->timeout_cb && !event->read_cb && !event->write_cb);
+
+ for (i = 0; i < MAX_TIMEOUTS; i++) {
+ if (my_loop->timeout_events[i] == NULL) {
+ my_loop->timeout_events[i] = event;
+ my_loop->timeout_times[i] = get_now_plus(timeout);
+ event->ev = (void *) (intptr_t) i + 1;
+
+ DEBUG_SCHED( "scheduled timeout at %d\n", (int)i);
+ return GETDNS_RETURN_GOOD;
+ }
+ }
+ return GETDNS_RETURN_GENERIC_ERROR;
+}
+
+getdns_return_t
+my_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event)
+{
+ my_eventloop *my_loop = (my_eventloop *)loop;
+ size_t i;
+
+ assert(loop);
+ assert(event);
+
+ DEBUG_SCHED( "%s(loop: %p, event: %p)\n", __FUNCTION__, loop, event);
+
+ i = (intptr_t)event->ev - 1;
+ assert(i >= 0 && i < FD_SETSIZE);
+
+ if (event->timeout_cb && !event->read_cb && !event->write_cb) {
+ assert(my_loop->timeout_events[i] == event);
+ my_loop->timeout_events[i] = NULL;
+ } else {
+ assert(my_loop->fd_events[i] == event);
+ my_loop->fd_events[i] = NULL;
+ }
+ event->ev = NULL;
+ return GETDNS_RETURN_GOOD;
+}
+
+void my_eventloop_cleanup(getdns_eventloop *loop)
+{
+}
+
+void my_read_cb(int fd, getdns_eventloop_event *event)
+{
+ DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event);
+ event->read_cb(event->userarg);
+}
+
+void my_write_cb(int fd, getdns_eventloop_event *event)
+{
+ DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event);
+ event->write_cb(event->userarg);
+}
+
+void my_timeout_cb(int fd, getdns_eventloop_event *event)
+{
+ DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event);
+ event->timeout_cb(event->userarg);
+}
+
+void my_eventloop_run_once(getdns_eventloop *loop, int blocking)
+{
+ my_eventloop *my_loop = (my_eventloop *)loop;
+
+ fd_set readfds, writefds;
+ int fd, max_fd = -1;
+ uint64_t now, timeout = (uint64_t)-1;
+ size_t i;
+ struct timeval tv;
+
+ assert(loop);
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ now = get_now_plus(0);
+
+ for (i = 0; i < MAX_TIMEOUTS; i++) {
+ if (!my_loop->timeout_events[i])
+ continue;
+ if (now > my_loop->timeout_times[i])
+ my_timeout_cb(-1, my_loop->timeout_events[i]);
+ else if (my_loop->timeout_times[i] < timeout)
+ timeout = my_loop->timeout_times[i];
+ }
+ for (fd = 0; fd < FD_SETSIZE; fd++) {
+ if (!my_loop->fd_events[fd])
+ continue;
+ if (my_loop->fd_events[fd]->read_cb)
+ FD_SET(fd, &readfds);
+ if (my_loop->fd_events[fd]->write_cb)
+ FD_SET(fd, &writefds);
+ if (fd > max_fd)
+ max_fd = fd;
+ if (my_loop->fd_timeout_times[fd] < timeout)
+ timeout = my_loop->fd_timeout_times[fd];
+ }
+ if (max_fd == -1 && timeout == (uint64_t)-1)
+ return;
+
+ if (! blocking || now > timeout) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_sec = (timeout - now) / 1000000;
+ tv.tv_usec = (timeout - now) % 1000000;
+ }
+ if (select(max_fd + 1, &readfds, &writefds, NULL, &tv) < 0) {
+ perror("select() failed");
+ exit(EXIT_FAILURE);
+ }
+ now = get_now_plus(0);
+ for (fd = 0; fd < FD_SETSIZE; fd++) {
+ if (my_loop->fd_events[fd] &&
+ my_loop->fd_events[fd]->read_cb &&
+ FD_ISSET(fd, &readfds))
+ my_read_cb(fd, my_loop->fd_events[fd]);
+
+ if (my_loop->fd_events[fd] &&
+ my_loop->fd_events[fd]->write_cb &&
+ FD_ISSET(fd, &writefds))
+ my_write_cb(fd, my_loop->fd_events[fd]);
+
+ if (my_loop->fd_events[fd] &&
+ my_loop->fd_events[fd]->timeout_cb &&
+ now > my_loop->fd_timeout_times[fd])
+ my_timeout_cb(fd, my_loop->fd_events[fd]);
+
+ i = fd;
+ if (my_loop->timeout_events[i] &&
+ my_loop->timeout_events[i]->timeout_cb &&
+ now > my_loop->timeout_times[i])
+ my_timeout_cb(-1, my_loop->timeout_events[i]);
+ }
+}
+
+void my_eventloop_run(getdns_eventloop *loop)
+{
+ my_eventloop *my_loop = (my_eventloop *)loop;
+ size_t i;
+
+ assert(loop);
+
+ i = 0;
+ while (i < MAX_TIMEOUTS) {
+ if (my_loop->fd_events[i] || my_loop->timeout_events[i]) {
+ my_eventloop_run_once(loop, 1);
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+}
+
+void my_eventloop_init(my_eventloop *loop)
+{
+ static getdns_eventloop_vmt my_eventloop_vmt = {
+ my_eventloop_cleanup,
+ my_eventloop_schedule,
+ my_eventloop_clear,
+ my_eventloop_run,
+ my_eventloop_run_once
+ };
+
+ (void) memset(loop, 0, sizeof(my_eventloop));
+ loop->base.vmt = &my_eventloop_vmt;
+}
+
+static int quiet = 0;
+static int batch_mode = 0;
+static char *query_file = NULL;
+static int json = 0;
+static char *the_root = ".";
+static char *name;
+static getdns_context *context;
+static getdns_dict *extensions;
+static getdns_list *pubkey_pinset = NULL;
+static size_t pincount = 0;
+static uint16_t request_type = GETDNS_RRTYPE_NS;
+static int timeout, edns0_size, padding_blocksize;
+static int async = 0, interactive = 0;
+static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL;
+
+int get_rrtype(const char *t);
+
+int gqldns_b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ const uint8_t pad64 = 64; /* is 64th in the b64 array */
+ const char* s = src;
+ uint8_t in[4];
+ size_t o = 0, incount = 0;
+
+ while(*s) {
+ /* skip any character that is not base64 */
+ /* conceptually we do:
+ const char* b64 = pad'=' is appended to array
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ const char* d = strchr(b64, *s++);
+ and use d-b64;
+ */
+ char d = *s++;
+ if(d <= 'Z' && d >= 'A')
+ d -= 'A';
+ else if(d <= 'z' && d >= 'a')
+ d = d - 'a' + 26;
+ else if(d <= '9' && d >= '0')
+ d = d - '0' + 52;
+ else if(d == '+')
+ d = 62;
+ else if(d == '/')
+ d = 63;
+ else if(d == '=')
+ d = 64;
+ else continue;
+ in[incount++] = (uint8_t)d;
+ if(incount != 4)
+ continue;
+ /* process whole block of 4 characters into 3 output bytes */
+ if(in[3] == pad64 && in[2] == pad64) { /* A B = = */
+ if(o+1 > targsize)
+ return -1;
+ target[o] = (in[0]<<2) | ((in[1]&0x30)>>4);
+ o += 1;
+ break; /* we are done */
+ } else if(in[3] == pad64) { /* A B C = */
+ if(o+2 > targsize)
+ return -1;
+ target[o] = (in[0]<<2) | ((in[1]&0x30)>>4);
+ target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2);
+ o += 2;
+ break; /* we are done */
+ } else {
+ if(o+3 > targsize)
+ return -1;
+ /* write xxxxxxyy yyyyzzzz zzwwwwww */
+ target[o] = (in[0]<<2) | ((in[1]&0x30)>>4);
+ target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2);
+ target[o+2]= ((in[2]&0x03)<<6) | in[3];
+ o += 3;
+ }
+ incount = 0;
+ }
+ return (int)o;
+}
+
+getdns_dict *
+ipaddr_dict(getdns_context *context, char *ipstr)
+{
+ getdns_dict *r = getdns_dict_create_with_context(context);
+ char *s = strchr(ipstr, '%'), *scope_id_str = "";
+ char *p = strchr(ipstr, '@'), *portstr = "";
+ char *t = strchr(ipstr, '#'), *tls_portstr = "";
+ char *n = strchr(ipstr, '~'), *tls_namestr = "";
+ /* ^[alg:]name:key */
+ char *T = strchr(ipstr, '^'), *tsig_name_str = ""
+ , *tsig_secret_str = ""
+ , *tsig_algorithm_str = "";
+ int tsig_secret_size;
+ uint8_t tsig_secret_buf[256]; /* 4 times SHA512 */
+ getdns_bindata tsig_secret;
+ uint8_t buf[sizeof(struct in6_addr)];
+ getdns_bindata addr;
+
+ addr.data = buf;
+
+ if (!r) return NULL;
+ if (s) {
+ *s = 0;
+ scope_id_str = s + 1;
+ }
+ if (p) {
+ *p = 0;
+ portstr = p + 1;
+ }
+ if (t) {
+ *t = 0;
+ tls_portstr = t + 1;
+ }
+ if (n) {
+ *n = 0;
+ tls_namestr = n + 1;
+ }
+ if (T) {
+ *T = 0;
+ tsig_name_str = T + 1;
+ if ((T = strchr(tsig_name_str, ':'))) {
+ *T = 0;
+ tsig_secret_str = T + 1;
+ if ((T = strchr(tsig_secret_str, ':'))) {
+ *T = 0;
+ tsig_algorithm_str = tsig_name_str;
+ tsig_name_str = tsig_secret_str;
+ tsig_secret_str = T + 1;
+ }
+ } else {
+ tsig_name_str = "";
+ }
+ }
+ if (strchr(ipstr, ':')) {
+ getdns_dict_util_set_string(r, "address_type", "IPv6");
+ addr.size = 16;
+ if (inet_pton(AF_INET6, ipstr, buf) <= 0) {
+ getdns_dict_destroy(r);
+ return NULL;
+ }
+ } else {
+ getdns_dict_util_set_string(r, "address_type", "IPv4");
+ addr.size = 4;
+ if (inet_pton(AF_INET, ipstr, buf) <= 0) {
+ getdns_dict_destroy(r);
+ return NULL;
+ }
+ }
+ getdns_dict_set_bindata(r, "address_data", &addr);
+ if (*portstr)
+ getdns_dict_set_int(r, "port", (int32_t)atoi(portstr));
+ if (*tls_portstr)
+ getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr));
+ if (*tls_namestr) {
+ getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr);
+ }
+ if (*scope_id_str)
+ getdns_dict_util_set_string(r, "scope_id", scope_id_str);
+ if (*tsig_name_str)
+ getdns_dict_util_set_string(r, "tsig_name", tsig_name_str);
+ if (*tsig_algorithm_str)
+ getdns_dict_util_set_string(r, "tsig_algorithm", tsig_algorithm_str);
+ if (*tsig_secret_str) {
+ tsig_secret_size = gqldns_b64_pton(
+ tsig_secret_str, tsig_secret_buf, sizeof(tsig_secret_buf));
+ if (tsig_secret_size > 0) {
+ tsig_secret.size = tsig_secret_size;
+ tsig_secret.data = tsig_secret_buf;
+ getdns_dict_set_bindata(r, "tsig_secret", &tsig_secret);
+ }
+ }
+ return r;
+}
+
+static getdns_return_t
+fill_transport_list(getdns_context *context, char *transport_list_str,
+ getdns_transport_list_t *transports, size_t *transport_count)
+{
+ size_t max_transports = *transport_count;
+ *transport_count = 0;
+ for ( size_t i = 0
+ ; i < max_transports && i < strlen(transport_list_str)
+ ; i++, (*transport_count)++) {
+ switch(*(transport_list_str + i)) {
+ case 'U':
+ transports[i] = GETDNS_TRANSPORT_UDP;
+ break;
+ case 'T':
+ transports[i] = GETDNS_TRANSPORT_TCP;
+ break;
+ case 'L':
+ transports[i] = GETDNS_TRANSPORT_TLS;
+ break;
+ default:
+ fprintf(stderr, "Unrecognised transport '%c' in string %s\n",
+ *(transport_list_str + i), transport_list_str);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ }
+ return GETDNS_RETURN_GOOD;
+}
+
+void
+print_usage(FILE *out, const char *progname)
+{
+ fprintf(out, "usage: %s [<option> ...] \\\n"
+ "\t\t[@<upstream> ...] [+<extension> ...] [<name>] [<type>]\n", progname);
+ fprintf(out, "\ndefault mode: "
+#ifdef HAVE_LIBUNBOUND
+ "recursive"
+#else
+ "stub"
+#endif
+ ", synchronous resolution of NS record using UDP with TCP fallback\n");
+ fprintf(out, "\nupstreams: @<ip>[%%<scope_id>][@<port>][#<tls port>][~<tls name>][^<tsig spec>]\n");
+ fprintf(out, "\ntsig spec: [<algorithm>:]<name>:<secret in Base64>\n");
+ fprintf(out, "\nextensions:\n");
+ fprintf(out, "\t+add_warning_for_bad_dns\n");
+ fprintf(out, "\t+dnssec_return_status\n");
+ fprintf(out, "\t+dnssec_return_only_secure\n");
+ fprintf(out, "\t+dnssec_return_validation_chain\n");
+#ifdef DNSSEC_ROADBLOCK_AVOIDANCE
+ fprintf(out, "\t+dnssec_roadblock_avoidance\n");
+#endif
+#ifdef EDNS_COOKIES
+ fprintf(out, "\t+edns_cookies\n");
+#endif
+ fprintf(out, "\t+return_both_v4_and_v6\n");
+ fprintf(out, "\t+return_call_reporting\n");
+ fprintf(out, "\t+sit=<cookie>\t\tSend along cookie OPT with value <cookie>\n");
+ fprintf(out, "\t+specify_class=<class>\n");
+ fprintf(out, "\t+0\t\t\tClear all extensions\n");
+ fprintf(out, "\noptions:\n");
+ fprintf(out, "\t-a\tPerform asynchronous resolution "
+ "(default = synchronous)\n");
+ fprintf(out, "\t-A\taddress lookup (<type> is ignored)\n");
+ fprintf(out, "\t-B\tBatch mode. Schedule all messages before processing responses.\n");
+ fprintf(out, "\t-b <bufsize>\tSet edns0 max_udp_payload size\n");
+ fprintf(out, "\t-c\tSend Client Subnet privacy request\n");
+ fprintf(out, "\t-D\tSet edns0 do bit\n");
+ fprintf(out, "\t-d\tclear edns0 do bit\n");
+ fprintf(out, "\t-e <idle_timeout>\tSet idle timeout in miliseconds\n");
+ fprintf(out, "\t-F <filename>\tread the queries from the specified file\n");
+ fprintf(out, "\t-f <filename>\tRead DNSSEC trust anchors from <filename>\n");
+ fprintf(out, "\t-G\tgeneral lookup\n");
+ fprintf(out, "\t-H\thostname lookup. (<name> must be an IP address; <type> is ignored)\n");
+ fprintf(out, "\t-h\tPrint this help\n");
+ fprintf(out, "\t-i\tPrint api information\n");
+ fprintf(out, "\t-I\tInteractive mode (> 1 queries on same context)\n");
+ fprintf(out, "\t-j\tOutput json response dict\n");
+ fprintf(out, "\t-J\tPretty print json response dict\n");
+ fprintf(out, "\t-k\tPrint root trust anchors\n");
+ fprintf(out, "\t-K <pin>\tPin a public key for TLS connections (can repeat)\n");
+ fprintf(out, "\t\t(should look like '" EXAMPLE_PIN "')\n");
+ fprintf(out, "\t-n\tSet TLS authentication mode to NONE (default)\n");
+ fprintf(out, "\t-m\tSet TLS authentication mode to REQUIRED\n");
+ fprintf(out, "\t-p\tPretty print response dict\n");
+ fprintf(out, "\t-P <blocksize>\tPad TLS queries to a multiple of blocksize\n");
+ fprintf(out, "\t-r\tSet recursing resolution type\n");
+ fprintf(out, "\t-R <filename>\tRead root hints from <filename>\n");
+ fprintf(out, "\t-q\tQuiet mode - don't print response\n");
+ fprintf(out, "\t-s\tSet stub resolution type (default = recursing)\n");
+ fprintf(out, "\t-S\tservice lookup (<type> is ignored)\n");
+ fprintf(out, "\t-t <timeout>\tSet timeout in miliseconds\n");
+
+ fprintf(out, "\t-W\tAppend suffix always (default)\n");
+ fprintf(out, "\t-1\tAppend suffix only to single label after failure\n");
+ fprintf(out, "\t-M\tAppend suffix only to multi label name after failure\n");
+ fprintf(out, "\t-N\tNever append a suffix\n");
+ fprintf(out, "\t-Z <suffixes>\tSet suffixes with the given comma separed list\n");
+
+ fprintf(out, "\t-T\tSet transport to TCP only\n");
+ fprintf(out, "\t-O\tSet transport to TCP only keep connections open\n");
+ fprintf(out, "\t-L\tSet transport to TLS only keep connections open\n");
+ fprintf(out, "\t-E\tSet transport to TLS with TCP fallback only keep connections open\n");
+ fprintf(out, "\t-u\tSet transport to UDP with TCP fallback (default)\n");
+ fprintf(out, "\t-U\tSet transport to UDP only\n");
+ fprintf(out, "\t-l <transports>\tSet transport list. List can contain 1 of each of the characters\n");
+ fprintf(out, "\t\t\t U T L S for UDP, TCP or TLS e.g 'UT' or 'LTU' \n");
+
+}
+
+static getdns_return_t validate_chain(getdns_dict *response)
+{
+ getdns_return_t r;
+ getdns_list *validation_chain;
+ getdns_list *replies_tree;
+ getdns_dict *reply;
+ getdns_list *to_validate;
+ getdns_list *trust_anchor;
+ size_t i;
+ int s;
+
+ if (!(to_validate = getdns_list_create()))
+ return GETDNS_RETURN_MEMORY_ERROR;
+
+ if (getdns_context_get_dnssec_trust_anchors(context, &trust_anchor))
+ trust_anchor = getdns_root_trust_anchor(NULL);
+
+ if ((r = getdns_dict_get_list(
+ response, "validation_chain", &validation_chain)))
+ goto error;
+
+ if ((r = getdns_dict_get_list(
+ response, "replies_tree", &replies_tree)))
+ goto error;
+
+ fprintf(stdout, "replies_tree dnssec_status: ");
+ switch ((s = getdns_validate_dnssec(
+ replies_tree, validation_chain, trust_anchor))) {
+
+ case GETDNS_DNSSEC_SECURE:
+ fprintf(stdout, "GETDNS_DNSSEC_SECURE\n");
+ break;
+ case GETDNS_DNSSEC_BOGUS:
+ fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n");
+ break;
+ case GETDNS_DNSSEC_INDETERMINATE:
+ fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n");
+ break;
+ case GETDNS_DNSSEC_INSECURE:
+ fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n");
+ break;
+ case GETDNS_DNSSEC_NOT_PERFORMED:
+ fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n");
+ break;
+ default:
+ fprintf(stdout, "%d\n", (int)s);
+ }
+
+ i = 0;
+ while (!(r = getdns_list_get_dict(replies_tree, i++, &reply))) {
+
+ if ((r = getdns_list_set_dict(to_validate, 0, reply)))
+ goto error;
+
+ printf("reply %u, dnssec_status: ", (unsigned)i);
+ switch ((s = getdns_validate_dnssec(
+ to_validate, validation_chain, trust_anchor))) {
+
+ case GETDNS_DNSSEC_SECURE:
+ fprintf(stdout, "GETDNS_DNSSEC_SECURE\n");
+ break;
+ case GETDNS_DNSSEC_BOGUS:
+ fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n");
+ break;
+ case GETDNS_DNSSEC_INDETERMINATE:
+ fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n");
+ break;
+ case GETDNS_DNSSEC_INSECURE:
+ fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n");
+ break;
+ case GETDNS_DNSSEC_NOT_PERFORMED:
+ fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n");
+ break;
+ default:
+ fprintf(stdout, "%d\n", (int)s);
+ }
+ }
+ if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM)
+ r = GETDNS_RETURN_GOOD;
+error:
+ getdns_list_destroy(trust_anchor);
+ getdns_list_destroy(to_validate);
+
+ return GETDNS_RETURN_GOOD;
+}
+
+void callback(getdns_context *context, getdns_callback_type_t callback_type,
+ getdns_dict *response, void *userarg, getdns_transaction_t trans_id)
+{
+ char *response_str;
+
+ /* This is a callback with data */;
+ if (response && !quiet && (response_str = json ?
+ getdns_print_json_dict(response, json == 1)
+ : getdns_pretty_print_dict(response))) {
+
+ fprintf(stdout, "ASYNC response:\n%s\n", response_str);
+ validate_chain(response);
+ free(response_str);
+ }
+
+ if (callback_type == GETDNS_CALLBACK_COMPLETE) {
+ printf("Response code was: GOOD. Status was: Callback with ID %"PRIu64" was successfull.\n",
+ trans_id);
+
+ } else if (callback_type == GETDNS_CALLBACK_CANCEL)
+ fprintf(stderr,
+ "An error occurred: The callback with ID %"PRIu64" was cancelled. Exiting.\n",
+ trans_id);
+ else {
+ fprintf(stderr,
+ "An error occurred: The callback got a callback_type of %d. Exiting.\n",
+ callback_type);
+ fprintf(stderr,
+ "Error : '%s'\n",
+ getdns_get_errorstr_by_id(callback_type));
+ }
+ getdns_dict_destroy(response);
+ response = NULL;
+}
+
+#define CONTINUE ((getdns_return_t)-2)
+#define CONTINUE_ERROR ((getdns_return_t)-3)
+
+static getdns_return_t set_cookie(getdns_dict *exts, char *cookie)
+{
+ uint8_t data[40];
+ size_t i;
+ getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR;
+ getdns_bindata bindata;
+
+ getdns_dict *opt_parameters = getdns_dict_create();
+ getdns_list *options = getdns_list_create();
+ getdns_dict *option = getdns_dict_create();
+
+ if (*cookie == '=')
+ cookie++;
+
+ for (i = 0; i < 40 && *cookie; i++) {
+ if (*cookie >= '0' && *cookie <= '9')
+ data[i] = (uint8_t)(*cookie - '0') << 4;
+ else if (*cookie >= 'a' && *cookie <= 'f')
+ data[i] = (uint8_t)(*cookie - 'a' + 10) << 4;
+ else if (*cookie >= 'A' && *cookie <= 'F')
+ data[i] = (uint8_t)(*cookie - 'A' + 10) << 4;
+ else
+ goto done;
+ cookie++;
+ if (*cookie >= '0' && *cookie <= '9')
+ data[i] |= (uint8_t)(*cookie - '0');
+ else if (*cookie >= 'a' && *cookie <= 'f')
+ data[i] |= (uint8_t)(*cookie - 'a' + 10);
+ else if (*cookie >= 'A' && *cookie <= 'F')
+ data[i] |= (uint8_t)(*cookie - 'A' + 10);
+ else
+ goto done;
+ cookie++;;
+ }
+ bindata.data = data;
+ bindata.size = i;
+ if ((r = getdns_dict_set_int(option, "option_code", 10)))
+ goto done;
+ if ((r = getdns_dict_set_bindata(option, "option_data", &bindata)))
+ goto done;
+ if ((r = getdns_list_set_dict(options, 0, option)))
+ goto done;
+ if ((r = getdns_dict_set_list(opt_parameters, "options", options)))
+ goto done;
+ r = getdns_dict_set_dict(exts, "add_opt_parameters", opt_parameters);
+done:
+ getdns_dict_destroy(option);
+ getdns_list_destroy(options);
+ getdns_dict_destroy(opt_parameters);
+ return r;
+}
+
+getdns_return_t parse_args(int argc, char **argv)
+{
+ getdns_return_t r = GETDNS_RETURN_GOOD;
+ size_t i, j;
+ char *arg, *c, *endptr;
+ int t, print_api_info = 0, print_trust_anchors = 0;
+ getdns_list *upstream_list = NULL;
+ getdns_list *tas = NULL, *hints = NULL;
+ getdns_dict *pubkey_pin = NULL;
+ getdns_list *suffixes;
+ char *suffix;
+ getdns_bindata bindata;
+ size_t upstream_count = 0;
+ FILE *fh;
+ uint32_t klass;
+
+ for (i = 1; i < argc; i++) {
+ arg = argv[i];
+ if ((t = get_rrtype(arg)) >= 0) {
+ request_type = t;
+ continue;
+
+ } else if (arg[0] == '+') {
+ if (arg[1] == 's' && arg[2] == 'i' && arg[3] == 't' &&
+ (arg[4] == '=' || arg[4] == '\0')) {
+ if ((r = set_cookie(extensions, arg+4))) {
+ fprintf(stderr, "Could not set cookie:"
+ " %d", r);
+ break;
+ }
+ } else if (strncmp(arg+1, "specify_class=", 14) == 0) {
+ if (strncasecmp(arg+15, "IN", 3) == 0)
+ r = getdns_dict_set_int(extensions,
+ "specify_class", GETDNS_RRCLASS_IN);
+ else if (strncasecmp(arg+15, "CH", 3) == 0)
+ r = getdns_dict_set_int(extensions,
+ "specify_class", GETDNS_RRCLASS_CH);
+ else if (strncasecmp(arg+15, "HS", 3) == 0)
+ r = getdns_dict_set_int(extensions,
+ "specify_class", GETDNS_RRCLASS_HS);
+ else if (strncasecmp(arg+15, "NONE", 5) == 0)
+ r = getdns_dict_set_int(extensions,
+ "specify_class", GETDNS_RRCLASS_NONE);
+ else if (strncasecmp(arg+15, "ANY", 4) == 0)
+ r = getdns_dict_set_int(extensions,
+ "specify_class", GETDNS_RRCLASS_ANY);
+ else if (strncasecmp(arg+15, "CLASS", 5) == 0) {
+ klass = strtol(arg + 20, &endptr, 10);
+ if (*endptr || klass > 255)
+ fprintf(stderr,
+ "Unknown class: %s\n",
+ arg+15);
+ else
+ r = getdns_dict_set_int(extensions,
+ "specify_class", klass);
+
+ } else
+ fprintf(stderr,
+ "Unknown class: %s\n", arg+15);
+ } else if (arg[1] == '0') {
+ /* Unset all existing extensions*/
+ getdns_dict_destroy(extensions);
+ extensions = getdns_dict_create();
+ break;
+ } else if ((r = getdns_dict_set_int(extensions, arg+1,
+ GETDNS_EXTENSION_TRUE))) {
+ fprintf(stderr, "Could not set extension "
+ "\"%s\": %d\n", argv[i], r);
+ break;
+ }
+ continue;
+
+ } else if (arg[0] == '@') {
+ getdns_dict *upstream = ipaddr_dict(context, arg + 1);
+ if (upstream) {
+ if (!upstream_list &&
+ !(upstream_list =
+ getdns_list_create_with_context(context))){
+ fprintf(stderr, "Could not create upstream list\n");
+ return GETDNS_RETURN_MEMORY_ERROR;
+ }
+ getdns_list_set_dict(upstream_list,
+ upstream_count++, upstream);
+ }
+ continue;
+ } else if (arg[0] != '-') {
+ name = arg;
+ continue;
+ }
+ for (c = arg+1; *c; c++) {
+ switch (*c) {
+ case 'a':
+ async = 1;
+ break;
+ case 'A':
+ calltype = ADDRESS;
+ break;
+ case 'b':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "max_udp_payload_size "
+ "expected after -b\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ edns0_size = strtol(argv[i], &endptr, 10);
+ if (*endptr || edns0_size < 0) {
+ fprintf(stderr, "positive "
+ "numeric max_udp_payload_size "
+ "expected after -b\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_context_set_edns_maximum_udp_payload_size(
+ context, (uint16_t) edns0_size);
+ goto next;
+ case 'c':
+ if (getdns_context_set_edns_client_subnet_private(context, 1))
+ return GETDNS_RETURN_GENERIC_ERROR;
+ break;
+ case 'D':
+ (void) getdns_context_set_edns_do_bit(context, 1);
+ break;
+ case 'd':
+ (void) getdns_context_set_edns_do_bit(context, 0);
+ break;
+ case 'f':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "file name expected "
+ "after -f\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (!(fh = fopen(argv[i], "r"))) {
+ fprintf(stderr, "Could not open \"%s\""
+ ": %s\n",argv[i], strerror(errno));
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (getdns_fp2rr_list(fh, &tas, NULL, 3600)) {
+ fprintf(stderr,"Could not parse "
+ "\"%s\"\n", argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ fclose(fh);
+ if (getdns_context_set_dnssec_trust_anchors(
+ context, tas)) {
+ fprintf(stderr,"Could not set "
+ "trust anchors from \"%s\"\n",
+ argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_list_destroy(tas);
+ tas = NULL;
+ break;
+ case 'F':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "file name expected "
+ "after -F\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ query_file = argv[i];
+ interactive = 1;
+ break;
+ case 'G':
+ calltype = GENERAL;
+ break;
+ case 'H':
+ calltype = HOSTNAME;
+ break;
+ case 'h':
+ print_usage(stdout, argv[0]);
+ return CONTINUE;
+ case 'i':
+ print_api_info = 1;
+ break;
+ case 'I':
+ interactive = 1;
+ break;
+ case 'j':
+ json = 2;
+ break;
+ case 'J':
+ json = 1;
+ break;
+ case 'K':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "pin string of the form "
+ EXAMPLE_PIN
+ "expected after -K\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ pubkey_pin = getdns_pubkey_pin_create_from_string(context,
+ argv[i]);
+ if (pubkey_pin == NULL) {
+ fprintf(stderr, "could not convert '%s' into a "
+ "public key pin.\n"
+ "Good pins look like: " EXAMPLE_PIN "\n"
+ "Please see RFC 7469 for details about "
+ "the format\n", argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (pubkey_pinset == NULL)
+ pubkey_pinset = getdns_list_create_with_context(context);
+ if (r = getdns_list_set_dict(pubkey_pinset, pincount++,
+ pubkey_pin), r) {
+ fprintf(stderr, "Failed to add pin to pinset (error %d: %s)\n",
+ r, getdns_get_errorstr_by_id(r));
+ getdns_dict_destroy(pubkey_pin);
+ pubkey_pin = NULL;
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_dict_destroy(pubkey_pin);
+ pubkey_pin = NULL;
+ break;
+ case 'k':
+ print_trust_anchors = 1;
+ break;
+ case 'n':
+ getdns_context_set_tls_authentication(context,
+ GETDNS_AUTHENTICATION_NONE);
+ break;
+ case 'm':
+ getdns_context_set_tls_authentication(context,
+ GETDNS_AUTHENTICATION_REQUIRED);
+ break;
+ case 'P':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "tls_query_padding_blocksize "
+ "expected after -P\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ padding_blocksize = strtol(argv[i], &endptr, 10);
+ if (*endptr || padding_blocksize < 0) {
+ fprintf(stderr, "non-negative "
+ "numeric padding blocksize expected "
+ "after -P\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (getdns_context_set_tls_query_padding_blocksize(
+ context, padding_blocksize))
+ return GETDNS_RETURN_GENERIC_ERROR;
+ goto next;
+ case 'p':
+ json = 0;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'r':
+ getdns_context_set_resolution_type(
+ context,
+ GETDNS_RESOLUTION_RECURSING);
+ break;
+ case 'R':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "file name expected "
+ "after -f\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (!(fh = fopen(argv[i], "r"))) {
+ fprintf(stderr, "Could not open \"%s\""
+ ": %s\n",argv[i], strerror(errno));
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (getdns_fp2rr_list(fh, &hints, NULL, 3600)) {
+ fprintf(stderr,"Could not parse "
+ "\"%s\"\n", argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ fclose(fh);
+ if (getdns_context_set_dns_root_servers(
+ context, hints)) {
+ fprintf(stderr,"Could not set "
+ "root servers from \"%s\"\n",
+ argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_list_destroy(hints);
+ hints = NULL;
+ break;
+ case 's':
+ getdns_context_set_resolution_type(
+ context, GETDNS_RESOLUTION_STUB);
+ break;
+ case 'S':
+ calltype = SERVICE;
+ break;
+ case 't':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "timeout expected "
+ "after -t\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ timeout = strtol(argv[i], &endptr, 10);
+ if (*endptr || timeout < 0) {
+ fprintf(stderr, "positive "
+ "numeric timeout expected "
+ "after -t\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_context_set_timeout(
+ context, timeout);
+ goto next;
+ case 'e':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "idle timeout expected "
+ "after -t\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ timeout = strtol(argv[i], &endptr, 10);
+ if (*endptr || timeout < 0) {
+ fprintf(stderr, "positive "
+ "numeric idle timeout expected "
+ "after -t\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_context_set_idle_timeout(
+ context, timeout);
+ goto next;
+ case 'W':
+ (void) getdns_context_set_append_name(context,
+ GETDNS_APPEND_NAME_ALWAYS);
+ break;
+ case '1':
+ (void) getdns_context_set_append_name(context,
+ GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE);
+ break;
+ case 'M':
+ (void) getdns_context_set_append_name(context,
+ GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE);
+ break;
+ case 'N':
+ (void) getdns_context_set_append_name(context,
+ GETDNS_APPEND_NAME_NEVER);
+ break;
+ case 'Z':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "suffixes expected"
+ "after -Z\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ if (!(suffixes = getdns_list_create()))
+ return GETDNS_RETURN_MEMORY_ERROR;
+ suffix = strtok(argv[i], ",");
+ j = 0;
+ while (suffix) {
+ bindata.size = strlen(suffix);
+ bindata.data = (void *)suffix;
+ (void) getdns_list_set_bindata(
+ suffixes, j++, &bindata);
+ suffix = strtok(NULL, ",");
+ }
+ (void) getdns_context_set_suffix(context,
+ suffixes);
+ getdns_list_destroy(suffixes);
+ goto next;
+ case 'T':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_TCP_ONLY);
+ break;
+ case 'O':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN);
+ break;
+ case 'L':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN);
+ break;
+ case 'E':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN);
+ break;
+ case 'u':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP);
+ break;
+ case 'U':
+ getdns_context_set_dns_transport(context,
+ GETDNS_TRANSPORT_UDP_ONLY);
+ break;
+ case 'l':
+ if (c[1] != 0 || ++i >= argc || !*argv[i]) {
+ fprintf(stderr, "transport list expected "
+ "after -l\n");
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ getdns_transport_list_t transports[10];
+ size_t transport_count = sizeof(transports);
+ if ((r = fill_transport_list(context, argv[i], transports, &transport_count)) ||
+ (r = getdns_context_set_dns_transport_list(context,
+ transport_count, transports))){
+ fprintf(stderr, "Could not set transports\n");
+ return r;
+ }
+ break;
+ case 'B':
+ batch_mode = 1;
+ break;
+
+
+ default:
+ fprintf(stderr, "Unknown option "
+ "\"%c\"\n", *c);
+ for (i = 0; i < argc; i++)
+ fprintf(stderr, "%d: \"%s\"\n", (int)i, argv[i]);
+ return GETDNS_RETURN_GENERIC_ERROR;
+ }
+ }
+next: ;
+ }
+ if (r)
+ return r;
+ if (pubkey_pinset && upstream_count) {
+ getdns_dict *upstream;
+ /* apply the accumulated pubkey pinset to all upstreams: */
+ for (i = 0; i < upstream_count; i++) {
+ if (r = getdns_list_get_dict(upstream_list, i, &upstream), r) {
+ fprintf(stderr, "Failed to get upstream %lu when adding pinset\n", (long unsigned int)i);
+ return r;
+ }
+ if (r = getdns_dict_set_list(upstream, "tls_pubkey_pinset", pubkey_pinset), r) {
+ fprintf(stderr, "Failed to set pubkey pinset on upstream %lu\n", (long unsigned int)i);
+ return r;
+ }
+ }
+ }
+ if (upstream_count &&
+ (r = getdns_context_set_upstream_recursive_servers(
+ context, upstream_list))) {
+ fprintf(stderr, "Error setting upstream recursive servers\n");
+ }
+ if (print_api_info) {
+ fprintf(stdout, "%s\n", getdns_pretty_print_dict(
+ getdns_context_get_api_information(context)));
+ return CONTINUE;
+ }
+ if (print_trust_anchors) {
+ if (!getdns_context_get_dnssec_trust_anchors(context, &tas)) {
+ /* if ((tas = getdns_root_trust_anchor(NULL))) { */
+ fprintf(stdout, "%s\n", getdns_pretty_print_list(tas));
+ return CONTINUE;
+ } else
+ return CONTINUE_ERROR;
+ }
+ return r;
+}
+
+getdns_return_t do_the_call(void)
+{
+ getdns_return_t r;
+ getdns_dict *address = NULL;
+ getdns_dict *response = NULL;
+ char *response_str;
+ uint32_t status;
+
+ if (calltype == HOSTNAME &&
+ !(address = ipaddr_dict(context, name))) {
+ fprintf(stderr, "Could not convert \"%s\" "
+ "to an IP address", name);
+ return GETDNS_RETURN_GOOD;
+ }
+ if (async) {
+ switch (calltype) {
+ case GENERAL:
+ r = getdns_general(context, name, request_type,
+ extensions, &response, NULL, callback);
+ break;
+ case ADDRESS:
+ r = getdns_address(context, name,
+ extensions, &response, NULL, callback);
+ break;
+ case HOSTNAME:
+ r = getdns_hostname(context, address,
+ extensions, &response, NULL, callback);
+ break;
+ case SERVICE:
+ r = getdns_service(context, name,
+ extensions, &response, NULL, callback);
+ break;
+ default:
+ r = GETDNS_RETURN_GENERIC_ERROR;
+ break;
+ }
+ if (r == GETDNS_RETURN_GOOD && !batch_mode)
+ getdns_context_run(context);
+ if (r != GETDNS_RETURN_GOOD)
+ fprintf(stderr, "An error occurred: %d '%s'\n", r,
+ getdns_get_errorstr_by_id(r));
+ } else {
+ switch (calltype) {
+ case GENERAL:
+ r = getdns_general_sync(context, name,
+ request_type, extensions, &response);
+ break;
+ case ADDRESS:
+ r = getdns_address_sync(context, name,
+ extensions, &response);
+ break;
+ case HOSTNAME:
+ r = getdns_hostname_sync(context, address,
+ extensions, &response);
+ break;
+ case SERVICE:
+ r = getdns_service_sync(context, name,
+ extensions, &response);
+ break;
+ default:
+ r = GETDNS_RETURN_GENERIC_ERROR;
+ break;
+ }
+ if (r != GETDNS_RETURN_GOOD) {
+ fprintf(stderr, "An error occurred: %d '%s'\n", r,
+ getdns_get_errorstr_by_id(r));
+ return r;
+ }
+ if (response && !quiet) {
+ if ((response_str = json ?
+ getdns_print_json_dict(response, json == 1)
+ : getdns_pretty_print_dict(response))) {
+
+ fprintf( stdout, "SYNC response:\n%s\n"
+ , response_str);
+ validate_chain(response);
+ free(response_str);
+ } else {
+ r = GETDNS_RETURN_MEMORY_ERROR;
+ fprintf( stderr
+ , "Could not print response\n");
+ }
+ }
+#if 1
+ FILE *support_out_fp = fopen("treeout_support", "w");
+ assert(support_out_fp);
+ getdns_list *validation_chain = NULL;
+ if ((r = getdns_dict_get_list(
+ response, "validation_chain", &validation_chain)))
+ assert(!r && "get_list validation_chain");
+ if (response && support_out_fp) {
+ ; //fwrite(support_out_fp, fixme, fixme_len);
+ }
+ fclose(support_out_fp);
+#endif
+ getdns_dict_get_int(response, "status", &status);
+ fprintf(stdout, "Response code was: GOOD. Status was: %s\n",
+ getdns_get_errorstr_by_id(status));
+ if (response)
+ getdns_dict_destroy(response);
+ }
+ return r;
+}
+
+my_eventloop my_loop;
+FILE *fp;
+
+void read_line_cb(void *userarg)
+{
+ getdns_eventloop_event *read_line_ev = userarg;
+ getdns_return_t r;
+
+ char line[1024], *token, *linev[256];
+ int linec;
+
+ if (!fgets(line, 1024, fp) || !*line) {
+ if (query_file)
+ fprintf(stdout,"End of file.");
+ my_eventloop_clear(&my_loop.base, read_line_ev);
+ return;
+ }
+ if (query_file)
+ fprintf(stdout,"Found query: %s", line);
+
+ linev[0] = __FILE__;
+ linec = 1;
+ if (!(token = strtok(line, " \t\f\n\r"))) {
+ if (! query_file) {
+ printf("> ");
+ fflush(stdout);
+ }
+ return;
+ }
+ if (*token == '#') {
+ fprintf(stdout,"Result: Skipping comment\n");
+ if (! query_file) {
+ printf("> ");
+ fflush(stdout);
+ }
+ return;
+ }
+ do linev[linec++] = token;
+ while (linec < 256 && (token = strtok(NULL, " \t\f\n\r")));
+
+ if (((r = parse_args(linec, linev)) || (r = do_the_call())) &&
+ (r != CONTINUE && r != CONTINUE_ERROR))
+ my_eventloop_clear(&my_loop.base, read_line_ev);
+
+ else if (! query_file) {
+ printf("> ");
+ fflush(stdout);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ getdns_return_t r;
+
+ name = the_root;
+ if ((r = getdns_context_create(&context, 1))) {
+ fprintf(stderr, "Create context failed: %d\n", r);
+ return r;
+ }
+ my_eventloop_init(&my_loop);
+ if ((r = getdns_context_set_eventloop(context, &my_loop.base)))
+ goto done_destroy_context;
+ if ((r = getdns_context_set_use_threads(context, 1)))
+ goto done_destroy_context;
+ extensions = getdns_dict_create();
+ if (! extensions) {
+ fprintf(stderr, "Could not create extensions dict\n");
+ r = GETDNS_RETURN_MEMORY_ERROR;
+ goto done_destroy_context;
+ }
+ if ((r = parse_args(argc, argv)))
+ goto done_destroy_context;
+
+ if (query_file) {
+ fp = fopen(query_file, "rt");
+ if (fp == NULL) {
+ fprintf(stderr, "Could not open query file: %s\n", query_file);
+ goto done_destroy_context;
+ }
+ } else
+ fp = stdin;
+
+ /* Make the call */
+ if (interactive) {
+ getdns_eventloop_event read_line_ev = {
+ &read_line_ev, read_line_cb, NULL, NULL, NULL };
+ (void) my_eventloop_schedule(
+ &my_loop.base, fileno(fp), -1, &read_line_ev);
+ if (!query_file) {
+ printf("> ");
+ fflush(stdout);
+ }
+ my_eventloop_run(&my_loop.base);
+ }
+ else
+ r = do_the_call();
+
+ if ((r == GETDNS_RETURN_GOOD && batch_mode))
+ getdns_context_run(context);
+
+ /* Clean up */
+ getdns_dict_destroy(extensions);
+done_destroy_context:
+ getdns_context_destroy(context);
+
+ if (fp)
+ fclose(fp);
+
+ if (r == CONTINUE)
+ return 0;
+ else if (r == CONTINUE_ERROR)
+ return 1;
+ fprintf(stdout, "\nAll done.\n");
+ return r;
+}
+
+int get_rrtype(const char *t) {
+ char *endptr;
+ int r;
+
+ switch (t[0]) {
+ case 'A':
+ case 'a': switch (t[1]) {
+ case '\0': return GETDNS_RRTYPE_A;
+ case '6': if (t[2] == '\0') return GETDNS_RRTYPE_A6;
+ return -1;
+ case 'A':
+ case 'a': /* before "AA", final "AA" (GETDNS_RRTYPE_AAAA) */
+ if ((t[2]|0x20) == 'a' && (t[3]|0x20) == 'a' && t[4] == '\0')
+ return GETDNS_RRTYPE_AAAA;
+ return -1;
+ case 'F':
+ case 'f': /* before "AF", final "SDB" (GETDNS_RRTYPE_AFSDB) */
+ if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'd' && (t[4]|0x20) == 'b' && t[5] == '\0')
+ return GETDNS_RRTYPE_AFSDB;
+ return -1;
+ case 'P':
+ case 'p': /* before "AP", final "L" (GETDNS_RRTYPE_APL) */
+ if ((t[2]|0x20) == 'l' && t[3] == '\0')
+ return GETDNS_RRTYPE_APL;
+ return -1;
+ case 'T':
+ case 't': /* before "AT", final "MA" (GETDNS_RRTYPE_ATMA) */
+ if ((t[2]|0x20) == 'm' && (t[3]|0x20) == 'a' && t[4] == '\0')
+ return GETDNS_RRTYPE_ATMA;
+ return -1;
+ case 'X':
+ case 'x': /* before "AX", final "FR" (GETDNS_RRTYPE_AXFR) */
+ if ((t[2]|0x20) == 'f' && (t[3]|0x20) == 'r' && t[4] == '\0')
+ return GETDNS_RRTYPE_AXFR;
+ return -1;
+ default : return -1;
+ };
+ case 'C':
+ case 'c': switch (t[1]) {
+ case 'A':
+ case 'a': /* before "CA", final "A" (GETDNS_RRTYPE_CAA) */
+ if ((t[2]|0x20) == 'a' && t[3] == '\0')
+ return GETDNS_RRTYPE_CAA;
+ return -1;
+ case 'D':
+ case 'd': switch (t[2]) {
+ case 'N':
+ case 'n': /* before "CDN", final "SKEY" (GETDNS_RRTYPE_CDNSKEY) */
+ if ((t[3]|0x20) == 's' && (t[4]|0x20) == 'k' && (t[5]|0x20) == 'e' && (t[6]|0x20) == 'y' && t[7] == '\0')
+ return GETDNS_RRTYPE_CDNSKEY;
+ return -1;
+ case 'S':
+ case 's': if (t[3] == '\0') return GETDNS_RRTYPE_CDS;
+ return -1;
+ default : return -1;
+ };
+ case 'E':
+ case 'e': /* before "CE", final "RT" (GETDNS_RRTYPE_CERT) */
+ if ((t[2]|0x20) == 'r' && (t[3]|0x20) == 't' && t[4] == '\0')
+ return GETDNS_RRTYPE_CERT;
+ return -1;
+ case 'N':
+ case 'n': /* before "CN", final "AME" (GETDNS_RRTYPE_CNAME) */
+ if ((t[2]|0x20) == 'a' && (t[3]|0x20) == 'm' && (t[4]|0x20) == 'e' && t[5] == '\0')
+ return GETDNS_RRTYPE_CNAME;
+ return -1;
+ case 'S':
+ case 's': /* before "CS", final "YNC" (GETDNS_RRTYPE_CSYNC) */
+ if ((t[2]|0x20) == 'y' && (t[3]|0x20) == 'n' && (t[4]|0x20) == 'c' && t[5] == '\0')
+ return GETDNS_RRTYPE_CSYNC;
+ return -1;
+
+ default : return -1;
+ };
+ case 'D':
+ case 'd': switch (t[1]) {
+ case 'H':
+ case 'h': /* before "DH", final "CID" (GETDNS_RRTYPE_DHCID) */
+ if ((t[2]|0x20) == 'c' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'd' && t[5] == '\0')
+ return GETDNS_RRTYPE_DHCID;
+ return -1;
+ case 'L':
+ case 'l': /* before "DL", final "V" (GETDNS_RRTYPE_DLV) */
+ if ((t[2]|0x20) == 'v' && t[3] == '\0')
+ return GETDNS_RRTYPE_DLV;
+ return -1;
+ case 'N':
+ case 'n': switch (t[2]) {
+ case 'A':
+ case 'a': /* before "DNA", final "ME" (GETDNS_RRTYPE_DNAME) */
+ if ((t[3]|0x20) == 'm' && (t[4]|0x20) == 'e' && t[5] == '\0')
+ return GETDNS_RRTYPE_DNAME;
+ return -1;
+ case 'S':
+ case 's': /* before "DNS", final "KEY" (GETDNS_RRTYPE_DNSKEY) */
+ if ((t[3]|0x20) == 'k' && (t[4]|0x20) == 'e' && (t[5]|0x20) == 'y' && t[6] == '\0')
+ return GETDNS_RRTYPE_DNSKEY;
+ return -1;
+ default : return -1;
+ };
+ case 'S':
+ case 's': if (t[2] == '\0') return GETDNS_RRTYPE_DS;
+ return -1;
+ default : return -1;
+ };
+ case 'E':
+ case 'e': switch (t[1]) {
+ case 'I':
+ case 'i': /* before "EI", final "D" (GETDNS_RRTYPE_EID) */
+ if ((t[2]|0x20) == 'd' && t[3] == '\0')
+ return GETDNS_RRTYPE_EID;
+ return -1;
+ case 'U':
+ case 'u': /* before "EU", next "I" */
+ if ((t[2]|0x20) != 'i')
+ return -1;
+ switch (t[3]) {
+ case '4': /* before "EUI4", final "8" (GETDNS_RRTYPE_EUI48) */
+ if (t[4] == '8' && t[5] == '\0')
+ return GETDNS_RRTYPE_EUI48;
+ return -1;
+ case '6': /* before "EUI6", final "4" (GETDNS_RRTYPE_EUI64) */
+ if (t[4] == '4' && t[5] == '\0')
+ return GETDNS_RRTYPE_EUI64;
+ return -1;
+ default : return -1;
+ };
+ default : return -1;
+ };
+ case 'G':
+ case 'g': switch (t[1]) {
+ case 'I':
+ case 'i': /* before "GI", final "D" (GETDNS_RRTYPE_GID) */
+ if ((t[2]|0x20) == 'd' && t[3] == '\0')
+ return GETDNS_RRTYPE_GID;
+ return -1;
+ case 'P':
+ case 'p': /* before "GP", final "OS" (GETDNS_RRTYPE_GPOS) */
+ if ((t[2]|0x20) == 'o' && (t[3]|0x20) == 's' && t[4] == '\0')
+ return GETDNS_RRTYPE_GPOS;
+ return -1;
+ default : return -1;
+ };
+ case 'H':
+ case 'h': /* before "H", next "I" */
+ if ((t[1]|0x20) != 'i')
+ return -1;
+ switch (t[2]) {
+ case 'N':
+ case 'n': /* before "HIN", final "FO" (GETDNS_RRTYPE_HINFO) */
+ if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0')
+ return GETDNS_RRTYPE_HINFO;
+ return -1;
+ case 'P':
+ case 'p': if (t[3] == '\0') return GETDNS_RRTYPE_HIP;
+ return -1;
+ default : return -1;
+ };
+ case 'I':
+ case 'i': switch (t[1]) {
+ case 'P':
+ case 'p': /* before "IP", final "SECKEY" (GETDNS_RRTYPE_IPSECKEY) */
+ if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'e' && (t[4]|0x20) == 'c' && (t[5]|0x20) == 'k' && (t[6]|0x20) == 'e' && (t[7]|0x20) == 'y' && t[8] == '\0')
+ return GETDNS_RRTYPE_IPSECKEY;
+ return -1;
+ case 'S':
+ case 's': /* before "IS", final "DN" (GETDNS_RRTYPE_ISDN) */
+ if ((t[2]|0x20) == 'd' && (t[3]|0x20) == 'n' && t[4] == '\0')
+ return GETDNS_RRTYPE_ISDN;
+ return -1;
+ case 'X':
+ case 'x': /* before "IX", final "FR" (GETDNS_RRTYPE_IXFR) */
+ if ((t[2]|0x20) == 'f' && (t[3]|0x20) == 'r' && t[4] == '\0')
+ return GETDNS_RRTYPE_IXFR;
+ return -1;
+ default : return -1;
+ };
+ case 'K':
+ case 'k': switch (t[1]) {
+ case 'E':
+ case 'e': /* before "KE", final "Y" (GETDNS_RRTYPE_KEY) */
+ if ((t[2]|0x20) == 'y' && t[3] == '\0')
+ return GETDNS_RRTYPE_KEY;
+ return -1;
+ case 'X':
+ case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_KX;
+ return -1;
+ default : return -1;
+ };
+ case 'L':
+ case 'l': switch (t[1]) {
+ case '3': /* before "L3", final "2" (GETDNS_RRTYPE_L32) */
+ if (t[2] == '2' && t[3] == '\0')
+ return GETDNS_RRTYPE_L32;
+ return -1;
+ case '6': /* before "L6", final "4" (GETDNS_RRTYPE_L64) */
+ if (t[2] == '4' && t[3] == '\0')
+ return GETDNS_RRTYPE_L64;
+ return -1;
+ case 'O':
+ case 'o': /* before "LO", final "C" (GETDNS_RRTYPE_LOC) */
+ if ((t[2]|0x20) == 'c' && t[3] == '\0')
+ return GETDNS_RRTYPE_LOC;
+ return -1;
+ case 'P':
+ case 'p': if (t[2] == '\0') return GETDNS_RRTYPE_LP;
+ return -1;
+ default : return -1;
+ };
+ case 'M':
+ case 'm': switch (t[1]) {
+ case 'A':
+ case 'a': /* before "MA", next "IL" */
+ if ((t[2]|0x20) != 'i' && (t[3]|0x20) != 'l')
+ return -1;
+ switch (t[4]) {
+ case 'A':
+ case 'a': if (t[5] == '\0') return GETDNS_RRTYPE_MAILA;
+ return -1;
+ case 'B':
+ case 'b': if (t[5] == '\0') return GETDNS_RRTYPE_MAILB;
+ return -1;
+ default : return -1;
+ };
+ case 'B':
+ case 'b': if (t[2] == '\0') return GETDNS_RRTYPE_MB;
+ return -1;
+ case 'D':
+ case 'd': if (t[2] == '\0') return GETDNS_RRTYPE_MD;
+ return -1;
+ case 'F':
+ case 'f': if (t[2] == '\0') return GETDNS_RRTYPE_MF;
+ return -1;
+ case 'G':
+ case 'g': if (t[2] == '\0') return GETDNS_RRTYPE_MG;
+ return -1;
+ case 'I':
+ case 'i': /* before "MI", final "NFO" (GETDNS_RRTYPE_MINFO) */
+ if ((t[2]|0x20) == 'n' && (t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0')
+ return GETDNS_RRTYPE_MINFO;
+ return -1;
+ case 'R':
+ case 'r': if (t[2] == '\0') return GETDNS_RRTYPE_MR;
+ return -1;
+ case 'X':
+ case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_MX;
+ return -1;
+ default : return -1;
+ };
+ case 'N':
+ case 'n': switch (t[1]) {
+ case 'A':
+ case 'a': /* before "NA", final "PTR" (GETDNS_RRTYPE_NAPTR) */
+ if ((t[2]|0x20) == 'p' && (t[3]|0x20) == 't' && (t[4]|0x20) == 'r' && t[5] == '\0')
+ return GETDNS_RRTYPE_NAPTR;
+ return -1;
+ case 'I':
+ case 'i': switch (t[2]) {
+ case 'D':
+ case 'd': if (t[3] == '\0') return GETDNS_RRTYPE_NID;
+ return -1;
+ case 'M':
+ case 'm': /* before "NIM", final "LOC" (GETDNS_RRTYPE_NIMLOC) */
+ if ((t[3]|0x20) == 'l' && (t[4]|0x20) == 'o' && (t[5]|0x20) == 'c' && t[6] == '\0')
+ return GETDNS_RRTYPE_NIMLOC;
+ return -1;
+ case 'N':
+ case 'n': /* before "NIN", final "FO" (GETDNS_RRTYPE_NINFO) */
+ if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0')
+ return GETDNS_RRTYPE_NINFO;
+ return -1;
+ default : return -1;
+ };
+ case 'S':
+ case 's': switch (t[2]) {
+ case '\0': return GETDNS_RRTYPE_NS;
+ case 'A':
+ case 'a': /* before "NSA", final "P" (GETDNS_RRTYPE_NSAP) */
+ if ((t[3]|0x20) == 'p' && t[4] == '\0')
+ return GETDNS_RRTYPE_NSAP;
+ return -1;
+ case 'E':
+ case 'e': /* before "NSE", final "C3PARAM" (GETDNS_RRTYPE_NSEC3PARAM) */
+ if ((t[3]|0x20) == 'c' && t[4] == '3' && (t[5]|0x20) == 'p' && (t[6]|0x20) == 'a' && (t[7]|0x20) == 'r' && (t[8]|0x20) == 'a' && (t[9]|0x20) == 'm' && t[10] == '\0')
+ return GETDNS_RRTYPE_NSEC3PARAM;
+ return -1;
+ default : return -1;
+ };
+ case 'U':
+ case 'u': /* before "NU", final "LL" (GETDNS_RRTYPE_NULL) */
+ if ((t[2]|0x20) == 'l' && (t[3]|0x20) == 'l' && t[4] == '\0')
+ return GETDNS_RRTYPE_NULL;
+ return -1;
+ case 'X':
+ case 'x': /* before "NX", final "T" (GETDNS_RRTYPE_NXT) */
+ if ((t[2]|0x20) == 't' && t[3] == '\0')
+ return GETDNS_RRTYPE_NXT;
+ return -1;
+ default : return -1;
+ };
+ case 'O':
+ case 'o': /* before "O", next "P" */
+ if ((t[1]|0x20) != 'p')
+ return -1;
+ switch (t[2]) {
+ case 'E':
+ case 'e': /* before "OPE", final "NPGPKEY" (GETDNS_RRTYPE_OPENPGPKEY) */
+ if ((t[3]|0x20) == 'n' && (t[4]|0x20) == 'p' && (t[5]|0x20) == 'g' && (t[6]|0x20) == 'p' && (t[7]|0x20) == 'k' && (t[8]|0x20) == 'e' && (t[9]|0x20) == 'y' && t[10] == '\0')
+ return GETDNS_RRTYPE_OPENPGPKEY;
+ return -1;
+ case 'T':
+ case 't': if (t[3] == '\0') return GETDNS_RRTYPE_OPT;
+ return -1;
+ default : return -1;
+ };
+ case 'P':
+ case 'p': switch (t[1]) {
+ case 'T':
+ case 't': /* before "PT", final "R" (GETDNS_RRTYPE_PTR) */
+ if ((t[2]|0x20) == 'r' && t[3] == '\0')
+ return GETDNS_RRTYPE_PTR;
+ return -1;
+ case 'X':
+ case 'x': if (t[2] == '\0') return GETDNS_RRTYPE_PX;
+ return -1;
+ default : return -1;
+ };
+ case 'R':
+ case 'r': switch (t[1]) {
+ case 'K':
+ case 'k': /* before "RK", final "EY" (GETDNS_RRTYPE_RKEY) */
+ if ((t[2]|0x20) == 'e' && (t[3]|0x20) == 'y' && t[4] == '\0')
+ return GETDNS_RRTYPE_RKEY;
+ return -1;
+ case 'P':
+ case 'p': if (t[2] == '\0') return GETDNS_RRTYPE_RP;
+ return -1;
+ case 'R':
+ case 'r': /* before "RR", final "SIG" (GETDNS_RRTYPE_RRSIG) */
+ if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'g' && t[5] == '\0')
+ return GETDNS_RRTYPE_RRSIG;
+ return -1;
+ case 'T':
+ case 't': if (t[2] == '\0') return GETDNS_RRTYPE_RT;
+ return -1;
+ default : return -1;
+ };
+ case 'S':
+ case 's': switch (t[1]) {
+ case 'I':
+ case 'i': switch (t[2]) {
+ case 'G':
+ case 'g': if (t[3] == '\0') return GETDNS_RRTYPE_SIG;
+ return -1;
+ case 'N':
+ case 'n': /* before "SIN", final "K" (GETDNS_RRTYPE_SINK) */
+ if ((t[3]|0x20) == 'k' && t[4] == '\0')
+ return GETDNS_RRTYPE_SINK;
+ return -1;
+ default : return -1;
+ };
+ case 'O':
+ case 'o': /* before "SO", final "A" (GETDNS_RRTYPE_SOA) */
+ if ((t[2]|0x20) == 'a' && t[3] == '\0')
+ return GETDNS_RRTYPE_SOA;
+ return -1;
+ case 'P':
+ case 'p': /* before "SP", final "F" (GETDNS_RRTYPE_SPF) */
+ if ((t[2]|0x20) == 'f' && t[3] == '\0')
+ return GETDNS_RRTYPE_SPF;
+ return -1;
+ case 'R':
+ case 'r': /* before "SR", final "V" (GETDNS_RRTYPE_SRV) */
+ if ((t[2]|0x20) == 'v' && t[3] == '\0')
+ return GETDNS_RRTYPE_SRV;
+ return -1;
+ case 'S':
+ case 's': /* before "SS", final "HFP" (GETDNS_RRTYPE_SSHFP) */
+ if ((t[2]|0x20) == 'h' && (t[3]|0x20) == 'f' && (t[4]|0x20) == 'p' && t[5] == '\0')
+ return GETDNS_RRTYPE_SSHFP;
+ return -1;
+ default : return -1;
+ };
+ case 'T':
+ case 't': switch (t[1]) {
+ case 'A':
+ case 'a': /* before "TA", final "LINK" (GETDNS_RRTYPE_TALINK) */
+ if ((t[2]|0x20) == 'l' && (t[3]|0x20) == 'i' && (t[4]|0x20) == 'n' && (t[5]|0x20) == 'k' && t[6] == '\0')
+ return GETDNS_RRTYPE_TALINK;
+ return -1;
+ case 'K':
+ case 'k': /* before "TK", final "EY" (GETDNS_RRTYPE_TKEY) */
+ if ((t[2]|0x20) == 'e' && (t[3]|0x20) == 'y' && t[4] == '\0')
+ return GETDNS_RRTYPE_TKEY;
+ return -1;
+ case 'L':
+ case 'l': /* before "TL", final "SA" (GETDNS_RRTYPE_TLSA) */
+ if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'a' && t[4] == '\0')
+ return GETDNS_RRTYPE_TLSA;
+ return -1;
+ case 'S':
+ case 's': /* before "TS", final "IG" (GETDNS_RRTYPE_TSIG) */
+ if ((t[2]|0x20) == 'i' && (t[3]|0x20) == 'g' && t[4] == '\0')
+ return GETDNS_RRTYPE_TSIG;
+ return -1;
+ case 'X':
+ case 'x': /* before "TX", final "T" (GETDNS_RRTYPE_TXT) */
+ if ((t[2]|0x20) == 't' && t[3] == '\0')
+ return GETDNS_RRTYPE_TXT;
+ return -1;
+ case 'Y':
+ case 'y': /* before "TY", then "PE" followed by a number */
+ if ((t[2]|0x20) == 'p' && (t[3]|0x20) == 'e' && t[4] != '\0') {
+ r = (int) strtol(t + 4, &endptr, 10);
+ if (*endptr == '\0') return r;
+ }
+ return -1;
+ default : return -1;
+ };
+ case 'U':
+ case 'u': switch (t[1]) {
+ case 'I':
+ case 'i': switch (t[2]) {
+ case 'D':
+ case 'd': if (t[3] == '\0') return GETDNS_RRTYPE_UID;
+ return -1;
+ case 'N':
+ case 'n': /* before "UIN", final "FO" (GETDNS_RRTYPE_UINFO) */
+ if ((t[3]|0x20) == 'f' && (t[4]|0x20) == 'o' && t[5] == '\0')
+ return GETDNS_RRTYPE_UINFO;
+ return -1;
+ default : return -1;
+ };
+ case 'N':
+ case 'n': /* before "UN", final "SPEC" (GETDNS_RRTYPE_UNSPEC) */
+ if ((t[2]|0x20) == 's' && (t[3]|0x20) == 'p' && (t[4]|0x20) == 'e' && (t[5]|0x20) == 'c' && t[6] == '\0')
+ return GETDNS_RRTYPE_UNSPEC;
+ return -1;
+ case 'R':
+ case 'r': /* before "UR", final "I" (GETDNS_RRTYPE_URI) */
+ if ((t[2]|0x20) == 'i' && t[3] == '\0')
+ return GETDNS_RRTYPE_URI;
+ return -1;
+ default : return -1;
+ };
+ case 'W':
+ case 'w': /* before "W", final "KS" (GETDNS_RRTYPE_WKS) */
+ if ((t[1]|0x20) == 'k' && (t[2]|0x20) == 's' && t[3] == '\0')
+ return GETDNS_RRTYPE_WKS;
+ return -1;
+ case 'X':
+ case 'x': /* before "X", final "25" (GETDNS_RRTYPE_X25) */
+ if (t[1] == '2' && t[2] == '5' && t[3] == '\0')
+ return GETDNS_RRTYPE_X25;
+ return -1;
+ default : return -1;
+ };
+}
+
+/*
+ * Copyright (c) 2013, 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.
+ */
+
+/* Local Variables: */
+/* c-file-style: "linux" */
+/* End: */
diff --git a/tools/dnssec/dns-wire2text.c b/tools/dnssec/dns-wire2text.c
new file mode 100644
index 0000000..8e4b55d
--- /dev/null
+++ b/tools/dnssec/dns-wire2text.c
@@ -0,0 +1,149 @@
+/*
+ Read RR's in getdns wire format and print them in presentation
+ format on stdout.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <getdns/getdns.h>
+#include <getdns/getdns_extra.h>
+
+#undef DEBUG
+
+#if defined(DEBUG)
+static void
+hd(const char *buf, size_t buf_len)
+{
+ for (size_t n = 0; n < buf_len; n++)
+ {
+ if (n % 16 == 0)
+ {
+ if (n != 0)
+ fprintf(stderr, "\n");
+ fprintf(stderr, "%08x ", n);
+ }
+ else if (n % 8 == 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, "%02hhx ", buf[n]);
+ }
+ fprintf(stderr, "\n");
+}
+#else /* !DEBUG */
+#define hd(a,b)
+#endif
+
+/* Return value:
+ <0 -- error, the return value being -errorcode
+ 0 -- done
+ >0 -- not done yet, call me again
+*/
+static int
+read_rr(const uint8_t **buf, size_t *buf_len, getdns_dict **rr_dict)
+{
+ getdns_return_t r = getdns_wire2rr_dict_scan(buf, buf_len, rr_dict);
+
+ if (r)
+ return -r;
+ return *buf_len;
+}
+
+#define INBUFLEN 4096
+
+static size_t
+read_inbuf(FILE *infp, uint8_t **bufp_out)
+{
+ size_t nread = 0;
+ uint8_t *wirebuf = malloc(INBUFLEN);
+ int chunks = 1;
+
+ if (wirebuf == NULL)
+ goto out;
+
+ while (1)
+ {
+ size_t n = fread(wirebuf + nread, 1, INBUFLEN, infp);
+ nread += n;
+ if (n < INBUFLEN)
+ break; /* Done. */
+
+ wirebuf = realloc(wirebuf, ++chunks * INBUFLEN);
+ if (wirebuf == NULL)
+ break;
+ }
+
+ out:
+ if (bufp_out != NULL)
+ *bufp_out = wirebuf;
+ return nread;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rrv = 0;
+ uint8_t *inbuf = NULL;
+ const uint8_t *bufp = NULL;
+ size_t inbuf_len = 0;
+ getdns_dict *rr_dict = NULL;
+ getdns_return_t r = 0;
+ FILE *infp = stdin;
+
+ if (argc > 1)
+ {
+ infp = fopen(argv[1], "r");
+ if (infp == NULL)
+ {
+ perror("fopen(argv[1])");
+ return -errno;
+ }
+ }
+ inbuf_len = read_inbuf(infp, &inbuf);
+ if (inbuf == NULL)
+ {
+ perror("read_inbuf");
+ return -errno;
+ }
+ if (infp != stdin)
+ if (fclose(infp))
+ perror("fclose");
+ hd((const char *) wirebuf, n);
+
+ bufp = inbuf;
+ while (1)
+ {
+ char *stringbuf = NULL;
+
+ rrv = read_rr(&bufp, &inbuf_len, &rr_dict);
+ if (rrv < 0)
+ break;
+
+ r = getdns_rr_dict2str(rr_dict, &stringbuf);
+ if (r)
+ break;
+
+ getdns_dict_destroy(rr_dict);
+ printf("%s", stringbuf);
+ free(stringbuf);
+
+ if (rrv == 0)
+ break; /* Done. */
+ }
+ free(inbuf);
+
+ if (rrv < 0)
+ {
+ fprintf(stderr, "parsing input failed: %s\n",
+ getdns_get_errorstr_by_id(-rrv));
+ return rrv;
+ }
+ if (r)
+ {
+ fprintf(stderr, "converting dict to string failed: %s\n",
+ getdns_get_errorstr_by_id(r));
+ return -r;
+ }
+
+ return 0;
+}
diff --git a/tools/dnssec/net2wire.c b/tools/dnssec/net2wire.c
new file mode 100644
index 0000000..fb4620b
--- /dev/null
+++ b/tools/dnssec/net2wire.c
@@ -0,0 +1,301 @@
+/* This program is based on (IIRC)
+ * getdns/spec/example/example-simple-answers.c.
+ *
+ * Resolve DNS name argv[1] of type argv[2] using libgetdns and write
+ * the resulting RRs to two files, in DNS binary wire format:
+ *
+ * - binout: /just_address_answers/0/address_data and
+ * /just_address_answers/1/address_data
+ *
+ * - treeout: output from getdns_rr_dict2wire for all RR's in the
+ * answer section
+ *
+ * Based on getdns/spec/example/example-simple-answers.c.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <getdns/getdns.h>
+#include <getdns/getdns_ext_libevent.h>
+#include <event2/event.h>
+
+static int debug = 1;
+
+static getdns_return_t
+dump_reply(FILE *fp, getdns_dict *reply, const char *section_name)
+{
+ getdns_list *section = NULL;
+ size_t section_len = -1;
+ getdns_return_t r;
+ uint8_t res_buf[4096], *res = NULL;
+ size_t res_len = sizeof(res_buf);
+
+ r = getdns_dict_get_list(reply, section_name, &section);
+ if (r) {
+ fprintf(stderr,
+ "unable to get section \"%s\" from reply\n",
+ section_name);
+ return r;
+ }
+
+ r = getdns_list_get_length(section, &section_len);
+ if (r) {
+ fprintf(stderr, "unable to get length of section\n");
+ return r;
+ }
+
+ for (size_t j = 0; j < section_len; j++) {
+ getdns_dict *rr = NULL;
+
+ r = getdns_list_get_dict(section, j , &rr);
+ if (r) {
+ fprintf(stderr, "unable to get rr from entry "
+ "%d: %d\n", j, r);
+ return r;
+ }
+
+ r = getdns_rr_dict2wire(rr, &res, &res_len);
+ if (r) {
+ fprintf(stderr,
+ "unable to convert entry %d "
+ "to wire format: %d\n", j, r);
+ return r;
+ }
+
+ if (0 && debug) {
+ char *s = getdns_pretty_print_dict(rr);
+ puts(s);
+ free(s);
+ }
+
+ if (fwrite(res, 1, res_len, fp) != res_len) {
+ fprintf(stderr,
+ "unable to write buffer to file: %s\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ free(res);
+ }
+
+ return 0;
+}
+
+int
+dump_tree(FILE *fp, const getdns_dict *response, const char *tree_name)
+{
+ getdns_return_t r;
+ getdns_list *tree = NULL;
+ size_t n_replies = -1;
+
+ r = getdns_dict_get_list(response, tree_name, &tree);
+ if (r) {
+ fprintf(stderr, "unable to get tree %s\n", tree_name);
+ return r;
+ }
+
+ r = getdns_list_get_length(tree, &n_replies);
+ if (r) {
+ fprintf(stderr, "unable to get number of replies\n");
+ return r;
+ }
+
+ for (size_t i = 0; i < n_replies; i++) {
+ getdns_dict *reply = NULL;
+
+ r = getdns_list_get_dict(tree, i, &reply);
+ if (r) {
+ fprintf(stderr, "unable to get reply %d from tree\n", i);
+ return r;
+ }
+
+ if (debug) {
+ char *s = getdns_pretty_print_dict(reply);
+ printf("Pretty-printing reply #%d:%s\n", i, s);
+ free(s);
+ }
+
+ dump_reply(fp, reply, "answer");
+ }
+
+ return 0;
+}
+
+
+/* Set up the callback function, which will also do the processing of the results */
+void callback(getdns_context *context,
+ getdns_callback_type_t callback_type,
+ getdns_dict *response,
+ void *userarg,
+ getdns_transaction_t transaction_id)
+{
+ getdns_return_t r; /* Holder for all function returns */
+ uint32_t status;
+ getdns_bindata *address_data;
+ char *first = NULL, *second = NULL;
+ FILE *binoutfp = NULL, *treeoutfp = NULL;
+
+ (void) context; /* unused parameter */
+
+ binoutfp = fopen("binout", "w");
+ assert(binoutfp != NULL);
+ treeoutfp = fopen("treeout", "w");
+ assert(treeoutfp != NULL);
+
+ printf( "Callback for query \"%s\" with request ID %"PRIu64".\n"
+ , (char *)userarg, transaction_id );
+
+ switch(callback_type) {
+ case GETDNS_CALLBACK_CANCEL:
+ printf("Transaction with ID %"PRIu64" was cancelled.\n", transaction_id);
+ return;
+ case GETDNS_CALLBACK_TIMEOUT:
+ printf("Transaction with ID %"PRIu64" timed out.\n", transaction_id);
+ return;
+ case GETDNS_CALLBACK_ERROR:
+ printf("An error occurred for transaction ID %"PRIu64".\n", transaction_id);
+ return;
+ default: break;
+ }
+ assert( callback_type == GETDNS_CALLBACK_COMPLETE );
+
+ if ((r = getdns_dict_get_int(response, "status", &status)))
+ fprintf(stderr, "Could not get \"status\" from reponse");
+
+ else if (status != GETDNS_RESPSTATUS_GOOD)
+ fprintf(stderr, "The search had no results, and a return value of %"PRIu32".\n", status);
+
+ static char *TREES[] = {"replies_tree", "validation_chain"};
+ static const int TREES_LEN = 2;
+ for (int i = 0; i < TREES_LEN; i++) {
+ if (dump_tree(treeoutfp, response, TREES[i]))
+ fprintf(stderr, "Could not dump %s to file\n", TREES[i]);
+
+ else if ((r = getdns_dict_get_bindata(response, "/just_address_answers/0/address_data", &address_data)))
+ fprintf(stderr, "Could not get first address");
+
+ else if (fwrite(address_data->data, 1, address_data->size, binoutfp) !=
+ address_data->size)
+ fprintf(stderr, "first fwrite: %s\n", strerror(errno));
+
+ else if (!(first = getdns_display_ip_address(address_data)))
+ fprintf(stderr, "Could not convert first address to string\n");
+
+ else if ((r = getdns_dict_get_bindata(response, "/just_address_answers/1/address_data", &address_data)))
+ fprintf(stderr, "Could not get second address");
+
+ else if (fwrite(address_data->data, 1, address_data->size, binoutfp) !=
+ address_data->size)
+ fprintf(stderr, "second fwrite: %s\n", strerror(errno));
+
+ else if (!(second = getdns_display_ip_address(address_data)))
+ fprintf(stderr, "Could not convert second address to string\n");
+ if (first) {
+ printf("The address is %s\n", first);
+ free(first);
+ }
+ if (second) {
+ printf("The address is %s\n", second);
+ free(second);
+ }
+ if (r) {
+ assert( r != GETDNS_RETURN_GOOD );
+ fprintf(stderr, ": %d\n", r);
+ }
+ }
+ getdns_dict_destroy(response);
+ if (binoutfp) fclose(binoutfp);
+ if (treeoutfp) fclose(treeoutfp);
+}
+
+static getdns_return_t
+send_query(getdns_context *context,
+ char *query_name,
+ const uint16_t query_type,
+ getdns_transaction_t *transaction_id)
+{
+ getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR;
+ getdns_dict *extensions = getdns_dict_create();
+ char *userarg = query_name;
+
+ assert(extensions != NULL);
+
+ r = getdns_dict_set_int(extensions,
+ "dnssec_return_validation_chain",
+ GETDNS_EXTENSION_TRUE);
+ if (r) return r;
+
+#if 0
+ r = getdns_dict_set_int(extensions,
+ "dnssec_return_status",
+ GETDNS_EXTENSION_TRUE);
+ if (r) return r;
+#endif
+
+ if (query_type == 0)
+ r = getdns_address(context, query_name, extensions, userarg,
+ transaction_id, callback);
+ else
+ r = getdns_general(context, query_name, query_type,
+ extensions, userarg,
+ transaction_id, callback);
+ if (r) return r;
+
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ getdns_return_t r; /* Holder for all function returns */
+ getdns_context *context = NULL;
+ struct event_base *event_base = NULL;
+ char *query_name = "www.example.com";
+ uint16_t query_type = 0;
+ getdns_transaction_t transaction_id;
+ getdns_list *trust_anchors;
+
+
+ if (argc > 1)
+ query_name = argv[1];
+ if (argc > 2)
+ query_type = (uint16_t) atoi(argv[2]);
+
+ if ((r = getdns_context_create(&context, 1)))
+ fprintf(stderr, "Trying to create the context failed");
+
+ else if ((r = getdns_context_get_dnssec_trust_anchors(
+ context, &trust_anchors)) ||
+ trust_anchors == NULL)
+ fprintf(stderr, "No trust anchor.\n");
+
+ else if (!(event_base = event_base_new()))
+ fprintf(stderr, "Trying to create the event base failed.\n");
+
+ else if ((r = getdns_extension_set_libevent_base(context, event_base)))
+ fprintf(stderr, "Setting the event base failed");
+
+ else if ((r = send_query(context, query_name, query_type,
+ &transaction_id)))
+ fprintf(stderr, "Queueing query failed: %d\n", r);
+
+ else {
+ printf("Request with transaction ID %"PRIu64" scheduled.\n",
+ transaction_id);
+ if (event_base_dispatch(event_base) < 0)
+ fprintf(stderr, "Error dispatching events\n");
+ }
+
+ /* Clean up */
+ if (event_base)
+ event_base_free(event_base);
+
+ if (context)
+ getdns_context_destroy(context);
+
+ /* Assuming we get here, leave gracefully */
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/tools/submitcert.py b/tools/submitcert.py
index 91d2111..b185a62 100755
--- a/tools/submitcert.py
+++ b/tools/submitcert.py
@@ -42,7 +42,9 @@ logpublickey = get_public_key_from_file(args.publickey) if args.publickey else N
lookup_in_log = False
-if certfilepath[-1] == "/":
+if certfilepath is None:
+ certfiles = None
+elif certfilepath[-1] == "/":
certfiles = [certfilepath + filename for filename in sorted(os.listdir(certfilepath)) if os.path.isfile(certfilepath + filename)]
else:
certfiles = [certfilepath]
@@ -159,12 +161,17 @@ p = Pool(args.parallel, lambda: signal.signal(signal.SIGINT, signal.SIG_IGN))
nsubmitted = 0
lastprinted = 0
-print "listing certs"
-ncerts = get_ncerts(certfiles)
-
-print ncerts, "certs"
+def get_cert_from_stdin():
+ yield (('<stdin>', base64.b64encode(sys.stdin.read())))
-certs = get_all_certificates(certfiles)
+print "listing certs"
+if certfiles is not None:
+ ncerts = get_ncerts(certfiles)
+ print ncerts, "certs"
+ certs = get_all_certificates(certfiles)
+else:
+ ncerts = 1
+ certs = get_cert_from_stdin()
(result, timing) = submitcert(certs.next())
if result != None: