gsstest: fixed compilation errors
[tridge/bind9.git] / contrib / dlz / example / dlz_example.c
1 /*
2  * Copyright (C) 2010 Andrew Tridgell
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR
10  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
11  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12  * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14  * 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
16  * USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /*
20   this provides a very simple example of an external loadable DLZ
21   driver, with update support
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdarg.h>
30
31 #include "dlz_minimal.h"
32
33
34 /* for this simple example, use fixed sized strings */
35 struct record {
36         char name[100];
37         char type[10];
38         char data[200];
39         uint32_t ttl;
40 };
41
42 #define MAX_RECORDS 100
43
44 struct dlz_example_data {
45         char *zone_name;
46
47         /* an example driver doesn't need good memory management :-) */
48         struct record current[MAX_RECORDS];
49         struct record adds[MAX_RECORDS];
50         struct record deletes[MAX_RECORDS];
51
52         bool transaction_started;
53
54         /* helper functions from the dlz_dlopen driver */
55         void (*log)(int level, const char *fmt, ...);
56         isc_result_t (*putrr)(dns_sdlzlookup_t *handle, const char *type,
57                               dns_ttl_t ttl, const char *data);
58         isc_result_t (*putnamedrr)(dns_sdlzlookup_t *handle, const char *name,
59                                    const char *type, dns_ttl_t ttl, const char *data);
60         isc_result_t (*writeable_zone)(dns_view_t *view, const char *zone_name);
61 };
62
63 static bool single_valued(const char *type)
64 {
65         const char *single[] = { "soa", "cname", NULL };
66         int i;
67         for (i=0; single[i]; i++) {
68                 if (strcasecmp(single[i], type) == 0) {
69                         return true;
70                 }
71         }
72         return false;
73 }
74
75 /*
76   add a record to a list
77  */
78 static isc_result_t add_name(struct dlz_example_data *state,
79                              struct record *list, const char *name, const char *type, 
80                              uint32_t ttl, const char *data)
81 {
82         int i;
83         bool single = single_valued(type);
84         int first_empty = -1;
85
86         for (i=0; i<MAX_RECORDS; i++) {
87                 if (first_empty == -1 && strlen(list[i].name) == 0) {
88                         first_empty = i;
89                 }
90                 if (strcasecmp(list[i].name, name) != 0) 
91                         continue;
92                 if (strcasecmp(list[i].type, type) != 0)
93                         continue;
94                 if (!single && strcasecmp(list[i].data, data) != 0)
95                         continue;
96                 break;
97         }
98         if (i == MAX_RECORDS && first_empty != -1) {
99                 i = first_empty;
100         }
101         if (i == MAX_RECORDS) {
102                 state->log(ISC_LOG_ERROR, "dlz_example: out of record space");
103                 return ISC_R_FAILURE;
104         }
105         strcpy(list[i].name, name);
106         strcpy(list[i].type, type);
107         strcpy(list[i].data, data);
108         list[i].ttl = ttl;
109         return ISC_R_SUCCESS;
110 }
111
112 /*
113   delete a record from a list
114  */
115 static isc_result_t del_name(struct dlz_example_data *state,
116                              struct record *list, const char *name, const char *type, 
117                              uint32_t ttl, const char *data)
118 {
119         int i;
120         for (i=0; i<MAX_RECORDS; i++) {
121                 if (strcasecmp(name, list[i].name) == 0 &&
122                     strcasecmp(type, list[i].type) == 0 &&
123                     strcasecmp(data, list[i].data) == 0 &&
124                     ttl == list[i].ttl) {
125                         break;
126                 }
127         }
128         if (i == MAX_RECORDS) {
129                 return ISC_R_NOTFOUND;
130         }
131         memset(&list[i], 0, sizeof(struct record));
132         return ISC_R_SUCCESS;
133 }
134
135
136
137 /*
138   return the version of the API
139  */
140 int dlz_version(unsigned int *flags)
141 {
142         return DLZ_DLOPEN_VERSION;
143 }
144
145 /*
146    remember a helper function from the bind9 dlz_dlopen driver
147  */
148 static void b9_add_helper(struct dlz_example_data *state, const char *helper_name, void *ptr)
149 {
150         if (strcmp(helper_name, "log") == 0) {
151                 state->log = ptr;
152         }
153         if (strcmp(helper_name, "putrr") == 0) {
154                 state->putrr = ptr;
155         }
156         if (strcmp(helper_name, "putnamedrr") == 0) {
157                 state->putnamedrr = ptr;
158         }
159         if (strcmp(helper_name, "writeable_zone") == 0) {
160                 state->writeable_zone = ptr;
161         }
162 }
163
164
165 /*
166   called to initialise the driver
167  */
168 isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[],
169                         void **dbdata, ...)
170 {
171         struct dlz_example_data *state;
172         const char *helper_name;
173         va_list ap;
174         char soa_data[200];
175
176         state = calloc(1, sizeof(struct dlz_example_data));
177         if (state == NULL) {
178                 return ISC_R_NOMEMORY;
179         }
180
181         /* fill in the helper functions */
182         va_start(ap, dbdata);
183         while ((helper_name = va_arg(ap, const char *)) != NULL) {
184                 b9_add_helper(state, helper_name, va_arg(ap, void*));
185         }
186         va_end(ap);
187
188         if (argc < 2) {
189                 state->log(ISC_LOG_ERROR, "dlz_example: please specify a zone name");
190                 return ISC_R_FAILURE;
191         }
192
193         state->zone_name = strdup(argv[1]);
194
195         sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600",
196                 state->zone_name, state->zone_name);
197
198         add_name(state, &state->current[0], state->zone_name, "soa", 3600, soa_data);
199         add_name(state, &state->current[0], state->zone_name, "ns", 3600, state->zone_name);
200         add_name(state, &state->current[0], state->zone_name, "a", 1800, "10.53.0.1");
201
202         state->log(ISC_LOG_INFO, "dlz_example: started for zone %s", state->zone_name);
203
204         *dbdata = state;
205         return ISC_R_SUCCESS;
206 }
207
208 /*
209   shutdown the backend
210  */
211 void dlz_destroy(void *dbdata)
212 {
213         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
214         state->log(ISC_LOG_INFO, "dlz_example: shutting down zone %s", state->zone_name);
215         free(state->zone_name);
216         free(state);
217 }
218
219
220 /*
221   see if we handle a given zone
222  */
223 isc_result_t dlz_findzonedb(void *dbdata, const char *name)
224 {
225         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
226         if (strcasecmp(state->zone_name, name) == 0) {
227                 return ISC_R_SUCCESS;
228         }
229         return ISC_R_NOTFOUND;
230 }
231
232
233
234 /*
235   lookup one record
236  */
237 isc_result_t dlz_lookup(const char *zone, const char *name, 
238                         void *dbdata, dns_sdlzlookup_t *lookup)
239 {
240         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
241         int i;
242         bool found = false;
243         char full_name[100];
244         
245         if (strcmp(name, "@") == 0) {
246                 strcpy(full_name, state->zone_name);
247         } else {
248                 sprintf(full_name, "%s.%s", name, state->zone_name);
249         }
250         for (i=0; i<MAX_RECORDS; i++) {
251                 if (strcasecmp(state->current[i].name, full_name) == 0) {
252                         isc_result_t result;
253                         found = true;
254                         result = state->putrr(lookup, state->current[i].type, 
255                                               state->current[i].ttl, state->current[i].data);
256                         if (result != ISC_R_SUCCESS) {
257                                 return result;
258                         }
259                 }
260         }
261         if (!found) {
262                 return ISC_R_NOTFOUND;
263         }
264         return ISC_R_SUCCESS;
265 }
266
267
268 /*
269   see if a zone transfer is allowed
270  */
271 isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
272 {
273         /* just say yes for all our zones */
274         return dlz_findzonedb(dbdata, name);
275 }
276
277 /*
278   perform a zone transfer
279  */
280 isc_result_t dlz_allnodes(const char *zone, void *dbdata,
281                           dns_sdlzallnodes_t *allnodes)
282 {
283         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
284         int i;
285
286         for (i=0; i<MAX_RECORDS; i++) {
287                 isc_result_t result;
288                 if (strlen(state->current[i].name) == 0) {
289                         continue;
290                 }
291                 result = state->putnamedrr(allnodes, state->current[i].name, state->current[i].type, 
292                                            state->current[i].ttl, state->current[i].data);
293                 if (result != ISC_R_SUCCESS) {
294                         return result;
295                 }
296         }
297
298         return ISC_R_SUCCESS;
299 }
300
301
302 /*
303   start a transaction
304  */
305 isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
306 {
307         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
308
309         if (state->transaction_started) {
310                 state->log(ISC_LOG_INFO, "dlz_example: transaction already started for zone %s", zone);
311                 return ISC_R_FAILURE;
312         }
313
314         state->transaction_started = true;
315
316         *versionp = (void *) &state->transaction_started;
317
318         return ISC_R_SUCCESS;
319 }
320
321 /*
322   end a transaction
323  */
324 void dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, void **versionp)
325 {
326         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
327
328         if (!state->transaction_started) {
329                 state->log(ISC_LOG_INFO, "dlz_example: transaction not started for zone %s", zone);
330                 *versionp = NULL;
331                 return;
332         }
333
334         state->transaction_started = false;
335
336         *versionp = NULL;
337
338         if (commit) {
339                 int i;
340                 state->log(ISC_LOG_INFO, "dlz_example: committing transaction on zone %s", zone);
341                 for (i=0; i<MAX_RECORDS; i++) {
342                         if (strlen(state->adds[i].name) > 0) {
343                                 add_name(state, &state->current[0], 
344                                          state->adds[i].name, 
345                                          state->adds[i].type, 
346                                          state->adds[i].ttl, 
347                                          state->adds[i].data);
348                         }
349                 }
350                 for (i=0; i<MAX_RECORDS; i++) {
351                         if (strlen(state->deletes[i].name) > 0) {
352                                 del_name(state, &state->current[0], 
353                                          state->deletes[i].name, 
354                                          state->deletes[i].type, 
355                                          state->deletes[i].ttl, 
356                                          state->deletes[i].data);
357                         }
358                 }
359         } else {
360                 state->log(ISC_LOG_INFO, "dlz_example: cancelling transaction on zone %s", zone);
361         }
362         memset(state->adds, 0, sizeof(state->adds));
363         memset(state->deletes, 0, sizeof(state->deletes));
364 }
365
366
367 /*
368   configure a writeable zone
369  */
370 isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
371 {
372         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
373         isc_result_t result;
374
375
376         state->log(ISC_LOG_INFO, "dlz_example: starting configure");
377         if (state->writeable_zone == NULL) {
378                 state->log(ISC_LOG_INFO, "dlz_example: no writeable_zone method available");
379                 return ISC_R_FAILURE;
380         }
381
382         result = state->writeable_zone(view, state->zone_name);
383         if (result != ISC_R_SUCCESS) {
384                 state->log(ISC_LOG_ERROR, "dlz_example: failed to configure zone %s", state->zone_name);
385                 return result;
386         }
387
388         state->log(ISC_LOG_INFO, "dlz_example: configured writeable zone %s", state->zone_name);
389         return ISC_R_SUCCESS;
390 }
391
392 /*
393   authorize a zone update
394  */
395 isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
396                            const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata, 
397                            void *dbdata)
398 {
399         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
400         if (strncmp(name, "deny.", 5) == 0) {
401                 state->log(ISC_LOG_INFO, "dlz_example: denying update of name=%s by %s", 
402                            name, signer);
403                 return false;
404         }
405         state->log(ISC_LOG_INFO, "dlz_example: allowing update of name=%s by %s", 
406                    name, signer);
407         return true;
408 }
409
410
411 static isc_result_t modrdataset(struct dlz_example_data *state, const char *name, const char *rdatastr,
412                                 struct record *list)
413 {
414         char *full_name, *dclass, *type, *data, *ttlstr;
415         char *buf = strdup(rdatastr);
416         isc_result_t result;
417         char *saveptr = NULL;
418
419         /*
420           the format is:
421           FULLNAME\tTTL\tDCLASS\tTYPE\tDATA
422
423           The DATA field is space separated, and is in the data format
424           for the type used by dig
425          */
426
427         full_name = strtok_r(buf, "\t", &saveptr);
428         if (full_name == NULL) return ISC_R_FAILURE;
429         ttlstr    = strtok_r(NULL, "\t", &saveptr);
430         if (ttlstr == NULL) return ISC_R_FAILURE;
431         dclass    = strtok_r(NULL, "\t", &saveptr);
432         if (dclass == NULL) return ISC_R_FAILURE;
433         type      = strtok_r(NULL, "\t", &saveptr);
434         if (type == NULL) return ISC_R_FAILURE;
435         data      = strtok_r(NULL, "\t", &saveptr);
436         if (data == NULL) return ISC_R_FAILURE;
437
438         result = add_name(state, list, name, type, strtoul(ttlstr, NULL, 10), data);
439         free(buf);
440         return result;
441 }
442
443
444 isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
445 {
446         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
447
448         if (version != (void *) &state->transaction_started) {
449                 return ISC_R_FAILURE;
450         }
451
452         state->log(ISC_LOG_INFO, "dlz_example: adding rdataset %s '%s'", name, rdatastr);
453
454         return modrdataset(state, name, rdatastr, &state->adds[0]);
455 }
456
457 isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
458 {
459         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
460
461         if (version != (void *) &state->transaction_started) {
462                 return ISC_R_FAILURE;
463         }
464
465         state->log(ISC_LOG_INFO, "dlz_example: subtracting rdataset %s '%s'", name, rdatastr);
466                    
467         return modrdataset(state, name, rdatastr, &state->deletes[0]);
468 }
469
470
471 isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
472 {
473         struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
474
475         if (version != (void *) &state->transaction_started) {
476                 return ISC_R_FAILURE;
477         }
478
479         state->log(ISC_LOG_INFO, "dlz_example: deleting rdataset %s of type %s", name, type);
480                    
481         return ISC_R_SUCCESS;
482 }