2 * Code in examine_result() is taken from official libunbound examples:
3 * https://nlnetlabs.nl/documentation/unbound/libunbound-tutorial-3/
4 * The rest of this file is
5 * Copyright (C) 2020 by Wojtek Kosior <echo a3dvanR1c0Bwcm90b25tYWlsLmNvbQo= | base64 --decode>
7 * Permission to use, copy, modify, and/or distribute this software
8 * for any purpose with or without fee is hereby granted.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
25 #include <arpa/inet.h>
31 /* To specify when creating unbound context - has nothing to do with
32 * out logging facility
34 #define DEFAULT_DEBUGLEVEL 0
37 * This is the path in Debian - in other systems it will be different
38 * and we will need to either somehow find it dynamically or get the path
41 #define CA_BUNDLE_FILE "/etc/ssl/certs/ca-certificates.crt"
43 /* In the long run me might rename this file to somewhere else... */
44 #define TRUST_ANCHOR_FILE "./root.key"
46 #define MALLOC_FAILURE_STRING "Couldn't allocate memory.\n"
48 /* examine the result structure in detail */
49 void examine_result(const char *query
, struct ub_result
*result
)
54 printf("The query is for: %s\n", query
);
55 printf("The result has:\n");
56 printf("qname: %s\n", result
->qname
);
57 printf("qtype: %d\n", result
->qtype
);
58 printf("qclass: %d\n", result
->qclass
);
60 printf("canonical name: %s\n",
62 else printf("canonical name: <none>\n");
66 else printf("has no data\n");
69 printf("nxdomain (name does not exist)\n");
70 else printf("not an nxdomain (name exists)\n");
73 printf("validated to be secure\n");
74 else printf("not validated as secure\n");
77 printf("a security failure! (bogus)\n");
78 else printf("not a security failure (not bogus)\n");
80 printf("DNS rcode: %d\n", result
->rcode
);
86 for(i
=0; result
->data
[i
]; i
++) {
87 printf("result data element %d has length %d\n",
89 printf("result data element %d is: %s\n",
90 i
, inet_ntoa(*(struct in_addr
*)result
->data
[i
]));
93 printf("result has %d data element(s)\n", num
);
96 enum resolution_mode
{
102 struct ub_ctx
*ztdns_create_ub_context(enum resolution_mode mode
,
103 const char *resolver_addr
,
107 const char *error_message_format
;
109 ctx
= ub_ctx_create();
111 fprintf(stderr
, "Couldn't create libunbound context.\n");
115 if (mode
== RECURSIVE
) {
116 error_message_format
= "Couldn't set forward server: %s\n";
117 rc
= ub_ctx_set_fwd(ctx
, resolver_addr
);
120 /* Make DNS over TLS mandatory for recursive resolvers */
121 /* TODO tls not working for some reason - this has to be fixed */
122 /* error_message_format = "Couldn't enable DNS over TLS: %s\n"; */
123 /* rc = ub_ctx_set_tls(ctx, 1); */
126 /* rc = ub_ctx_set_option(ctx, "tls-cert-bundle:", CA_BUNDLE_FILE); */
127 } else if (mode
== FULL
) {
128 /* TODO use root_hints here for better reliability */
129 /* For iterative queries we use DNSSEC if possible */
130 error_message_format
= "Couldn't set trust anchors: %s\n";
131 rc
= ub_ctx_add_ta_autr(ctx
, TRUST_ANCHOR_FILE
);
132 } else /* if (mode == RESOLV_CONF) */ {
133 /* NULL can be passed to use system's default resolv.conf */
134 error_message_format
= "Couldn't use system resolv.conf: %s\n";
135 rc
= ub_ctx_resolvconf(ctx
, NULL
);
141 error_message_format
= "Couldn't set debuglevel: %s\n";
142 rc
= ub_ctx_debuglevel(ctx
, debuglevel
);
146 fprintf(stderr
, error_message_format
, ub_strerror(rc
));
154 void ztdns_try_resolve(struct ub_ctx
*ctx
, const char *name
) {
155 struct ub_result
* result
;
157 rc
= ub_resolve(ctx
, name
,
158 1 /* TYPE A (IPv4 address) */,
159 1 /* CLASS IN (internet) */, &result
);
161 printf("resolve error: %s\n", ub_strerror(rc
));
163 examine_result(name
, result
);
164 ub_resolve_free(result
);
168 struct ztdns_resolver
{
170 const char *name
; /* arbitrary name - only used for printing to user */
171 const char *address
; /* IP addr in dot notation stored as string */
172 /* Compatible answer must be returned by resolvers with their
173 * trust levels summing to at least 100 - otherwise the answer is
174 * considered unreliable.
177 /* Whether we want ztdns to report when this resolver gives answer,
178 * that is not confirmed by others (i.e. trust levels sum for this
179 * answer doesn't reach 100).
182 struct ztdns_resolver
*next
;
185 struct ztdns_resolver
*ztdns_create_recursive_resolver
186 (const char *name
, const char *address
, uint8_t trust_level
, bool report_errors
,
189 struct ztdns_resolver
*resolver
;
190 resolver
= malloc(sizeof(struct ztdns_resolver
));
192 fprintf(stderr
, MALLOC_FAILURE_STRING
);
196 resolver
->ctx
= ztdns_create_ub_context(RECURSIVE
, address
, debuglevel
);
200 resolver
->name
= name
;
201 resolver
->address
= address
;
202 resolver
->trust_level
= trust_level
;
203 resolver
->report_errors
= report_errors
;
204 resolver
->next
= NULL
;
212 void ztdns_delete_recursive_resolver(struct ztdns_resolver
*resolver
) {
213 ub_ctx_delete(resolver
->ctx
);
217 struct ztdns_instance
{
218 struct ub_ctx
*ctx_resolv_conf
, *ctx_full
;
219 struct ztdns_resolver
*recursive_resolvers
;
223 * Hardcoded recursive DNS servers. A temporary solution - those should
224 * ideally by obtained from command line or configuration file.
226 const char *resolvers_addresses
[] = {"8.8.8.8", "8.8.4.4",
227 "1.1.1.1", "127.0.0.1"};
228 const char *resolvers_names
[] = {"google", "google",
229 "cloudflare", "localhost"};
230 uint8_t resolvers_trust_levels
[] = {40, 40, 80, 20};
231 uint8_t resolvers_report_errors
[] = {false, false, false, true};
233 #define RESOLVERS_COUNT 4
235 struct ztdns_instance
*ztdns_create_instance(int argc
, char **argv
)
237 struct ztdns_instance
*ztdns
;
239 struct ztdns_resolver
*tmp
;
241 ztdns
= malloc(sizeof(struct ztdns_instance
));
243 /* This is an example of how rest of the code shold be
244 * written/rewritten to use our logging facility.
246 ztdns_log(ERROR
, "No memory, no fun :(\n");
250 /* Create context for performing full resolution */
252 ztdns_create_ub_context(FULL
, NULL
, DEFAULT_DEBUGLEVEL
);
253 if (!ztdns
->ctx_full
)
254 goto out_err_cleanup_instance
;
256 /* Create context for performing resolution with default resolver */
257 ztdns
->ctx_resolv_conf
=
258 ztdns_create_ub_context(RESOLV_CONF
, NULL
, DEFAULT_DEBUGLEVEL
);
259 if (!ztdns
->ctx_resolv_conf
)
260 goto out_err_cleanup_ctx_full
;
262 /* Create contexts for performing resolution with resolvers provided
263 * by user (well, hardcoded ones in this case)
265 ztdns
->recursive_resolvers
= NULL
;
266 for (i
= 0; i
< RESOLVERS_COUNT
; i
++) {
267 tmp
= ztdns_create_recursive_resolver
268 (resolvers_names
[i
], resolvers_addresses
[i
],
269 resolvers_trust_levels
[i
], resolvers_report_errors
[i
],
272 goto out_err_cleanup_recursive_resolvers
;
274 tmp
->next
= ztdns
->recursive_resolvers
;
275 ztdns
->recursive_resolvers
= tmp
;
280 out_err_cleanup_recursive_resolvers
:
281 while (ztdns
->recursive_resolvers
) {
282 tmp
= ztdns
->recursive_resolvers
->next
;
283 ztdns_delete_recursive_resolver(ztdns
->recursive_resolvers
);
284 ztdns
->recursive_resolvers
= tmp
;
287 ub_ctx_delete(ztdns
->ctx_resolv_conf
);
288 out_err_cleanup_ctx_full
:
289 ub_ctx_delete(ztdns
->ctx_full
);
290 out_err_cleanup_instance
:
295 void ztdns_delete_instance(struct ztdns_instance
*ztdns
)
297 struct ztdns_resolver
*tmp
;
299 while (ztdns
->recursive_resolvers
) {
300 tmp
= ztdns
->recursive_resolvers
->next
;
301 ztdns_delete_recursive_resolver(ztdns
->recursive_resolvers
);
302 ztdns
->recursive_resolvers
= tmp
;
305 ub_ctx_delete(ztdns
->ctx_resolv_conf
);
306 ub_ctx_delete(ztdns
->ctx_full
);
310 int main(int argc
, char** argv
)
312 struct ztdns_instance
*ztdns
;
313 struct ztdns_resolver
*tmp
;
314 const char *queried_name
= "google.com";
316 ztdns
= ztdns_create_instance(argc
, argv
);
320 printf("* FULL RESOLUTION\n");
321 ztdns_try_resolve(ztdns
->ctx_full
, queried_name
);
322 printf("* USING RESOLVER FROM resolv.conf\n");
323 ztdns_try_resolve(ztdns
->ctx_resolv_conf
, queried_name
);
325 for (tmp
= ztdns
->recursive_resolvers
; tmp
; tmp
= tmp
->next
) {
326 printf("* VIA %s (%s)\n", tmp
->name
, tmp
->address
);
327 ztdns_try_resolve(tmp
->ctx
, queried_name
);
330 ztdns_delete_instance(ztdns
);