/* This file is part of NSsync Copyright (C) 2011-2017 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 struct grecs_list *synclist; static struct grecs_keyword server_kw[] = { { "address", NULL, "Listen on that address", grecs_type_sockaddr, GRECS_DFLT, &server_addr }, { "wakeup", "N", "Wake up and resync each N seconds", grecs_type_uint, GRECS_DFLT, &periodic_timeout }, { "delay", "N", "Wait N seconds before actually performing the requested sync", grecs_type_uint, GRECS_DFLT, &delay_timeout }, { NULL } }; static struct grecs_keyword sync_kw[] = { { "zone-conf", NULL, "zone configuration file", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, zone_conf_file) }, { "zonefile-pattern", NULL, "Pattern for new zone file names", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, zone_file_pattern) }, { "add-statements", NULL, "add these statements to each generated zone", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, zone_add_stmt) }, { "soa-query", NULL, "Set query for retrieving SOA records", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, soa_query) }, { "ns-query", NULL, "Set query for retrieving NS and similar records", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, ns_query) }, { "rr-query", NULL, "Set query for retrieving the rest of RRs", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, rr_query) }, { "rev-rr-query", NULL, "Set query for retrieving RRs from reverse delegation zones", grecs_type_string, GRECS_DFLT, NULL, offsetof(struct nssync, rev_rr_query) }, { NULL } }; static void node_undo(void *ptr) { struct grecs_node *node = ptr; grecs_node_unlink(node); grecs_node_free(node); } static int cb_sync(enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data) { grecs_locus_t *locus = &node->locus; grecs_value_t *value = node->v.value; int err = 0; struct nssync *sp; void **pdata = cb_data; switch (cmd) { case grecs_callback_section_begin: if (!value || value->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "tag must be a string"); return 0; } sp = grecs_zalloc(sizeof(*sp)); sp->tag = grecs_strdup(value->v.string); *pdata = sp; break; case grecs_callback_section_end: sp = *pdata; if (!sp->soa_query) { grecs_error(locus, 0, "soa-query not defined"); err = 1; } if (!sp->rr_query) { grecs_error(locus, 0, "rr-query not defined"); err = 1; } if (err) grecs_free(sp); else { sp->new_nodes = grecs_list_create(); sp->new_nodes->free_entry = node_undo; if (!synclist) synclist = grecs_list_create(); grecs_list_append(synclist, sp); } *pdata = NULL; break; case grecs_callback_set_value: grecs_error(locus, 0, "invalid use of block statement"); } return err; } static struct grecs_keyword mysql_kw[] = { { "config-file", "file", "Read MySQL configuration from ", grecs_type_string, GRECS_DFLT, &sql_config_file }, { "config-group", "name", "Read the named group from the SQL configuration file", grecs_type_string, GRECS_DFLT, &sql_config_group }, { "host", "host", "Set SQL server hostname or IP address", grecs_type_string, GRECS_DFLT, &sql_host }, { "database", "dbname", "Set database name", grecs_type_string, GRECS_DFLT, &database_name }, { "user", "name", "Set SQL user name", grecs_type_string, GRECS_DFLT, &sql_user }, { "password", "arg", "Set SQL user password", grecs_type_string, GRECS_DFLT, &sql_password }, { "ssl-ca", "file", "File name of the Certificate Authority (CA) certificate", grecs_type_string, GRECS_DFLT, &sql_cacert }, { "slave-status-file", NULL, "Check slave status and save it in the given file", grecs_type_string, GRECS_DFLT, &slave_status_file }, { NULL } }; struct transtab { char const *name; int val; }; struct transtab facilities[] = { { "user", LOG_USER }, { "daemon", LOG_DAEMON }, { "auth", LOG_AUTH }, { "authpriv",LOG_AUTHPRIV }, { "mail", LOG_MAIL }, { "cron", LOG_CRON }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { NULL } }; static int cb_syslog_facility(enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data) { grecs_locus_t *locus = &node->locus; grecs_value_t *value = node->v.value; int i; if (cmd != grecs_callback_set_value) { grecs_error(locus, 0, "Unexpected block statement"); return 1; } if (GRECS_VALUE_EMPTY_P(value) || value->type != GRECS_TYPE_STRING) { grecs_error(locus, 0, "expected string"); return 1; } for (i = 0; facilities[i].name; i++) { if (strcmp(facilities[i].name, value->v.string) == 0) { *(int*)varptr = facilities[i].val; return 0; } } grecs_error(&value->locus, 0, "unknown syslog facility `%s'", value->v.string); return 0; } static struct grecs_keyword syslog_kw[] = { { "facility", "name", "Set syslog facility. Arg is one of the following: user, daemon, " "auth, authpriv, mail, cron, local0 through local7 " "(case-insensitive), or a facility number.", grecs_type_string, GRECS_DFLT, &syslog_facility, 0, cb_syslog_facility }, { "tag", "string", "Tag syslog messages with this string", grecs_type_string, GRECS_DFLT, &syslog_tag }, { NULL } }; static struct grecs_keyword nssync_kw[] = { { "pidfile", "file", "At startup, check if already exists and is " "owned by an existing process. Exit if so.", grecs_type_string, GRECS_DFLT, &pidfile }, { "user", "name", "Run as this user", grecs_type_string, GRECS_DFLT, &runas_user }, { "group", "name", "Run as this group", grecs_type_string, GRECS_DFLT, &runas_group }, { "tempdir", NULL, "Name for the temporary directory (must exist)", grecs_type_string, GRECS_DFLT, &tempdir }, { "named-conf", NULL, "Name of the named configuration file", grecs_type_string, GRECS_DFLT, &named_conf_file }, { "bind-include-path", NULL, "Set include path for BIND configuration", grecs_type_string, GRECS_LIST, &bind_include_path }, { "zonefile-pattern", NULL, "Pattern for new zone file names", grecs_type_string, GRECS_DFLT, &zone_file_pattern }, { "zone-conf", NULL, "Pattern for zone configuration file", grecs_type_string, GRECS_DFLT, &zone_conf_file }, { "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", "tag: string", "Define a synchronization block", grecs_type_section, GRECS_DFLT, NULL, 0, cb_sync, NULL, sync_kw }, { "server", NULL, "Configure HTTP server", grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, server_kw }, { "mysql", NULL, "Configure MySQL access", grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, mysql_kw }, { "syslog", NULL, "Configure syslog logging", grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, syslog_kw }, { NULL } }; void config_help() { static char docstring[] = "Configuration file structure for NSsync.\n"; grecs_print_docstring(docstring, 0, stdout); grecs_print_statement_array(nssync_kw, 1, 0, stdout); } void config_init() { grecs_include_path_setup(DEFAULT_VERSION_INCLUDE_DIR, DEFAULT_INCLUDE_DIR, NULL); grecs_preprocessor = DEFAULT_PREPROCESSOR; grecs_parser_options = GRECS_OPTION_QUOTED_STRING_CONCAT; grecs_log_to_stderr = 1; } static int sql_host_fixup() { char *p; if (!sql_host) return 0; p = strchr(sql_host, ':'); if (p) { *p++ = 0; if (p[0] == '/') { sql_socket = grecs_strdup (p); grecs_free(sql_host); sql_host = grecs_strdup ("localhost"); } else { char *end; unsigned long n = strtoul(p, &end, 10); if (*end) { grecs_error (NULL, 0, "invalid port number (near %s)", end); return 1; } if (n == 0 || n > USHRT_MAX) { grecs_error (NULL, 0, "port number out of range 1..%d", USHRT_MAX); return 1; } sql_port = n; } } return 0; } static int synclist_fixup() { int err = 0; struct grecs_list_entry *ep; for (ep = synclist->head; ep; ep = ep->next) { struct nssync *sp = ep->data; if (!sp->zone_file_pattern) { if (!zone_file_pattern) { error("%s: zonefile-pattern not defined", sp->tag); err = 1; } else sp->zone_file_pattern = zone_file_pattern; } if (!sp->zone_conf_file) { if (!zone_conf_file) { error("%s: zone-conf not defined", sp->tag); err = 1; } else { sp->zone_conf_file = grecs_strdup(zone_conf_file); } } if (strchr(sp->zone_conf_file, '$')) { char *env[3]; struct wordsplit ws; env[0] = "synctag"; env[1] = sp->tag; env[2] = NULL; ws.ws_env = (const char**)env; if (wordsplit(sp->zone_conf_file, &ws, WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) { error("cannot split zone-conf: %s", wordsplit_strerror(&ws)); exit(EX_SOFTWARE); } grecs_free(zone_conf_file); zone_conf_file = ws.ws_wordv[0]; ws.ws_wordv[0] = 0; wordsplit_free(&ws); } } return err; } void config_parse() { int err = 0; struct grecs_node *tree = grecs_parse(config_file); if (!tree) exit(EX_CONFIG); grecs_tree_reduce(tree, nssync_kw, GRECS_AGGR); if (grecs_tree_process(tree, nssync_kw)) exit(EX_CONFIG); grecs_tree_free(tree); if (!synclist) { error("nothing to do!"); err = 1; } if (synclist_fixup()) err = 1; if (sql_host_fixup()) err = 1; source_named_conf(); if (err) exit(EX_CONFIG); if (lint_mode) exit(0); if (!tempdir) { tempdir = getenv("TMP"); if (!tempdir) tempdir = "/tmp"; } }