driver.c 19.4 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
5
6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8
9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10
11
12
13
14
15
16
17
18
 */

/*
 * This provides a very simple example of an external loadable DLZ
 * driver, with update support.
 */

#include <config.h>

19
#include <stdbool.h>
20
#include <stdio.h>
21
#include <inttypes.h>
22
23
24
25
26
#include <stdlib.h>
#include <stdarg.h>

#include <isc/log.h>
#include <isc/result.h>
27
#include <isc/string.h>
28
29
30
31
#include <isc/types.h>
#include <isc/util.h>

#include <dns/types.h>
32
#include <dns/dlz_dlopen.h>
33
34
35
36

#include "driver.h"

#ifdef WIN32
37
38
39
40
41
#define STRTOK_R(a, b, c)	strtok_s(a, b, c)
#elif defined(_REENTRANT)
#define STRTOK_R(a, b, c)       strtok_r(a, b, c)
#else
#define STRTOK_R(a, b, c)       strtok(a, b)
42
43
#endif

44
45
46
47
48
49
50
#define CHECK(x) \
	do { \
		result = (x); \
		if (result != ISC_R_SUCCESS) \
			goto failure; \
	} while (0)

51
52
53
54
55
56
57
58
59
60
/* For this simple example, use fixed sized strings */
struct record {
	char name[100];
	char type[10];
	char data[200];
	dns_ttl_t ttl;
};

#define MAX_RECORDS 100

61
62
typedef void log_t(int level, const char *fmt, ...);

63
64
65
66
67
68
69
70
struct dlz_example_data {
	char *zone_name;

	/* An example driver doesn't need good memory management :-) */
	struct record current[MAX_RECORDS];
	struct record adds[MAX_RECORDS];
	struct record deletes[MAX_RECORDS];

71
	bool transaction_started;
72
73

	/* Helper functions from the dlz_dlopen driver */
74
75
76
77
	log_t *log;
	dns_sdlz_putrr_t *putrr;
	dns_sdlz_putnamedrr_t *putnamedrr;
	dns_dlz_writeablezone_t *writeable_zone;
78
79
};

80
static bool
81
82
83
84
85
86
single_valued(const char *type) {
	const char *single[] = { "soa", "cname", NULL };
	int i;

	for (i = 0; single[i]; i++) {
		if (strcasecmp(single[i], type) == 0) {
87
			return (true);
88
89
		}
	}
90
	return (false);
91
92
93
94
95
96
97
98
99
100
}

/*
 * Add a record to a list
 */
static isc_result_t
add_name(struct dlz_example_data *state, struct record *list,
	 const char *name, const char *type, dns_ttl_t ttl, const char *data)
{
	int i;
101
	bool single = single_valued(type);
102
103
104
	int first_empty = -1;

	for (i = 0; i < MAX_RECORDS; i++) {
105
		if (first_empty == -1 && strlen(list[i].name) == 0U) {
106
107
			first_empty = i;
		}
Automatic Updater's avatar
Automatic Updater committed
108
		if (strcasecmp(list[i].name, name) != 0)
109
110
111
112
113
114
115
116
117
118
119
			continue;
		if (strcasecmp(list[i].type, type) != 0)
			continue;
		if (!single && strcasecmp(list[i].data, data) != 0)
			continue;
		break;
	}
	if (i == MAX_RECORDS && first_empty != -1) {
		i = first_empty;
	}
	if (i == MAX_RECORDS) {
120
121
122
		if (state->log != NULL)
			state->log(ISC_LOG_ERROR,
				   "dlz_example: out of record space");
123
124
		return (ISC_R_FAILURE);
	}
125
126
127
128
129
130

	if (strlen(name) >= sizeof(list[i].name) ||
	    strlen(type) >= sizeof(list[i].type) ||
	    strlen(data) >= sizeof(list[i].data))
		return (ISC_R_NOSPACE);

131
	strlcpy(list[i].name, name, sizeof(list[i].name));
132

133
	strlcpy(list[i].type, type, sizeof(list[i].type));
134

135
	strlcpy(list[i].data, data, sizeof(list[i].data));
136

137
	list[i].ttl = ttl;
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
	return (ISC_R_SUCCESS);
}

/*
 * Delete a record from a list
 */
static isc_result_t
del_name(struct dlz_example_data *state, struct record *list,
	 const char *name, const char *type, dns_ttl_t ttl,
	 const char *data)
{
	int i;

	UNUSED(state);

	for (i = 0; i < MAX_RECORDS; i++) {
		if (strcasecmp(name, list[i].name) == 0 &&
		    strcasecmp(type, list[i].type) == 0 &&
		    strcasecmp(data, list[i].data) == 0 &&
		    ttl == list[i].ttl) {
			break;
		}
	}
	if (i == MAX_RECORDS) {
		return (ISC_R_NOTFOUND);
	}
	memset(&list[i], 0, sizeof(struct record));
	return (ISC_R_SUCCESS);
}

169
170
171
172
static isc_result_t
fmt_address(isc_sockaddr_t *addr, char *buffer, size_t size) {
	char addr_buf[100];
	const char *ret;
173
	uint16_t port = 0;
174
175
176
177
178
179
180
181
182
183
184

	switch (addr->type.sa.sa_family) {
	case AF_INET:
		port = ntohs(addr->type.sin.sin_port);
		ret = inet_ntop(AF_INET, &addr->type.sin.sin_addr, addr_buf,
				sizeof(addr_buf));
		break;
	case AF_INET6:
		port = ntohs(addr->type.sin6.sin6_port);
		ret = inet_ntop(AF_INET6, &addr->type.sin6.sin6_addr, addr_buf,
				sizeof(addr_buf));
Mark Andrews's avatar
Mark Andrews committed
185
		break;
186
187
188
	default:
		return (ISC_R_FAILURE);
	}
189

190
191
192
193
194
195
	if (ret == NULL)
		return (ISC_R_FAILURE);

	snprintf(buffer, size, "%s#%u", addr_buf, port);
	return (ISC_R_SUCCESS);
}
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

/*
 * Return the version of the API
 */
int
dlz_version(unsigned int *flags) {
	UNUSED(flags);
	return (DLZ_DLOPEN_VERSION);
}

/*
 * Remember a helper function from the bind9 dlz_dlopen driver
 */
static void
b9_add_helper(struct dlz_example_data *state,
	      const char *helper_name, void *ptr)
{
	if (strcmp(helper_name, "log") == 0)
214
		state->log = (log_t *)ptr;
215
	if (strcmp(helper_name, "putrr") == 0)
216
		state->putrr = (dns_sdlz_putrr_t *)ptr;
217
	if (strcmp(helper_name, "putnamedrr") == 0)
218
		state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
219
	if (strcmp(helper_name, "writeable_zone") == 0)
220
		state->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
221
222
223
224
225
226
227
228
229
230
231
232
}

/*
 * Called to initialize the driver
 */
isc_result_t
dlz_create(const char *dlzname, unsigned int argc, char *argv[],
	   void **dbdata, ...)
{
	struct dlz_example_data *state;
	const char *helper_name;
	va_list ap;
233
	char soa_data[1024];
Evan Hunt's avatar
Evan Hunt committed
234
	const char *extra;
235
236
	isc_result_t result;
	int n;
237
238
239
240
241
242
243
244
245
246

	UNUSED(dlzname);

	state = calloc(1, sizeof(struct dlz_example_data));
	if (state == NULL)
		return (ISC_R_NOMEMORY);

	/* Fill in the helper functions */
	va_start(ap, dbdata);
	while ((helper_name = va_arg(ap, const char *)) != NULL) {
247
		b9_add_helper(state, helper_name, va_arg(ap, void *));
248
249
250
	}
	va_end(ap);

251
	if (argc < 2 || argv[1][0] == '\0') {
252
253
254
		if (state->log != NULL)
			state->log(ISC_LOG_ERROR,
				   "dlz_example: please specify a zone name");
255
		dlz_destroy(state);
256
257
258
		return (ISC_R_FAILURE);
	}

259
	/* Ensure zone name is absolute */
260
	state->zone_name = malloc(strlen(argv[1]) + 2);
261
262
263
264
	if (state->zone_name == NULL) {
		free(state);
		return (ISC_R_NOMEMORY);
	}
265
266
267
268
	if (argv[1][strlen(argv[1]) - 1] == '.')
		strcpy(state->zone_name, argv[1]);
	else
		sprintf(state->zone_name, "%s.", argv[1]);
269

Evan Hunt's avatar
Evan Hunt committed
270
271
272
273
274
275
276
277
	if (strcmp(state->zone_name, ".") == 0)
		extra = ".root";
	else
		extra = ".";

	n = sprintf(soa_data, "%s hostmaster%s%s 123 900 600 86400 3600",
		    state->zone_name, extra, state->zone_name);

278
279
280
281
282
	if (n < 0)
		CHECK(ISC_R_FAILURE);
	if ((unsigned)n >= sizeof(soa_data))
		CHECK(ISC_R_NOSPACE);

Evan Hunt's avatar
Evan Hunt committed
283
284
285
286
287
288
	add_name(state, &state->current[0], state->zone_name,
		 "soa", 3600, soa_data);
	add_name(state, &state->current[0], state->zone_name,
		 "ns", 3600, state->zone_name);
	add_name(state, &state->current[0], state->zone_name,
		 "a", 1800, "10.53.0.1");
289

290
291
292
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: started for zone %s",
			   state->zone_name);
293
294
295

	*dbdata = state;
	return (ISC_R_SUCCESS);
296
297
298
299
300

 failure:
	free(state);
	return (result);

301
302
303
304
305
306
307
308
309
}

/*
 * Shut down the backend
 */
void
dlz_destroy(void *dbdata) {
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

310
311
312
313
	if (state->log != NULL)
		state->log(ISC_LOG_INFO,
			   "dlz_example: shutting down zone %s",
			   state->zone_name);
314
315
316
317
318
319
320
321
	free(state->zone_name);
	free(state);
}

/*
 * See if we handle a given zone
 */
isc_result_t
322
323
324
325
dlz_findzonedb(void *dbdata, const char *name,
	   dns_clientinfomethods_t *methods,
	   dns_clientinfo_t *clientinfo)
{
326
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
327
328
	isc_sockaddr_t *src;
	char addrbuf[100];
329
	char absolute[1024];
330

331
	strcpy(addrbuf, "unknown");
332
333
	if (methods != NULL &&
	    methods->sourceip != NULL &&
334
335
	    methods->version - methods->age <= DNS_CLIENTINFOMETHODS_VERSION &&
	    DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
336
337
338
339
	{
		methods->sourceip(clientinfo, &src);
		fmt_address(src, addrbuf, sizeof(addrbuf));
	}
340

341
	state->log(ISC_LOG_INFO,
342
		   "dlz_example: dlz_findzonedb called with name '%s' "
343
344
		   "in zone DB '%s' from %s",
		   name, state->zone_name, addrbuf);
345
346
347
348
349
350
351
352
353
354
355

	/*
	 * Returning ISC_R_NOTFOUND will cause the query logic to
	 * check the database for parent names, looking for zone cuts.
	 *
	 * Returning ISC_R_NOMORE prevents the query logic from doing
	 * this; it will move onto the next database after a single query.
	 */
	if (strcasecmp(name, "test.example.com") == 0)
		return (ISC_R_NOMORE);

356
357
358
359
360
361
362
363
	/*
	 * For example.net, only return ISC_R_NOMORE when queried
	 * from 10.53.0.1.
	 */
	if (strcasecmp(name, "test.example.net") == 0 &&
	    strncmp(addrbuf, "10.53.0.1", 9) == 0)
		return (ISC_R_NOMORE);

364
365
366
367
368
369
370
371
372
373
374
375
	/*
	 * For bigcname.domain, return success so it appears to be
	 * the zone origin; this regression tests a bug in which
	 * zone origin nodes could fail to return SERVFAIL to the client.
	 */
	if (strcasecmp(name, "bigcname.domain") == 0)
		return (ISC_R_SUCCESS);

	/*
	 * Return success if we have an exact match between the
	 * zone name and the qname
	 */
376
377
378
	if (strcasecmp(state->zone_name, name) == 0)
		return (ISC_R_SUCCESS);

379
380
381
382
	snprintf(absolute, sizeof(absolute), "%s.", name);
	if (strcasecmp(state->zone_name, absolute) == 0)
		return (ISC_R_SUCCESS);

383
384
385
386
	return (ISC_R_NOTFOUND);
}

/*
387
388
 * Look up one record in the sample database.
 *
Evan Hunt's avatar
Evan Hunt committed
389
 * If the queryname is "source-addr", send back a TXT record containing
390
 * the address of the client, to test the use of 'methods' and 'clientinfo'
Evan Hunt's avatar
Evan Hunt committed
391
392
393
 *
 * If the queryname is "too-long", send back a TXT record that's too long
 * to process; this should result in a SERVFAIL when queried.
394
395
396
 */
isc_result_t
dlz_lookup(const char *zone, const char *name, void *dbdata,
397
398
	   dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods,
	   dns_clientinfo_t *clientinfo)
399
{
400
	isc_result_t result;
401
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
402
	bool found = false;
403
	void *dbversion = NULL;
404
	isc_sockaddr_t *src;
Evan Hunt's avatar
Evan Hunt committed
405
	char full_name[256];
Evan Hunt's avatar
Evan Hunt committed
406
	char buf[512];
407
	static char last[256];
408
	static int count = 0;
409
	int i, size;
410
411

	UNUSED(zone);
Automatic Updater's avatar
Automatic Updater committed
412

413
	if (state->putrr == NULL) {
414
		return (ISC_R_NOTIMPLEMENTED);
415
	}
416

Evan Hunt's avatar
Evan Hunt committed
417
	if (strcmp(name, "@") == 0) {
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
		size = snprintf(full_name, sizeof(full_name),
				"%s", state->zone_name);
	} else if (strcmp(state->zone_name, ".") == 0) {
		size = snprintf(full_name, sizeof(full_name),
				"%s.", name);
	} else {
		size = snprintf(full_name, sizeof(full_name),
				"%s.%s", name, state->zone_name);
	}

	if (size < 0 ||
	    (size_t)size >= sizeof(full_name) ||
	    (size_t)size >= sizeof(last))
	{
		return (ISC_R_NOSPACE);
	}
434

435
436
437
	/*
	 * For test purposes, log all calls to dlz_lookup()
	 */
438
	if (strcasecmp(full_name, last) == 0) {
439
		count++;
440
	} else {
441
		count = 1;
442
		memcpy(last, full_name, size + 1);
443
444
445
	}
	state->log(ISC_LOG_INFO, "lookup #%d for %s", count, full_name);

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
	/*
	 * If we need to know the database version (as set in
	 * the 'newversion' dlz function) we can pick it up from the
	 * clientinfo.
	 *
	 * This allows a lookup to query the correct version of the DNS
	 * data, if the DLZ can differentiate between versions.
	 *
	 * For example, if a new database transaction is created by
	 * 'newversion', the lookup should query within the same
	 * transaction scope if it can.
	 *
	 * If the DLZ only operates on 'live' data, then version
	 * wouldn't necessarily be needed.
	 */
	if (clientinfo != NULL &&
	    clientinfo->version >= DNS_CLIENTINFO_VERSION) {
		dbversion = clientinfo->dbversion;
464
		if (dbversion != NULL && *(bool *)dbversion)
465
466
			state->log(ISC_LOG_INFO,
				   "dlz_example: lookup against live "
467
				   "transaction");
468
469
	}

470
	if (strcmp(name, "source-addr") == 0) {
471
		strncpy(buf, "unknown", sizeof(buf));
472
		if (methods != NULL &&
473
		    methods->sourceip != NULL &&
474
475
476
		    (methods->version - methods->age <=
		     DNS_CLIENTINFOMETHODS_VERSION) &&
		    DNS_CLIENTINFOMETHODS_VERSION <= methods->version)
477
478
479
480
481
		{
			methods->sourceip(clientinfo, &src);
			fmt_address(src, buf, sizeof(buf));
		}

482
		state->log(ISC_LOG_INFO,
483
			   "dlz_example: lookup connection from %s", buf);
484

485
		found = true;
486
		result = state->putrr(lookup, "TXT", 0, buf);
Evan Hunt's avatar
Evan Hunt committed
487
488
489
490
		if (result != ISC_R_SUCCESS)
			return (result);
	}

491
492
493
	if (strcmp(name, "too-long") == 0 ||
	    strcmp(zone, "bigcname.domain") == 0)
	{
Evan Hunt's avatar
Evan Hunt committed
494
495
496
		for (i = 0; i < 511; i++)
			buf[i] = 'x';
		buf[i] = '\0';
497
		found = true;
Evan Hunt's avatar
Evan Hunt committed
498
		result = state->putrr(lookup, "TXT", 0, buf);
499
500
501
502
		if (result != ISC_R_SUCCESS)
			return (result);
	}

503
504
505
	/* Tests for DLZ redirection zones */
	if (strcmp(name, "*") == 0 && strcmp(zone, ".") == 0) {
		result = state->putrr(lookup, "A", 0, "100.100.100.2");
506
		found = true;
507
508
509
510
511
512
513
514
		if (result != ISC_R_SUCCESS)
			return (result);
	}

	if (strcmp(name, "long.name.is.not.there") == 0 &&
	    strcmp(zone, ".") == 0)
	{
		result = state->putrr(lookup, "A", 0, "100.100.100.3");
515
		found = true;
516
517
518
519
520
		if (result != ISC_R_SUCCESS)
			return (result);
	}

	/* Answer from current records */
521
522
	for (i = 0; i < MAX_RECORDS; i++) {
		if (strcasecmp(state->current[i].name, full_name) == 0) {
523
			found = true;
Automatic Updater's avatar
Automatic Updater committed
524
			result = state->putrr(lookup, state->current[i].type,
525
526
					      state->current[i].ttl,
					      state->current[i].data);
527
			if (result != ISC_R_SUCCESS)
528
529
530
531
532
533
				return (result);
		}
	}

	if (!found)
		return (ISC_R_NOTFOUND);
534

535
536
537
538
539
540
541
542
543
544
545
546
	return (ISC_R_SUCCESS);
}


/*
 * See if a zone transfer is allowed
 */
isc_result_t
dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
	UNUSED(client);

	/* Just say yes for all our zones */
547
	return (dlz_findzonedb(dbdata, name, NULL, NULL));
548
549
550
551
552
553
554
555
556
557
558
}

/*
 * Perform a zone transfer
 */
isc_result_t
dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
	int i;

	UNUSED(zone);
Tinderbox User's avatar
Tinderbox User committed
559

560
561
	if (state->putnamedrr == NULL)
		return (ISC_R_NOTIMPLEMENTED);
562
563
564

	for (i = 0; i < MAX_RECORDS; i++) {
		isc_result_t result;
565
		if (strlen(state->current[i].name) == 0U) {
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
			continue;
		}
		result = state->putnamedrr(allnodes, state->current[i].name,
					   state->current[i].type,
					   state->current[i].ttl,
					   state->current[i].data);
		if (result != ISC_R_SUCCESS)
			return (result);
	}

	return (ISC_R_SUCCESS);
}


/*
 * Start a transaction
 */
isc_result_t
dlz_newversion(const char *zone, void *dbdata, void **versionp) {
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	if (state->transaction_started) {
588
589
590
591
		if (state->log != NULL)
			state->log(ISC_LOG_INFO,
				   "dlz_example: transaction already "
				   "started for zone %s", zone);
592
593
594
		return (ISC_R_FAILURE);
	}

595
	state->transaction_started = true;
596
597
598
599
600
601
602
603
604
	*versionp = (void *) &state->transaction_started;

	return (ISC_R_SUCCESS);
}

/*
 * End a transaction
 */
void
605
dlz_closeversion(const char *zone, bool commit,
606
607
608
609
610
		 void *dbdata, void **versionp)
{
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	if (!state->transaction_started) {
611
612
613
		if (state->log != NULL)
			state->log(ISC_LOG_INFO, "dlz_example: transaction not "
				   "started for zone %s", zone);
614
615
616
617
		*versionp = NULL;
		return;
	}

618
	state->transaction_started = false;
619
620
621
622
623

	*versionp = NULL;

	if (commit) {
		int i;
624
625
626
		if (state->log != NULL)
			state->log(ISC_LOG_INFO, "dlz_example: committing "
				   "transaction on zone %s", zone);
627
		for (i = 0; i < MAX_RECORDS; i++) {
628
629
630
631
632
633
			if (strlen(state->deletes[i].name) > 0U) {
				(void)del_name(state, &state->current[0],
					       state->deletes[i].name,
					       state->deletes[i].type,
					       state->deletes[i].ttl,
					       state->deletes[i].data);
634
635
636
			}
		}
		for (i = 0; i < MAX_RECORDS; i++) {
637
638
639
640
641
642
			if (strlen(state->adds[i].name) > 0U) {
				(void)add_name(state, &state->current[0],
					       state->adds[i].name,
					       state->adds[i].type,
					       state->adds[i].ttl,
					       state->adds[i].data);
643
644
645
			}
		}
	} else {
646
647
648
		if (state->log != NULL)
			state->log(ISC_LOG_INFO, "dlz_example: cancelling "
				   "transaction on zone %s", zone);
649
650
651
652
653
654
655
656
657
658
	}
	memset(state->adds, 0, sizeof(state->adds));
	memset(state->deletes, 0, sizeof(state->deletes));
}


/*
 * Configure a writeable zone
 */
isc_result_t
Evan Hunt's avatar
Evan Hunt committed
659
dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *dbdata) {
660
661
662
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
	isc_result_t result;

663
664
665
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: starting configure");

666
	if (state->writeable_zone == NULL) {
667
668
669
		if (state->log != NULL)
			state->log(ISC_LOG_INFO, "dlz_example: no "
				   "writeable_zone method available");
670
671
672
		return (ISC_R_FAILURE);
	}

Evan Hunt's avatar
Evan Hunt committed
673
	result = state->writeable_zone(view, dlzdb, state->zone_name);
674
	if (result != ISC_R_SUCCESS) {
675
676
677
		if (state->log != NULL)
			state->log(ISC_LOG_ERROR, "dlz_example: failed to "
				   "configure zone %s", state->zone_name);
678
679
680
		return (result);
	}

681
682
683
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: configured writeable "
			   "zone %s", state->zone_name);
684
685
686
687
688
689
	return (ISC_R_SUCCESS);
}

/*
 * Authorize a zone update
 */
690
bool
691
dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
692
	     const char *type, const char *key, uint32_t keydatalen,
693
694
695
696
697
698
699
700
701
702
703
	     unsigned char *keydata, void *dbdata)
{
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	UNUSED(tcpaddr);
	UNUSED(type);
	UNUSED(key);
	UNUSED(keydatalen);
	UNUSED(keydata);

	if (strncmp(name, "deny.", 5) == 0) {
704
705
706
		if (state->log != NULL)
			state->log(ISC_LOG_INFO, "dlz_example: denying update "
				   "of name=%s by %s", name, signer);
707
		return (false);
708
	}
709
710
711
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: allowing update of "
			   "name=%s by %s", name, signer);
712
	return (true);
713
714
715
716
717
718
719
}


static isc_result_t
modrdataset(struct dlz_example_data *state, const char *name,
	    const char *rdatastr, struct record *list)
{
720
	char *full_name, *dclass, *type, *data, *ttlstr, *buf;
721
	char absolute[1024];
722
	isc_result_t result;
723
#if defined(WIN32) || defined(_REENTRANT)
724
	char *saveptr = NULL;
725
#endif
726

727
728
729
730
	buf = strdup(rdatastr);
	if (buf == NULL)
		return (ISC_R_FAILURE);

731
732
733
734
735
736
737
738
	/*
	 * The format is:
	 * FULLNAME\tTTL\tDCLASS\tTYPE\tDATA
	 *
	 * The DATA field is space separated, and is in the data format
	 * for the type used by dig
	 */

739
	full_name = STRTOK_R(buf, "\t", &saveptr);
740
	if (full_name == NULL)
741
		goto error;
742

743
	ttlstr = STRTOK_R(NULL, "\t", &saveptr);
744
	if (ttlstr == NULL)
745
		goto error;
746

747
	dclass = STRTOK_R(NULL, "\t", &saveptr);
748
	if (dclass == NULL)
749
		goto error;
750

751
	type = STRTOK_R(NULL, "\t", &saveptr);
752
	if (type == NULL)
753
		goto error;
754

755
	data = STRTOK_R(NULL, "\t", &saveptr);
756
	if (data == NULL)
757
		goto error;
758

759
760
761
762
763
	if (name[strlen(name) - 1] != '.') {
		snprintf(absolute, sizeof(absolute), "%s.", name);
		name = absolute;
	}

764
765
766
767
	result = add_name(state, list, name, type,
			  strtoul(ttlstr, NULL, 10), data);
	free(buf);
	return (result);
768
769
770
771

 error:
	free(buf);
	return (ISC_R_FAILURE);
772
773
774
775
776
777
778
779
780
781
782
783
}


isc_result_t
dlz_addrdataset(const char *name, const char *rdatastr,
		void *dbdata, void *version)
{
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	if (version != (void *) &state->transaction_started)
		return (ISC_R_FAILURE);

784
785
786
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'",
			   name, rdatastr);
787
788
789
790
791
792
793
794
795
796
797
798
799

	return (modrdataset(state, name, rdatastr, &state->adds[0]));
}

isc_result_t
dlz_subrdataset(const char *name, const char *rdatastr,
		void *dbdata, void *version)
{
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	if (version != (void *) &state->transaction_started)
		return (ISC_R_FAILURE);

800
801
802
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: subtracting rdataset "
			   "%s '%s'", name, rdatastr);
Automatic Updater's avatar
Automatic Updater committed
803

804
805
806
807
808
809
810
811
812
813
814
815
	return (modrdataset(state, name, rdatastr, &state->deletes[0]));
}

isc_result_t
dlz_delrdataset(const char *name, const char *type,
		void *dbdata, void *version)
{
	struct dlz_example_data *state = (struct dlz_example_data *)dbdata;

	if (version != (void *) &state->transaction_started)
		return (ISC_R_FAILURE);

816
817
818
	if (state->log != NULL)
		state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s "
			   "of type %s", name, type);
Automatic Updater's avatar
Automatic Updater committed
819

820
821
	return (ISC_R_SUCCESS);
}