Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / bin / tests / system / dlzexternal / driver.c
blob460db14d8abb9f04aa7d868bb7710dbf06378562
1 /* $NetBSD: driver.c,v 1.3 2014/12/10 04:37:54 christos Exp $ */
3 /*
4 * Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
20 * This provides a very simple example of an external loadable DLZ
21 * driver, with update support.
24 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
30 #include <isc/log.h>
31 #include <isc/print.h>
32 #include <isc/result.h>
33 #include <isc/string.h>
34 #include <isc/types.h>
35 #include <isc/util.h>
37 #include <dns/types.h>
38 #include <dns/dlz_dlopen.h>
40 #include "driver.h"
42 #ifdef WIN32
43 #define STRTOK_R(a, b, c) strtok_s(a, b, c)
44 #elif defined(_REENTRANT)
45 #define STRTOK_R(a, b, c) strtok_r(a, b, c)
46 #else
47 #define STRTOK_R(a, b, c) strtok(a, b)
48 #endif
50 #define CHECK(x) \
51 do { \
52 result = (x); \
53 if (result != ISC_R_SUCCESS) \
54 goto failure; \
55 } while (/*CONSTCOND*/0)
57 /* For this simple example, use fixed sized strings */
58 struct record {
59 char name[100];
60 char type[10];
61 char data[200];
62 dns_ttl_t ttl;
65 #define MAX_RECORDS 100
67 typedef void log_t(int level, const char *fmt, ...);
69 struct dlz_example_data {
70 char *zone_name;
72 /* An example driver doesn't need good memory management :-) */
73 struct record current[MAX_RECORDS];
74 struct record adds[MAX_RECORDS];
75 struct record deletes[MAX_RECORDS];
77 isc_boolean_t transaction_started;
79 /* Helper functions from the dlz_dlopen driver */
80 log_t *log;
81 dns_sdlz_putrr_t *putrr;
82 dns_sdlz_putnamedrr_t *putnamedrr;
83 dns_dlz_writeablezone_t *writeable_zone;
86 static isc_boolean_t
87 single_valued(const char *type) {
88 const char *single[] = { "soa", "cname", NULL };
89 int i;
91 for (i = 0; single[i]; i++) {
92 if (strcasecmp(single[i], type) == 0) {
93 return (ISC_TRUE);
96 return (ISC_FALSE);
100 * Add a record to a list
102 static isc_result_t
103 add_name(struct dlz_example_data *state, struct record *list,
104 const char *name, const char *type, dns_ttl_t ttl, const char *data)
106 int i;
107 isc_boolean_t single = single_valued(type);
108 int first_empty = -1;
110 for (i = 0; i < MAX_RECORDS; i++) {
111 if (first_empty == -1 && strlen(list[i].name) == 0U) {
112 first_empty = i;
114 if (strcasecmp(list[i].name, name) != 0)
115 continue;
116 if (strcasecmp(list[i].type, type) != 0)
117 continue;
118 if (!single && strcasecmp(list[i].data, data) != 0)
119 continue;
120 break;
122 if (i == MAX_RECORDS && first_empty != -1) {
123 i = first_empty;
125 if (i == MAX_RECORDS) {
126 if (state->log != NULL)
127 state->log(ISC_LOG_ERROR,
128 "dlz_example: out of record space");
129 return (ISC_R_FAILURE);
132 if (strlen(name) >= sizeof(list[i].name) ||
133 strlen(type) >= sizeof(list[i].type) ||
134 strlen(data) >= sizeof(list[i].data))
135 return (ISC_R_NOSPACE);
137 strncpy(list[i].name, name, sizeof(list[i].name));
138 list[i].name[sizeof(list[i].name) - 1] = '\0';
140 strncpy(list[i].type, type, sizeof(list[i].type));
141 list[i].type[sizeof(list[i].type) - 1] = '\0';
143 strncpy(list[i].data, data, sizeof(list[i].data));
144 list[i].data[sizeof(list[i].data) - 1] = '\0';
146 list[i].ttl = ttl;
148 return (ISC_R_SUCCESS);
152 * Delete a record from a list
154 static isc_result_t
155 del_name(struct dlz_example_data *state, struct record *list,
156 const char *name, const char *type, dns_ttl_t ttl,
157 const char *data)
159 int i;
161 UNUSED(state);
163 for (i = 0; i < MAX_RECORDS; i++) {
164 if (strcasecmp(name, list[i].name) == 0 &&
165 strcasecmp(type, list[i].type) == 0 &&
166 strcasecmp(data, list[i].data) == 0 &&
167 ttl == list[i].ttl) {
168 break;
171 if (i == MAX_RECORDS) {
172 return (ISC_R_NOTFOUND);
174 memset(&list[i], 0, sizeof(struct record));
175 return (ISC_R_SUCCESS);
178 static isc_result_t
179 fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) {
180 char addr_buf[100];
181 const char *ret;
182 isc_uint16_t port = 0;
184 switch (addr->type.sa.sa_family) {
185 case AF_INET:
186 port = ntohs(addr->type.sin.sin_port);
187 ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf,
188 sizeof(addr_buf));
189 break;
190 case AF_INET6:
191 port = ntohs(addr->type.sin6.sin6_port);
192 ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf,
193 sizeof(addr_buf));
194 break;
195 default:
196 return (ISC_R_FAILURE);
199 if (ret == NULL)
200 return (ISC_R_FAILURE);
202 snprintf(buffer, size, "%s#%u", addr_buf, port);
203 return (ISC_R_SUCCESS);
207 * Return the version of the API
210 dlz_version(unsigned int *flags) {
211 UNUSED(flags);
212 return (DLZ_DLOPEN_VERSION);
216 * Remember a helper function from the bind9 dlz_dlopen driver
218 static void
219 b9_add_helper(struct dlz_example_data *state,
220 const char *helper_name, void *ptr)
222 if (strcmp(helper_name, "log") == 0)
223 state->log = (log_t *)ptr;
224 if (strcmp(helper_name, "putrr") == 0)
225 state->putrr = (dns_sdlz_putrr_t *)ptr;
226 if (strcmp(helper_name, "putnamedrr") == 0)
227 state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
228 if (strcmp(helper_name, "writeable_zone") == 0)
229 state->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
233 * Called to initialize the driver
235 isc_result_t
236 dlz_create(const char *dlzname, unsigned int argc, char *argv[],
237 void **dbdata, ...)
239 struct dlz_example_data *state;
240 const char *helper_name;
241 va_list ap;
242 char soa_data[1024];
243 const char *extra;
244 isc_result_t result;
245 int n;
247 UNUSED(dlzname);
249 state = calloc(1, sizeof(struct dlz_example_data));
250 if (state == NULL)
251 return (ISC_R_NOMEMORY);
253 /* Fill in the helper functions */
254 va_start(ap, dbdata);
255 while ((helper_name = va_arg(ap, const char *)) != NULL) {
256 b9_add_helper(state, helper_name, va_arg(ap, void *));
258 va_end(ap);
260 if (argc < 2 || argv[1][0] == '\0') {
261 if (state->log != NULL)
262 state->log(ISC_LOG_ERROR,
263 "dlz_example: please specify a zone name");
264 dlz_destroy(state);
265 return (ISC_R_FAILURE);
268 /* Ensure zone name is absolute */
269 state->zone_name = malloc(strlen(argv[1]) + 2);
270 if (state->zone_name == NULL) {
271 free(state);
272 return (ISC_R_NOMEMORY);
274 if (argv[1][strlen(argv[1]) - 1] == '.')
275 strcpy(state->zone_name, argv[1]);
276 else
277 sprintf(state->zone_name, "%s.", argv[1]);
279 if (strcmp(state->zone_name, ".") == 0)
280 extra = ".root";
281 else
282 extra = ".";
284 n = sprintf(soa_data, "%s hostmaster%s%s 123 900 600 86400 3600",
285 state->zone_name, extra, state->zone_name);
287 if (n < 0)
288 CHECK(ISC_R_FAILURE);
289 if ((unsigned)n >= sizeof(soa_data))
290 CHECK(ISC_R_NOSPACE);
292 add_name(state, &state->current[0], state->zone_name,
293 "soa", 3600, soa_data);
294 add_name(state, &state->current[0], state->zone_name,
295 "ns", 3600, state->zone_name);
296 add_name(state, &state->current[0], state->zone_name,
297 "a", 1800, "10.53.0.1");
299 if (state->log != NULL)
300 state->log(ISC_LOG_INFO, "dlz_example: started for zone %s",
301 state->zone_name);
303 *dbdata = state;
304 return (ISC_R_SUCCESS);
306 failure:
307 free(state);
308 return (result);
313 * Shut down the backend
315 void
316 dlz_destroy(void *dbdata) {
317 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
319 if (state->log != NULL)
320 state->log(ISC_LOG_INFO,
321 "dlz_example: shutting down zone %s",
322 state->zone_name);
323 free(state->zone_name);
324 free(state);
328 * See if we handle a given zone
330 isc_result_t
331 dlz_findzonedb(void *dbdata, const char *name,
332 dns_clientinfomethods_t *methods,
333 dns_clientinfo_t *clientinfo)
335 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
336 isc_sockaddr_t *src;
337 char addrbuf[100];
338 char absolute[1024];
340 strcpy(addrbuf, "unknown");
341 if (methods != NULL &&
342 methods->sourceip != NULL &&
343 methods->version - methods->age <= DNS_CLIENTINFOMETHODS_VERSION &&
344 DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
346 methods->sourceip(clientinfo, &src);
347 fmt_address(src, addrbuf, sizeof(addrbuf));
349 fprintf(stderr, "findzonedb: connection from: %s\n", addrbuf);
351 state->log(ISC_LOG_INFO,
352 "dlz_example: dlz_findzonedb called with name '%s' "
353 "in zone DB '%s'", name, state->zone_name);
356 * Returning ISC_R_NOTFOUND will cause the query logic to
357 * check the database for parent names, looking for zone cuts.
359 * Returning ISC_R_NOMORE prevents the query logic from doing
360 * this; it will move onto the next database after a single query.
362 if (strcasecmp(name, "test.example.com") == 0)
363 return (ISC_R_NOMORE);
366 * For example.net, only return ISC_R_NOMORE when queried
367 * from 10.53.0.1.
369 if (strcasecmp(name, "test.example.net") == 0 &&
370 strncmp(addrbuf, "10.53.0.1", 9) == 0)
371 return (ISC_R_NOMORE);
374 * For bigcname.domain, return success so it appears to be
375 * the zone origin; this regression tests a bug in which
376 * zone origin nodes could fail to return SERVFAIL to the client.
378 if (strcasecmp(name, "bigcname.domain") == 0)
379 return (ISC_R_SUCCESS);
382 * Return success if we have an exact match between the
383 * zone name and the qname
385 if (strcasecmp(state->zone_name, name) == 0)
386 return (ISC_R_SUCCESS);
388 snprintf(absolute, sizeof(absolute), "%s.", name);
389 if (strcasecmp(state->zone_name, absolute) == 0)
390 return (ISC_R_SUCCESS);
392 return (ISC_R_NOTFOUND);
396 * Look up one record in the sample database.
398 * If the queryname is "source-addr", send back a TXT record containing
399 * the address of the client, to test the use of 'methods' and 'clientinfo'
401 * If the queryname is "too-long", send back a TXT record that's too long
402 * to process; this should result in a SERVFAIL when queried.
404 isc_result_t
405 dlz_lookup(const char *zone, const char *name, void *dbdata,
406 dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods,
407 dns_clientinfo_t *clientinfo)
409 isc_result_t result;
410 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
411 isc_boolean_t found = ISC_FALSE;
412 isc_sockaddr_t *src;
413 char full_name[256];
414 char buf[512];
415 int i;
417 UNUSED(zone);
419 if (state->putrr == NULL)
420 return (ISC_R_NOTIMPLEMENTED);
422 if (strcmp(name, "@") == 0) {
423 strncpy(full_name, state->zone_name, 255);
424 full_name[255] = '\0';
425 } else
426 snprintf(full_name, 255, "%s.%s", name, state->zone_name);
428 if (strcmp(name, "source-addr") == 0) {
429 strcpy(buf, "unknown");
430 if (methods != NULL &&
431 methods->sourceip != NULL &&
432 (methods->version - methods->age <=
433 DNS_CLIENTINFOMETHODS_VERSION) &&
434 DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
436 methods->sourceip(clientinfo, &src);
437 fmt_address(src, buf, sizeof(buf));
440 fprintf(stderr, "lookup: connection from: %s\n", buf);
442 found = ISC_TRUE;
443 result = state->putrr(lookup, "TXT", 0, buf);
444 if (result != ISC_R_SUCCESS)
445 return (result);
448 if (strcmp(name, "too-long") == 0 ||
449 strcmp(zone, "bigcname.domain") == 0)
451 for (i = 0; i < 511; i++)
452 buf[i] = 'x';
453 buf[i] = '\0';
454 found = ISC_TRUE;
455 result = state->putrr(lookup, "TXT", 0, buf);
456 if (result != ISC_R_SUCCESS)
457 return (result);
460 for (i = 0; i < MAX_RECORDS; i++) {
461 if (strcasecmp(state->current[i].name, full_name) == 0) {
462 found = ISC_TRUE;
463 result = state->putrr(lookup, state->current[i].type,
464 state->current[i].ttl,
465 state->current[i].data);
466 if (result != ISC_R_SUCCESS)
467 return (result);
471 if (!found)
472 return (ISC_R_NOTFOUND);
474 return (ISC_R_SUCCESS);
479 * See if a zone transfer is allowed
481 isc_result_t
482 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
483 UNUSED(client);
485 /* Just say yes for all our zones */
486 return (dlz_findzonedb(dbdata, name, NULL, NULL));
490 * Perform a zone transfer
492 isc_result_t
493 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
494 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
495 int i;
497 UNUSED(zone);
499 if (state->putnamedrr == NULL)
500 return (ISC_R_NOTIMPLEMENTED);
502 for (i = 0; i < MAX_RECORDS; i++) {
503 isc_result_t result;
504 if (strlen(state->current[i].name) == 0U) {
505 continue;
507 result = state->putnamedrr(allnodes, state->current[i].name,
508 state->current[i].type,
509 state->current[i].ttl,
510 state->current[i].data);
511 if (result != ISC_R_SUCCESS)
512 return (result);
515 return (ISC_R_SUCCESS);
520 * Start a transaction
522 isc_result_t
523 dlz_newversion(const char *zone, void *dbdata, void **versionp) {
524 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
526 if (state->transaction_started) {
527 if (state->log != NULL)
528 state->log(ISC_LOG_INFO,
529 "dlz_example: transaction already "
530 "started for zone %s", zone);
531 return (ISC_R_FAILURE);
534 state->transaction_started = ISC_TRUE;
535 *versionp = (void *) &state->transaction_started;
537 return (ISC_R_SUCCESS);
541 * End a transaction
543 void
544 dlz_closeversion(const char *zone, isc_boolean_t commit,
545 void *dbdata, void **versionp)
547 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
549 if (!state->transaction_started) {
550 if (state->log != NULL)
551 state->log(ISC_LOG_INFO, "dlz_example: transaction not "
552 "started for zone %s", zone);
553 *versionp = NULL;
554 return;
557 state->transaction_started = ISC_FALSE;
559 *versionp = NULL;
561 if (commit) {
562 int i;
563 if (state->log != NULL)
564 state->log(ISC_LOG_INFO, "dlz_example: committing "
565 "transaction on zone %s", zone);
566 for (i = 0; i < MAX_RECORDS; i++) {
567 if (strlen(state->deletes[i].name) > 0U) {
568 (void)del_name(state, &state->current[0],
569 state->deletes[i].name,
570 state->deletes[i].type,
571 state->deletes[i].ttl,
572 state->deletes[i].data);
575 for (i = 0; i < MAX_RECORDS; i++) {
576 if (strlen(state->adds[i].name) > 0U) {
577 (void)add_name(state, &state->current[0],
578 state->adds[i].name,
579 state->adds[i].type,
580 state->adds[i].ttl,
581 state->adds[i].data);
584 } else {
585 if (state->log != NULL)
586 state->log(ISC_LOG_INFO, "dlz_example: cancelling "
587 "transaction on zone %s", zone);
589 memset(state->adds, 0, sizeof(state->adds));
590 memset(state->deletes, 0, sizeof(state->deletes));
595 * Configure a writeable zone
597 isc_result_t
598 dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *dbdata) {
599 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
600 isc_result_t result;
602 if (state->log != NULL)
603 state->log(ISC_LOG_INFO, "dlz_example: starting configure");
605 if (state->writeable_zone == NULL) {
606 if (state->log != NULL)
607 state->log(ISC_LOG_INFO, "dlz_example: no "
608 "writeable_zone method available");
609 return (ISC_R_FAILURE);
612 result = state->writeable_zone(view, dlzdb, state->zone_name);
613 if (result != ISC_R_SUCCESS) {
614 if (state->log != NULL)
615 state->log(ISC_LOG_ERROR, "dlz_example: failed to "
616 "configure zone %s", state->zone_name);
617 return (result);
620 if (state->log != NULL)
621 state->log(ISC_LOG_INFO, "dlz_example: configured writeable "
622 "zone %s", state->zone_name);
623 return (ISC_R_SUCCESS);
627 * Authorize a zone update
629 isc_boolean_t
630 dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
631 const char *type, const char *key, isc_uint32_t keydatalen,
632 unsigned char *keydata, void *dbdata)
634 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
636 UNUSED(tcpaddr);
637 UNUSED(type);
638 UNUSED(key);
639 UNUSED(keydatalen);
640 UNUSED(keydata);
642 if (strncmp(name, "deny.", 5) == 0) {
643 if (state->log != NULL)
644 state->log(ISC_LOG_INFO, "dlz_example: denying update "
645 "of name=%s by %s", name, signer);
646 return (ISC_FALSE);
648 if (state->log != NULL)
649 state->log(ISC_LOG_INFO, "dlz_example: allowing update of "
650 "name=%s by %s", name, signer);
651 return (ISC_TRUE);
655 static isc_result_t
656 modrdataset(struct dlz_example_data *state, const char *name,
657 const char *rdatastr, struct record *list)
659 char *full_name, *dclass, *type, *data, *ttlstr, *buf;
660 char absolute[1024];
661 isc_result_t result;
662 #if defined(WIN32) || defined(_REENTRANT)
663 char *saveptr = NULL;
664 #endif
666 buf = strdup(rdatastr);
667 if (buf == NULL)
668 return (ISC_R_FAILURE);
671 * The format is:
672 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA
674 * The DATA field is space separated, and is in the data format
675 * for the type used by dig
678 full_name = STRTOK_R(buf, "\t", &saveptr);
679 if (full_name == NULL)
680 goto error;
682 ttlstr = STRTOK_R(NULL, "\t", &saveptr);
683 if (ttlstr == NULL)
684 goto error;
686 dclass = STRTOK_R(NULL, "\t", &saveptr);
687 if (dclass == NULL)
688 goto error;
690 type = STRTOK_R(NULL, "\t", &saveptr);
691 if (type == NULL)
692 goto error;
694 data = STRTOK_R(NULL, "\t", &saveptr);
695 if (data == NULL)
696 goto error;
698 if (name[strlen(name) - 1] != '.') {
699 snprintf(absolute, sizeof(absolute), "%s.", name);
700 name = absolute;
703 result = add_name(state, list, name, type,
704 strtoul(ttlstr, NULL, 10), data);
705 free(buf);
706 return (result);
708 error:
709 free(buf);
710 return (ISC_R_FAILURE);
714 isc_result_t
715 dlz_addrdataset(const char *name, const char *rdatastr,
716 void *dbdata, void *version)
718 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
720 if (version != (void *) &state->transaction_started)
721 return (ISC_R_FAILURE);
723 if (state->log != NULL)
724 state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'",
725 name, rdatastr);
727 return (modrdataset(state, name, rdatastr, &state->adds[0]));
730 isc_result_t
731 dlz_subrdataset(const char *name, const char *rdatastr,
732 void *dbdata, void *version)
734 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
736 if (version != (void *) &state->transaction_started)
737 return (ISC_R_FAILURE);
739 if (state->log != NULL)
740 state->log(ISC_LOG_INFO, "dlz_example: subtracting rdataset "
741 "%s '%s'", name, rdatastr);
743 return (modrdataset(state, name, rdatastr, &state->deletes[0]));
746 isc_result_t
747 dlz_delrdataset(const char *name, const char *type,
748 void *dbdata, void *version)
750 struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
752 if (version != (void *) &state->transaction_started)
753 return (ISC_R_FAILURE);
755 if (state->log != NULL)
756 state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s "
757 "of type %s", name, type);
759 return (ISC_R_SUCCESS);