From 2d83770e46cee7501972d768da95b46dcc5d0460 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Mon, 1 Dec 2014 13:41:56 +0200 Subject: 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) : 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. --- NEWS | 12 ++++++- doc/nssync.8 | 8 ++++- doc/nssync.texi | 7 ++++ src/Makefile.am | 1 + src/config.c | 5 +++ src/iflist.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++ src/nssync.c | 7 +++- src/nssync.h | 14 ++++++++ src/output.c | 109 ++++++++++++++++++++++++++++++++++++++++++++------------ 9 files changed, 231 insertions(+), 25 deletions(-) create mode 100644 src/iflist.c diff --git a/NEWS b/NEWS index b392847..0a2ae0d 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -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. @@ -6,6 +6,16 @@ Please send nssync bug reports to 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 diff --git a/doc/nssync.8 b/doc/nssync.8 index 1f62a88..6ea3121 100644 --- a/doc/nssync.8 +++ b/doc/nssync.8 @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Nssync. If not, see . -.TH NSSYNC "8" "June 27, 2014" "NSSYNC" "" +.TH NSSYNC "8" "December 1, 2014" "NSSYNC" "" .SH NAME \fBnssync\fR \- A DNS Zone File Maintenance Utility @@ -141,6 +141,12 @@ 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 diff --git a/doc/nssync.texi b/doc/nssync.texi index 8c1a784..ed3099b 100644 --- a/doc/nssync.texi +++ b/doc/nssync.texi @@ -206,6 +206,13 @@ Sets the name for the temporary directory. This is a directory where 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}. diff --git a/src/Makefile.am b/src/Makefile.am index 8fab7c0..00509e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,7 @@ sbin_PROGRAMS = nssync nssync_SOURCES = \ bindcf.c\ config.c\ + iflist.c\ nssync.c\ nssync.h\ output.c\ diff --git a/src/config.c b/src/config.c index 07e684a..6f51a46 100644 --- a/src/config.c +++ b/src/config.c @@ -156,6 +156,11 @@ static struct grecs_keyword nssync_kw[] = { { "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 }, 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 . */ + +#include "nssync.h" +#include +#include +#include +#include +#include +#include +#include + +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 @@ -31,6 +31,7 @@ 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" @@ -425,13 +426,17 @@ 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(); diff --git a/src/nssync.h b/src/nssync.h index fc7ef0c..69149e8 100644 --- a/src/nssync.h +++ b/src/nssync.h @@ -37,6 +37,7 @@ extern char *pidfile; extern char *tempdir; extern char *compare_command; extern char *reload_command; +extern int check_ns; extern unsigned changed_zones; @@ -88,6 +89,16 @@ 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; @@ -99,6 +110,9 @@ struct nssync { 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; diff --git a/src/output.c b/src/output.c index a684201..fb8b66c 100644 --- a/src/output.c +++ b/src/output.c @@ -15,6 +15,9 @@ along with NSsync. If not, see . */ #include "nssync.h" +#include +#include +#include #define S(s) ((s) ? (s) : NULL) @@ -26,15 +29,49 @@ enum { }; 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; } @@ -95,12 +132,30 @@ is_reverse_zone(const char *str) 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"); @@ -109,6 +164,32 @@ format_soa_record(MYSQL_ROW row, unsigned nf, void *data) 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) @@ -140,29 +221,13 @@ format_soa_record(MYSQL_ROW row, unsigned nf, void *data) 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); } -- cgit v1.2.1