summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2008-06-09 17:22:43 +0000
committerStef Walter <stef@memberwebs.com>2008-06-09 17:22:43 +0000
commitb9ecd6e5e5b87fe1c4dab960e92246772002dd6a (patch)
treeeb05eb48888e391678fa706ec5f62b012df2456e
parent3add3c0e19b659427a2624ec85daf67019b8ed7f (diff)
Merge the two dns plugins
-rw-r--r--common/soa.c77
-rw-r--r--common/soa.h6
-rw-r--r--common/strset.c276
-rw-r--r--common/strset.h75
-rw-r--r--plugin/Makefile.am6
-rw-r--r--plugin/autoserial.c573
-rw-r--r--plugin/dnsnotify.c (renamed from plugin/slapi-dnsnotify.c)242
-rw-r--r--plugin/plugin.c352
-rw-r--r--plugin/plugin.h72
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/unit-test-soa.c136
-rw-r--r--tests/unit-test-strset.c106
-rw-r--r--tools/notify-dns-slaves.c20
13 files changed, 1758 insertions, 187 deletions
diff --git a/common/soa.c b/common/soa.c
new file mode 100644
index 0000000..c2bbcc4
--- /dev/null
+++ b/common/soa.c
@@ -0,0 +1,77 @@
+
+#include "soa.h"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define SEPARATORS " \t\n\v()"
+
+int
+soa_serial_increment (const char *old_soa, char *new_soa, unsigned int new_len)
+{
+ unsigned long value;
+ const char *p, *beg;
+ char *end;
+ unsigned int len;
+
+ assert (old_soa);
+ assert (new_soa);
+
+ /* Check that we can add a digit */
+ len = strlen (old_soa);
+ assert (new_len >= len + 2);
+
+ p = old_soa;
+
+ /* Eat up space at the beginning */
+ while (*p && strchr (SEPARATORS, *p))
+ ++p;
+ /* Skip over domain name, to next sep */
+ while (*p && !strchr (SEPARATORS, *p))
+ ++p;
+ /* Eat up space between */
+ while (*p && strchr (SEPARATORS, *p))
+ ++p;
+ /* Skip over administrator email, to next sep */
+ while (*p && !strchr (SEPARATORS, *p))
+ ++p;
+ /* Eat up spaces between */
+ while (*p && strchr (SEPARATORS, *p))
+ ++p;
+
+ if (*p == 0 || !isdigit (*p)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Parse the integer here */
+ beg = p;
+ value = strtoul (beg, &end, 10);
+ if (beg == end || !strchr (SEPARATORS, *end)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Increment the integer appropriately */
+ if (value == 0xFFFFFFFF)
+ value = 1; /* Zero is recommended against in RFC 1982 */
+ else
+ ++value;
+
+ /* Build up the new SOA */
+ memset (new_soa, 0, new_len);
+ len = beg - old_soa;
+ strncpy (new_soa, old_soa, len);
+ snprintf (new_soa + len, 32, "%lu", value);
+ len = strlen (new_soa);
+ strcpy (new_soa + len, end);
+
+ len = strlen (new_soa);
+ assert (len < new_len);
+
+ return len;
+}
diff --git a/common/soa.h b/common/soa.h
new file mode 100644
index 0000000..ca842e3
--- /dev/null
+++ b/common/soa.h
@@ -0,0 +1,6 @@
+#ifndef SOA_H_
+#define SOA_H_
+
+int soa_serial_increment (const char *old_soa, char *new_soa, unsigned int new_len);
+
+#endif /*SOA_H_*/
diff --git a/common/strset.c b/common/strset.c
new file mode 100644
index 0000000..801a358
--- /dev/null
+++ b/common/strset.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2008, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Originally from apache 2.0
+ */
+
+/* Copyright 2000-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "strset.h"
+
+typedef struct _entry {
+ struct _entry* next;
+ unsigned int hash;
+ char *value;
+} entry;
+
+struct _strset {
+ entry **array;
+ unsigned int count;
+ unsigned int max;
+};
+
+#define INITIAL_MAX 15 /* tunable == 2^n - 1 */
+
+static entry**
+alloc_array (unsigned int max)
+{
+ void *ret;
+ size_t len;
+
+ assert (max);
+
+ len = sizeof (entry*) * (max + 1);
+ ret = strset_malloc (len);
+ if (ret)
+ memset (ret, 0, len);
+
+ return ret;
+}
+
+strset*
+strset_create (void)
+{
+ strset *set = strset_malloc (sizeof (strset));
+ if (set) {
+ memset (set, 0, sizeof (strset));
+ set->max = INITIAL_MAX;
+ set->array = alloc_array (set->max);
+ if (!set->array) {
+ strset_free (set);
+ set = NULL;
+ }
+ }
+
+ return set;
+}
+
+void
+strset_destroy (strset *set)
+{
+ unsigned int i;
+ entry *ent, *next;
+
+ if (!set)
+ return;
+
+ /* Free all the entries */
+ for (i = 0; i < set->max; ++i) {
+ next = NULL;
+ for (ent = set->array[i]; ent; ent = next) {
+ next = ent->next;
+
+ assert (ent->value);
+ strset_free (ent);
+ }
+ }
+
+ /* Free the set itself */
+ strset_free (set->array);
+ strset_free (set);
+}
+
+static int
+expand_array (strset* set)
+{
+ entry** new_array;
+ entry* ent, *next;
+ unsigned int new_max, n, i;
+
+ assert (set);
+ assert (set->max);
+ assert (set->array);
+
+ new_max = set->max * 2 + 1;
+ new_array = alloc_array (new_max);
+
+ if(!new_array)
+ return 0;
+
+ /* Go through all the entries */
+ for (i = 0; i < set->max; ++i) {
+ next = NULL;
+ for (ent = set->array[i]; ent; ent = next) {
+ next = ent->next;
+
+ n = ent->hash & new_max;
+ ent->next = new_array[n];
+ new_array[n] = ent;
+ }
+ }
+
+ strset_free (set->array);
+ set->array = new_array;
+ set->max = new_max;
+
+ return 1;
+}
+
+static entry**
+find_entry (strset* set, const char *value, int add)
+{
+ entry **ep, *e;
+ const char* p;
+ unsigned int hash;
+ size_t len;
+
+ assert (set);
+ assert (set->max);
+ assert (set->array);
+ assert (value);
+
+ hash = 0;
+
+ for(p = value; *p; p++)
+ hash = hash * 33 + *p;
+
+ /* scan linked list */
+ for (ep = &set->array[hash & set->max], e = *ep;
+ e; ep = &e->next, e = *ep)
+ {
+ if (e->hash == hash && strcmp (e->value, value) == 0)
+ break;
+ }
+
+ if (e || !add)
+ return ep;
+
+ /* Add an entry */
+ len = strlen (value);
+ e = strset_malloc (sizeof (entry) + len + 1);
+ if (e) {
+ /* Value data points past end of entry */
+ e->value = ((char*)e) + sizeof (entry);
+ memcpy (e->value, value, len + 1);
+
+ e->next = NULL;
+ e->hash = hash;
+
+ *ep = e;
+ ++set->count;
+ }
+
+ return ep;
+}
+
+int
+strset_has (strset *set, const char *value)
+{
+ entry **ep;
+
+ assert (set);
+
+ if (!value)
+ return 0;
+
+ ep = find_entry (set, value, 0);
+ return ep && *ep ? 1 : 0;
+}
+
+int
+strset_add (strset *set, const char *value)
+{
+ entry **ep;
+
+ assert (set);
+
+ if (!value)
+ return -1;
+
+ ep = find_entry (set, value, 1);
+ if (!ep || !*ep)
+ return -1;
+
+ if (set->count > set->max)
+ if (!expand_array (set))
+ return -1;
+
+ return 0;
+}
+
+int
+strset_remove (strset *set, const char* value)
+{
+ entry **ep, *old;
+
+ assert (set);
+
+ if (!value)
+ return -1;
+
+ ep = find_entry (set, value, 0);
+ if (!ep || !*ep)
+ return -1;
+
+ old = *ep;
+ *ep = (*ep)->next;
+ --set->count;
+ strset_free (old);
+ return 0;
+}
+
+unsigned int
+strset_size (strset *set)
+{
+ assert (set);
+ return set->count;
+}
diff --git a/common/strset.h b/common/strset.h
new file mode 100644
index 0000000..627856b
--- /dev/null
+++ b/common/strset.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2008, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * Originally from apache 2.0
+ */
+
+/* Copyright 2000-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STRSET_H__
+#define __STRSET_H__
+
+/* Abstract type for hash tables. */
+typedef struct _strset strset;
+
+strset* strset_create (void);
+void strset_destroy (strset *set);
+unsigned int strset_size (strset *set);
+int strset_has (strset *set, const char *value);
+int strset_add (strset *set, const char *value);
+int strset_remove (strset *set, const char *value);
+
+/* Need to rebuild strset.c if you set this variable */
+#if STRSET_CUSTMEM
+extern void* strset_malloc (size_t length);
+extern void strset_free (void *ptr);
+#else
+#define strset_malloc(x) malloc(x)
+#define strset_free(x) free(x)
+#endif
+
+#endif /* __STRSET_H__ */
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
index 9b8f4de..29266b9 100644
--- a/plugin/Makefile.am
+++ b/plugin/Makefile.am
@@ -7,7 +7,11 @@ INCLUDES = \
lib_LTLIBRARIES = slapi-dnsnotify.la
slapi_dnsnotify_la_SOURCES = \
- slapi-dnsnotify.c
+ plugin.c plugin.h \
+ autoserial.c \
+ dnsnotify.c \
+ ../common/soa.c \
+ ../common/strset.c
slapi_dnsnotify_la_LDFLAGS = -module -avoid-version \
-export-symbols-regex 'plugin_init'
diff --git a/plugin/autoserial.c b/plugin/autoserial.c
new file mode 100644
index 0000000..d10f8a5
--- /dev/null
+++ b/plugin/autoserial.c
@@ -0,0 +1,573 @@
+
+#include "config.h"
+
+#include "plugin.h"
+
+#include "common/soa.h"
+#include "common/strset.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+/*----------------------------------------------------------------------------------
+ * DECLARATIONS
+ */
+
+/* TODO: Fill these in from settings */
+static const char *dnsserial_soa_attribute = "sOARecord";
+static const char *dnsserial_soa_base = "";
+
+static Slapi_Mutex *dnsserial_mutex = NULL;
+static strset *dnsserial_soa_cache = NULL;
+
+/* ---------------------------------------------------------------------------------
+ * MEMORY ALLOCATION
+ */
+
+void*
+strset_malloc (size_t length)
+{
+ return slapi_ch_malloc (length);
+}
+
+void
+strset_free (void *ptr)
+{
+ return slapi_ch_free (&ptr);
+}
+
+/* ---------------------------------------------------------------------------------
+ * LDAP OPERATIONS
+ */
+
+static int
+search_for_dns (const char *base, const char *filter, char ***results)
+{
+ Slapi_Entry **entries, **e;
+ Slapi_PBlock *pb;
+ LDAPControl *ctrl;
+ char **dns, **d;
+ char *attr, *dn;
+ int rc, num, code;
+
+ assert (results);
+ assert (filter && filter[0]);
+ assert (base && base[0]);
+
+ ctrl = NULL; /* No controls */
+ attr = NULL; /* No attributes */
+
+ trace ("performing internal search");
+
+ /* Do the actual search */
+ pb = slapi_search_internal ((char*)base, LDAP_SCOPE_SUBTREE, (char*)filter, &ctrl, &attr, 1);
+ return_val_if_fail (pb, -1);
+
+ /* Was it successful? */
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS) {
+ log_plugin ("error performing search on %s for %s (code %d)", base, filter, code);
+ slapi_pblock_destroy (pb);
+ trace ("failure");
+ return -1;
+ }
+
+ /* Dig out all the entries */
+ entries = NULL;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ return_val_if_fail (rc >= 0, -1);
+
+ /* How many entries? */
+ num = 0;
+ for (e = entries; entries && *e; ++e)
+ ++num;
+
+ /* Allocate memory and copy over all dns found */
+ d = dns = (char**)slapi_ch_calloc (num + 1, sizeof (char*));
+ for (e = entries; entries && *e; ++e) {
+ dn = slapi_entry_get_dn (*e);
+ return_val_if_fail (dn, -1);
+
+ dn = slapi_ch_strdup (dn);
+ dn = slapi_dn_normalize_case (dn);
+ *(d++) = dn;
+ }
+
+ slapi_pblock_destroy (pb);
+ *results = dns;
+
+ trace ("success");
+ return 0;
+}
+
+static int
+populate_soa_cache (void)
+{
+ char **dns, **d;
+ char *filter;
+ int rc, len, have;
+
+ assert (dnsserial_mutex);
+
+ have = 0;
+ slapi_lock_mutex (dnsserial_mutex);
+
+ /* Already populated? */
+ have = dnsserial_soa_cache ? 1 : 0;
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ if (have)
+ return 1;
+
+ /* Build up the filter */
+ len = strlen (dnsserial_soa_attribute) +
+ strlen ("(%s=*)");
+ filter = slapi_ch_malloc (len);
+ strcpy (filter, "(");
+ strcat (filter, dnsserial_soa_attribute);
+ strcat (filter, "=*)");
+
+ trace (filter);
+
+ rc = search_for_dns (dnsserial_soa_base, filter, &dns);
+ slapi_ch_free_string (&filter);
+
+ if (rc < 0)
+ return rc;
+
+ /* Normalize all the DNs */
+ for (d = dns; *d; ++d)
+ slapi_dn_normalize_case (*d);
+
+ /* Now we're ready to put them in the cache */
+ assert (dnsserial_mutex);
+ slapi_lock_mutex (dnsserial_mutex);
+
+ /* Already populated? */
+ if (!dnsserial_soa_cache) {
+ dnsserial_soa_cache = strset_create ();
+ for (d = dns; *d; ++d)
+ strset_add (dnsserial_soa_cache, *d);
+ }
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ slapi_ch_array_free (dns);
+ return 1;
+}
+
+static int
+present_in_soa_cache (const char *dn)
+{
+ int present;
+
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return 0;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ present = strset_has (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return present;
+}
+
+static char*
+ancestor_in_soa_cache (const char *dn)
+{
+ Slapi_DN *sdn = NULL;
+ Slapi_DN *parent = NULL;
+ char *ancestor;
+
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return 0;
+
+ sdn = slapi_sdn_new_dn_byval (dn);
+ return_val_if_fail (sdn, 0);
+
+ ancestor = NULL;
+ slapi_lock_mutex (dnsserial_mutex);
+
+ for (;;) {
+
+ /* Get the parent of the dn */
+ assert (sdn);
+ assert (!parent);
+ parent = slapi_sdn_new ();
+ slapi_sdn_get_parent (sdn, parent);
+
+ /* Reached the root? */
+ if (slapi_sdn_isempty (parent))
+ break;
+
+ /* Reached the top of our SOA block? */
+ dn = slapi_sdn_get_dn (parent);
+ if (strcmp (dn, dnsserial_soa_base) == 0)
+ break;
+
+ /* Found this DN? */
+ if (strset_has (dnsserial_soa_cache, dn)) {
+ ancestor = slapi_ch_strdup (dn);
+ break;
+ }
+
+ slapi_sdn_free (&sdn);
+ sdn = parent;
+ parent = NULL;
+ }
+
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ if (sdn)
+ slapi_sdn_free (&sdn);
+ if (parent)
+ slapi_sdn_free (&parent);
+
+ return ancestor;
+}
+
+static int
+add_to_soa_cache (const char *dn)
+{
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return -1;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ strset_add (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return 0;
+}
+
+static int
+remove_from_soa_cache (const char *dn)
+{
+ assert (dn && dn[0]);
+
+ if (populate_soa_cache () < 0)
+ return -1;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ strset_remove (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return 0;
+}
+
+static char*
+load_soa_attribute (const char *dn)
+{
+ Slapi_Entry **entries;
+ struct berval **values;
+ Slapi_PBlock *pb;
+ Slapi_Attr *attr;
+ LDAPControl *ctrl;
+ char *attrs[2];
+ char *soa;
+ int rc, code;
+
+ trace (dn);
+ assert (dn && dn[0]);
+ assert (dnsserial_soa_attribute);
+
+ ctrl = NULL; /* No controls */
+ attrs[0] = (char*)dnsserial_soa_attribute;
+ attrs[1] = NULL;
+
+ trace ("performing internal search");
+
+ /* Do the actual search */
+ pb = slapi_search_internal ((char*)dn, LDAP_SCOPE_BASE, "(objectClass=*)", &ctrl, attrs, 0);
+ return_val_if_fail (pb, NULL);
+
+ /* Was it successful? */
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (rc >= 0, NULL);
+ if (code != LDAP_SUCCESS) {
+ log_plugin ("error loading attribute %s from %s (code %d)", dnsserial_soa_attribute, dn, code);
+ slapi_pblock_destroy (pb);
+ trace ("failure");
+ return NULL;
+ }
+
+ /* Dig out all the entries */
+ entries = NULL;
+ slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ return_val_if_fail (entries, NULL);
+
+ soa = NULL;
+ if (entries[0]) {
+ if (slapi_entry_attr_find (entries[0], (char*)dnsserial_soa_attribute, &attr) >= 0 && attr &&
+ slapi_attr_get_values (attr, &values) >= 0 && values && values[0]) {
+
+ /* Convert the berval into a string */
+ soa = slapi_ch_malloc (values[0]->bv_len + 1);
+ if (values[0]->bv_len)
+ memcpy (soa, values[0]->bv_val, values[0]->bv_len);
+ soa[values[0]->bv_len] = 0;
+ }
+ }
+
+ slapi_pblock_destroy (pb);
+ trace ("success");
+
+ return soa;
+}
+
+static int
+change_soa_attribute (const char *dn, const char *soa_old, const char *soa_new)
+{
+ Slapi_PBlock *pb;
+ LDAPMod *mods[3];
+ char *values_add[2];
+ char *values_remove[2];
+ LDAPMod mod_add;
+ LDAPMod mod_remove;
+ int rc, code;
+
+ trace (dn);
+
+ assert (dn && dn[0]);
+ assert (soa_old);
+ assert (soa_new);
+
+ /* Setup the mods to change */
+ values_add[0] = (char*)soa_new;
+ values_add[1] = NULL;
+ mod_add.mod_op = LDAP_MOD_ADD;
+ mod_add.mod_type = (char*)dnsserial_soa_attribute;
+ mod_add.mod_values = values_add;
+
+ values_remove[0] = (char*)soa_old;
+ values_remove[1] = NULL;
+ mod_remove.mod_op = LDAP_MOD_DELETE;
+ mod_remove.mod_type = (char*)dnsserial_soa_attribute;
+ mod_remove.mod_values = values_remove;
+
+ mods[0] = &mod_remove;
+ mods[1] = &mod_add;
+ mods[2] = NULL;
+
+ trace ("performing modify internal");
+
+ pb = slapi_modify_internal ((char*)dn, mods, NULL, 1);
+ return_val_if_fail (pb, -1);
+
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (pb, -1);
+ slapi_pblock_destroy (pb);
+
+ if (code != LDAP_SUCCESS && code != LDAP_TYPE_OR_VALUE_EXISTS && code != LDAP_NO_SUCH_ATTRIBUTE) {
+ log_plugin ("couldn't replace %s on %s with '%s' (code %d)",
+ dnsserial_soa_attribute, dn, soa_new, code);
+ trace ("failure");
+ return -1;
+ }
+
+ trace ("success");
+ return 0;
+}
+
+static int
+increment_soa_dn (const char *dn)
+{
+ char *soa_new;
+ char *soa_old;
+ int len, ret = -1;
+
+ assert (dn && dn[0]);
+
+ soa_old = load_soa_attribute (dn);
+ if (!soa_old) {
+ log_plugin ("couldn't increment SOA record at %s: not found", dn);
+ return -1;
+ }
+
+ len = strlen (soa_old) + 32;
+ soa_new = slapi_ch_malloc (len);
+ if (soa_serial_increment (soa_old, soa_new, len) < 0) {
+ if (errno == EINVAL)
+ log_plugin ("invalid SOA record at %s: %s", dn, soa_old);
+ else
+ log_plugin ("couldn't increment SOA record at %s: %s", dn, strerror (errno));
+ ret = -1;
+ } else {
+ ret = change_soa_attribute (dn, soa_old, soa_new);
+ }
+
+ slapi_ch_free_string (&soa_new);
+ slapi_ch_free_string (&soa_old);
+
+ return ret;
+}
+
+void
+autoserial_post_delete (const char *dn)
+{
+ char *soa_dn;
+
+ return_if_fail (dn && dn[0]);
+
+ /* A DN that is an SOA? */
+ if (present_in_soa_cache (dn)) {
+ remove_from_soa_cache (dn);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_modify (const char *dn, LDAPMod **mods)
+{
+ char *soa, *soa_dn;
+ int present;
+
+ return_if_fail (dn && dn[0]);
+
+ /* Add or remove from cache as appropriate */
+ soa = load_soa_attribute(dn);
+ if (soa) {
+ add_to_soa_cache (dn);
+ slapi_ch_free_string (&soa);
+ present = 1;
+ } else {
+ remove_from_soa_cache (dn);
+ present = 0;
+ }
+
+ /* A DN underneath an SOA? */
+ if (!present) {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_modrdn (const char *odn, const char *ndn)
+{
+ char *soa_dn;
+
+ return_if_fail (odn && odn[0]);
+ return_if_fail (ndn && ndn[0]);
+
+ /* A DN that is an SOA? */
+ if (present_in_soa_cache (odn)) {
+
+ remove_from_soa_cache (odn);
+ add_to_soa_cache (ndn);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (ndn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_add (const char *dn)
+{
+ char *soa, *soa_dn;
+
+ return_if_fail (dn && dn[0]);
+
+ /* A DN that is an SOA? */
+ soa = load_soa_attribute (dn);
+ if (soa) {
+ add_to_soa_cache (dn);
+ slapi_ch_free_string (&soa);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+int
+autoserial_config (const char *name, const char *value)
+{
+ return_val_if_fail (name, 0);
+
+ if (strcmp ("soa-attribute", name) == 0 && value) {
+ dnsserial_soa_attribute = value;
+ return 1;
+
+
+ } else if (strcmp ("base-dn", name) == 0 && value) {
+ dnsserial_soa_base = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+autoserial_destroy (void)
+{
+ assert (dnsserial_mutex);
+ slapi_lock_mutex (dnsserial_mutex);
+
+ if (dnsserial_soa_cache)
+ strset_destroy (dnsserial_soa_cache);
+ dnsserial_soa_cache = NULL;
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ slapi_destroy_mutex (dnsserial_mutex);
+ dnsserial_mutex = NULL;
+}
+
+int
+autoserial_init (void)
+{
+ if (!dnsserial_soa_base || !dnsserial_soa_base[0]) {
+ log_plugin ("'base-dn' argument not specified");
+ return -1;
+ }
+
+ assert (!dnsserial_mutex);
+ dnsserial_mutex = slapi_new_mutex ();
+ return_val_if_fail (dnsserial_mutex, -1);
+
+ return 0;
+}
diff --git a/plugin/slapi-dnsnotify.c b/plugin/dnsnotify.c
index 329b913..132726f 100644
--- a/plugin/slapi-dnsnotify.c
+++ b/plugin/dnsnotify.c
@@ -1,6 +1,8 @@
#include "config.h"
+#include "plugin.h"
+
#include <sys/wait.h>
#include <stdarg.h>
@@ -13,91 +15,19 @@
#include <signal.h>
#include <ctype.h>
-#include <lber.h>
-#include <ldap.h>
-
-#ifdef HAVE_SLAPI_PLUGIN_H
-#include <slapi-plugin.h>
-#else
-#include "include/slapi-plugin.h"
-#endif
-
-#define PLUGIN_NAME "slapi-dnsnotify"
-
/*----------------------------------------------------------------------------------
* DECLARATIONS
*/
static const char *dnsnotify_soa_attribute = "sOARecord";
static const char *dnsnotify_ns_attribute = "nSRecord";
+static int dnsnotify_enable = 1;
+static int dnsnotify_delay = 5;
static Slapi_Mutex *dnsnotify_mutex = NULL;
static int dnsnotify_pipe = -1;
static pid_t dnsnotify_pid = -1;
-/* ---------------------------------------------------------------------
- * LOGGING/TRACING
- */
-
-static void
-log_msg_va (int level, const char* msg, va_list ap)
-{
- size_t len;
- char buf[1024];
-
- assert (level >= 0);
- assert (msg != NULL);
-
- vsnprintf (buf, sizeof (buf) - 2, msg, ap);
-
- len = strlen (buf);
- buf[len] = '\n';
- buf[len + 1] = '\0';
-
-#if _DEBUG
- fprintf (stderr, "%s", buf);
-#endif
- slapi_log_error (level, PLUGIN_NAME, buf);
-}
-
-static void
-log_msg (int level, const char* msg, ...)
-{
- va_list ap;
- va_start (ap, msg);
- log_msg_va (level, msg, ap);
- va_end (ap);
-}
-
-#if _DEBUG
-static void
-log_trace (const char *where, int line, const char *msg)
-{
- log_msg (SLAPI_LOG_TRACE, "*** %s *** %s:%d %s%s",
- PLUGIN_NAME, where, line, msg ? ": " : "", msg ? msg : "");
-}
-#endif
-
-static void
-log_plugin (const char* msg, ...)
-{
- va_list ap;
- va_start (ap, msg);
- log_msg_va (SLAPI_LOG_PLUGIN, msg, ap);
- va_end (ap);
-}
-
-#define return_val_if_fail(expr, ret) \
- do { if (expr) { } else { \
- log_plugin ("*** %s *** check failed: '%s' at %s:%d", PLUGIN_NAME, #expr, __PRETTY_FUNCTION__, __LINE__); \
- return (ret); \
- } } while (0)
-
-#if _DEBUG
-#define trace(x) log_trace (__PRETTY_FUNCTION__, __LINE__, (x))
-#else
-#define trace(x)
-#endif
/* ---------------------------------------------------------------------------------
* OPERATIONS
@@ -214,7 +144,8 @@ fork_notify_process (void)
{
int open_max, fd;
int commpipe[2];
- char *args[3];
+ char *args[5];
+ char delay[16];
pid_t pid;
if (pipe (commpipe) < 0) {
@@ -262,9 +193,12 @@ fork_notify_process (void)
for (fd = 3; fd < open_max; ++fd)
close (fd);
+ snprintf (delay, sizeof (delay), "-w%d", dnsnotify_delay);
+
args[0] = DNS_NOTIFY_PATH;
args[1] = "-s";
- args[2] = NULL;
+ args[2] = delay;
+ args[3] = NULL;
execv (args[0], args);
log_plugin ("couldn't launch '%s' process: %s", DNS_NOTIFY_PATH, strerror (errno));
@@ -371,31 +305,17 @@ notify_dns_slaves (char *soa, char **ns)
slapi_unlock_mutex (dnsnotify_mutex);
}
-static int
-slapi_dnsnotify_modify (Slapi_PBlock *pb)
+void
+dnsnotify_post_modify (const char *dn, LDAPMod **mods)
{
- LDAPMod **mods, **m, *mod;
- char *dn, *soa, **ns;
- int rc, code;
-
- return_val_if_fail (pb, -1);
-
- trace ("detect modify");
-
- /* Make sure it was successful, don't process errors */
- rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
- return_val_if_fail (rc >= 0, -1);
- if (code != LDAP_SUCCESS)
- return 0;
+ LDAPMod **m, *mod;
+ char *soa, **ns;
- /* Get out the DN and normalize it */
- rc = slapi_pblock_get (pb, SLAPI_MODIFY_TARGET, &dn);
- return_val_if_fail (rc >= 0 && dn, -1);
- dn = slapi_ch_strdup (dn);
- slapi_dn_normalize_case (dn);
+ return_if_fail (dn && dn[0]);
+ return_if_fail (mods);
- rc = slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
- return_val_if_fail (rc >= 0 && mods, -1);
+ if (!dnsnotify_enable)
+ return;
for (m = mods; *m; ++m) {
mod = *m;
@@ -409,30 +329,17 @@ slapi_dnsnotify_modify (Slapi_PBlock *pb)
break;
}
}
-
- slapi_ch_free_string (&dn);
- return 0;
}
-static int
-slapi_dnsnotify_add (Slapi_PBlock *pb)
+void
+dnsnotify_post_add (const char *dn)
{
- char *dn, *soa, **ns;
- int rc, code, ret;
-
- return_val_if_fail (pb, -1);
+ char *soa, **ns;
- /* Make sure it was successful, don't process errors */
- rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
- return_val_if_fail (rc >= 0, -1);
- if (code != LDAP_SUCCESS)
- return 0;
+ return_if_fail (dn);
- /* Get out the DN and normalize it */
- rc = slapi_pblock_get (pb, SLAPI_ADD_TARGET, &dn);
- return_val_if_fail (rc >= 0 && dn, -1);
- dn = slapi_ch_strdup (dn);
- slapi_dn_normalize_case (dn);
+ if (!dnsnotify_enable)
+ return;
/* A DN that is an SOA? */
if (load_soa_ns_attributes (dn, &soa, &ns)) {
@@ -440,82 +347,41 @@ slapi_dnsnotify_add (Slapi_PBlock *pb)
slapi_ch_free_string (&soa);
slapi_ch_array_free (ns);
}
-
- slapi_ch_free_string (&dn);
- return ret;
}
-static Slapi_PluginDesc plugin_description = {
- PLUGIN_NAME, /* plug-in identifier */
- "stef@memberwebs.com", /* vendor name */
- VERSION, /* plug-in revision number */
- "Notify's DNS slaves when SOA change is made" /* plug-in description */
-};
-
-static int
-plugin_destroy (Slapi_PBlock *pb)
+int
+dnsnotify_config (const char *name, const char *value)
{
- if (dnsnotify_mutex)
- slapi_lock_mutex (dnsnotify_mutex);
+ return_val_if_fail (name, 0);
- kill_notify_process ();
-
- if (dnsnotify_mutex) {
- slapi_unlock_mutex (dnsnotify_mutex);
- slapi_destroy_mutex (dnsnotify_mutex);
- dnsnotify_mutex = NULL;
+ if (strcmp (name, "soa-attribute") == 0 && value) {
+ dnsnotify_soa_attribute = value;
+ return 1;
}
- return 0;
-}
-
-int
-plugin_init (Slapi_PBlock *pb)
-{
- char **argv = NULL;
- int argc = 0;
- int rc;
-
- return_val_if_fail (pb, -1);
-
- rc = slapi_pblock_get (pb, SLAPI_PLUGIN_ARGV, &argv);
- return_val_if_fail (rc >= 0, -1);
- slapi_pblock_get (pb, SLAPI_PLUGIN_ARGC, &argc);
- return_val_if_fail (rc >= 0, -1);
-
- if (argc >= 1)
- dnsnotify_soa_attribute = argv[0];
- if (argc >= 2)
- dnsnotify_ns_attribute = argv[1];
- if (argc > 2)
- log_plugin ("extra arguments present");
-
- if (!dnsnotify_soa_attribute || !dnsnotify_soa_attribute[0]) {
- log_plugin ("DNS SOA attribute specified for plugin is empty");
- return -1;
+ else if (strcmp (name, "ns-attribute") == 0 && value) {
+ dnsnotify_ns_attribute = value;
+ return 1;
}
- if (!dnsnotify_ns_attribute || !dnsnotify_ns_attribute[0]) {
- log_plugin ("DNS SOA attribute specified for plugin is empty");
- return -1;
+ else if (strcmp (name, "notify-delay") == 0 && value) {
+ dnsnotify_delay = atoi (value);
+ return 1;
}
- if (slapi_pblock_set (pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03) != 0 ||
- slapi_pblock_set (pb, SLAPI_PLUGIN_DESCRIPTION, &plugin_description) != 0 ||
- slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, plugin_destroy)) {
- log_plugin ("error registring plugin");
- return -1;
+ else if (strcmp (name, "disable-notify") == 0 && !value) {
+ dnsnotify_enable = 0;
+ return 1;
}
- /*
- * TODO: Add support for changing the default attributes and search uri
- * Accept an argument for this.
- */
+ return 0;
+}
- /* Setup the entry add/mobify functions */
- if (slapi_pblock_set (pb, SLAPI_PLUGIN_POST_ADD_FN, (void*)slapi_dnsnotify_add) != 0 ||
- slapi_pblock_set (pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void*)slapi_dnsnotify_modify)) {
- log_plugin ("error registering plugin hooks");
+int
+dnsnotify_init (void)
+{
+ if (!dnsnotify_ns_attribute || !dnsnotify_ns_attribute[0]) {
+ log_plugin ("SOA or NS attribute specified for plugin is empty");
return -1;
}
@@ -523,7 +389,21 @@ plugin_init (Slapi_PBlock *pb)
dnsnotify_mutex = slapi_new_mutex ();
return_val_if_fail (dnsnotify_mutex, -1);
- log_plugin ("%s initialized", PLUGIN_NAME);
return 0;
}
+
+void
+dnsnotify_destroy (void)
+{
+ if (dnsnotify_mutex)
+ slapi_lock_mutex (dnsnotify_mutex);
+
+ kill_notify_process ();
+
+ if (dnsnotify_mutex) {
+ slapi_unlock_mutex (dnsnotify_mutex);
+ slapi_destroy_mutex (dnsnotify_mutex);
+ dnsnotify_mutex = NULL;
+ }
+}
diff --git a/plugin/plugin.c b/plugin/plugin.c
new file mode 100644
index 0000000..c38eb67
--- /dev/null
+++ b/plugin/plugin.c
@@ -0,0 +1,352 @@
+
+#include "config.h"
+
+#include "plugin.h"
+
+#include <sys/wait.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define WHITESPACE " \t\r\n\v"
+
+static char **plugin_arguments = NULL;
+
+/* ---------------------------------------------------------------------
+ * LOGGING/TRACING
+ */
+
+static void
+log_msg_va (int level, const char* msg, va_list ap)
+{
+ size_t len;
+ char buf[1024];
+
+ assert (level >= 0);
+ assert (msg != NULL);
+
+ vsnprintf (buf, sizeof (buf) - 2, msg, ap);
+
+ len = strlen (buf);
+ buf[len] = '\n';
+ buf[len + 1] = '\0';
+
+#if _DEBUG
+ fprintf (stderr, "%s", buf);
+#endif
+ slapi_log_error (level, PLUGIN_NAME, buf);
+}
+
+void
+log_msg (int level, const char* msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ log_msg_va (level, msg, ap);
+ va_end (ap);
+}
+
+#if _DEBUG
+void
+log_trace (const char *where, int line, const char *msg)
+{
+ log_msg (SLAPI_LOG_TRACE, "*** %s *** %s:%d %s%s",
+ PLUGIN_NAME, where, line, msg ? ": " : "", msg ? msg : "");
+}
+#endif
+
+void
+log_plugin (const char* msg, ...)
+{
+ va_list ap;
+ va_start (ap, msg);
+ log_msg_va (SLAPI_LOG_PLUGIN, msg, ap);
+ va_end (ap);
+}
+
+/* --------------------------------------------------------------------------------
+ * HELPERS
+ */
+
+const char*
+ltrim (const char *data)
+{
+ while (*data && isspace (*data))
+ ++data;
+ return data;
+}
+
+void
+rtrim (char *data)
+{
+ char *t = data + strlen (data);
+ while (t > data && isspace (*(t - 1))) {
+ t--;
+ *t = 0;
+ }
+}
+
+char*
+trim (char *data)
+{
+ data = (char*)ltrim (data);
+ rtrim (data);
+ return data;
+}
+
+void
+lowercase (char *data)
+{
+ while (*data) {
+ *data = tolower (*data);
+ ++data;
+ }
+}
+
+/* ---------------------------------------------------------------------------------
+ * OPERATIONS
+ */
+
+static char*
+dn_build_normalize (const char *dn, const char *rdn)
+{
+ char *result;
+ Slapi_DN *sdn;
+ Slapi_DN *parent;
+ Slapi_RDN *srdn;
+
+ /* Get the parent of the dn */
+ sdn = slapi_sdn_new_dn_byval (dn);
+ return_val_if_fail (sdn, NULL);
+ parent = slapi_sdn_new ();
+ slapi_sdn_get_parent (sdn, parent);
+
+ /* Add the rdn to the parent */
+ srdn = slapi_rdn_new_dn (rdn);
+ return_val_if_fail (srdn, NULL);
+ slapi_sdn_add_rdn (parent, srdn);
+
+ /* Get the result out */
+ result = (char*)slapi_sdn_get_dn (parent);
+ return_val_if_fail (result, NULL);
+
+ /* Normalize the result */
+ result = slapi_ch_strdup (result);
+ slapi_dn_normalize_case (result);
+
+ slapi_sdn_free (&sdn);
+ slapi_rdn_free (&srdn);
+ slapi_sdn_free (&parent);
+
+ trace (result);
+ return result;
+}
+
+static int
+post_delete (Slapi_PBlock *pb)
+{
+ int rc, code;
+ char *dn;
+
+ return_val_if_fail (pb, -1);
+
+ /* Make sure it was successful, don't process errors */
+ rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS)
+ return 0;
+
+ /* Get out the DN and normalize it */
+ rc = slapi_pblock_get (pb, SLAPI_DELETE_TARGET, &dn);
+ return_val_if_fail (rc >= 0 && dn, -1);
+ dn = slapi_ch_strdup (dn);
+ slapi_dn_normalize_case (dn);
+
+ /* Send off to components */
+ autoserial_post_delete (dn);
+
+ slapi_ch_free_string (&dn);
+ return 0;
+}
+
+static int
+post_modrdn (Slapi_PBlock *pb)
+{
+ char *rdn, *odn, *ndn;
+ int rc, code;
+
+ return_val_if_fail (pb, -1);
+
+ /* Make sure it was successful, don't process errors */
+ rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS)
+ return 0;
+
+ /* Get out the DN and normalize it */
+ rc = slapi_pblock_get (pb, SLAPI_MODRDN_TARGET, &odn);
+ return_val_if_fail (rc >= 0 && odn, -1);
+ odn = slapi_ch_strdup (odn);
+ slapi_dn_normalize_case (odn);
+
+ /* Get out the changed DN and calculate it */
+ rc = slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &rdn);
+ return_val_if_fail (rc >= 0 && rdn, -1);
+ ndn = dn_build_normalize (odn, rdn);
+ return_val_if_fail (ndn, -1);
+
+ /* Send off to components */
+ autoserial_post_modrdn (odn, ndn);
+
+ slapi_ch_free_string (&odn);
+ slapi_ch_free_string (&ndn);
+ return 0;
+}
+
+static int
+post_modify (Slapi_PBlock *pb)
+{
+ LDAPMod **mods;
+ char *dn;
+ int rc, code;
+
+ return_val_if_fail (pb, -1);
+
+ /* Make sure it was successful, don't process errors */
+ rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS)
+ return 0;
+
+ /* Get out the DN and normalize it */
+ rc = slapi_pblock_get (pb, SLAPI_MODIFY_TARGET, &dn);
+ return_val_if_fail (rc >= 0 && dn, -1);
+ dn = slapi_ch_strdup (dn);
+ slapi_dn_normalize_case (dn);
+
+ rc = slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
+ return_val_if_fail (rc >= 0 && mods, -1);
+
+ /* Send off to components */
+ autoserial_post_modify (dn, mods);
+ dnsnotify_post_modify (dn, mods);
+
+ slapi_ch_free_string (&dn);
+ return 0;
+}
+
+static int
+post_add (Slapi_PBlock *pb)
+{
+ char *dn;
+ int rc, code;
+
+ return_val_if_fail (pb, -1);
+
+ /* Make sure it was successful, don't process errors */
+ rc = slapi_pblock_get (pb, SLAPI_RESULT_CODE, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS)
+ return 0;
+
+ /* Get out the DN and normalize it */
+ rc = slapi_pblock_get (pb, SLAPI_ADD_TARGET, &dn);
+ return_val_if_fail (rc >= 0 && dn, -1);
+ dn = slapi_ch_strdup (dn);
+ slapi_dn_normalize_case (dn);
+
+ /* Send off to components */
+ autoserial_post_add (dn);
+ dnsnotify_post_add (dn);
+
+ slapi_ch_free_string (&dn);
+ return 0;
+}
+
+static Slapi_PluginDesc plugin_description = {
+ PLUGIN_NAME, /* plug-in identifier */
+ "stef@memberwebs.com", /* vendor name */
+ VERSION, /* plug-in revision number */
+ "Notify's DNS slaves when SOA change is made" /* plug-in description */
+};
+
+static int
+plugin_destroy (Slapi_PBlock *pb)
+{
+ autoserial_destroy ();
+ dnsnotify_destroy ();
+
+ slapi_ch_array_free (plugin_arguments);
+ plugin_arguments = NULL;
+
+ return 0;
+}
+
+int
+plugin_init (Slapi_PBlock *pb)
+{
+ char **argv = NULL, *arg, *value;
+ int argc = 0;
+ int rc, i;
+
+ return_val_if_fail (pb, -1);
+
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_ARGV, &argv);
+ return_val_if_fail (rc >= 0, -1);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_ARGC, &argc);
+ return_val_if_fail (rc >= 0, -1);
+
+ /*
+ * Copy all the arguments, until we get destroyed, and
+ * send the arguments to the components to configure
+ * themselves.
+ */
+ plugin_arguments = (char**)slapi_ch_calloc (argc + 1, sizeof (char*));
+ for (i = 0; i < argc; ++i) {
+ plugin_arguments[i] = slapi_ch_strdup (argv[i]);
+ arg = trim (plugin_arguments[i]);
+
+ value = strchr (arg, '=');
+ if (value) {
+ *value = 0;
+ value = trim (value + 1);
+ }
+
+ lowercase (arg);
+
+ rc = autoserial_config (arg, value);
+ rc |= dnsnotify_config (arg, value);
+
+ if (!rc)
+ log_plugin ("unrecognized plugin argument: %s", argv[i]);
+ }
+
+ /* Next we initialize all components */
+ if (autoserial_init () < 0 ||
+ dnsnotify_init() < 0)
+ return -1;
+
+ if (slapi_pblock_set (pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03) != 0 ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_DESCRIPTION, &plugin_description) != 0 ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, plugin_destroy)) {
+ log_plugin ("error registring plugin");
+ return -1;
+ }
+
+ /* Setup the entry add/mobify functions */
+ if (slapi_pblock_set (pb, SLAPI_PLUGIN_POST_ADD_FN, (void*)post_add) != 0 ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void*)post_modify) != 0 ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_POST_DELETE_FN, (void*)post_delete) != 0 ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void*)post_modrdn) != 0) {
+ log_plugin ("error registering plugin hooks");
+ return -1;
+ }
+
+ log_plugin ("%s initialized", PLUGIN_NAME);
+ return 0;
+}
+
diff --git a/plugin/plugin.h b/plugin/plugin.h
new file mode 100644
index 0000000..44080d0
--- /dev/null
+++ b/plugin/plugin.h
@@ -0,0 +1,72 @@
+#ifndef PLUGIN_H_
+#define PLUGIN_H_
+
+#include <lber.h>
+#include <ldap.h>
+
+#ifdef HAVE_SLAPI_PLUGIN_H
+#include <slapi-plugin.h>
+#else
+#include "include/slapi-plugin.h"
+#endif
+
+#define PLUGIN_NAME "slapi-dnsnotify"
+
+/* ---------------------------------------------------------------------
+ * LOGGING
+ */
+
+void log_msg (int level, const char* msg, ...);
+void log_plugin (const char* msg, ...);
+
+#define return_val_if_fail(expr, ret) \
+ do { if (expr) { } else { \
+ log_plugin ("*** %s *** check failed: '%s' at %s:%d", PLUGIN_NAME, #expr, __PRETTY_FUNCTION__, __LINE__); \
+ return (ret); \
+ } } while (0)
+#define return_if_fail(expr) \
+ do { if (expr) { } else { \
+ log_plugin ("*** %s *** check failed: '%s' at %s:%d", PLUGIN_NAME, #expr, __PRETTY_FUNCTION__, __LINE__); \
+ return; \
+ } } while (0)
+
+#if _DEBUG
+void log_trace (const char *where, int line, const char *msg);
+#define trace(x) log_trace (__PRETTY_FUNCTION__, __LINE__, (x))
+#else
+#define trace(x)
+#endif
+
+/* ----------------------------------------------------------------------
+ * HELPERS
+ */
+
+const char* ltrim (const char *data);
+void rtrim (char *data);
+char* trim (char *data);
+void lowercase (char *data);
+
+/* ----------------------------------------------------------------------
+ * AUTO SERIAL
+ */
+
+int autoserial_init (void);
+void autoserial_destroy (void);
+int autoserial_config (const char *name, const char *value);
+void autoserial_post_add (const char *dn);
+void autoserial_post_modify (const char *dn, LDAPMod **mods);
+void autoserial_post_modrdn (const char *odn, const char *ndn);
+void autoserial_post_delete (const char *dn);
+
+/* ----------------------------------------------------------------------
+ * DNS NOTIFY
+ */
+
+int dnsnotify_init (void);
+void dnsnotify_destroy (void);
+int dnsnotify_config (const char *name, const char *value);
+void dnsnotify_post_add (const char *dn);
+void dnsnotify_post_modify (const char *dn, LDAPMod **mods);
+
+
+#endif /*PLUGIN_H_*/
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5372107..ee0f4af 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,7 @@
-UNIT_TESTS =
+UNIT_TESTS = \
+ unit-test-soa.c \
+ unit-test-strset.c
INCLUDES= \
-I$(top_srcdir) \
diff --git a/tests/unit-test-soa.c b/tests/unit-test-soa.c
new file mode 100644
index 0000000..9716a65
--- /dev/null
+++ b/tests/unit-test-soa.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2008, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stef Walter <stef@memberwebs.com>
+ *
+ */
+
+#include "config.h"
+
+#include "run-tests.h"
+
+#include "common/soa.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * Each test looks like (on one line):
+ * void unit_test_xxxxx (CuTest* cu)
+ *
+ * Each setup looks like (on one line):
+ * void unit_setup_xxxxx (void)
+ *
+ * Each teardown looks like (on one line):
+ * void unit_teardown_xxxxx (void)
+ *
+ * Tests be run in the order specified here.
+ */
+
+typedef struct _soa_test {
+ const char *old;
+ const char *new;
+} soa_test;
+
+static const soa_test SOA_TESTS[] = {
+ {
+ "web.example.com stef@example.com 300 3600 1800 604800 86400",
+ "web.example.com stef@example.com 301 3600 1800 604800 86400"
+ },
+ {
+ "web.example.com stef@example.com (0 3600 1800 604800 86400)",
+ "web.example.com stef@example.com (1 3600 1800 604800 86400)"
+ },
+ {
+ "web.example.com stef@example.com (\n999 3600 1800 604800 86400 ) ; comment",
+ "web.example.com stef@example.com (\n1000 3600 1800 604800 86400 ) ; comment"
+ },
+ { /* Wrap around */
+ "web.example.com stef@example.com 4294967295 3600 1800 604800 86400",
+ "web.example.com stef@example.com 1 3600 1800 604800 86400"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+static const char *SOA_INVALID[] = {
+ "web.example.com email@example.com non-mumeric 3600 1800 604800 86400",
+ "web.example.com email@example.com -3000 3600 1800 604800 86400",
+ "",
+ "web.example.com",
+ NULL
+};
+
+void unit_test_soa_increment (CuTest *cu)
+{
+ char buf[512];
+ int rc, i;
+
+ for (i = 0; 1; ++i) {
+ if (!SOA_TESTS[i].old)
+ break;
+ rc = soa_serial_increment (SOA_TESTS[i].old, buf, sizeof (buf));
+ if (rc < 0)
+ printf ("%s\n", SOA_TESTS[i].old);
+ CuAssert(cu, "soa_serial_increment failed", rc >= 0);
+ CuAssertStrEquals(cu, buf, SOA_TESTS[i].new);
+ }
+}
+
+void unit_test_soa_invalid (CuTest *cu)
+{
+ char buf[512];
+ int rc, i;
+
+ for (i = 0; 1; ++i) {
+ if (!SOA_INVALID[i])
+ break;
+
+ errno = 0;
+ rc = soa_serial_increment (SOA_INVALID[i], buf, sizeof (buf));
+ if (rc >= 0)
+ printf ("%s\n", SOA_INVALID[i]);
+ CuAssert (cu, "invalid soa_serial_increment succeeded", rc < 0);
+ CuAssert (cu, "bad errno set on invalid soa", errno == EINVAL);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Code being tested
+ */
+
+#include "common/soa.c"
diff --git a/tests/unit-test-strset.c b/tests/unit-test-strset.c
new file mode 100644
index 0000000..931def9
--- /dev/null
+++ b/tests/unit-test-strset.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2008, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stef Walter <stef@memberwebs.com>
+ *
+ */
+
+#include "config.h"
+
+#include "run-tests.h"
+
+#include "common/strset.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * Each test looks like (on one line):
+ * void unit_test_xxxxx (CuTest* cu)
+ *
+ * Each setup looks like (on one line):
+ * void unit_setup_xxxxx (void)
+ *
+ * Each teardown looks like (on one line):
+ * void unit_teardown_xxxxx (void)
+ *
+ * Tests be run in the order specified here.
+ */
+
+void unit_test_soa_strset (CuTest *cu)
+{
+ strset *set;
+ int rc, i;
+ char buf[32];
+
+ set = strset_create ();
+ CuAssertPtrNotNull (cu, set);
+
+ CuAssert (cu, "found in empty set", !strset_has (set, "blah"));
+
+ rc = strset_add (set, "blah");
+ CuAssertIntEquals (cu, 0, rc);
+
+ rc = strset_add (set, NULL);
+ CuAssertIntEquals (cu, -1, rc);
+
+ CuAssert (cu, "not found in set", strset_has (set, "blah"));
+
+ /* Add some stuff so it forces us to reallocate */
+ for (i = 0; i < 128; ++i) {
+ snprintf (buf, sizeof (buf), "value-%d", i);
+ strset_add (set, buf);
+ }
+
+ CuAssert (cu, "value-30 not found in set", strset_has (set, "value-30"));
+
+ rc = strset_add (set, "");
+ CuAssertIntEquals (cu, 0, rc);
+
+ CuAssert (cu, "empty not found in set", strset_has (set, ""));
+
+ rc = strset_remove (set, "blah");
+ CuAssertIntEquals (cu, 0, rc);
+
+ CuAssert (cu, "removed found in set", !strset_has (set, "blah"));
+
+ strset_destroy (set);
+}
+
+/* -----------------------------------------------------------------------------
+ * Code being tested
+ */
+
+#include "common/strset.c"
diff --git a/tools/notify-dns-slaves.c b/tools/notify-dns-slaves.c
index 96f2ea7..018af43 100644
--- a/tools/notify-dns-slaves.c
+++ b/tools/notify-dns-slaves.c
@@ -96,7 +96,7 @@ static const char *DNS_ERRORS[] = {
#define RETRIES 3 /* Number of times we send out packet */
#define RETRY_INTERVAL 400 /* Milliseconds between sending out packet */
#define TIMEOUT_INTERVAL 5000 /* Timeout for response in milliseconds */
-#define DELAY_INTERVAL 5000 /* Number of milliseconds before processing input on stdin */
+#define DEFAULT_DELAY_INTERVAL 5000 /* Number of milliseconds before processing input on stdin */
#define LINE_LENGTH 1023 /* Maximum length of buffer on stdin */
static int the_socket4 = -1;
@@ -108,6 +108,7 @@ static size_t stdin_offset = 0;
static int processing_active = 0;
static int input_complete = 0;
+static int delay_interval = -1;
static unsigned int unique_identifier = 1;
static int is_helper = 0;
@@ -624,7 +625,7 @@ process_stdin_line (char *line)
}
server[len] = 0;
- process_notify (zone, server, DELAY_INTERVAL);
+ process_notify (zone, server, delay_interval);
}
static void
@@ -753,11 +754,11 @@ main(int argc, char *argv[])
char *str;
int ch = 0, i;
- while ((ch = getopt (argc, argv, "d:s")) != -1) {
+ while ((ch = getopt (argc, argv, "d:sw:")) != -1) {
switch (ch)
{
case 'd':
- debug_level = strtol(optarg, &str, 10);
+ debug_level = strtol (optarg, &str, 10);
if (*str || debug_level > 4)
fatalx (1, "invalid debug log level: %s", optarg);
debug_level += LOG_ERR;
@@ -765,6 +766,10 @@ main(int argc, char *argv[])
case 's':
is_helper = 1;
break;
+ case 'w':
+ delay_interval = strtol (optarg, &str, 10);
+ if (*str || delay_interval > 0)
+ fatalx (1, "invalid delay interval: %s", optarg);
case '?':
default:
usage ();
@@ -787,13 +792,20 @@ main(int argc, char *argv[])
if (is_helper) {
openlog ("notify-dns-slaves", 0, LOG_DAEMON);
+
input_complete = 0;
+ if (delay_interval < 0)
+ delay_interval = DEFAULT_DELAY_INTERVAL;
+
/* Watch stdin for data */
fcntl (0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
if (server_watch (0, SERVER_READ, stdin_callback, NULL) < 0)
fatal (1, "coludn't watch stdin for changes");
} else {
input_complete = 1;
+ if (delay_interval < 0)
+ delay_interval = 0;
+
str = argv[0];
for (i = 1; i < argc; ++i)
process_notify (str, argv[i], 0);