/* 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";
}
}