From c00cf65b1c29b836e0c95c28b22279157585a2e7 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 10 Jun 2008 02:47:43 +0000 Subject: Add support for resolving name server to multiple ip addresses --- common/sock-any.c | 609 ++++++++++++++++++++++------------------------ common/sock-any.h | 41 ++-- tools/notify-dns-slaves.c | 75 ++++-- 3 files changed, 375 insertions(+), 350 deletions(-) diff --git a/common/sock-any.c b/common/sock-any.c index 4476fde..4613931 100644 --- a/common/sock-any.c +++ b/common/sock-any.c @@ -36,6 +36,8 @@ * */ +#include "config.h" + #include #include @@ -50,357 +52,338 @@ #include -#define LOCALHOST_ADDR 0x7F000001 +#define LOCALHOST_ADDR 0x7F000001 -int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts) +int +sock_any_pton(const char* addr, struct sockaddr_any* any, int opts) { - size_t l; - char buf[256]; - char* t; - char* t2; - int defport = (opts & 0xFFFF); + size_t l; + char buf[256]; + char* t; + char* t2; + int defport = (opts & 0xFFFF); - memset(any, 0, sizeof(*any)); + memset(any, 0, sizeof(*any)); - /* Just a port? */ - do - { - #define PORT_CHARS "0123456789" - #define PORT_MIN 1 - #define PORT_MAX 5 + /* Just a port? */ + do { + #define PORT_CHARS "0123456789" + #define PORT_MIN 1 + #define PORT_MAX 5 - int port = 0; + int port = 0; - l = strspn(addr, PORT_CHARS); - if(l < PORT_MIN || l > PORT_MAX || addr[l] != 0) - break; + l = strspn (addr, PORT_CHARS); + if (l < PORT_MIN || l > PORT_MAX || addr[l] != 0) + break; - port = strtol(addr, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; + port = strtol (addr, &t2, 10); + if (*t2 || port <= 0 || port >= 65536) + break; - any->s.in.sin_port = htons(port); + any->s.in.sin_port = htons (port); - /* Fill in the type based on defaults */ + /* Fill in the type based on defaults */ #ifdef HAVE_INET6 - if(opts & SANY_OPT_DEFINET6) - any->s.in.sin_family = AF_INET6; - else + if (opts & SANY_OPT_DEFINET6) + any->s.in.sin_family = AF_INET6; + else #endif - any->s.in.sin_family = AF_INET; + any->s.in.sin_family = AF_INET; - /* Fill in the address based on defaults */ - if(opts & SANY_OPT_DEFLOCAL) - { + /* Fill in the address based on defaults */ + if (opts & SANY_OPT_DEFLOCAL) { #ifdef HAVE_INET6 - if(opts & SANY_OPT_DEFINET6) - memcpy(&(any->s.in.sin6_addr), &in6addr_loopback, sizeof(struct in6_addr)); - else + if(opts & SANY_OPT_DEFINET6) + memcpy(&(any->s.in.sin6_addr), &in6addr_loopback, sizeof(struct in6_addr)); + else + any->s.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); #endif - any->s.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - } - - /* - * Note the 'any' option is the default since we zero out - * the entire structure above. - */ - - any->namelen = sizeof(any->s.in); - return AF_INET; - } - while(0); - - /* Look and see if we can parse an ipv4 address */ - do - { - #define IPV4_PORT_CHARS - #define IPV4_CHARS "0123456789." - #define IPV4_MIN 3 - #define IPV4_MAX 21 - - int port = 0; - t = NULL; - - l = strlen(addr); - if(l < IPV4_MIN || l > IPV4_MAX) - break; - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - l = strspn(buf, IPV4_CHARS); - if(l < IPV4_MIN) - break; - - /* Either end of string or port */ - if(buf[l] != 0 && buf[l] != ':') - break; - - /* Get the port out */ - if(buf[l] != 0) - { - t = buf + l + 1; - buf[l] = 0; - } - - if(t) - { - port = strtol(t, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; - } - - any->s.in.sin_family = AF_INET; - any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port)); - - if(inet_pton(AF_INET, buf, &(any->s.in.sin_addr)) <= 0) - break; - - any->namelen = sizeof(any->s.in); - return AF_INET; - } - while(0); + } + + /* + * Note the 'any' option is the default since we zero out + * the entire structure above. + */ + + any->namelen = sizeof (any->s.in); + return AF_INET; + } + while (0); + + /* Look and see if we can parse an ipv4 address */ + do + { + #define IPV4_CHARS "0123456789." + #define IPV4_MIN 3 + #define IPV4_MAX 21 + + int port = 0; + t = NULL; + + l = strlen (addr); + if(l < IPV4_MIN || l > IPV4_MAX) + break; + + strcpy (buf, addr); + + /* Find the last set that contains just numbers */ + l = strspn (buf, IPV4_CHARS); + if(l < IPV4_MIN) + break; + + /* Either end of string or port */ + if (buf[l] != 0 && buf[l] != ':') + break; + + /* Get the port out */ + if (buf[l] != 0) { + t = buf + l + 1; + buf[l] = 0; + } + + if (t) { + port = strtol(t, &t2, 10); + if(*t2 || port <= 0 || port >= 65536) + break; + } + + any->s.in.sin_family = AF_INET; + any->s.in.sin_port = htons ((unsigned short)(port <= 0 ? defport : port)); + + if (inet_pton (AF_INET, buf, &(any->s.in.sin_addr)) <= 0) + break; + + any->namelen = sizeof(any->s.in); + return AF_INET; + } + while(0); #ifdef HAVE_INET6 - do - { - #define IPV6_CHARS "0123456789:" - #define IPV6_MIN 3 - #define IPV6_MAX 51 - - int port = -1; - t = NULL; - - l = strlen(addr); - if(l < IPV6_MIN || l > IPV6_MAX) - break; - - /* If it starts with a '[' then we can get port */ - if(buf[0] == '[') - { - port = 0; - addr++; - } - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - l = strspn(buf, IPV6_CHARS); - if(l < IPV6_MIN) - break; - - /* Either end of string or port */ - if(buf[l] != 0) - { - /* If had bracket, then needs to end with a bracket */ - if(port != 0 || buf[l] != ']') - break; - - /* Get the port out */ - t = buf + l + 1; - - if(*t = ':') - t++; - } - - if(t) - { - port = strtol(t, &t, 10); - if(*t || port <= 0 || port >= 65536) - break; - } - - any->s.in6.sin6_family = AF_INET6; - any->s.in6.sin6_port = htons((unsigned short)port <= 0 : defport : port); - - if(inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0) - break; - - any->namelen = sizeof(any->s.in6); - return AF_INET6; - } - while(0); + do + { + #define IPV6_CHARS "0123456789:" + #define IPV6_MIN 3 + #define IPV6_MAX 51 + + int port = -1; + t = NULL; + + l = strlen(addr); + if (l < IPV6_MIN || l > IPV6_MAX) + break; + + /* If it starts with a '[' then we can get port */ + if (buf[0] == '[') { + port = 0; + addr++; + } + + strcpy (buf, addr); + + /* Find the last set that contains just numbers */ + l = strspn(buf, IPV6_CHARS); + if (l < IPV6_MIN) + break; + + /* Either end of string or port */ + if (buf[l] != 0) { + /* If had bracket, then needs to end with a bracket */ + if (port != 0 || buf[l] != ']') + break; + + /* Get the port out */ + t = buf + l + 1; + + if(*t = ':') + t++; + } + + if (t) { + port = strtol (t, &t, 10); + if (*t || port <= 0 || port >= 65536) + break; + } + + any->s.in6.sin6_family = AF_INET6; + any->s.in6.sin6_port = htons ((unsigned short)port <= 0 : defport : port); + + if (inet_pton(AF_INET6, buf, &(any->s.in6.sin6_addr)) >= 0) + break; + + any->namelen = sizeof (any->s.in6); + return AF_INET6; + } while (0); #endif - /* A unix socket path */ - do - { - /* No colon and must have a path component */ - if(strchr(addr, ':') || !strchr(addr, '/')) - break; - - l = strlen(addr); - if(l >= sizeof(any->s.un.sun_path)) - break; - - any->s.un.sun_family = AF_UNIX; - strcpy(any->s.un.sun_path, addr); - - any->namelen = sizeof(any->s.un) - (sizeof(any->s.un.sun_path) - l); - return AF_UNIX; - } - while(0); - - /* A DNS name and a port? */ - do - { - struct addrinfo* res; - int port = 0; - int family = 0; - t = NULL; - - l = strlen(addr); - if(l >= 255 || !isalpha(addr[0])) - break; - - /* Some basic illegal character checks */ - if(strcspn(addr, " /\\") != l) - break; - - strcpy(buf, addr); - - /* Find the last set that contains just numbers */ - t = strchr(buf, ':'); - if(t) - { - *t = 0; - t++; - } - - if(t) - { - port = strtol(t, &t2, 10); - if(*t2 || port <= 0 || port >= 65536) - break; - } - - if(!(opts & SANY_OPT_NORESOLV)) - { - /* Try and resolve the domain name */ - if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res) - break; - - memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr)); - any->namelen = res->ai_addrlen; - family = any->s.a.sa_family; - freeaddrinfo(res); - } - else - { - family = SANY_AF_DNS; + /* A unix socket path */ + do { + /* No colon and must have a path component */ + if (strchr (addr, ':') || !strchr (addr, '/')) + break; + + l = strlen(addr); + if (l >= sizeof(any->s.un.sun_path)) + break; + + any->s.un.sun_family = AF_UNIX; + strcpy (any->s.un.sun_path, addr); + + any->namelen = sizeof (any->s.un) - (sizeof (any->s.un.sun_path) - l); + return AF_UNIX; + } while(0); + + /* A DNS name and a port? */ + do { + struct addrinfo* res; + int port = 0; + int family = 0; + t = NULL; + + l = strlen(addr); + if (l >= 255 || !isalpha(addr[0])) + break; + + /* Some basic illegal character checks */ + if (strcspn(addr, " /\\") != l) + break; + + strcpy (buf, addr); + + /* Find the last set that contains just numbers */ + t = strchr (buf, ':'); + if (t) { + *t = 0; + t++; + } + + if (t) { + port = strtol (t, &t2, 10); + if (*t2 || port <= 0 || port >= 65536) + break; + } + + if (!(opts & SANY_OPT_NORESOLV)) { + /* Try and resolve the domain name */ + if (getaddrinfo (buf, NULL, NULL, &res) != 0 || !res) + break; + + memcpy (&(any->s.a), res->ai_addr, sizeof (struct sockaddr)); + any->namelen = res->ai_addrlen; + family = any->s.a.sa_family; + freeaddrinfo (res); + } else { + family = SANY_AF_DNS; #ifdef HAVE_INET6 - if(opt & SANY_OPT_DEFINET6) - { - any->s.a.sa_family = AF_INET6; - any->namelen = sizeof(any->s.in6); - } - else + if (opt & SANY_OPT_DEFINET6) + { + any->s.a.sa_family = AF_INET6; + any->namelen = sizeof (any->s.in6); + } else #endif - { - any->s.a.sa_family = AF_INET; - any->namelen = sizeof(any->s.in); - } - } - - port = htons((unsigned short)(port <= 0 ? defport : port)); - - switch(any->s.a.sa_family) - { - case PF_INET: - any->s.in.sin_port = port; - break; + { + any->s.a.sa_family = AF_INET; + any->namelen = sizeof(any->s.in); + } + } + + port = htons ((unsigned short)(port <= 0 ? defport : port)); + + switch (any->s.a.sa_family) { + case PF_INET: + any->s.in.sin_port = port; + break; #ifdef HAVE_INET6 - case PF_INET6: - any->s.in6.sin6_port = port; - break; + case PF_INET6: + any->s.in6.sin6_port = port; + break; #endif - }; + }; - return family; - } - while(0); + return family; + } while (0); - return -1; + errno = EAFNOSUPPORT; + return -1; } -int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts) +int +sock_any_ntop (const struct sockaddr_any* any, char* addr, size_t addrlen, int opts) { - int len = 0; - int port = 0; - - switch(any->s.a.sa_family) - { - case AF_UNIX: - len = strlen(any->s.un.sun_path); - if(addrlen < len + 1) - { - errno = ENOSPC; - return -1; - } - - strcpy(addr, any->s.un.sun_path); - break; - - case AF_INET: - if(inet_ntop(any->s.a.sa_family, &(any->s.in.sin_addr), addr, addrlen) == NULL) - return -1; - port = ntohs(any->s.in.sin_port); - break; + int len = 0; + int port = 0; + + switch (any->s.a.sa_family) { + case AF_UNIX: + len = strlen (any->s.un.sun_path); + if (addrlen < len + 1) { + errno = ENOSPC; + return -1; + } + + strcpy (addr, any->s.un.sun_path); + break; + + case AF_INET: + if (inet_ntop (any->s.a.sa_family, &(any->s.in.sin_addr), addr, addrlen) == NULL) + return -1; + port = ntohs (any->s.in.sin_port); + break; #ifdef HAVE_INET6 - case AF_INET6: - if(inet_ntop(any->s.a.sa_family, &(any->s.in6.sin6_addr), addr, addrlen) == NULL) - return -1; - port = ntohs(any->s.in6.sin6_port); - break; + case AF_INET6: + if (inet_ntop (any->s.a.sa_family, &(any->s.in6.sin6_addr), addr, addrlen) == NULL) + return -1; + port = ntohs (any->s.in6.sin6_port); + break; #endif - default: - errno = EAFNOSUPPORT; - return -1; - } + default: + errno = EAFNOSUPPORT; + return -1; + } - if(!(opts & SANY_OPT_NOPORT) && port != 0) - { - strncat(addr, ":", addrlen); - addr[addrlen - 1] = 0; + if (!(opts & SANY_OPT_NOPORT) && port != 0) + { + strncat (addr, ":", addrlen); + addr[addrlen - 1] = 0; - len = strlen(addr); - addr += len; - addrlen -= len; + len = strlen (addr); + addr += len; + addrlen -= len; - snprintf(addr, addrlen, "%d", port); - } + snprintf (addr, addrlen, "%d", port); + } - return 0; + return 0; } -int sock_any_cmp(const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts) +int +sock_any_cmp (const struct sockaddr_any* a1, const struct sockaddr_any* a2, int opts) { - if(a1->s.a.sa_family != a2->s.a.sa_family) - return -1; - - switch(a1->s.a.sa_family) - { - case AF_UNIX: - return strcmp(a1->s.un.sun_path, a2->s.un.sun_path); - - case AF_INET: - if(memcmp(&(a1->s.in.sin_addr), &(a2->s.in.sin_addr), sizeof(a2->s.in.sin_addr)) != 0) - return -1; - if(!(opts && SANY_OPT_NOPORT) && a1->s.in.sin_port != a2->s.in.sin_port) - return -1; - return 0; + if (a1->s.a.sa_family != a2->s.a.sa_family) + return -1; + + switch (a1->s.a.sa_family) { + case AF_UNIX: + return strcmp (a1->s.un.sun_path, a2->s.un.sun_path); + + case AF_INET: + if (memcmp (&(a1->s.in.sin_addr), &(a2->s.in.sin_addr), sizeof(a2->s.in.sin_addr)) != 0) + return -1; + if (!(opts && SANY_OPT_NOPORT) && a1->s.in.sin_port != a2->s.in.sin_port) + return -1; + return 0; #ifdef HAVE_INET6 - case AF_INET6: - if(memcmp(&(a1->s.in6.sin6_addr), &(a2->s.in6.sin6_addr), sizeof(a2->s.in6.sin6_addr)) != 0) - return -1; - if(!(opts && SANY_OPT_NOPORT) && a1->s.in6.sin6_port != a2->s.in6.sin6_port) - return -1; - return 0; + case AF_INET6: + if(memcmp(&(a1->s.in6.sin6_addr), &(a2->s.in6.sin6_addr), sizeof(a2->s.in6.sin6_addr)) != 0) + return -1; + if(!(opts && SANY_OPT_NOPORT) && a1->s.in6.sin6_port != a2->s.in6.sin6_port) + return -1; + return 0; #endif - default: - errno = EAFNOSUPPORT; - return -1; - } + default: + errno = EAFNOSUPPORT; + return -1; + } } diff --git a/common/sock-any.h b/common/sock-any.h index e9b57ef..7b5da34 100644 --- a/common/sock-any.h +++ b/common/sock-any.h @@ -45,24 +45,23 @@ struct sockaddr_any { - union _sockaddr_any - { - /* The header */ - struct sockaddr a; - - /* The different types */ - struct sockaddr_un un; - struct sockaddr_in in; + union _sockaddr_any { + /* The header */ + struct sockaddr a; + + /* The different types */ + struct sockaddr_un un; + struct sockaddr_in in; #ifdef HAVE_INET6 - struct sockaddr_in6 in6; + struct sockaddr_in6 in6; #endif - } s; - size_t namelen; + } s; + size_t namelen; }; -#define SANY_ADDR(any) ((any).s.a) -#define SANY_LEN(any) ((any).namelen) -#define SANY_TYPE(any) ((any).s.a.sa_family) +#define SANY_ADDR(any) ((any).s.a) +#define SANY_LEN(any) ((any).namelen) +#define SANY_TYPE(any) ((any).s.a.sa_family) /* -------------------------------------------------------------------------- */ @@ -70,22 +69,22 @@ struct sockaddr_any int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts); /* The default port to fill in when no IP/IPv6 port specified */ -#define SANY_OPT_DEFPORT(p) (int)((p) & 0xFFFF) +#define SANY_OPT_DEFPORT(p) (int)((p) & 0xFFFF) /* When only port specified default to IPANY */ -#define SANY_OPT_DEFANY 0x00000000 +#define SANY_OPT_DEFANY 0x00000000 /* When only port specified default to LOCALHOST */ -#define SANY_OPT_DEFLOCAL 0x00100000 +#define SANY_OPT_DEFLOCAL 0x00100000 /* When only port specified default to IPv6 */ -#define SANY_OPT_DEFINET6 0x00200000 +#define SANY_OPT_DEFINET6 0x00200000 /* Don't resolve host name */ -#define SANY_OPT_NORESOLV 0x01000000 +#define SANY_OPT_NORESOLV 0x01000000 /* The family type returned when resolving is needed */ -#define SANY_AF_DNS 0x01000000 +#define SANY_AF_DNS 0x01000000 /* -------------------------------------------------------------------------- */ @@ -93,7 +92,7 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int opts); int sock_any_ntop(const struct sockaddr_any* any, char* addr, size_t addrlen, int opts); /* Don't print or compare the port */ -#define SANY_OPT_NOPORT 0x01000000 +#define SANY_OPT_NOPORT 0x01000000 /* -------------------------------------------------------------------------- */ diff --git a/tools/notify-dns-slaves.c b/tools/notify-dns-slaves.c index 3a7f342..956fdac 100644 --- a/tools/notify-dns-slaves.c +++ b/tools/notify-dns-slaves.c @@ -386,7 +386,7 @@ process_all_packets (uint64_t when, void *unused) } static void -prepare_and_process (notification *notif, uint64_t when) +complete_packet (notification *notif, uint64_t when) { assert (notif); assert (SANY_TYPE (notif->address)); @@ -397,39 +397,81 @@ prepare_and_process (notification *notif, uint64_t when) notif->timeout = notif->start + TIMEOUT_INTERVAL; notif->retries = RETRIES; notif->retry = when; +} + +static notification* +duplicate_notification (notification *orig) +{ + notification *notif; + HEADER *hdr; + + assert (orig); + + notif = calloc (1, sizeof (notification)); + if (!notif) { + warningx ("out of memory"); + return NULL; + } + + memcpy (notif, orig, sizeof (notification)); + hdr = (HEADER*)notif->packet; + notif->unique = hdr->id = htons (unique_identifier++); + + /* Add it to the queue */ + notif->next = the_notifications; + the_notifications = notif; - process_all_packets (when, NULL); + return notif; } static void address_resolved (int ecode, struct addrinfo *ai, void *arg) { - notification **not, *notif; + notification **not, *notif, *copy; + char hostname[256]; + uint64_t when; + int num = 0; 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); - break; + return; + } + + when = server_get_time (); /* 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; + while (ai) { + if (num == 0) + copy = notif; + else + copy = duplicate_notification (notif); + + if (copy) { + memcpy (&SANY_ADDR (copy->address), ai->ai_addr, ai->ai_addrlen); + SANY_LEN (copy->address) = ai->ai_addrlen; + copy->resolved = 1; + ++num; + + if (sock_any_ntop (©->address, hostname, sizeof (hostname), SANY_OPT_NOPORT) < 0) + strcpy (hostname, "[unknown]"); + debug ("resolved address for: %s", hostname); + + complete_packet (copy, when); + } + + /* The next resolved address */ + ai = ai->ai_next; } + + process_all_packets (when, NULL); + return; } } @@ -578,7 +620,8 @@ process_notify (const char *zone, const char *server, uint64_t delay) async_resolver_queue (notif->server, "53", &hints, address_resolved, notif); } else { notif->resolved = 1; - prepare_and_process (notif, when); + complete_packet (notif, when); + process_all_packets (when, NULL); } return 0; -- cgit v1.2.3