#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <isc/log.h>
#include <isc/result.h>
#include <isc/types.h>
#include <isc/util.h>
#include <dns/types.h>
#include <dns/dlz_dlopen.h>
#include "driver.h"
#ifdef WIN32
#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)
#endif
struct record {
char name[100];
char type[10];
char data[200];
dns_ttl_t ttl;
};
#define MAX_RECORDS 100
typedef void log_t(int level, const char *fmt, ...);
struct dlz_example_data {
char *zone_name;
struct record current[MAX_RECORDS];
struct record adds[MAX_RECORDS];
struct record deletes[MAX_RECORDS];
isc_boolean_t transaction_started;
log_t *log;
dns_sdlz_putrr_t *putrr;
dns_sdlz_putnamedrr_t *putnamedrr;
dns_dlz_writeablezone_t *writeable_zone;
};
static isc_boolean_t
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) {
return (ISC_TRUE);
}
}
return (ISC_FALSE);
}
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;
isc_boolean_t single = single_valued(type);
int first_empty = -1;
for (i = 0; i < MAX_RECORDS; i++) {
if (first_empty == -1 && strlen(list[i].name) == 0U) {
first_empty = i;
}
if (strcasecmp(list[i].name, name) != 0)
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) {
state->log(ISC_LOG_ERROR, "dlz_example: out of record space");
return (ISC_R_FAILURE);
}
strcpy(list[i].name, name);
strcpy(list[i].type, type);
strcpy(list[i].data, data);
list[i].ttl = ttl;
return (ISC_R_SUCCESS);
}
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);
}
int
dlz_version(unsigned int *flags) {
UNUSED(flags);
return (DLZ_DLOPEN_VERSION);
}
static void
b9_add_helper(struct dlz_example_data *state,
const char *helper_name, void *ptr)
{
if (strcmp(helper_name, "log") == 0)
state->log = (log_t *)ptr;
if (strcmp(helper_name, "putrr") == 0)
state->putrr = (dns_sdlz_putrr_t *)ptr;
if (strcmp(helper_name, "putnamedrr") == 0)
state->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
if (strcmp(helper_name, "writeable_zone") == 0)
state->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
}
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;
char soa_data[200];
UNUSED(dlzname);
state = calloc(1, sizeof(struct dlz_example_data));
if (state == NULL)
return (ISC_R_NOMEMORY);
va_start(ap, dbdata);
while ((helper_name = va_arg(ap, const char *)) != NULL) {
b9_add_helper(state, helper_name, va_arg(ap, void*));
}
va_end(ap);
if (argc < 2) {
state->log(ISC_LOG_ERROR,
"dlz_example: please specify a zone name");
dlz_destroy(state);
return (ISC_R_FAILURE);
}
state->zone_name = strdup(argv[1]);
sprintf(soa_data, "%s hostmaster.%s 123 900 600 86400 3600",
state->zone_name, state->zone_name);
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");
state->log(ISC_LOG_INFO,
"dlz_example: started for zone %s",
state->zone_name);
*dbdata = state;
return (ISC_R_SUCCESS);
}
void
dlz_destroy(void *dbdata) {
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
state->log(ISC_LOG_INFO,
"dlz_example: shutting down zone %s",
state->zone_name);
free(state->zone_name);
free(state);
}
isc_result_t
dlz_findzonedb(void *dbdata, const char *name) {
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
if (strcasecmp(state->zone_name, name) == 0)
return (ISC_R_SUCCESS);
return (ISC_R_NOTFOUND);
}
isc_result_t
dlz_lookup(const char *zone, const char *name, void *dbdata,
dns_sdlzlookup_t *lookup)
{
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
isc_boolean_t found = ISC_FALSE;
char full_name[100];
int i;
UNUSED(zone);
if (strcmp(name, "@") == 0)
strcpy(full_name, state->zone_name);
else
sprintf(full_name, "%s.%s", name, state->zone_name);
for (i = 0; i < MAX_RECORDS; i++) {
if (strcasecmp(state->current[i].name, full_name) == 0) {
isc_result_t result;
found = ISC_TRUE;
result = state->putrr(lookup, state->current[i].type,
state->current[i].ttl,
state->current[i].data);
if (result != ISC_R_SUCCESS) {
return (result);
}
}
}
if (!found)
return (ISC_R_NOTFOUND);
return (ISC_R_SUCCESS);
}
isc_result_t
dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
UNUSED(client);
return (dlz_findzonedb(dbdata, name));
}
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);
for (i = 0; i < MAX_RECORDS; i++) {
isc_result_t result;
if (strlen(state->current[i].name) == 0U) {
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);
}
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) {
state->log(ISC_LOG_INFO,
"dlz_example: transaction already "
"started for zone %s", zone);
return (ISC_R_FAILURE);
}
state->transaction_started = ISC_TRUE;
*versionp = (void *) &state->transaction_started;
return (ISC_R_SUCCESS);
}
void
dlz_closeversion(const char *zone, isc_boolean_t commit,
void *dbdata, void **versionp)
{
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
if (!state->transaction_started) {
state->log(ISC_LOG_INFO,
"dlz_example: transaction not started for zone %s",
zone);
*versionp = NULL;
return;
}
state->transaction_started = ISC_FALSE;
*versionp = NULL;
if (commit) {
int i;
state->log(ISC_LOG_INFO,
"dlz_example: committing transaction on zone %s",
zone);
for (i = 0; i < MAX_RECORDS; i++) {
if (strlen(state->adds[i].name) > 0U) {
add_name(state, &state->current[0],
state->adds[i].name,
state->adds[i].type,
state->adds[i].ttl,
state->adds[i].data);
}
}
for (i = 0; i < MAX_RECORDS; i++) {
if (strlen(state->deletes[i].name) > 0U) {
del_name(state, &state->current[0],
state->deletes[i].name,
state->deletes[i].type,
state->deletes[i].ttl,
state->deletes[i].data);
}
}
} else {
state->log(ISC_LOG_INFO,
"dlz_example: cancelling transaction on zone %s",
zone);
}
memset(state->adds, 0, sizeof(state->adds));
memset(state->deletes, 0, sizeof(state->deletes));
}
isc_result_t
dlz_configure(dns_view_t *view, void *dbdata) {
struct dlz_example_data *state = (struct dlz_example_data *)dbdata;
isc_result_t result;
state->log(ISC_LOG_INFO, "dlz_example: starting configure");
if (state->writeable_zone == NULL) {
state->log(ISC_LOG_INFO,
"dlz_example: no writeable_zone method available");
return (ISC_R_FAILURE);
}
result = state->writeable_zone(view, state->zone_name);
if (result != ISC_R_SUCCESS) {
state->log(ISC_LOG_ERROR,
"dlz_example: failed to configure zone %s",
state->zone_name);
return (result);
}
state->log(ISC_LOG_INFO,
"dlz_example: configured writeable zone %s",
state->zone_name);
return (ISC_R_SUCCESS);
}
isc_boolean_t
dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
const char *type, const char *key, isc_uint32_t keydatalen,
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) {
state->log(ISC_LOG_INFO,
"dlz_example: denying update of name=%s by %s",
name, signer);
return (ISC_FALSE);
}
state->log(ISC_LOG_INFO,
"dlz_example: allowing update of name=%s by %s",
name, signer);
return (ISC_TRUE);
}
static isc_result_t
modrdataset(struct dlz_example_data *state, const char *name,
const char *rdatastr, struct record *list)
{
char *full_name, *dclass, *type, *data, *ttlstr, *buf;
isc_result_t result;
#if defined(WIN32) || defined(_REENTRANT)
char *saveptr = NULL;
#endif
buf = strdup(rdatastr);
if (buf == NULL)
return (ISC_R_FAILURE);
full_name = STRTOK_R(buf, "\t", &saveptr);
if (full_name == NULL)
goto error;
ttlstr = STRTOK_R(NULL, "\t", &saveptr);
if (ttlstr == NULL)
goto error;
dclass = STRTOK_R(NULL, "\t", &saveptr);
if (dclass == NULL)
goto error;
type = STRTOK_R(NULL, "\t", &saveptr);
if (type == NULL)
goto error;
data = STRTOK_R(NULL, "\t", &saveptr);
if (data == NULL)
goto error;
result = add_name(state, list, name, type,
strtoul(ttlstr, NULL, 10), data);
free(buf);
return (result);
error:
free(buf);
return (ISC_R_FAILURE);
}
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);
state->log(ISC_LOG_INFO,
"dlz_example: adding rdataset %s '%s'",
name, rdatastr);
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);
state->log(ISC_LOG_INFO,
"dlz_example: subtracting rdataset %s '%s'",
name, rdatastr);
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);
state->log(ISC_LOG_INFO,
"dlz_example: deleting rdataset %s of type %s",
name, type);
return (ISC_R_SUCCESS);
}