void engine_runloop(engine_type* engine, start_cb_t start, int single_run) { /* run */ while (engine->need_to_exit == 0) { if (engine->need_to_reload) { ods_log_info("[%s] enforcer reloading", engine_str); engine->need_to_reload = 0; } else { ods_log_info("[%s] enforcer started", engine_str); /* try to recover from backups */ /* not for now: engine_recover_from_backups(engine); */ } engine_run(engine, start, single_run); } /* shutdown */ ods_log_info("[%s] enforcer shutdown", engine_str); hsm_close(); if (engine->cmdhandler != NULL) { engine_stop_cmdhandler(engine); } }
static int run(int sockfd, cmdhandler_ctx_type* context, const char *cmd) { db_connection_t* dbconn = getconnectioncontext(context); engine_type* engine = getglobalcontext(context); (void)cmd; if (!engine) { return 1; } if (!engine->config) { return 1; } if (!engine->config->zonelist_filename) { return 1; } if (!dbconn) { return 1; } ods_log_debug("[%s] %s command", module_str, zonelist_export_funcblock.cmdname); if (zonelist_export(sockfd, dbconn, engine->config->zonelist_filename, 1) != ZONELIST_EXPORT_OK) { ods_log_error("[%s] zonelist exported to %s failed", module_str, engine->config->zonelist_filename); client_printf_err(sockfd, "Exported zonelist to %s failed!\n", engine->config->zonelist_filename); return 1; } ods_log_info("[%s] zonelist exported to %s successfully", module_str, engine->config->zonelist_filename); client_printf(sockfd, "Exported zonelist to %s successfully\n", engine->config->zonelist_filename); return 0; }
static void ods_protobuf_loghandler(::google::protobuf::LogLevel level, const char *filename, int line, const std::string &message) { const char * const fmt = "[%s] %s %s:%d] %s"; switch (level) { case ::google::protobuf::LOGLEVEL_INFO: ods_log_info(fmt,module_str,"INFO",filename,line,message.c_str()); break; case ::google::protobuf::LOGLEVEL_WARNING: ods_log_warning(fmt,module_str,"WARNING",filename,line,message.c_str()); break; case ::google::protobuf::LOGLEVEL_ERROR: ods_log_crit(fmt,module_str,"ERROR",filename,line,message.c_str()); break; case ::google::protobuf::LOGLEVEL_FATAL: ods_fatal_exit(fmt,module_str,"FATAL",filename,line,message.c_str()); break; default: ods_log_assert(false); break; } }
/** * Open HSM. * */ int lhsm_open(const char* filename) { int result = hsm_open(filename, hsm_check_pin); if (result != HSM_OK) { char* error = hsm_get_error(NULL); if (error != NULL) { ods_log_error("[%s] %s", hsm_str, error); free(error); } else { ods_log_crit("[%s] error opening libhsm (errno %i)", hsm_str, result); } /* exit? */ } else { ods_log_info("[%s] libhsm connection opened succesfully", hsm_str); } return result; }
static time_t perform_enforce(int sockfd, engine_type *engine, int bForceUpdate, task_type* task, db_connection_t *dbconn) { zone_list_t *zonelist = NULL; zone_t *zone, *firstzone = NULL; policy_t *policy; key_data_list_t *keylist; const key_data_t *key; time_t t_next, t_now = time_now(), t_reschedule = -1; /* Flags that indicate tasks to be scheduled after zones have been * enforced. */ int bSignerConfNeedsWriting = 0; int bSubmitToParent = 0; int bRetractFromParent = 0; int zone_updated; if (!(zonelist = zone_list_new(dbconn)) /*|| zone_list_associated_fetch(zonelist)*/ || zone_list_get(zonelist)) { zone_list_free(zonelist); zonelist = NULL; } if (!zonelist) { /* TODO: log error */ ods_log_error("[%s] zonelist NULL", module_str); /* TODO: backoff? */ return t_reschedule; } for (zone = zone_list_get_next(zonelist); zone; zone_free(zone), zone = zone_list_get_next(zonelist)) { if (engine->need_to_reload || engine->need_to_exit) break; if (!bForceUpdate && (zone_next_change(zone) == -1)) { continue; } else if (zone_next_change(zone) > t_now && !bForceUpdate) { /* This zone needs no update, however it might be the first * for future updates */ if (zone_next_change(zone) < t_reschedule || !firstzone) { t_reschedule = zone_next_change(zone); if (firstzone) { zone_free(firstzone); } firstzone = zone; zone = NULL; /* keeps firstzone from being freed. */ } continue; } if (!(policy = zone_get_policy(zone))) { client_printf(sockfd, "Next update for zone %s NOT scheduled " "because policy is missing !\n", zone_name(zone)); if (zone_next_change(zone) != -1 && (zone_set_next_change(zone, -1) || zone_update(zone))) { /* TODO: Log error */ } continue; } if (policy_passthrough(policy)) { ods_log_info("Passing through zone %s.\n", zone_name(zone)); zone_set_signconf_needs_writing(zone, 1); zone_update(zone); bSignerConfNeedsWriting = 1; policy_free(policy); continue; } zone_updated = 0; t_next = update(engine, dbconn, zone, policy, t_now, &zone_updated); policy_free(policy); bSignerConfNeedsWriting |= zone_signconf_needs_writing(zone); keylist = zone_get_keys(zone); while ((key = key_data_list_next(keylist))) { if (key_data_ds_at_parent(key) == KEY_DATA_DS_AT_PARENT_SUBMIT) { ods_log_warning("[%s] please submit DS " "with keytag %d for zone %s", module_str, key_data_keytag(key)&0xFFFF, zone_name(zone)); bSubmitToParent = 1; } else if (key_data_ds_at_parent(key) == KEY_DATA_DS_AT_PARENT_RETRACT) { ods_log_warning("[%s] please retract DS " "with keytag %d for zone %s", module_str, key_data_keytag(key)&0xFFFF, zone_name(zone)); bRetractFromParent = 1; } } key_data_list_free(keylist); if (t_next == -1) { client_printf(sockfd, "Next update for zone %s NOT scheduled " "by enforcer !\n", zone_name(zone)); ods_log_debug("Next update for zone %s NOT scheduled " "by enforcer !\n", zone_name(zone)); } else { /* Invalid schedule time then skip the zone.*/ char tbuf[32] = "date/time invalid\n"; /* at least 26 bytes */ ctime_r(&t_next, tbuf); /* note that ctime_r inserts \n */ client_printf(sockfd, "Next update for zone %s scheduled at %s", zone_name(zone), tbuf); ods_log_debug("Next update for zone %s scheduled at %s", zone_name(zone), tbuf); } if (zone_next_change(zone) != t_next) { zone_set_next_change(zone, t_next); zone_updated = 1; } /* * Commit the changes to the zone if there where any. */ if (zone_updated) { if (zone_update(zone)) { ods_log_debug("[%s] error zone_update(%s)", module_str, zone_name(zone)); } } /* * Find out when to schedule the next change. */ if (zone_next_change(zone) != -1 && (zone_next_change(zone) < t_reschedule || !firstzone)) { t_reschedule = zone_next_change(zone); if (firstzone) { zone_free(firstzone); } firstzone = zone; zone = NULL; } } zone_list_free(zonelist); /* * Schedule the next change if needed. */ if (firstzone) { reschedule_enforce(task, t_reschedule, zone_name(firstzone)); zone_free(firstzone); } /* Launch signer configuration writer task when one of the * zones indicated that it needs to be written. * TODO: unschedule it first! */ if (bSignerConfNeedsWriting) { task_type *signconf = signconf_task(dbconn, "signconf", "signer configurations"); enf_schedule_task(sockfd,engine,signconf,"signconf"); } else { ods_log_info("[%s] No changes to any signconf file required", module_str); } /* Launch ds-submit task when one of the updated key states has the * DS_SUBMIT flag set. */ if (bSubmitToParent) { task_type *submit = keystate_ds_submit_task(engine); enf_schedule_task(sockfd, engine, submit, "ds-submit"); } /* Launch ds-retract task when one of the updated key states has the * DS_RETRACT flag set. */ if (bRetractFromParent) { task_type *retract = keystate_ds_retract_task(engine); enf_schedule_task(sockfd, engine, retract, "ds-retract"); } return t_reschedule; }
/** * Perform task. * */ static void worker_perform_task(worker_type* worker) { engine_type* engine = NULL; zone_type* zone = NULL; task_type* task = NULL; task_id what = TASK_NONE; time_t when = 0; time_t never = (3600*24*365); ods_status status = ODS_STATUS_OK; int backup = 0; time_t start = 0; time_t end = 0; if (!worker || !worker->task || !worker->task->zone || !worker->engine) { return; } engine = (engine_type*) worker->engine; task = (task_type*) worker->task; zone = (zone_type*) worker->task->zone; ods_log_debug("[%s[%i]] perform task %s for zone %s at %u", worker2str(worker->type), worker->thread_num, task_what2str(task->what), task_who2str(task), (uint32_t) worker->clock_in); /* do what you have been told to do */ switch (task->what) { case TASK_SIGNCONF: /* perform 'load signconf' task */ worker_working_with(worker, TASK_SIGNCONF, TASK_READ, "configure", task_who2str(task), &what, &when); status = tools_signconf(zone); if (status == ODS_STATUS_UNCHANGED) { if (!zone->signconf->last_modified) { ods_log_debug("[%s[%i]] no signconf.xml for zone %s yet", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_ERR; } } if (status == ODS_STATUS_UNCHANGED) { if (task->halted != TASK_NONE && task->halted != TASK_SIGNCONF) { goto task_perform_continue; } status = ODS_STATUS_OK; } else if (status == ODS_STATUS_OK) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } else { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* break; */ case TASK_READ: /* perform 'read input adapter' task */ worker_working_with(worker, TASK_READ, TASK_SIGN, "read", task_who2str(task), &what, &when); task->what = TASK_READ; if (!zone->signconf->last_modified) { ods_log_debug("[%s[%i]] no signconf.xml for zone %s yet", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_ERR; } else { lhsm_check_connection((void*)engine); status = tools_input(zone); } if (status == ODS_STATUS_UNCHANGED) { ods_log_verbose("[%s[%i]] zone %s unsigned data not changed, " "continue", worker2str(worker->type), worker->thread_num, task_who2str(task)); status = ODS_STATUS_OK; } if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* break; */ case TASK_SIGN: /* perform 'sign' task */ worker_working_with(worker, TASK_SIGN, TASK_WRITE, "sign", task_who2str(task), &what, &when); task->what = TASK_SIGN; status = zone_update_serial(zone); if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { ods_log_error("[%s[%i]] unable to sign zone %s: " "failed to increment serial", worker2str(worker->type), worker->thread_num, task_who2str(task)); if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } /* start timer */ start = time(NULL); if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); if (!zone->stats->start_time) { zone->stats->start_time = start; } zone->stats->sig_count = 0; zone->stats->sig_soa_count = 0; zone->stats->sig_reuse = 0; zone->stats->sig_time = 0; lock_basic_unlock(&zone->stats->stats_lock); } /* check the HSM connection before queuing sign operations */ lhsm_check_connection((void*)engine); /* queue menial, hard signing work */ worker_queue_zone(worker, engine->signq, zone); ods_log_deeebug("[%s[%i]] wait until drudgers are finished " "signing zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); /* sleep until work is done */ worker_sleep_unless(worker, 0); /* stop timer */ end = time(NULL); status = worker_check_jobs(worker, task); worker_clear_jobs(worker); if (status == ODS_STATUS_OK && zone->stats) { lock_basic_lock(&zone->stats->stats_lock); zone->stats->sig_time = (end-start); lock_basic_unlock(&zone->stats->stats_lock); } if (status != ODS_STATUS_OK) { if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } else { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } /* break; */ case TASK_WRITE: /* perform 'write to output adapter' task */ worker_working_with(worker, TASK_WRITE, TASK_SIGN, "write", task_who2str(task), &what, &when); task->what = TASK_WRITE; status = tools_output(zone, engine); if (status == ODS_STATUS_OK) { if (task->interrupt > TASK_SIGNCONF) { task->interrupt = TASK_NONE; task->halted = TASK_NONE; } } else { /* clear signatures? */ if (task->halted == TASK_NONE) { goto task_perform_fail; } goto task_perform_continue; } zone->db->is_processed = 1; if (zone->signconf && duration2time(zone->signconf->sig_resign_interval)) { what = TASK_SIGN; when = worker->clock_in + duration2time(zone->signconf->sig_resign_interval); } else { ods_log_error("[%s[%i]] unable to retrieve resign interval " "for zone %s: duration2time() failed", worker2str(worker->type), worker->thread_num, task_who2str(task)); ods_log_info("[%s[%i]] defaulting to 1H resign interval for " "zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); what = TASK_SIGN; when = worker->clock_in + 3600; } backup = 1; break; case TASK_NONE: worker->working_with = TASK_NONE; /* no task */ ods_log_warning("[%s[%i]] none task for zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); when = time_now() + never; break; default: worker->working_with = TASK_NONE; /* unknown task */ ods_log_warning("[%s[%i]] unknown task, trying full sign zone %s", worker2str(worker->type), worker->thread_num, task_who2str(task)); what = TASK_SIGNCONF; when = time_now(); break; } /* no error */ task->backoff = 0; if (task->interrupt != TASK_NONE && task->interrupt != what) { ods_log_debug("[%s[%i]] interrupt task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(what), task_who2str(task)); task->halted = what; task->halted_when = when; task->what = task->interrupt; task->when = time_now(); } else { ods_log_debug("[%s[%i]] next task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(what), task_who2str(task)); task->what = what; task->when = when; task->interrupt = TASK_NONE; task->halted = TASK_NONE; task->halted_when = 0; } /* backup the last successful run */ if (backup) { status = zone_backup2(zone); if (status != ODS_STATUS_OK) { ods_log_warning("[%s[%i]] unable to backup zone %s: %s", worker2str(worker->type), worker->thread_num, task_who2str(task), ods_status2str(status)); /* just a warning */ status = ODS_STATUS_OK; } backup = 0; } return; task_perform_fail: if (status != ODS_STATUS_XFR_NOT_READY) { /* other statuses is critical, and we know it is not ODS_STATUS_OK */ ods_log_crit("[%s[%i]] CRITICAL: failed to sign zone %s: %s", worker2str(worker->type), worker->thread_num, task_who2str(task), ods_status2str(status)); } /* in case of failure, also mark zone processed (for single run usage) */ zone->db->is_processed = 1; if (task->backoff) { task->backoff *= 2; } else { task->backoff = 60; } if (task->backoff > ODS_SE_MAX_BACKOFF) { task->backoff = ODS_SE_MAX_BACKOFF; } ods_log_info("[%s[%i]] backoff task %s for zone %s with %u seconds", worker2str(worker->type), worker->thread_num, task_what2str(task->what), task_who2str(task), task->backoff); task->when = time_now() + task->backoff; return; task_perform_continue: ods_log_info("[%s[%i]] continue task %s for zone %s", worker2str(worker->type), worker->thread_num, task_what2str(task->halted), task_who2str(task)); task->what = task->halted; task->when = task->halted_when; task->interrupt = TASK_NONE; task->halted = TASK_NONE; task->halted_when = 0; return; }
static int run(int sockfd, cmdhandler_ctx_type* context, const char *cmd) { #define NARGV 6 char* buf; const char* argv[NARGV]; int argc = 0; const char *zone_name2 = NULL; int all = 0; int write_xml = 0; int long_index = 0, opt = 0; zone_list_db_t* zone_list; zone_db_t* zone; int ret = 0; char path[PATH_MAX]; char *signconf_del = NULL; db_connection_t* dbconn = getconnectioncontext(context);; engine_type* engine = getglobalcontext(context); static struct option long_options[] = { {"zone", required_argument, 0, 'z'}, {"all", no_argument, 0, 'a'}, {"xml", no_argument, 0, 'u'}, {0, 0, 0, 0} }; ods_log_debug("[%s] %s command", module_str, zone_del_funcblock.cmdname); if (!(buf = strdup(cmd))) { client_printf_err(sockfd, "memory error\n"); return -1; } argc = ods_str_explode(buf, NARGV, argv); if (argc == -1) { client_printf_err(sockfd, "too many arguments\n"); ods_log_error("[%s] too many arguments for %s command", module_str, zone_del_funcblock.cmdname); free(buf); return -1; } optind = 0; while ((opt = getopt_long(argc, (char* const*)argv, "z:au", long_options, &long_index)) != -1) { switch (opt) { case 'z': zone_name2 = optarg; break; case 'a': all = 1; break; case 'u': write_xml = 1; break; default: client_printf_err(sockfd, "unknown arguments\n"); ods_log_error("[%s] unknown arguments for %s command", module_str, zone_del_funcblock.cmdname); free(buf); return -1; } } if (zone_name2 && !all) { if (!(zone = zone_db_new_get_by_name(dbconn, zone_name2))) { client_printf_err(sockfd, "Unable to delete zone, zone %s not found!\n", zone_name2); free(buf); return 1; } if (!delete_key_data(zone, dbconn, sockfd)) { zone_db_free(zone); free(buf); return 1; } if (zone_db_delete(zone)) { client_printf_err(sockfd, "Unable to delete zone %s from database!\n", zone_name2); zone_db_free(zone); free(buf); return 1; } signconf_del = (char*) calloc(strlen(zone_db_signconf_path(zone)) + strlen(".ZONE_DELETED") + 1, sizeof(char)); if (!signconf_del) { ods_log_error("[%s] malloc failed", module_str); zone_db_free(zone); free(buf); return 1; } strncpy(signconf_del, zone_db_signconf_path(zone), strlen(zone_db_signconf_path(zone))); strncat(signconf_del, ".ZONE_DELETED", strlen(".ZONE_DELETED")); rename(zone_db_signconf_path(zone), signconf_del); free(signconf_del); signconf_del = NULL; /* Delete all 'zone' related tasks */ schedule_purge_owner(engine->taskq, TASK_CLASS_ENFORCER, zone_name2); ods_log_info("[%s] zone %s deleted", module_str, zone_name2); client_printf(sockfd, "Deleted zone %s successfully\n", zone_name2); } else if (!zone_name2 && all) { if (!(zone_list = zone_list_db_new_get(dbconn))) { client_printf_err(sockfd, "Unable to get list of zones from database!\n"); free(buf); return 1; } for (zone = zone_list_db_get_next(zone_list); zone; zone_db_free(zone), zone = zone_list_db_get_next(zone_list)) { if (!delete_key_data(zone, dbconn, sockfd)) { continue; } if (zone_db_delete(zone)) { client_printf_err(sockfd, "Unable to delete zone %s from database!\n", zone_db_name(zone)); continue; } signconf_del = (char*) calloc(strlen(zone_db_signconf_path(zone)) + strlen(".ZONE_DELETED") + 1, sizeof(char)); if (!signconf_del) { ods_log_error("[%s] malloc failed", module_str); zone_db_free(zone); zone_list_db_free(zone_list); free(buf); return 1; } strncpy(signconf_del, zone_db_signconf_path(zone), strlen(zone_db_signconf_path(zone))); strncat(signconf_del, ".ZONE_DELETED", strlen(".ZONE_DELETED")); rename(zone_db_signconf_path(zone), signconf_del); free(signconf_del); signconf_del = NULL; /* Delete all 'zone' related tasks */ schedule_purge_owner(engine->taskq, TASK_CLASS_ENFORCER, zone_db_name(zone)); ods_log_info("[%s] zone %s deleted", module_str, zone_db_name(zone)); client_printf(sockfd, "Deleted zone %s successfully\n", zone_db_name(zone)); } zone_list_db_free(zone_list); zone = NULL; client_printf(sockfd, "All zones deleted successfully\n"); } else { client_printf_err(sockfd, "expected either --zone <zone> or --all\n"); free(buf); return -1; } free(buf); if (write_xml) { if (zone) { if (zonelist_update_delete(sockfd, engine->config->zonelist_filename, zone, 1) != ZONELIST_UPDATE_OK) { ods_log_error("[%s] zonelist %s updated failed", module_str, engine->config->zonelist_filename); client_printf_err(sockfd, "Zonelist %s update failed!\n", engine->config->zonelist_filename); ret = 1; } else { ods_log_info("[%s] zonelist %s updated successfully", module_str, engine->config->zonelist_filename); client_printf(sockfd, "Zonelist %s updated successfully\n", engine->config->zonelist_filename); } } else { if (zonelist_export(sockfd, dbconn, engine->config->zonelist_filename, 1) != ZONELIST_EXPORT_OK) { ods_log_error("[%s] zonelist exported to %s failed", module_str, engine->config->zonelist_filename); client_printf_err(sockfd, "Exported zonelist to %s failed!\n", engine->config->zonelist_filename); ret = 1; } else { ods_log_info("[%s] zonelist exported to %s successfully", module_str, engine->config->zonelist_filename); client_printf(sockfd, "Exported zonelist to %s successfully\n", engine->config->zonelist_filename); } } } if (zone) { if (snprintf(path, sizeof(path), "%s/%s", engine->config->working_dir, OPENDNSSEC_ENFORCER_ZONELIST) >= (int)sizeof(path) || zonelist_update_delete(sockfd, path, zone, 0) != ZONELIST_UPDATE_OK) { ods_log_error("[%s] internal zonelist update failed", module_str); client_printf_err(sockfd, "Unable to update the internal zonelist %s, updates will not reach the Signer!\n", path); ret = 1; } else { ods_log_info("[%s] internal zonelist updated successfully", module_str); } } else { if (snprintf(path, sizeof(path), "%s/%s", engine->config->working_dir, OPENDNSSEC_ENFORCER_ZONELIST) >= (int)sizeof(path) || zonelist_export(sockfd, dbconn, path, 0) != ZONELIST_EXPORT_OK) { ods_log_error("[%s] internal zonelist update failed", module_str); client_printf_err(sockfd, "Unable to update the internal zonelist %s, updates will not reach the Signer!\n", path); ret = 1; } else { ods_log_info("[%s] internal zonelist updated successfully", module_str); } } zone_db_free(zone); return ret; }
/** * Start engine. * */ void engine_start(const char* cfgfile, int cmdline_verbosity, int daemonize, int info, int single_run) { engine_type* engine = NULL; int use_syslog = 0; ods_status zl_changed = ODS_STATUS_UNCHANGED; ods_status status = ODS_STATUS_OK; int close_hsm = 0; ods_log_assert(cfgfile); ods_log_init(NULL, use_syslog, cmdline_verbosity); ods_log_verbose("[%s] starting signer", engine_str); /* initialize */ xmlInitGlobals(); xmlInitParser(); xmlInitThreads(); engine = engine_create(); if (!engine) { ods_fatal_exit("[%s] create failed", engine_str); return; } engine->daemonize = daemonize; /* config */ engine->config = engine_config(engine->allocator, cfgfile, cmdline_verbosity); status = engine_config_check(engine->config); if (status != ODS_STATUS_OK) { ods_log_error("[%s] cfgfile %s has errors", engine_str, cfgfile); goto earlyexit; } if (info) { engine_config_print(stdout, engine->config); /* for debugging */ goto earlyexit; } /* check pidfile */ if (!util_check_pidfile(engine->config->pid_filename)) { exit(1); } /* open log */ ods_log_init(engine->config->log_filename, engine->config->use_syslog, engine->config->verbosity); /* setup */ tzset(); /* for portability */ status = engine_setup(engine); if (status != ODS_STATUS_OK) { ods_log_error("[%s] setup failed: %s", engine_str, ods_status2str(status)); engine->need_to_exit = 1; if (status != ODS_STATUS_WRITE_PIDFILE_ERR) { /* command handler had not yet been started */ engine->cmdhandler_done = 1; } } else { /* setup ok, mark hsm open */ close_hsm = 1; } /* run */ while (engine->need_to_exit == 0) { /* update zone list */ lock_basic_lock(&engine->zonelist->zl_lock); zl_changed = zonelist_update(engine->zonelist, engine->config->zonelist_filename); engine->zonelist->just_removed = 0; engine->zonelist->just_added = 0; engine->zonelist->just_updated = 0; lock_basic_unlock(&engine->zonelist->zl_lock); /* start/reload */ if (engine->need_to_reload) { ods_log_info("[%s] signer reloading", engine_str); engine->need_to_reload = 0; } else { ods_log_info("[%s] signer started", engine_str); zl_changed = engine_recover(engine); } if (zl_changed == ODS_STATUS_OK || zl_changed == ODS_STATUS_UNCHANGED) { engine_update_zones(engine, zl_changed); } engine_run(engine, single_run); } /* shutdown */ ods_log_info("[%s] signer shutdown", engine_str); if (close_hsm) { ods_log_verbose("[%s] close hsm", engine_str); hsm_close(); } if (!engine->cmdhandler_done) { engine_stop_xfrhandler(engine); engine_stop_dnshandler(engine); engine_stop_cmdhandler(engine); } earlyexit: if (engine && engine->config) { if (engine->config->pid_filename) { (void)unlink(engine->config->pid_filename); } if (engine->config->clisock_filename) { (void)unlink(engine->config->clisock_filename); } } tsig_handler_cleanup(); engine_cleanup(engine); engine = NULL; ods_log_close(); xmlCleanupParser(); xmlCleanupGlobals(); xmlCleanupThreads(); return; }
/** * NOTIFY. * */ static query_state query_process_notify(query_type* q, ldns_rr_type qtype, void* engine) { engine_type* e = (engine_type*) engine; dnsin_type* dnsin = NULL; uint16_t count = 0; uint16_t rrcount = 0; uint32_t serial = 0; size_t pos = 0; char address[128]; if (!e || !q || !q->zone) { return QUERY_DISCARDED; } ods_log_assert(e->dnshandler); ods_log_assert(q->zone->name); ods_log_debug("[%s] incoming notify for zone %s", query_str, q->zone->name); if (buffer_pkt_rcode(q->buffer) != LDNS_RCODE_NOERROR || buffer_pkt_qr(q->buffer) || !buffer_pkt_aa(q->buffer) || buffer_pkt_tc(q->buffer) || buffer_pkt_rd(q->buffer) || buffer_pkt_ra(q->buffer) || buffer_pkt_ad(q->buffer) || buffer_pkt_cd(q->buffer) || buffer_pkt_qdcount(q->buffer) != 1 || buffer_pkt_ancount(q->buffer) > 1 || qtype != LDNS_RR_TYPE_SOA) { return query_formerr(q); } if (!q->zone->adinbound || q->zone->adinbound->type != ADAPTER_DNS) { ods_log_error("[%s] zone %s is not configured to have input dns " "adapter", query_str, q->zone->name); return query_notauth(q); } ods_log_assert(q->zone->adinbound->config); dnsin = (dnsin_type*) q->zone->adinbound->config; if (!acl_find(dnsin->allow_notify, &q->addr, q->tsig_rr)) { if (addr2ip(q->addr, address, sizeof(address))) { ods_log_info("[%s] unauthorized notify for zone %s from client %s: " "no acl matches", query_str, q->zone->name, address); } else { ods_log_info("[%s] unauthorized notify for zone %s from unknown " "client: no acl matches", query_str, q->zone->name); } return query_notauth(q); } ods_log_assert(q->zone->xfrd); /* skip header and question section */ buffer_skip(q->buffer, BUFFER_PKT_HEADER_SIZE); count = buffer_pkt_qdcount(q->buffer); for (rrcount = 0; rrcount < count; rrcount++) { if (!buffer_skip_rr(q->buffer, 1)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad question section)", query_str, q->zone->name); return QUERY_DISCARDED; } } pos = buffer_position(q->buffer); /* examine answer section */ count = buffer_pkt_ancount(q->buffer); if (count) { if (!buffer_skip_dname(q->buffer) || !query_parse_soa(q->buffer, &serial)) { ods_log_error("[%s] dropped packet: zone %s received bad notify " "(bad soa in answer section)", query_str, q->zone->name); return QUERY_DISCARDED; } lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = serial; q->zone->xfrd->serial_notify_acquired = time_now(); if (!util_serial_gt(q->zone->xfrd->serial_notify, q->zone->xfrd->serial_disk)) { ods_log_debug("[%s] ignore notify: already got zone %s serial " "%u on disk", query_str, q->zone->name, q->zone->xfrd->serial_notify); lock_basic_unlock(&q->zone->xfrd->serial_lock); goto send_notify_ok; } lock_basic_unlock(&q->zone->xfrd->serial_lock); } else { lock_basic_lock(&q->zone->xfrd->serial_lock); q->zone->xfrd->serial_notify = 0; q->zone->xfrd->serial_notify_acquired = 0; lock_basic_unlock(&q->zone->xfrd->serial_lock); } /* forward notify to xfrd */ xfrd_set_timer_now(q->zone->xfrd); dnshandler_fwd_notify(e->dnshandler, buffer_begin(q->buffer), buffer_remaining(q->buffer)); send_notify_ok: /* send notify ok */ buffer_pkt_set_qr(q->buffer); buffer_pkt_set_aa(q->buffer); buffer_pkt_set_ancount(q->buffer, 0); buffer_clear(q->buffer); /* lim = pos, pos = 0; */ buffer_set_position(q->buffer, pos); buffer_set_limit(q->buffer, buffer_capacity(q->buffer)); q->reserved_space = edns_rr_reserved_space(q->edns_rr); q->reserved_space += tsig_rr_reserved_space(q->tsig_rr); return QUERY_PROCESSED; }
/** * Start engine. * */ int engine_start(const char* cfgfile, int cmdline_verbosity, int daemonize, int info, int single_run) { engine_type* engine = NULL; ods_status zl_changed = ODS_STATUS_UNCHANGED; ods_status status = ODS_STATUS_OK; engine = engine_create(); if (!engine) { ods_fatal_exit("[%s] create failed", engine_str); return 1; } engine->daemonize = daemonize; /* config */ engine->config = engine_config(cfgfile, cmdline_verbosity); status = engine_config_check(engine->config); if (status != ODS_STATUS_OK) { ods_log_error("[%s] cfgfile %s has errors", engine_str, cfgfile); goto earlyexit; } if (info) { engine_config_print(stdout, engine->config); /* for debugging */ goto earlyexit; } /* check pidfile */ if (!util_check_pidfile(engine->config->pid_filename)) { exit(1); } /* open log */ ods_log_init("ods-signerd", engine->config->use_syslog, engine->config->log_filename, engine->config->verbosity); /* setup */ status = engine_setup(engine); if (status != ODS_STATUS_OK) { ods_log_error("[%s] setup failed: %s", engine_str, ods_status2str(status)); if (status != ODS_STATUS_WRITE_PIDFILE_ERR) { /* command handler had not yet been started */ engine->cmdhandler_done = 1; } goto earlyexit; } /* run */ while (engine->need_to_exit == 0) { /* update zone list */ lock_basic_lock(&engine->zonelist->zl_lock); zl_changed = zonelist_update(engine->zonelist, engine->config->zonelist_filename); engine->zonelist->just_removed = 0; engine->zonelist->just_added = 0; engine->zonelist->just_updated = 0; lock_basic_unlock(&engine->zonelist->zl_lock); /* start/reload */ if (engine->need_to_reload) { ods_log_info("[%s] signer reloading", engine_str); fifoq_wipe(engine->signq); engine->need_to_reload = 0; } else { ods_log_info("[%s] signer started (version %s), pid %u", engine_str, PACKAGE_VERSION, engine->pid); if (hsm_open2(engine->config->repositories, hsm_check_pin) != HSM_OK) { char* error = hsm_get_error(NULL); if (error != NULL) { ods_log_error("[%s] %s", "hsm", error); free(error); } ods_log_error("[%s] opening hsm failed (for engine recover)", engine_str); break; } zl_changed = engine_recover(engine); hsm_close(); } if (zl_changed == ODS_STATUS_OK || zl_changed == ODS_STATUS_UNCHANGED) { engine_update_zones(engine, zl_changed); } if (hsm_open2(engine->config->repositories, hsm_check_pin) != HSM_OK) { char* error = hsm_get_error(NULL); if (error != NULL) { ods_log_error("[%s] %s", "hsm", error); free(error); } ods_log_error("[%s] opening hsm failed (for engine run)", engine_str); break; } engine_run(engine, single_run); hsm_close(); } /* shutdown */ ods_log_info("[%s] signer shutdown", engine_str); engine_stop_cmdhandler(engine); engine_stop_xfrhandler(engine); engine_stop_dnshandler(engine); earlyexit: if (engine && engine->config) { if (engine->config->pid_filename) { (void)unlink(engine->config->pid_filename); } if (engine->config->clisock_filename) { (void)unlink(engine->config->clisock_filename); } } tsig_handler_cleanup(); engine_cleanup(engine); engine = NULL; return 1; }
/** * Read zone from input adapter. * */ ods_status tools_input(zone_type* zone) { ods_status status = ODS_STATUS_OK; time_t start = 0; time_t end = 0; ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->adinbound); ods_log_assert(zone->signconf); /* Key Rollover? */ status = zone_publish_dnskeys(zone, 0); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to read zone %s: failed to " "publish dnskeys (%s)", tools_str, zone->name, ods_status2str(status)); zone_rollback_dnskeys(zone); zone_rollback_nsec3param(zone); namedb_rollback(zone->db, 0); return status; } /* Denial of Existence Rollover? */ if (!zone->signconf->passthrough) status = zone_publish_nsec3param(zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to read zone %s: failed to " "publish nsec3param (%s)", tools_str, zone->name, ods_status2str(status)); zone_rollback_dnskeys(zone); zone_rollback_nsec3param(zone); namedb_rollback(zone->db, 0); return status; } if (zone->stats) { pthread_mutex_lock(&zone->stats->stats_lock); zone->stats->sort_done = 0; zone->stats->sort_count = 0; zone->stats->sort_time = 0; pthread_mutex_unlock(&zone->stats->stats_lock); } /* Input Adapter */ start = time(NULL); status = adapter_read((void*)zone); if (status != ODS_STATUS_OK && status != ODS_STATUS_UNCHANGED) { if (status == ODS_STATUS_XFRINCOMPLETE) { ods_log_info("[%s] read zone %s: xfr in progress", tools_str, zone->name); } else { ods_log_error("[%s] unable to read zone %s: adapter failed (%s)", tools_str, zone->name, ods_status2str(status)); } zone_rollback_dnskeys(zone); zone_rollback_nsec3param(zone); namedb_rollback(zone->db, 0); } end = time(NULL); if ((status == ODS_STATUS_OK || status == ODS_STATUS_UNCHANGED) && zone->stats) { pthread_mutex_lock(&zone->stats->stats_lock); zone->stats->start_time = start; zone->stats->sort_time = (end-start); zone->stats->sort_done = 1; pthread_mutex_unlock(&zone->stats->stats_lock); } return status; }
/** * Read IXFR from file. * */ static ods_status addns_read_file(FILE* fd, zone_type* zone) { ldns_rr* rr = NULL; uint32_t new_serial = 0; uint32_t old_serial = 0; uint32_t tmp_serial = 0; ldns_rdf* prev = NULL; ldns_rdf* orig = NULL; ldns_rdf* dname = NULL; uint32_t ttl = 0; size_t rr_count = 0; ods_status result = ODS_STATUS_OK; ldns_status status = LDNS_STATUS_OK; char line[SE_ADFILE_MAXLINE]; unsigned is_axfr = 0; unsigned del_mode = 0; unsigned soa_seen = 0; unsigned line_update_interval = 100000; unsigned line_update = line_update_interval; unsigned l = 0; ods_log_assert(fd); ods_log_assert(zone); /* $ORIGIN <zone name> */ dname = adapi_get_origin(zone); if (!dname) { ods_log_error("[%s] error getting default value for $ORIGIN", adapter_str); return ODS_STATUS_ERR; } orig = ldns_rdf_clone(dname); if (!orig) { ods_log_error("[%s] error setting default value for $ORIGIN", adapter_str); return ODS_STATUS_ERR; } /* $TTL <default ttl> */ ttl = adapi_get_ttl(zone); /* read RRs */ while ((rr = addns_read_rr(fd, line, &orig, &prev, &ttl, &status, &l)) != NULL) { /* check status */ if (status != LDNS_STATUS_OK) { ods_log_error("[%s] error reading RR at line %i (%s): %s", adapter_str, l, ldns_get_errorstr_by_id(status), line); result = ODS_STATUS_ERR; break; } /* debug update */ if (l > line_update) { ods_log_debug("[%s] ...at line %i: %s", adapter_str, l, line); line_update += line_update_interval; } /* first RR: check if SOA and correct zone & serialno */ if (rr_count == 0) { rr_count++; if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_SOA) { ods_log_error("[%s] bad xfr, first rr is not soa", adapter_str); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_ERR; break; } soa_seen++; if (ldns_dname_compare(ldns_rr_owner(rr), zone->apex)) { ods_log_error("[%s] bad xfr, soa dname not equal to zone " "dname %s", adapter_str, zone->name); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_ERR; break; } tmp_serial = ldns_rdf2native_int32(ldns_rr_rdf(rr, SE_SOA_RDATA_SERIAL)); old_serial = adapi_get_serial(zone); if (!util_serial_gt(tmp_serial, old_serial)) { ods_log_info("[%s] zone %s is already up to date, have " "serial %u, got serial %u", adapter_str, zone->name, old_serial, tmp_serial); new_serial = tmp_serial; ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_UNCHANGED; break; } ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_OK; continue; } /* second RR: if not soa, this is an AXFR */ if (rr_count == 1) { if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_SOA) { ods_log_verbose("[%s] detected axfr serial=%u for zone %s", adapter_str, tmp_serial, zone->name); new_serial = tmp_serial; is_axfr = 1; del_mode = 0; } else { ods_log_verbose("[%s] detected ixfr serial=%u for zone %s", adapter_str, tmp_serial, zone->name); new_serial = tmp_serial; tmp_serial = ldns_rdf2native_int32(ldns_rr_rdf(rr, SE_SOA_RDATA_SERIAL)); ldns_rr_free(rr); rr = NULL; rr_count++; if (tmp_serial < new_serial) { del_mode = 1; result = ODS_STATUS_OK; continue; } else { ods_log_error("[%s] bad xfr for zone %s, bad soa serial", adapter_str, zone->name); result = ODS_STATUS_ERR; break; } } } /* soa means swap */ rr_count++; if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { if (!is_axfr) { tmp_serial = ldns_rdf2native_int32(ldns_rr_rdf(rr, SE_SOA_RDATA_SERIAL)); if (tmp_serial <= new_serial) { if (tmp_serial == new_serial) { soa_seen++; } del_mode = !del_mode; ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_OK; continue; } else { ods_log_assert(tmp_serial > new_serial); ods_log_error("[%s] bad xfr for zone %s, bad soa serial", adapter_str, zone->name); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_ERR; break; } } else { /* for axfr */ soa_seen++; } } /* [add to/remove from] the zone */ if (!is_axfr && del_mode) { ods_log_debug("[%s] delete RR #%i at line %i: %s", adapter_str, rr_count, l, line); result = adapi_del_rr(zone, rr, 0); ldns_rr_free(rr); rr = NULL; } else { ods_log_debug("[%s] add RR #%i at line %i: %s", adapter_str, rr_count, l, line); result = adapi_add_rr(zone, rr, 0); } if (result == ODS_STATUS_UNCHANGED) { ods_log_debug("[%s] skipping RR at line %i (%s): %s", adapter_str, l, del_mode?"not found":"duplicate", line); ldns_rr_free(rr); rr = NULL; result = ODS_STATUS_OK; continue; } else if (result != ODS_STATUS_OK) { ods_log_error("[%s] error %s RR at line %i: %s", adapter_str, del_mode?"deleting":"adding", l, line); ldns_rr_free(rr); rr = NULL; break; } } /* and done */ if (orig) { ldns_rdf_deep_free(orig); orig = NULL; } if (prev) { ldns_rdf_deep_free(prev); prev = NULL; } if (result == ODS_STATUS_OK && status != LDNS_STATUS_OK) { ods_log_error("[%s] error reading RR at line %i (%s): %s", adapter_str, l, ldns_get_errorstr_by_id(status), line); result = ODS_STATUS_ERR; } /* check the number of SOAs seen */ if (result == ODS_STATUS_OK) { if ((is_axfr && soa_seen != 2) || (!is_axfr && soa_seen != 3)) { ods_log_error("[%s] bad %s, wrong number of SOAs (%u)", adapter_str, is_axfr?"axfr":"ixfr", soa_seen); result = ODS_STATUS_ERR; } } /* input zone ok, set inbound serial and apply differences */ if (result == ODS_STATUS_OK || result == ODS_STATUS_UNCHANGED) { adapi_set_serial(zone, new_serial); if (is_axfr) { adapi_trans_full(zone); } else { adapi_trans_diff(zone); } if (result == ODS_STATUS_UNCHANGED) { result = ODS_STATUS_OK; } } return result; }