list-nsd at pwns.ms
2008-Aug-07 03:39 UTC
[nsd-users] [PATCH] Source address selection for nsd-notify, +relevant nsdc changes
nsd-notify, prior to this patch, lets the OS select the source address; unfortunately, the OS-selected address in many cases will differ from the address nsd listens on (in my configuration, I have a server with three IP addresses - recursive DNS, authorative DNS, host IP address; NSD just listens on the authorative one). This patch adds a '-s' options to nsd-notify allowing selection of source address; multiple -s options may be given, nsd-notify will try to bind to them in order. It supports setting both the v4 and v6 source address. The manpage is updated accordingly. nsdc is modified to pass nsd-notify an IP address included in the config file, except for 127.0.0.1 and ::1. Index: nsdc.sh.in ==================================================================--- nsdc.sh.in (revision 2759) +++ nsdc.sh.in (working copy) @@ -221,7 +221,8 @@ port="-p "`echo ${ip_spec} | sed -e 's/[^@]*@\([0-9]*\)/\1/'` ipaddr=`echo ${ip_spec} | sed -e 's/\([^@]*\)@[0-9]*/\1/'` fi - ${sbindir}/nsd-notify ${port} ${secret} -z ${zonename} ${ipaddr} + source="-s "`${nsd_checkconf} -o ip-address ${configfile} | egrep -v "127.0.0.1|::1"` + ${sbindir}/nsd-notify ${source} ${port} ${secret} -z ${zonename} ${ipaddr} fi done } Index: nsd-notify.8 ==================================================================--- nsd-notify.8 (revision 2759) +++ nsd-notify.8 (working copy) @@ -11,6 +11,8 @@ .RB [ \-4 ] .RB [ \-6 ] .RB [ \-h ] +.RB [ \-s +.IR source ] .RB [ \-p .IR port ] .RB [ \-y @@ -34,6 +36,10 @@ .B \-h Print help information and exit. .TP +.B \-p\fI source +Specify the source address used; multiple addresses may be +given, they will be tried in order. +.TP .B \-p\fI port Specify the port to send to. .TP Index: nsd-notify.c ==================================================================--- nsd-notify.c (revision 2759) +++ nsd-notify.c (working copy) @@ -28,6 +28,8 @@ #include "query.h" +enum { EXIST = 1, BOUND = 2 }; + extern char *optarg; extern int optind; @@ -47,7 +49,7 @@ static void usage (void) { - fprintf(stderr, "usage: nsd-notify [-4] [-6] [-h] [-p port] [-y key:secret] " + fprintf(stderr, "usage: nsd-notify [-4] [-6] [-h] [-s source] [-p port] [-y key:secret] " "-z zone servers\n\n"); fprintf(stderr, "\tSend NOTIFY to secondary servers to force a zone update.\n"); fprintf(stderr, "\tVersion %s. Report bugs to <%s>.\n\n", @@ -55,6 +57,7 @@ fprintf(stderr, "\t-4\t\tSend using IPv4.\n"); fprintf(stderr, "\t-6\t\tSend using IPv6.\n"); fprintf(stderr, "\t-h\t\tPrint this help information.\n"); + fprintf(stderr, "\t-s\t\tSource address; multiple may be given.\n"); fprintf(stderr, "\t-p port\t\tPort number of secondary server.\n"); fprintf(stderr, "\t-y key:secret\tTSIG keyname and base64 secret blob.\n"); fprintf(stderr, "\t-z zone\t\tName of zone to be updated.\n"); @@ -185,14 +188,15 @@ int main (int argc, char *argv[]) { - int c, udp_s; + int c, udp_4, udp_6, v4stat, v6stat; struct query q; struct query answer; const dname_type *zone = NULL; struct addrinfo hints, *res0, *res; int error; int default_family = DEFAULT_AI_FAMILY; - const char *port = UDP_PORT; + const char *port = UDP_PORT, *source[16]; + size_t nsource = 0, i; region_type *region = region_create(xalloc, free); #ifdef TSIG tsig_key_type *tsig_key = 0; @@ -208,7 +212,7 @@ #endif /* TSIG */ /* Parse the command line... */ - while ((c = getopt(argc, argv, "46hp:y:z:")) != -1) { + while ((c = getopt(argc, argv, "46hp:s:y:z:")) != -1) { switch (c) { case '4': default_family = AF_INET; @@ -221,6 +225,13 @@ log_msg(LOG_ERR, "IPv6 support not enabled\n"); exit(1); #endif /* !INET6 */ + case 's': + if (nsource >= sizeof(source)/sizeof(source[0])) { + log_msg(LOG_ERR, "too many source addresses\n"); + exit(1); + } + source[nsource++] = optarg; + break; case 'p': port = optarg; break; @@ -291,6 +302,72 @@ answer.packet = buffer_create(region, QIOBUFSZ); memset(buffer_begin(answer.packet), 0, buffer_remaining(answer.packet)); + if ((udp_4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + log_msg(LOG_ERR, "unable to create v4 socket"); + exit(1); + } + #ifdef INET6 + if ((udp_6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + log_msg(LOG_ERR, "unable to create v6 socket"); + exit(1); + } + #endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = default_family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + for (v4stat = v6stat = i = 0; i < nsource; i++) { + error = getaddrinfo(source[i], NULL, &hints, &res0); + if (error) { + warning("bad source address %s: %s\n", source, + gai_strerror(error)); + continue; + } + + for (res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_INET && !(v4stat&BOUND)) { + v4stat |= EXIST; + if (bind(udp_4, res->ai_addr, + res->ai_addrlen) == -1) { + warning("unable to bind to address %s: %s\n", source[i], strerror(errno)); + continue; + } else + v4stat |= BOUND; + } + + #ifdef INET6 + if (res->ai_family == AF_INET6 && !(v6stat&BOUND)) { + v6stat |= EXIST; + if (bind(udp_6, res->ai_addr, + res->ai_addrlen) == -1) { + warning("unable to bind to address %s: %s\n", source[i], strerror(errno)); + continue; + } else + v6stat |= BOUND; + } + #endif + } + } + + if (nsource) { + if (!(v4stat&EXIST) && !(v6stat&EXIST)) { + log_msg(LOG_ERR, "all source addresses given are invalid"); + exit(1); + } + if (v4stat == EXIST) { /* exists but not bound */ + log_msg(LOG_ERR, "unable to bind to any v4 source addresses."); + exit(1); + } + #ifdef INET6 + if (v6stat == EXIST) { /* exists but not bound */ + log_msg(LOG_ERR, "unable to bind to any v6 source addresses."); + exit(1); + } + #endif + } + + for (/*empty*/; *argv; argv++) { /* Set up UDP */ memset(&hints, 0, sizeof(hints)); @@ -309,14 +386,9 @@ continue; } - udp_s = socket(res->ai_family, res->ai_socktype, - res->ai_protocol); - if (udp_s == -1) { - continue; - } - memcpy(&q.addr, res->ai_addr, res->ai_addrlen); - notify_host(udp_s, &q, &answer, res, zone, *argv); + notify_host(res->ai_family == AF_INET ? udp_4 : udp_6, + &q, &answer, res, zone, *argv); } freeaddrinfo(res0); }