diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-12-01 13:41:56 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2014-12-01 13:48:44 +0200 |
commit | 2d83770e46cee7501972d768da95b46dcc5d0460 (patch) | |
tree | 2c822d95af84dc385c6f5257999a335eb650d5df | |
parent | 7817c61276f2402488c1450d6df03c12a97b8af5 (diff) | |
download | nssync-2d83770e46cee7501972d768da95b46dcc5d0460.tar.gz nssync-2d83770e46cee7501972d768da95b46dcc5d0460.tar.bz2 |
Optionally select only zones which list the current machine as one of their NSs.
* src/iflist.c: New file.
* src/Makefile.am (nssync_SOURCES): Add iflist.c
* src/config.c: New configuration statement "check-ns".
* src/nssync.c (main): Call get_host_addresses if check-ns is
requested.
* src/nssync.h (check_ns): New extern.
(get_host_addresses, is_my_sockaddr): New protos.
(nsdef): New struct.
(nssync) <nsdef_head, nsdef_tail>
<myzone>: New members.
* src/output.c (format_ns_record): Remove.
(save_ns_record): New function. Replaces format_ns_record.
(format_soa_record): Don't create zone file if myzone is false.
* NEWS: Document check-ns
* doc/nssync.8: Likewise.
* doc/nssync.texi: Likewise.
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | doc/nssync.8 | 8 | ||||
-rw-r--r-- | doc/nssync.texi | 7 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/config.c | 5 | ||||
-rw-r--r-- | src/iflist.c | 93 | ||||
-rw-r--r-- | src/nssync.c | 7 | ||||
-rw-r--r-- | src/nssync.h | 14 | ||||
-rw-r--r-- | src/output.c | 109 |
9 files changed, 231 insertions, 25 deletions
@@ -1,14 +1,24 @@ -NSsync NEWS -- history of user-visible changes. 2014-06-27 +NSsync NEWS -- history of user-visible changes. 2014-12-01 Copyright (C) 2011, 2012, 2014 Sergey Poznyakoff See the end of file for copying conditions. Please send nssync bug reports to <gray+nssync@gnu.org.ua> Version 1.1.90 (Git) +* check-ns + +This new configuration statement controls which zones are served by +nssync. If set to true, nssync will check the list of NS servers +prior to creating a zone file. The file will be created only if IPv4 +address of one of the servers matches one of the IP addresses of the +host on which nssync is run. + +* Fix coredump if mysql is not in slave mode and slave status is requested + Version 1.1, 2012-03-27 First actual release. diff --git a/doc/nssync.8 b/doc/nssync.8 index 1f62a88..6ea3121 100644 --- a/doc/nssync.8 +++ b/doc/nssync.8 @@ -10,13 +10,13 @@ .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public License .\" along with Nssync. If not, see <http://www.gnu.org/licenses/>. -.TH NSSYNC "8" "June 27, 2014" "NSSYNC" "" +.TH NSSYNC "8" "December 1, 2014" "NSSYNC" "" .SH NAME \fBnssync\fR \- A DNS Zone File Maintenance Utility .SH SYNOPSIS \fBnssync\fR [\-EVXdfhntx] [\-D \fISYMBOL[=VALUE\fR]] [\-I \fIDIR\fR]\ [\-c \fIFILE\fR] [\-\-config\-file=\fIFILE\fR] [\-\-config\-help]\ @@ -138,12 +138,18 @@ Give this help list. .TP \fB\-\-usage\fR Give a short usage message. .SH CONFIGURATION FILE .SS General Settings .TP +\fBcheck\-ns\fR \fIBOOL\fR; +If set to \fBtrue\fR, \fBnssync\fR will check the list of NS +servers prior to creating a zone file. The file will be created only +if IPv4 address of one of the servers matches one of the IP addresses +of the host on which \fBnssync\fR is run. +.TP \fBpidfile\fR \fIFILE\fR; At startup, check if \fIFILE\fR already exists and is owned by an existing process. Exit if so. Use this statement to avoid accidentally running two copies of \fBnssync\fR simultaneously. .TP \fBtempdir\fR \fIDIR\fR; diff --git a/doc/nssync.texi b/doc/nssync.texi index 8c1a784..ed3099b 100644 --- a/doc/nssync.texi +++ b/doc/nssync.texi @@ -203,12 +203,19 @@ running two copies of @command{nssync} simultaneously. @deffn {Configuration} tempdir @var{dir} Sets the name for the temporary directory. This is a directory where @command{nssync} creates temporary zone files. The argument must point to an existing directory. @end deffn +@deffn {Configuration} check-ns @var{bool} +If set to @code{true}, @command{nssync} will check the list of NS +servers prior to creating a zone file. The file will be created only +if IPv4 address of one of the servers matches one of the IP addresses +of the host on which @command{nssync} is run. +@end deffn + @deffn {Configuration} named-conf @var{file} Defines the full pathname of the @command{named} configuration file. Default is @file{/etc/named.conf}. @end deffn @deffn {Configuration} bind-include-path @var{list} diff --git a/src/Makefile.am b/src/Makefile.am index 8fab7c0..00509e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,12 +15,13 @@ # along with NSsync. If not, see <http://www.gnu.org/licenses/>. sbin_PROGRAMS = nssync nssync_SOURCES = \ bindcf.c\ config.c\ + iflist.c\ nssync.c\ nssync.h\ output.c\ sqlop.c LDADD=@GRECS_LDADD@ diff --git a/src/config.c b/src/config.c index 07e684a..6f51a46 100644 --- a/src/config.c +++ b/src/config.c @@ -153,12 +153,17 @@ static struct grecs_keyword nssync_kw[] = { { "compare-command", NULL, "Command to compare two zone files", grecs_type_string, GRECS_DFLT, &compare_command }, { "reload-command", NULL, "Command to reload the nameserver", grecs_type_string, GRECS_DFLT, &reload_command }, + + { "check-ns", + NULL, + "Synchronize only if this host is listed as one of the nameservers", + grecs_type_bool, GRECS_DFLT, &check_ns }, { "sync", N_("tag: string"), N_("Define a synchronization block"), grecs_type_section, GRECS_DFLT, NULL, 0, cb_sync, NULL, sync_kw }, { NULL } }; diff --git a/src/iflist.c b/src/iflist.c new file mode 100644 index 0000000..a7d0a1d --- /dev/null +++ b/src/iflist.c @@ -0,0 +1,93 @@ +/* This file is part of NSsync + Copyright (C) 2011, 2014 Sergey Poznyakoff + + NSsync is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + NSsync is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NSsync. If not, see <http://www.gnu.org/licenses/>. */ + +#include "nssync.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <netinet/in.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <arpa/inet.h> + +struct sockaddr_in *hostaddr; +size_t addr_count; + +void +get_host_addresses() +{ +#ifdef SIOCGIFADDR + struct if_nameindex *idx; + size_t i, j; + struct ifreq ifr; + int fd; + + idx = if_nameindex(); + if (!idx) { + error("can't get list of interfaces: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + + + for (addr_count = 0; idx[addr_count].if_index > 0; addr_count++) + ; + + if (addr_count) { + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + error("can't open socket: %s", strerror(errno)); + exit(EX_UNAVAILABLE); + } + + hostaddr = grecs_calloc(addr_count, sizeof(hostaddr[0])); + for (i = j = 0; i < addr_count; i++) { + size_t len = strlen(idx[i].if_name); + if (len >= sizeof(ifr.ifr_name)) { + error("interface name too long: %s", + idx[i].if_name); + continue; + } + strcpy(ifr.ifr_name, idx[i].if_name); + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) { + error("ioctl failed for %s: %s", + idx[i].if_name, strerror(errno)); + continue; + } + memcpy(&hostaddr[j], &ifr.ifr_addr, + sizeof(struct sockaddr_in)); + ++j; + } + addr_count = j; + close(fd); + } + + if_freenameindex(idx); +#else + error("check-ns is not supported on this host"); + exit(EX_UNAVAILABLE); +#endif +} + +int +is_my_sockaddr(struct sockaddr_in *s) +{ + size_t i; + + for (i = 0; i < addr_count; i++) + if (memcmp(hostaddr + i, s, sizeof(*s)) == 0) + return 1; + return 0; +} diff --git a/src/nssync.c b/src/nssync.c index 8ea95d3..dcc3592 100644 --- a/src/nssync.c +++ b/src/nssync.c @@ -28,12 +28,13 @@ char *pidfile; int force; char *tempdir; char *reload_command = "/usr/sbin/rndc reload"; char *compare_command = "cmp $oldfile $newfile > /dev/null"; unsigned error_count; unsigned changed_zones; +int check_ns; #include "cmdline.h" void verror(const char *fmt, va_list ap) { @@ -422,19 +423,23 @@ check_pidfile() int main(int argc, char **argv) { struct grecs_list_entry *ep; - + config_init(); parse_options(argc, argv); if (preprocess_only) exit(grecs_preproc_run(config_file, grecs_preprocessor) ? EX_CONFIG : 0); config_parse(); + + if (check_ns) + get_host_addresses(); + sql_connect(); check_pidfile(); check_slave_status(); diff --git a/src/nssync.h b/src/nssync.h index fc7ef0c..69149e8 100644 --- a/src/nssync.h +++ b/src/nssync.h @@ -34,12 +34,13 @@ extern int debug_level; extern char *config_file; extern char *slave_status_file; extern char *pidfile; extern char *tempdir; extern char *compare_command; extern char *reload_command; +extern int check_ns; extern unsigned changed_zones; extern char *sql_config_file; extern char *sql_config_group; extern char *database_name; @@ -85,23 +86,36 @@ void debug_printf(const char *fmt, ...) __PRINTFLIKE(1,2); void sql_connect(void); void sql_disconnect(void); int sql_do_query(const char *query, int (*fun)(MYSQL_ROW, unsigned, void*), void *data); int sql_get_slave_status(char **pfile, char **poff); +void get_host_addresses(void); +struct sockaddr_in; +int is_my_sockaddr(struct sockaddr_in *s); + +struct nsdef { + struct nsdef *next; + char *type; + char *data; +}; + struct nssync { char *tag; char *zone_conf_file; char *zone_file_pattern; char *zone_add_stmt; struct grecs_node *zone_tree; char *soa_query; char *rr_query; char *rev_rr_query; char *ns_query; + + struct nsdef *nsdef_head, *nsdef_tail; + int myzone; char *file_name; char *temp_file_name; FILE *fp; }; diff --git a/src/output.c b/src/output.c index a684201..fb8b66c 100644 --- a/src/output.c +++ b/src/output.c @@ -12,32 +12,69 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with NSsync. If not, see <http://www.gnu.org/licenses/>. */ #include "nssync.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> #define S(s) ((s) ? (s) : NULL) enum { f_ns_ttl, f_ns_type, f_ns_data, _ns_nfields }; int -format_ns_record(MYSQL_ROW row, unsigned nf, void *data) +save_ns_record(MYSQL_ROW row, unsigned nf, void *data) { struct nssync *sp = data; + struct nsdef *ns; if (nf != _ns_nfields) { error("NS query returned wrong number of fields"); return 1; } + + ns = grecs_malloc(sizeof(*ns)); + ns->type = grecs_strdup(row[f_ns_type]); + ns->data = grecs_strdup(row[f_ns_data]); /* FIXME: TTL */ - fprintf(sp->fp, "\t%s\t%s\n", row[f_ns_type], row[f_ns_data]); + ns->next = NULL; + + if (sp->nsdef_tail) + sp->nsdef_tail->next = ns; + else + sp->nsdef_head = ns; + sp->nsdef_tail = ns; + + if (check_ns && !sp->myzone && strcasecmp(ns->type, "NS") == 0) { + struct addrinfo *res, *ap, hints; + int rc; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + rc = getaddrinfo(ns->data, NULL, &hints, &res); + if (rc) { + error("can't resolve %s: %s", ns->data, + gai_strerror(rc)); + return 0; + } + + for (ap = res; ap; ap = ap->ai_next) { + if (is_my_sockaddr((struct sockaddr_in*)res->ai_addr)) { + sp->myzone = 1; + break; + } + } + freeaddrinfo(res); + } + return 0; } enum { f_rr_host, @@ -92,26 +129,70 @@ is_reverse_zone(const char *str) size_t len = strlen(str); return len > sizeof(suffix) && strcasecmp(str + len - sizeof(suffix) + 1, suffix) == 0; } +void +free_nsdef(struct nssync *sp) +{ + struct nsdef *ns; + + for (ns = sp->nsdef_head; ns; ) { + struct nsdef *next = ns->next; + free(ns->type); + free(ns->data); + free(ns); + ns = ns->next; + } + sp->nsdef_head = sp->nsdef_tail = NULL; +} + int format_soa_record(MYSQL_ROW row, unsigned nf, void *data) { struct nssync *sp = data; struct wordsplit ws; const char *env[3]; + int wsflags = WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | + WRDSF_NOSPLIT | WRDSF_KEEPUNDEF; + struct nsdef *ns; if (nf != _soa_nfields) { error("SOA query returned wrong number of fields"); return 1; } if (!row[f_soa_type]) return 0; + free_nsdef(sp); + sp->myzone = !check_ns; + + /* Fill the list of NS servers */ + env[0] = "zone"; + env[1] = row[f_soa_zone]; + env[2] = 0; + + if (sp->ns_query) { + ws.ws_env = env; + if (wordsplit(sp->ns_query, &ws, wsflags)) { + error("cannot split ns_query: %s", + wordsplit_strerror(&ws)); + exit(EX_SOFTWARE); + } + wsflags |= WRDSF_REUSE; + if (sql_do_query(ws.ws_wordv[0], save_ns_record, sp)) + exit(EX_UNAVAILABLE); + } + + if (!sp->myzone) { + debug(1,("%s: not served by me", row[f_soa_zone])); + wordsplit_free(&ws); + return 0; + } + grecs_free(sp->file_name); sp->file_name = bindcf_lookup(sp, row[f_soa_zone]); if (!sp->file_name) return 0; debug(2, ("%s: zone %s, file %s", sp->tag, row[f_soa_zone], sp->file_name)); @@ -137,35 +218,19 @@ format_soa_record(MYSQL_ROW row, unsigned nf, void *data) row[f_soa_person], row[f_soa_serial], row[f_soa_refresh], row[f_soa_retry], row[f_soa_expire], row[f_soa_minimum]); - - env[0] = "zone"; - env[1] = row[f_soa_zone]; - env[2] = 0; - if (sp->ns_query) { - ws.ws_env = env; - if (wordsplit(sp->ns_query, &ws, - WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | - WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) { - error("cannot split ns_query: %s", - wordsplit_strerror(&ws)); - exit(EX_SOFTWARE); - } - - if (sql_do_query(ws.ws_wordv[0], format_ns_record, sp)) - exit(EX_UNAVAILABLE); - } + for (ns = sp->nsdef_head; ns; ns = ns->next) + /* FIXME: TTL */ + fprintf(sp->fp, "\t%s\t%s\n", ns->type, ns->data); if (wordsplit((sp->rev_rr_query && is_reverse_zone(row[f_soa_zone])) ? - sp->rev_rr_query : sp->rr_query, &ws, - WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | WRDSF_NOSPLIT | - WRDSF_KEEPUNDEF | WRDSF_REUSE)) { + sp->rev_rr_query : sp->rr_query, &ws, wsflags)) { error("cannot split rr_query: %s", wordsplit_strerror(&ws)); exit(EX_SOFTWARE); } if (sql_do_query(ws.ws_wordv[0], format_rr_record, sp)) exit(EX_UNAVAILABLE); |