add mock of our logging facility
[0tDNS.git] / src / 0tDNS.c
blobfbd3a78e902b367e520e7b510c3b01bfa47ec92a
1 /*
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
16 * OF THIS SOFTWARE.
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <arpa/inet.h>
26 #include <stdbool.h>
27 #include <unbound.h>
29 #include "log.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
39 * from the user.
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)
51 int i;
52 int num;
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);
59 if(result->canonname)
60 printf("canonical name: %s\n",
61 result->canonname);
62 else printf("canonical name: <none>\n");
64 if(result->havedata)
65 printf("has data\n");
66 else printf("has no data\n");
68 if(result->nxdomain)
69 printf("nxdomain (name does not exist)\n");
70 else printf("not an nxdomain (name exists)\n");
72 if(result->secure)
73 printf("validated to be secure\n");
74 else printf("not validated as secure\n");
76 if(result->bogus)
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);
82 if(!result->havedata)
83 return;
85 num = 0;
86 for(i=0; result->data[i]; i++) {
87 printf("result data element %d has length %d\n",
88 i, result->len[i]);
89 printf("result data element %d is: %s\n",
90 i, inet_ntoa(*(struct in_addr*)result->data[i]));
91 num++;
93 printf("result has %d data element(s)\n", num);
96 enum resolution_mode {
97 RECURSIVE,
98 FULL,
99 RESOLV_CONF
102 struct ub_ctx *ztdns_create_ub_context(enum resolution_mode mode,
103 const char *resolver_addr,
104 int debuglevel) {
105 int rc;
106 struct ub_ctx* ctx;
107 const char *error_message_format;
109 ctx = ub_ctx_create();
110 if (!ctx) {
111 fprintf(stderr, "Couldn't create libunbound context.\n");
112 return NULL;
115 if (mode == RECURSIVE) {
116 error_message_format = "Couldn't set forward server: %s\n";
117 rc = ub_ctx_set_fwd(ctx, resolver_addr);
118 if (rc)
119 goto out;
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); */
124 /* if (rc) */
125 /* goto out; */
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);
138 if (rc)
139 goto out;
141 error_message_format = "Couldn't set debuglevel: %s\n";
142 rc = ub_ctx_debuglevel(ctx, debuglevel);
144 out:
145 if (rc) {
146 fprintf(stderr, error_message_format, ub_strerror(rc));
147 ub_ctx_delete(ctx);
148 return NULL;
151 return ctx;
154 void ztdns_try_resolve(struct ub_ctx *ctx, const char *name) {
155 struct ub_result* result;
156 int rc;
157 rc = ub_resolve(ctx, name,
158 1 /* TYPE A (IPv4 address) */,
159 1 /* CLASS IN (internet) */, &result);
160 if(rc)
161 printf("resolve error: %s\n", ub_strerror(rc));
162 else {
163 examine_result(name, result);
164 ub_resolve_free(result);
168 struct ztdns_resolver {
169 struct ub_ctx *ctx;
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.
176 uint8_t trust_level;
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).
181 bool report_errors;
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,
187 int debuglevel)
189 struct ztdns_resolver *resolver;
190 resolver = malloc(sizeof(struct ztdns_resolver));
191 if (!resolver) {
192 fprintf(stderr, MALLOC_FAILURE_STRING);
193 return NULL;
196 resolver->ctx = ztdns_create_ub_context(RECURSIVE, address, debuglevel);
197 if (!resolver->ctx)
198 goto out_err;
200 resolver->name = name;
201 resolver->address = address;
202 resolver->trust_level = trust_level;
203 resolver->report_errors = report_errors;
204 resolver->next = NULL;
205 return resolver;
207 out_err:
208 free(resolver);
209 return NULL;
212 void ztdns_delete_recursive_resolver(struct ztdns_resolver *resolver) {
213 ub_ctx_delete(resolver->ctx);
214 free(resolver);
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;
238 int i;
239 struct ztdns_resolver *tmp;
241 ztdns = malloc(sizeof(struct ztdns_instance));
242 if (!ztdns) {
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");
247 return NULL;
250 /* Create context for performing full resolution */
251 ztdns->ctx_full =
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],
270 DEFAULT_DEBUGLEVEL);
271 if (!tmp)
272 goto out_err_cleanup_recursive_resolvers;
274 tmp->next = ztdns->recursive_resolvers;
275 ztdns->recursive_resolvers = tmp;
278 return ztdns;
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:
291 free(ztdns);
292 return NULL;
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);
307 free(ztdns);
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);
317 if (!ztdns)
318 return EXIT_FAILURE;
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);
332 return EXIT_SUCCESS;