1 /* $NetBSD: dlz_example.c,v 1.7 2014/12/10 04:37:55 christos Exp $ */
4 * Copyright (C) 2011 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.
30 #include "../modules/include/dlz_minimal.h"
33 #define STRTOK_R(a, b, c) strtok_s(a, b, c)
34 #elif defined(_REENTRANT)
35 #define STRTOK_R(a, b, c) strtok_r(a, b, c)
37 #define STRTOK_R(a, b, c) strtok(a, b)
43 if (result != ISC_R_SUCCESS) \
45 } while (/*CONSTCOND*/0)
47 /* For this simple example, use fixed sized strings */
55 #define MAX_RECORDS 100
57 struct dlz_example_data
{
60 /* An example driver doesn't need good memory management :-) */
61 struct record current
[MAX_RECORDS
];
62 struct record adds
[MAX_RECORDS
];
63 struct record deletes
[MAX_RECORDS
];
65 isc_boolean_t transaction_started
;
67 /* Helper functions from the dlz_dlopen driver */
69 dns_sdlz_putrr_t
*putrr
;
70 dns_sdlz_putnamedrr_t
*putnamedrr
;
71 dns_dlz_writeablezone_t
*writeable_zone
;
75 single_valued(const char *type
) {
76 const char *single
[] = { "soa", "cname", NULL
};
79 for (i
= 0; single
[i
]; i
++) {
80 if (strcasecmp(single
[i
], type
) == 0) {
88 * Add a record to a list
91 add_name(struct dlz_example_data
*state
, struct record
*list
,
92 const char *name
, const char *type
, dns_ttl_t ttl
, const char *data
)
95 isc_boolean_t single
= single_valued(type
);
98 for (i
= 0; i
< MAX_RECORDS
; i
++) {
99 if (first_empty
== -1 && strlen(list
[i
].name
) == 0U) {
102 if (strcasecmp(list
[i
].name
, name
) != 0)
104 if (strcasecmp(list
[i
].type
, type
) != 0)
106 if (!single
&& strcasecmp(list
[i
].data
, data
) != 0)
110 if (i
== MAX_RECORDS
&& first_empty
!= -1) {
113 if (i
== MAX_RECORDS
) {
114 if (state
->log
!= NULL
)
115 state
->log(ISC_LOG_ERROR
,
116 "dlz_example: out of record space");
117 return (ISC_R_FAILURE
);
120 if (strlen(name
) >= sizeof(list
[i
].name
) ||
121 strlen(type
) >= sizeof(list
[i
].type
) ||
122 strlen(data
) >= sizeof(list
[i
].data
))
123 return (ISC_R_NOSPACE
);
125 strncpy(list
[i
].name
, name
, sizeof(list
[i
].name
));
126 list
[i
].name
[sizeof(list
[i
].name
) - 1] = '\0';
128 strncpy(list
[i
].type
, type
, sizeof(list
[i
].type
));
129 list
[i
].type
[sizeof(list
[i
].type
) - 1] = '\0';
131 strncpy(list
[i
].data
, data
, sizeof(list
[i
].data
));
132 list
[i
].data
[sizeof(list
[i
].data
) - 1] = '\0';
136 return (ISC_R_SUCCESS
);
140 * Delete a record from a list
143 del_name(struct dlz_example_data
*state
, struct record
*list
,
144 const char *name
, const char *type
, dns_ttl_t ttl
,
151 for (i
= 0; i
< MAX_RECORDS
; i
++) {
152 if (strcasecmp(name
, list
[i
].name
) == 0 &&
153 strcasecmp(type
, list
[i
].type
) == 0 &&
154 strcasecmp(data
, list
[i
].data
) == 0 &&
155 ttl
== list
[i
].ttl
) {
159 if (i
== MAX_RECORDS
) {
160 return (ISC_R_NOTFOUND
);
162 memset(&list
[i
], 0, sizeof(struct record
));
163 return (ISC_R_SUCCESS
);
167 fmt_address(isc_sockaddr_t
*addr
, char *buffer
, size_t size
) {
172 switch (addr
->type
.sa
.sa_family
) {
174 port
= ntohs(addr
->type
.sin
.sin_port
);
175 ret
= inet_ntop(AF_INET
, &addr
->type
.sin
.sin_addr
, addr_buf
,
179 port
= ntohs(addr
->type
.sin6
.sin6_port
);
180 ret
= inet_ntop(AF_INET6
, &addr
->type
.sin6
.sin6_addr
, addr_buf
,
184 return (ISC_R_FAILURE
);
188 return (ISC_R_FAILURE
);
190 snprintf(buffer
, size
, "%s#%u", addr_buf
, port
);
191 return (ISC_R_SUCCESS
);
195 * Return the version of the API
198 dlz_version(unsigned int *flags
) {
200 return (DLZ_DLOPEN_VERSION
);
204 * Remember a helper function from the bind9 dlz_dlopen driver
207 b9_add_helper(struct dlz_example_data
*state
,
208 const char *helper_name
, void *ptr
)
210 if (strcmp(helper_name
, "log") == 0)
211 state
->log
= (log_t
*)ptr
;
212 if (strcmp(helper_name
, "putrr") == 0)
213 state
->putrr
= (dns_sdlz_putrr_t
*)ptr
;
214 if (strcmp(helper_name
, "putnamedrr") == 0)
215 state
->putnamedrr
= (dns_sdlz_putnamedrr_t
*)ptr
;
216 if (strcmp(helper_name
, "writeable_zone") == 0)
217 state
->writeable_zone
= (dns_dlz_writeablezone_t
*)ptr
;
221 * Called to initialize the driver
224 dlz_create(const char *dlzname
, unsigned int argc
, char *argv
[],
227 struct dlz_example_data
*state
;
228 const char *helper_name
;
237 state
= calloc(1, sizeof(struct dlz_example_data
));
239 return (ISC_R_NOMEMORY
);
241 /* Fill in the helper functions */
242 va_start(ap
, dbdata
);
243 while ((helper_name
= va_arg(ap
, const char *)) != NULL
) {
244 b9_add_helper(state
, helper_name
, va_arg(ap
, void *));
248 if (argc
< 2 || argv
[1][0] == '\0') {
249 if (state
->log
!= NULL
)
250 state
->log(ISC_LOG_ERROR
,
251 "dlz_example: please specify a zone name");
253 return (ISC_R_FAILURE
);
256 /* Ensure zone name is absolute */
257 state
->zone_name
= malloc(strlen(argv
[1]) + 2);
258 if (state
->zone_name
== NULL
) {
260 return (ISC_R_NOMEMORY
);
262 if (argv
[1][strlen(argv
[1]) - 1] == '.')
263 strcpy(state
->zone_name
, argv
[1]);
265 sprintf(state
->zone_name
, "%s.", argv
[1]);
267 if (strcmp(state
->zone_name
, ".") == 0)
272 n
= sprintf(soa_data
, "%s hostmaster%s%s 123 900 600 86400 3600",
273 state
->zone_name
, extra
, state
->zone_name
);
276 CHECK(ISC_R_FAILURE
);
277 if ((unsigned)n
>= sizeof(soa_data
))
278 CHECK(ISC_R_NOSPACE
);
280 add_name(state
, &state
->current
[0], state
->zone_name
,
281 "soa", 3600, soa_data
);
282 add_name(state
, &state
->current
[0], state
->zone_name
,
283 "ns", 3600, state
->zone_name
);
284 add_name(state
, &state
->current
[0], state
->zone_name
,
285 "a", 1800, "10.53.0.1");
287 if (state
->log
!= NULL
)
288 state
->log(ISC_LOG_INFO
, "dlz_example: started for zone %s",
292 return (ISC_R_SUCCESS
);
301 * Shut down the backend
304 dlz_destroy(void *dbdata
) {
305 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
307 if (state
->log
!= NULL
)
308 state
->log(ISC_LOG_INFO
,
309 "dlz_example: shutting down zone %s",
311 free(state
->zone_name
);
316 * See if we handle a given zone
319 dlz_findzonedb(void *dbdata
, const char *name
,
320 dns_clientinfomethods_t
*methods
,
321 dns_clientinfo_t
*clientinfo
)
323 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
328 strcpy(addrbuf
, "unknown");
329 if (methods
!= NULL
&&
330 methods
->sourceip
!= NULL
&&
331 methods
->version
- methods
->age
<= DNS_CLIENTINFOMETHODS_VERSION
&&
332 DNS_CLIENTINFOMETHODS_VERSION
<= methods
->version
)
334 methods
->sourceip(clientinfo
, &src
);
335 fmt_address(src
, addrbuf
, sizeof(addrbuf
));
337 state
->log(ISC_LOG_INFO
,
338 "dlz_example: findzonedb connection from: %s\n", addrbuf
);
340 state
->log(ISC_LOG_INFO
,
341 "dlz_example: dlz_findzonedb called with name '%s' "
342 "in zone DB '%s'", name
, state
->zone_name
);
345 * Returning ISC_R_NOTFOUND will cause the query logic to
346 * check the database for parent names, looking for zone cuts.
348 * Returning ISC_R_NOMORE prevents the query logic from doing
349 * this; it will move onto the next database after a single query.
351 if (strcasecmp(name
, "test.example.com") == 0)
352 return (ISC_R_NOMORE
);
355 * For example.net, only return ISC_R_NOMORE when queried
358 if (strcasecmp(name
, "test.example.net") == 0 &&
359 strncmp(addrbuf
, "10.53.0.1", 9) == 0)
360 return (ISC_R_NOMORE
);
362 if (strcasecmp(state
->zone_name
, name
) == 0)
363 return (ISC_R_SUCCESS
);
365 snprintf(absolute
, sizeof(absolute
), "%s.", name
);
366 if (strcasecmp(state
->zone_name
, absolute
) == 0)
367 return (ISC_R_SUCCESS
);
369 return (ISC_R_NOTFOUND
);
373 * Look up one record in the sample database.
375 * If the queryname is "source-addr", send back a TXT record containing
376 * the address of the client; this demonstrates the use of 'methods'
379 * If the queryname is "too-long", send back a TXT record that's too long
380 * to process; this should result in a SERVFAIL when queried.
383 dlz_lookup(const char *zone
, const char *name
, void *dbdata
,
384 dns_sdlzlookup_t
*lookup
, dns_clientinfomethods_t
*methods
,
385 dns_clientinfo_t
*clientinfo
)
388 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
389 isc_boolean_t found
= ISC_FALSE
;
397 if (state
->putrr
== NULL
)
398 return (ISC_R_NOTIMPLEMENTED
);
400 if (strcmp(name
, "@") == 0) {
401 strncpy(full_name
, state
->zone_name
, 255);
402 full_name
[255] = '\0';
404 snprintf(full_name
, 255, "%s.%s", name
, state
->zone_name
);
406 if (strcmp(name
, "source-addr") == 0) {
407 strcpy(buf
, "unknown");
408 if (methods
!= NULL
&&
409 methods
->sourceip
!= NULL
&&
410 (methods
->version
- methods
->age
<=
411 DNS_CLIENTINFOMETHODS_VERSION
) &&
412 DNS_CLIENTINFOMETHODS_VERSION
<= methods
->version
)
414 methods
->sourceip(clientinfo
, &src
);
415 fmt_address(src
, buf
, sizeof(buf
));
418 state
->log(ISC_LOG_INFO
,
419 "dlz_example: lookup connection from: %s\n", buf
);
422 result
= state
->putrr(lookup
, "TXT", 0, buf
);
423 if (result
!= ISC_R_SUCCESS
)
427 if (strcmp(name
, "too-long") == 0) {
428 for (i
= 0; i
< 511; i
++)
432 result
= state
->putrr(lookup
, "TXT", 0, buf
);
433 if (result
!= ISC_R_SUCCESS
)
437 for (i
= 0; i
< MAX_RECORDS
; i
++) {
438 if (strcasecmp(state
->current
[i
].name
, full_name
) == 0) {
440 result
= state
->putrr(lookup
, state
->current
[i
].type
,
441 state
->current
[i
].ttl
,
442 state
->current
[i
].data
);
443 if (result
!= ISC_R_SUCCESS
)
449 return (ISC_R_NOTFOUND
);
451 return (ISC_R_SUCCESS
);
456 * See if a zone transfer is allowed
459 dlz_allowzonexfr(void *dbdata
, const char *name
, const char *client
) {
462 /* Just say yes for all our zones */
463 return (dlz_findzonedb(dbdata
, name
, NULL
, NULL
));
467 * Perform a zone transfer
470 dlz_allnodes(const char *zone
, void *dbdata
, dns_sdlzallnodes_t
*allnodes
) {
471 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
476 if (state
->putnamedrr
== NULL
)
477 return (ISC_R_NOTIMPLEMENTED
);
479 for (i
= 0; i
< MAX_RECORDS
; i
++) {
481 if (strlen(state
->current
[i
].name
) == 0U) {
484 result
= state
->putnamedrr(allnodes
, state
->current
[i
].name
,
485 state
->current
[i
].type
,
486 state
->current
[i
].ttl
,
487 state
->current
[i
].data
);
488 if (result
!= ISC_R_SUCCESS
)
492 return (ISC_R_SUCCESS
);
497 * Start a transaction
500 dlz_newversion(const char *zone
, void *dbdata
, void **versionp
) {
501 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
503 if (state
->transaction_started
) {
504 if (state
->log
!= NULL
)
505 state
->log(ISC_LOG_INFO
,
506 "dlz_example: transaction already "
507 "started for zone %s", zone
);
508 return (ISC_R_FAILURE
);
511 state
->transaction_started
= ISC_TRUE
;
512 *versionp
= (void *) &state
->transaction_started
;
514 return (ISC_R_SUCCESS
);
521 dlz_closeversion(const char *zone
, isc_boolean_t commit
,
522 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
, "dlz_example: transaction not "
529 "started for zone %s", zone
);
534 state
->transaction_started
= ISC_FALSE
;
540 if (state
->log
!= NULL
)
541 state
->log(ISC_LOG_INFO
, "dlz_example: committing "
542 "transaction on zone %s", zone
);
543 for (i
= 0; i
< MAX_RECORDS
; i
++) {
544 if (strlen(state
->deletes
[i
].name
) > 0U) {
545 (void)del_name(state
, &state
->current
[0],
546 state
->deletes
[i
].name
,
547 state
->deletes
[i
].type
,
548 state
->deletes
[i
].ttl
,
549 state
->deletes
[i
].data
);
552 for (i
= 0; i
< MAX_RECORDS
; i
++) {
553 if (strlen(state
->adds
[i
].name
) > 0U) {
554 (void)add_name(state
, &state
->current
[0],
558 state
->adds
[i
].data
);
562 if (state
->log
!= NULL
)
563 state
->log(ISC_LOG_INFO
, "dlz_example: cancelling "
564 "transaction on zone %s", zone
);
566 memset(state
->adds
, 0, sizeof(state
->adds
));
567 memset(state
->deletes
, 0, sizeof(state
->deletes
));
572 * Configure a writeable zone
575 dlz_configure(dns_view_t
*view
, dns_dlzdb_t
*dlzdb
, void *dbdata
) {
576 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
579 if (state
->log
!= NULL
)
580 state
->log(ISC_LOG_INFO
, "dlz_example: starting configure");
582 if (state
->writeable_zone
== NULL
) {
583 if (state
->log
!= NULL
)
584 state
->log(ISC_LOG_INFO
, "dlz_example: no "
585 "writeable_zone method available");
586 return (ISC_R_FAILURE
);
589 result
= state
->writeable_zone(view
, dlzdb
, state
->zone_name
);
590 if (result
!= ISC_R_SUCCESS
) {
591 if (state
->log
!= NULL
)
592 state
->log(ISC_LOG_ERROR
, "dlz_example: failed to "
593 "configure zone %s", state
->zone_name
);
597 if (state
->log
!= NULL
)
598 state
->log(ISC_LOG_INFO
, "dlz_example: configured writeable "
599 "zone %s", state
->zone_name
);
600 return (ISC_R_SUCCESS
);
604 * Authorize a zone update
607 dlz_ssumatch(const char *signer
, const char *name
, const char *tcpaddr
,
608 const char *type
, const char *key
, uint32_t keydatalen
,
609 unsigned char *keydata
, void *dbdata
)
611 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
619 if (strncmp(name
, "deny.", 5) == 0) {
620 if (state
->log
!= NULL
)
621 state
->log(ISC_LOG_INFO
, "dlz_example: denying update "
622 "of name=%s by %s", name
, signer
);
625 if (state
->log
!= NULL
)
626 state
->log(ISC_LOG_INFO
, "dlz_example: allowing update of "
627 "name=%s by %s", name
, signer
);
633 modrdataset(struct dlz_example_data
*state
, const char *name
,
634 const char *rdatastr
, struct record
*list
)
636 char *full_name
, *dclass
, *type
, *data
, *ttlstr
, *buf
;
639 #if defined(WIN32) || defined(_REENTRANT)
640 char *saveptr
= NULL
;
643 buf
= strdup(rdatastr
);
645 return (ISC_R_FAILURE
);
649 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA
651 * The DATA field is space separated, and is in the data format
652 * for the type used by dig
655 full_name
= STRTOK_R(buf
, "\t", &saveptr
);
656 if (full_name
== NULL
)
659 ttlstr
= STRTOK_R(NULL
, "\t", &saveptr
);
663 dclass
= STRTOK_R(NULL
, "\t", &saveptr
);
667 type
= STRTOK_R(NULL
, "\t", &saveptr
);
671 data
= STRTOK_R(NULL
, "\t", &saveptr
);
675 if (name
[strlen(name
) - 1] != '.') {
676 snprintf(absolute
, sizeof(absolute
), "%s.", name
);
680 result
= add_name(state
, list
, name
, type
,
681 strtoul(ttlstr
, NULL
, 10), data
);
687 return (ISC_R_FAILURE
);
692 dlz_addrdataset(const char *name
, const char *rdatastr
,
693 void *dbdata
, void *version
)
695 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
697 if (version
!= (void *) &state
->transaction_started
)
698 return (ISC_R_FAILURE
);
700 if (state
->log
!= NULL
)
701 state
->log(ISC_LOG_INFO
, "dlz_example: adding rdataset %s '%s'",
704 return (modrdataset(state
, name
, rdatastr
, &state
->adds
[0]));
708 dlz_subrdataset(const char *name
, const char *rdatastr
,
709 void *dbdata
, void *version
)
711 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
713 if (version
!= (void *) &state
->transaction_started
)
714 return (ISC_R_FAILURE
);
716 if (state
->log
!= NULL
)
717 state
->log(ISC_LOG_INFO
, "dlz_example: subtracting rdataset "
718 "%s '%s'", name
, rdatastr
);
720 return (modrdataset(state
, name
, rdatastr
, &state
->deletes
[0]));
724 dlz_delrdataset(const char *name
, const char *type
,
725 void *dbdata
, void *version
)
727 struct dlz_example_data
*state
= (struct dlz_example_data
*)dbdata
;
729 if (version
!= (void *) &state
->transaction_started
)
730 return (ISC_R_FAILURE
);
732 if (state
->log
!= NULL
)
733 state
->log(ISC_LOG_INFO
, "dlz_example: deleting rdataset %s "
734 "of type %s", name
, type
);
736 return (ISC_R_SUCCESS
);