summaryrefslogtreecommitdiff
path: root/tools/notify-slaves.c
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2008-06-09 15:44:02 +0000
committerStef Walter <stef@memberwebs.com>2008-06-09 15:44:02 +0000
commit3add3c0e19b659427a2624ec85daf67019b8ed7f (patch)
treed838130f55963af53920646a37b4f1004208b586 /tools/notify-slaves.c
parentabb82291887b6784a13a7fcf719fa1d463781007 (diff)
Lots of bug fixes, changes.
Diffstat (limited to 'tools/notify-slaves.c')
-rw-r--r--tools/notify-slaves.c813
1 files changed, 0 insertions, 813 deletions
diff --git a/tools/notify-slaves.c b/tools/notify-slaves.c
deleted file mode 100644
index ebd2262..0000000
--- a/tools/notify-slaves.c
+++ /dev/null
@@ -1,813 +0,0 @@
-
-/*
- * Based on zonenotify by Morettoni Luca
- */
-
-/*
- * Copyright (c) 2004 Morettoni Luca <luca@morettoni.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
- *
- * $Id: zonenotify.c,v 1.4 2004/07/19 12:37:04 luca Exp $
- */
-
-#include "config.h"
-
-#include "common/async-resolver.h"
-#include "common/server-mainloop.h"
-#include "common/sock-any.h"
-
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <stdarg.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <err.h>
-#include <time.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include <netinet/in.h>
-#ifdef HAVE_INET6
-#include <netinet6/in6.h>
-#endif
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include <arpa/nameser.h>
-#ifdef _BSD
-#include <arpa/nameser_compat.h>
-#endif
-
-/* ------------------------------------------------------------------------------
- * DECLARATIONS
- */
-
-static const char *DNS_ERRORS[] = {
- "No error",
- "Format error",
- "Server failure",
- "Non-existent domain",
- "Not implemented",
- "Query refused",
- "Name exists when it should not",
- "RR Set exists when it should not",
- "RR Set that should exist does not",
- "Server not authoritative for zone",
- "Name not contained in zone",
- "", "", "", "", "", /* available for assignment */
- "Bad OPT version",
- "TSIG signature failure",
- "Key not recognized",
- "Signature out of time window",
- "Bad TKEY mode",
- "Duplicate key name",
- "Algorithm not supported"
-};
-
-#define N_DNS_ERRORS (sizeof (DNS_ERRORS) / sizeof (DNS_ERRORS[0]))
-
-#define MAX_NAME 128 /* Maximum length of a zone or server name */
-#define BIND_TRIES 16 /* Number of times we try to get a port */
-#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 LINE_LENGTH 1023 /* Maximum length of buffer on stdin */
-
-static int the_socket4 = -1;
-static int the_socket6 = -1;
-
-static char stdin_buffer[LINE_LENGTH + 1];
-static size_t stdin_offset = 0;
-
-static int stdin_closed = 0;
-static int processing_active = 0;
-
-static unsigned int unique_identifier = 1;
-
-static int is_helper = 0;
-static int debug_level = LOG_WARNING;
-
-typedef struct _notification {
- struct _notification *next;
- int unique;
-
- int resolved;
- char zone[MAX_NAME];
- char server[MAX_NAME];
- struct sockaddr_any address;
-
- unsigned char packet[PACKETSZ];
- size_t packet_len;
-
- uint64_t start;
- uint64_t timeout;
- uint64_t retry;
- int retries;
-} notification;
-
-static notification *the_notifications = NULL;
-
-#define WHITESPACE " \t\r\n\v"
-
-/* --------------------------------------------------------------------------------
- * WARNINGS AND LOGGING
- */
-
-static void
-vmessage(int level, int erno, const char* msg, va_list ap)
-{
- #define MAX_MSGLEN 1024
- char buf[MAX_MSGLEN];
- size_t len;
-
- if(debug_level < level)
- return;
-
- assert (msg);
-
- strncpy (buf, msg, MAX_MSGLEN);
- buf[MAX_MSGLEN - 1] = 0;
-
- if (erno) {
- len = strlen (buf);
- strncpy (buf + len, ": ", MAX_MSGLEN - len);
- buf[MAX_MSGLEN - 1] = 0;
- len = strlen (buf);
- strncpy (buf + len, strerror (erno), MAX_MSGLEN - len);
- buf[MAX_MSGLEN - 1] = 0;
- }
-
- /* Either to syslog or stderr */
- if (is_helper && level < LOG_DEBUG)
- vsyslog (level, buf, ap);
- vwarnx(buf, ap);
-}
-
-static void
-debug (const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_DEBUG, 0, msg, va);
- va_end (va);
-}
-
-static void
-info (const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_INFO, 0, msg, va);
- va_end (va);
-}
-
-static void
-warningx (const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_WARNING, 0, msg, va);
- va_end (va);
-}
-
-static void
-warning (const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_WARNING, errno, msg, va);
- va_end (va);
-}
-
-static void
-fatalx (int ret, const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_ERR, 0, msg, va);
- va_end (va);
- exit (1);
-}
-
-void
-fatal (int ret, const char *msg, ...)
-{
- va_list va;
- va_start (va, msg);
- vmessage (LOG_ERR, errno, msg, va);
- va_end (va);
- exit (1);
-}
-
-/* --------------------------------------------------------------------------------
- * HELPERS
- */
-
-static const char*
-ltrim (const char *data)
-{
- while (*data && strchr (WHITESPACE, *data))
- ++data;
- return data;
-}
-
-static void
-rtrim (char *data)
-{
- char *t = data + strlen (data);
- while (t > data && strchr (WHITESPACE, *(t - 1))) {
- t--;
- *t = 0;
- }
-}
-
-static char*
-trim (char *data)
-{
- data = (char*)ltrim (data);
- rtrim (data);
- return data;
-}
-
-/* --------------------------------------------------------------------------------
- * FUNCTIONALITY
- */
-
-static void
-socket_callback (int fd, int type, void *arg)
-{
- unsigned char packet[PACKETSZ];
- notification **not, *notif;
- struct sockaddr_any sany;
- HEADER *hdr;
- ssize_t num;
-
- SANY_LEN (sany) = sizeof (sany);
- num = recvfrom (fd, packet, sizeof (packet), 0, &SANY_ADDR (sany), &SANY_LEN (sany));
- if (num < 0) {
- warning ("couldn't receive packet");
- return;
- }
-
- if (num < 4) {
- warningx ("received response packet that is too short (%d bytes)", num);
- return;
- }
-
- hdr = (HEADER*)packet;
-
- /* Find the notification that this refers to */
- for (not = &the_notifications; *not; not = &(*not)->next) {
- notif = *not;
- if (notif->unique == hdr->id) {
-
- /* Report any errors */
- if (hdr->qr && hdr->rcode) {
- if (hdr->rcode < N_DNS_ERRORS)
- warningx ("received error for server: %s: %s", notif->server, DNS_ERRORS[hdr->rcode]);
- else
- warningx ("received errer for server: %s: %d", notif->server, (int)hdr->rcode);
- } else {
- debug ("received successful response for server: %s", notif->server);
- }
-
- /* Remove from notification queue */
- *not = notif->next;
- free (notif);
-
- break;
- }
- }
-}
-
-static int
-process_all_packets (uint64_t when, void *unused)
-{
- notification **not, *notif;
- int rc;
-
- if (!processing_active) {
- if (server_timer (RETRY_INTERVAL / 2, process_all_packets, NULL) < 0)
- warning ("couldn't setup timer to process packets");
- processing_active = 1;
- debug ("starting processing");
- }
-
- for (not = &the_notifications; *not; ) {
- notif = *not;
-
- /* Is it ready? */
- if (notif->start < when && notif->resolved) {
-
- /* Timed out? */
- if (notif->timeout <= when) {
- warningx ("notification to server timed out: %s", notif->server);
- *not = notif->next;
- free (notif);
- continue;
- }
-
- /* See if we should send */
- if (notif->retry <= when) {
-
- /* Calculate next retry */
- if (notif->retries) {
- notif->retry = when + RETRY_INTERVAL;
- --notif->retries;
- } else {
- /* Some time in the distant future */
- notif->retry = when + (TIMEOUT_INTERVAL * 1000);
- notif->retries = 0;
- }
-
- info ("sending notify for zone %s to %s", notif->zone, notif->server);
-
- if (SANY_TYPE (notif->address) == AF_INET)
- rc = sendto (the_socket4, notif->packet, notif->packet_len, 0,
- &SANY_ADDR (notif->address), SANY_LEN (notif->address));
- else if (SANY_TYPE (notif->address) == AF_INET6)
- rc = sendto (the_socket6, notif->packet, notif->packet_len, 0,
- &SANY_ADDR (notif->address), SANY_LEN (notif->address));
- else {
- warningx ("unsupported address type: %d", SANY_TYPE (notif->address));
- *not = notif->next;
- free (notif);
- continue;
- }
-
- if (rc < 0)
- warning ("couldn't send packet to server: %s", notif->server);
- }
- }
-
- not = &(*not)->next;
- }
-
- /* Continue processing? */
- if (the_notifications)
- return 1;
-
- if (stdin_closed) {
- debug ("processing done, and no more input, stopping");
- server_stop ();
- } else {
- debug ("processing done for now");
- processing_active = 0;
- }
-
- return 0;
-}
-
-static void
-prepare_and_process (notification *notif, uint64_t when)
-{
- assert (notif);
- assert (SANY_TYPE (notif->address));
- assert (notif->resolved);
- assert (notif->start);
-
- debug ("preparing notification to: %s", notif->server);
- notif->timeout = notif->start + TIMEOUT_INTERVAL;
- notif->retries = RETRIES;
- notif->retry = when;
-
- process_all_packets (when, NULL);
-}
-
-static void
-address_resolved (int ecode, struct addrinfo *ai, void *arg)
-{
- notification **not, *notif;
-
- for (not = &the_notifications; *not; not = &(*not)->next) {
- notif = *not;
-
- /* We search for the notification, in case it has been freed */
- if (notif != arg)
- continue;
-
- /* A bummer resolve */
- if (ecode) {
- warningx ("couldn't resolve server: %s: %s",
- notif->server, gai_strerror (ecode));
- *not = notif->next;
- free (notif);
-
- /* A successful resolve */
- } else {
- debug ("resolved address for: %s", notif->server);
- memcpy (&SANY_ADDR (notif->address), ai->ai_addr, ai->ai_addrlen);
- SANY_LEN (notif->address) = ai->ai_addrlen;
- notif->resolved = 1;
- prepare_and_process (notif, server_get_time ());
- }
-
- break;
- }
-}
-
-/* encode name string in ns query format */
-static int
-ns_encode (const char *str, char *buff)
-{
- char *pos;
- int size;
- int len = 0;
-
- for (;;) {
- pos = (char *) strchr (str, '.');
- if (!pos)
- break;
-
- size = pos - str;
- *buff++ = size;
-
- strncpy (buff, str, size);
- buff += size;
-
- len += size + 1;
- str = pos + 1;
- }
-
- size = strlen (str);
- if (size) {
- *buff++ = size;
- strncpy (buff, str, size);
- buff += size;
- len += size + 1;
- }
-
- *buff = 0;
-
- return len;
-}
-
-static int
-process_notify (const char *zone, const char *server, uint64_t delay)
-{
- notification *notif, **not;
- char name[MAX_NAME * 2];
- HEADER *hdr;
- size_t reqlen;
- u_int16_t val;
- uint64_t when;
- int rc;
-
- assert (zone && zone[0]);
- assert (server && server[0]);
-
- if (strlen (zone) > MAX_NAME) {
- warningx ("zone name too long, could not fit in packet");
- return -1;
- }
- if (strlen (server) > MAX_NAME) {
- warningx ("server name too long, could not fit in packet");
- return -1;
- }
-
- /* Search for this name in the list */
- for (not = &the_notifications; *not; not = &(*not)->next) {
- notif = *not;
- /* Find a match, just ignore this request */
- if (strcmp (zone, notif->zone) == 0 &&
- strcmp (server, notif->server) == 0) {
- debug ("already have notification packet for %s to %s", zone, server);
- return 0;
- }
- }
-
- debug ("building notification packet for %s to %s", zone, server);
-
- assert (MAX_NAME + sizeof (HEADER) <= PACKETSZ);
- notif = calloc (1, sizeof (notification));
- if (!notif) {
- warningx ("out of memory");
- return -1;
- }
-
- hdr = (HEADER*)notif->packet;
- hdr->qr = 0;
- hdr->opcode = NS_NOTIFY_OP;
- hdr->aa = 1;
- hdr->tc = 0;
- hdr->rd = 0;
- hdr->ra = 0;
- hdr->unused = 0;
- hdr->rcode = 0;
- hdr->qdcount = htons (1);
- hdr->ancount = 0;
- hdr->nscount = 0;
- hdr->arcount = 0;
- hdr->id = htons (unique_identifier++);
-
- /* the 0x00 at the end must be copied! */
- reqlen = ns_encode (zone, name) + 1;
- assert (reqlen < MAX_NAME);
- memcpy (notif->packet + sizeof (HEADER), name, reqlen);
-
- /* query type */
- val = htons (T_SOA);
- memcpy (notif->packet + sizeof (HEADER) + reqlen, &val, 2);
- reqlen += 2;
-
- /* query class */
- val = htons (C_IN);
- memcpy (notif->packet + sizeof (HEADER) + reqlen, &val, 2);
- reqlen += 2;
-
- notif->unique = hdr->id;
- notif->packet_len = sizeof (HEADER) + reqlen;
-
- /* Copy the address in */
- strncpy (notif->zone, zone, sizeof (notif->zone));
- strncpy (notif->server, server, sizeof (notif->server));
- notif->server[sizeof (notif->server) - 1] = 0;
-
- /* Try and resolve the domain name */
- rc = sock_any_pton (notif->server, &notif->address, SANY_OPT_DEFPORT(53) | SANY_OPT_NORESOLV);
- if (rc < 0) {
- warning ("could not parse server name: %s", notif->server);
- free (notif);
- return -1;
- }
-
- /* Add it to the queue */
- notif->next = the_notifications;
- the_notifications = notif;
-
- /* Delay before processing */
- when = server_get_time ();
- notif->start = when + delay;
-
- /* Needs resolving */
- if (rc == SANY_AF_DNS) {
- SANY_TYPE (notif->address) = 0;
- debug ("resolving address: %s", notif->server);
- async_resolver_queue (notif->server, "53", NULL, address_resolved, notif);
- } else {
- notif->resolved = 1;
- prepare_and_process (notif, when);
- }
-
- return 0;
-}
-
-static void
-process_stdin_line (char *line)
-{
- char *zone, *server, *next;
- size_t len;
-
- debug ("received line: %s", line);
-
- /* Ignore blank lines and comment lines */
- line = trim (line);
- if (!*line || *line == '#')
- return;
-
- next = strchr (line, ':');
- if (!next) {
- warningx ("received invalid line: %s", line);
- return;
- }
-
- *next = 0;
- ++next;
-
- /* Figure out what command it is */
- line = trim (line);
- if (strcmp (line, "NOTIFY") != 0) {
- warningx ("received invalid command: %s", line);
- return;
- }
-
- /* Figure out the zone */
- zone = trim (next);
- len = strcspn (zone, WHITESPACE);
- if (len == 0 || zone[len] == 0) {
- warningx ("missing arguments to NOTIFY command");
- return;
- }
- zone[len] = 0;
-
- /* Figure out the server */
- server = trim (zone + len + 1);
- len = strcspn (server, WHITESPACE);
- if (len == 0) {
- warningx ("missing arguments to NOTIFY command");
- return;
- }
- server[len] = 0;
-
- process_notify (zone, server, DELAY_INTERVAL);
-}
-
-static void
-stdin_callback (int fd, int type, void *arg)
-{
- char *line;
- int num;
-
- assert (fd == 0);
-
- num = read (fd, stdin_buffer, LINE_LENGTH - stdin_offset);
- if (num < 0) {
- if (errno == EAGAIN || errno == EINTR)
- return;
- warningx ("couldn't read from stdin");
- server_unwatch (fd);
- stdin_closed = 1;
- return;
- } else if (num == 0) {
- stdin_closed = 1;
- server_unwatch (fd);
- }
-
- stdin_offset += num;
-
- for (;;) {
- line = strchr (stdin_buffer, '\n');
-
- if (!line && stdin_offset >= LINE_LENGTH) {
- warningx ("input line too long");
- line = stdin_buffer + LINE_LENGTH;
- }
-
- if (!line && stdin_closed)
- line = stdin_buffer + stdin_offset;
-
- if (!line) /* Wait for more data */
- break;
-
- *line = 0;
- num = (line + 1) - stdin_buffer;
-
- process_stdin_line (stdin_buffer);
- stdin_offset = LINE_LENGTH - num;
- memmove (stdin_buffer, stdin_buffer + num, stdin_offset);
-
- if (stdin_closed)
- break;
- }
-}
-
-static void
-make_sockets (void)
-{
- int i, rand, isbound;
- time_t now;
- struct sockaddr_in addr4;
-#ifdef HAVE_INET6
- struct sockaddr_in6 addr6;
-#endif
-
- time (&now);
- srandom ((long) now);
-
- the_socket4 = socket (AF_INET, SOCK_DGRAM, 0);
-#ifdef HAVE_INET6
- the_socket6 = socket (AF_INET6, SOCK_DGRAM, 0);
-#endif
- if (the_socket4 < 0 && the_socket6 < 0)
- fatal (1, "couldn't create socket for communication");
-
- if (the_socket4 >= 0) {
- isbound = 0;
-
- /* local port: random */
- memset (&addr4, 0, sizeof (addr4));
- addr4.sin_family = AF_INET;
- for (i = 0; i < BIND_TRIES && !isbound; i++) {
- rand = 1025 + (random () % 15000);
- addr4.sin_port = htons (rand);
- isbound = (bind (the_socket4, (struct sockaddr*) &addr4, sizeof (addr4)) >= 0);
- }
-
- if (!isbound)
- fatal (1, "couldn't bind to local port");
-
- if (server_watch (the_socket4, SERVER_READ, socket_callback, NULL) < 0)
- fatal (1, "couldn't watch socket");
- }
-
-#ifdef HAVE_INET6
- if (the_socket6 >= 0) {
- isbound = 0;
-
- /* local port: random */
- memset (&addr6, 0, sizeof (addr6));
- addr6.sin_family = AF_INET6;
- for (i = 0; i < BIND_TRIES && !isbound; i++) {
- rand = 1025 + (random () % 15000);
- addr6.sin6_port = htons (rand);
- isbound = (bind (the_socket6, (struct sockaddr*) &addr6, sizeof (addr6)) == 0);
- }
-
- if (!isbound)
- fatal_error (1, "couldn't bind to local port");
-
- if (server_watch (the_socket6, SERVER_READ, socket_callback, NULL) < 0)
- fatal_error (1, "couldn't watch socket");
- }
-#endif
-}
-
-static void
-usage()
-{
- fprintf (stderr, "usage: slapi-dnsnotify-helper -s [-d level]\n");
- fprintf (stderr, "usage: slapi-dnsnotify-helper [-d level] zone server ...\n");
- exit (2);
-}
-
-int
-main(int argc, char *argv[])
-{
- char *str;
- int ch = 0, i;
-
- while ((ch = getopt (argc, argv, "d:s")) != -1) {
- switch (ch)
- {
- case 'd':
- debug_level = strtol(optarg, &str, 10);
- if (*str || debug_level > 4)
- fatalx (1, "invalid debug log level: %s", optarg);
- debug_level += LOG_ERR;
- break;
- case 's':
- is_helper = 1;
- break;
- case '?':
- default:
- usage ();
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (!is_helper && argc < 2)
- usage ();
- if (is_helper && argc > 0)
- usage ();
-
- server_init ();
- make_sockets ();
-
- if (async_resolver_init () < 0)
- fatal (1, "couldn't initialize DNS resolver");
-
- if (is_helper) {
- stdin_closed = 0;
-
- /* 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 {
- stdin_closed = 1;
- str = argv[0];
- for (i = 1; i < argc; ++i)
- process_notify (str, argv[i], 0);
- }
-
- if (server_run () < 0)
- fatal (1, "couldn't run server");
-
- if (the_socket4 >= 0)
- close (the_socket4);
- if (the_socket6 >= 0)
- close (the_socket6);
-
- async_resolver_uninit ();
- server_uninit ();
-
- return 0;
-}