diff options
Diffstat (limited to 'tools/notify-slaves.c')
-rw-r--r-- | tools/notify-slaves.c | 813 |
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, ¬if->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; -} |