1 /* $NetBSD: driver.c,v 1.3 2014/12/10 04:37:54 christos Exp $ */
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.
31 #include <isc/print.h>
32 #include <isc/result.h>
33 #include <isc/string.h>
34 #include <isc/types.h>
37 #include <dns/types.h>
38 #include <dns/dlz_dlopen.h>
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)
47 #define STRTOK_R(a, b, c) strtok(a, b)
53 if (result != ISC_R_SUCCESS) \
55 } while (/*CONSTCOND*/0)
57 /* For this simple example, use fixed sized strings */
65 #define MAX_RECORDS 100
67 typedef void log_t(int level
, const char *fmt
, ...);
69 struct dlz_example_data
{
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 */
81 dns_sdlz_putrr_t
*putrr
;
82 dns_sdlz_putnamedrr_t
*putnamedrr
;
83 dns_dlz_writeablezone_t
*writeable_zone
;
87 single_valued(const char *type
) {
88 const char *single
[] = { "soa", "cname", NULL
};
91 for (i
= 0; single
[i
]; i
++) {
92 if (strcasecmp(single
[i
], type
) == 0) {
100 * Add a record to a list
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
)
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) {
114 if (strcasecmp(list
[i
].name
, name
) != 0)
116 if (strcasecmp(list
[i
].type
, type
) != 0)
118 if (!single
&& strcasecmp(list
[i
].data
, data
) != 0)
122 if (i
== MAX_RECORDS
&& first_empty
!= -1) {
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';
148 return (ISC_R_SUCCESS
);
152 * Delete a record from a list
155 del_name(struct dlz_example_data
*state
, struct record
*list
,
156 const char *name
, const char *type
, dns_ttl_t ttl
,
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
) {
171 if (i
== MAX_RECORDS
) {
172 return (ISC_R_NOTFOUND
);
174 memset(&list
[i
], 0, sizeof(struct record
));
175 return (ISC_R_SUCCESS
);
179 fmt_address(isc_sockaddr_t
*addr
, char *buffer
, size_t size
) {
182 isc_uint16_t port
= 0;
184 switch (addr
->type
.sa
.sa_family
) {
186 port
= ntohs(addr
->type
.sin
.sin_port
);
187 ret
= inet_ntop(AF_INET
, &addr
->type
.sin
.sin_addr
, addr_buf
,
191 port
= ntohs(addr
->type
.sin6
.sin6_port
);
192 ret
= inet_ntop(AF_INET6
, &addr
->type
.sin6
.sin6_addr
, addr_buf
,
196 return (ISC_R_FAILURE
);
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
) {
212 return (DLZ_DLOPEN_VERSION
);
216 * Remember a helper function from the bind9 dlz_dlopen driver
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
236 dlz_create(const char *dlzname
, unsigned int argc
, char *argv
[],
239 struct dlz_example_data
*state
;
240 const char *helper_name
;
249 state
= calloc(1, sizeof(struct dlz_example_data
));
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 *));
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");
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
) {
272 return (ISC_R_NOMEMORY
);
274 if (argv
[1][strlen(argv
[1]) - 1] == '.')
275 strcpy(state
->zone_name
, argv
[1]);
277 sprintf(state
->zone_name
, "%s.", argv
[1]);
279 if (strcmp(state
->zone_name
, ".") == 0)
284 n
= sprintf(soa_data
, "%s hostmaster%s%s 123 900 600 86400 3600",
285 state
->zone_name
, extra
, state
->zone_name
);
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",
304 return (ISC_R_SUCCESS
);
313 * Shut down the backend
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",
323 free(state
->zone_name
);
328 * See if we handle a given zone
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
;
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
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.
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
)
410 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
411 isc_boolean_t found
= ISC_FALSE
;
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';
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
);
443 result
= state
->putrr(lookup
, "TXT", 0, buf
);
444 if (result
!= ISC_R_SUCCESS
)
448 if (strcmp(name
, "too-long") == 0 ||
449 strcmp(zone
, "bigcname.domain") == 0)
451 for (i
= 0; i
< 511; i
++)
455 result
= state
->putrr(lookup
, "TXT", 0, buf
);
456 if (result
!= ISC_R_SUCCESS
)
460 for (i
= 0; i
< MAX_RECORDS
; i
++) {
461 if (strcasecmp(state
->current
[i
].name
, full_name
) == 0) {
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
)
472 return (ISC_R_NOTFOUND
);
474 return (ISC_R_SUCCESS
);
479 * See if a zone transfer is allowed
482 dlz_allowzonexfr(void *dbdata
, const char *name
, const char *client
) {
485 /* Just say yes for all our zones */
486 return (dlz_findzonedb(dbdata
, name
, NULL
, NULL
));
490 * Perform a zone transfer
493 dlz_allnodes(const char *zone
, void *dbdata
, dns_sdlzallnodes_t
*allnodes
) {
494 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
499 if (state
->putnamedrr
== NULL
)
500 return (ISC_R_NOTIMPLEMENTED
);
502 for (i
= 0; i
< MAX_RECORDS
; i
++) {
504 if (strlen(state
->current
[i
].name
) == 0U) {
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
)
515 return (ISC_R_SUCCESS
);
520 * Start a transaction
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
);
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
);
557 state
->transaction_started
= ISC_FALSE
;
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],
581 state
->adds
[i
].data
);
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
598 dlz_configure(dns_view_t
*view
, dns_dlzdb_t
*dlzdb
, void *dbdata
) {
599 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
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
);
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
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
;
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
);
648 if (state
->log
!= NULL
)
649 state
->log(ISC_LOG_INFO
, "dlz_example: allowing update of "
650 "name=%s by %s", name
, signer
);
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
;
662 #if defined(WIN32) || defined(_REENTRANT)
663 char *saveptr
= NULL
;
666 buf
= strdup(rdatastr
);
668 return (ISC_R_FAILURE
);
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
)
682 ttlstr
= STRTOK_R(NULL
, "\t", &saveptr
);
686 dclass
= STRTOK_R(NULL
, "\t", &saveptr
);
690 type
= STRTOK_R(NULL
, "\t", &saveptr
);
694 data
= STRTOK_R(NULL
, "\t", &saveptr
);
698 if (name
[strlen(name
) - 1] != '.') {
699 snprintf(absolute
, sizeof(absolute
), "%s.", name
);
703 result
= add_name(state
, list
, name
, type
,
704 strtoul(ttlstr
, NULL
, 10), data
);
710 return (ISC_R_FAILURE
);
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'",
727 return (modrdataset(state
, name
, rdatastr
, &state
->adds
[0]));
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]));
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
);