/** * (Create) and change ownership of directories * */ void ods_chown(const char* file, uid_t uid, gid_t gid, int getdir) { char* dir = NULL; if (!file) { ods_log_warning("[%s] no filename given for chown()", file_str); return; } if (!getdir) { ods_log_debug("[%s] create and chown %s with user=%ld group=%ld", file_str, file, (signed long) uid, (signed long) gid); if (chown(file, uid, gid) != 0) { ods_log_error("[%s] chown() %s failed: %s", file_str, file, strerror(errno)); } } else if ((dir = ods_dir_name(file)) != NULL) { ods_log_debug("[%s] create and chown %s with user=%ld group=%ld", file_str, dir, (signed long) uid, (signed long) gid); if (chown(dir, uid, gid) != 0) { ods_log_error("[%s] chown() %s failed: %s", file_str, dir, strerror(errno)); } free((void*) dir); } else { ods_log_warning("[%s] use of relative path: %s", file_str, file); } }
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; } }
/** * Convert a duration to a time. * */ time_t duration2time(duration_type* duration) { time_t period = 0; char* dstr = NULL; if (duration) { period += (duration->seconds); period += (duration->minutes)*60; period += (duration->hours)*3600; period += (duration->days)*86400; period += (duration->weeks)*86400*7; period += (duration->months)*86400*31; period += (duration->years)*86400*365; if (duration->months || duration->years) { /* [TODO] calculate correct number of days in this month/year */ dstr = duration2string(duration); ods_log_warning("[%s] converting duration %s to approximate value", duration_str, dstr?dstr:"(null)"); free((void*) dstr); } } return period; }
/** * Add zone. * */ zone_type* zonelist_add_zone(zonelist_type* zlist, zone_type* zone) { ldns_rbnode_t* new_node = NULL; if (!zone) { return NULL; } if (!zlist || !zlist->zones) { zone_cleanup(zone); return NULL; } /* look up */ if (zonelist_lookup_zone(zlist, zone) != NULL) { ods_log_warning("[%s] unable to add zone %s: already present", zl_str, zone->name); zone_cleanup(zone); return NULL; } /* add */ new_node = zone2node(zone); if (ldns_rbtree_insert(zlist->zones, new_node) == NULL) { ods_log_error("[%s] unable to add zone %s: ldns_rbtree_insert() " "failed", zl_str, zone->name); free((void*) new_node); zone_cleanup(zone); return NULL; } zone->zl_status = ZONE_ZL_ADDED; zlist->just_added++; return zone; }
int handled_keystate_list_cmd(int sockfd, engine_type* engine, const char *cmd, ssize_t n) { char buf[ODS_SE_MAXLINE]; const char *argv[8]; const int NARGV = sizeof(argv)/sizeof(char*); int argc; const char *scmd = "key list"; cmd = ods_check_command(cmd,n,scmd); if (!cmd) return 0; // not handled ods_log_debug("[%s] %s command", module_str, scmd); // Use buf as an intermediate buffer for the command. strncpy(buf,cmd,sizeof(buf)); buf[sizeof(buf)-1] = '\0'; // separate the arguments argc = ods_str_explode(buf,NARGV,argv); if (argc > NARGV) { ods_log_warning("[%s] too many arguments for %s command", module_str,scmd); ods_printf(sockfd,"too many arguments\n"); help_keystate_list_cmd(sockfd); return 1; // errors, but handled } bool bVerbose = ods_find_arg(&argc,argv,"verbose","v") != -1; bool bDebug = ods_find_arg(&argc,argv,"debug","d") != -1; if (argc) { ods_log_warning("[%s] unknown arguments for %s command", module_str,scmd); ods_printf(sockfd,"unknown arguments\n"); help_keystate_list_cmd(sockfd); return 1; // errors, but handled } time_t tstart = time(NULL); perform_keystate_list(sockfd, engine->config, bVerbose, bDebug); ods_printf(sockfd,"%s completed in %ld seconds.\n",scmd,time(NULL)-tstart); return 1; }
/** * Reopen HSM. * */ int lhsm_reopen(const char* filename) { if (hsm_check_context(NULL) != HSM_OK) { ods_log_warning("[%s] idle libhsm connection, trying to reopen", hsm_str); hsm_close(); return lhsm_open(filename); } return HSM_OK; }
/** * Enable notify. * */ void notify_enable(notify_type* notify, ldns_rr* soa) { xfrhandler_type* xfrhandler = NULL; zone_type* zone = NULL; dnsout_type* dnsout = NULL; if (!notify) { return; } xfrhandler = (xfrhandler_type*) notify->xfrhandler; ods_log_assert(xfrhandler); zone = (zone_type*) notify->zone; ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->adoutbound); ods_log_assert(zone->adoutbound->config); ods_log_assert(zone->adoutbound->type == ADAPTER_DNS); dnsout = (dnsout_type*) zone->adoutbound->config; if (!dnsout->do_notify) { ods_log_warning("[%s] zone %s has no notify acl", notify_str, zone->name); return; /* nothing to do */ } notify_update_soa(notify, soa); if (notify->is_waiting) { ods_log_debug("[%s] zone %s already on waiting list", notify_str, zone->name); return; } if (xfrhandler->notify_udp_num < NOTIFY_MAX_UDP) { notify_setup(notify); xfrhandler->notify_udp_num++; ods_log_debug("[%s] zone %s notify enabled", notify_str, zone->name); return; } /* put it in waiting list */ notify->secondary = dnsout->do_notify; notify->is_waiting = 1; notify->waiting_next = NULL; if (xfrhandler->notify_waiting_last) { xfrhandler->notify_waiting_last->waiting_next = notify; } else { xfrhandler->notify_waiting_first = notify; } xfrhandler->notify_waiting_last = notify; notify->handler.timeout = NULL; ods_log_debug("[%s] zone %s notify on waiting list", notify_str, zone->name); return; }
/** * Delete RR. * */ ods_status zone_del_rr(zone_type* zone, ldns_rr* rr, int do_stats) { domain_type* domain = NULL; rrset_type* rrset = NULL; rr_type* record = NULL; ods_log_assert(rr); ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->db); ods_log_assert(zone->signconf); domain = namedb_lookup_domain(zone->db, ldns_rr_owner(rr)); if (!domain) { ods_log_warning("[%s] unable to delete RR from zone %s: " "domain not found", zone_str, zone->name); return ODS_STATUS_UNCHANGED; } rrset = domain_lookup_rrset(domain, ldns_rr_get_type(rr)); if (!rrset) { ods_log_warning("[%s] unable to delete RR from zone %s: " "RRset not found", zone_str, zone->name); return ODS_STATUS_UNCHANGED; } record = rrset_lookup_rr(rrset, rr); if (!record) { ods_log_error("[%s] unable to delete RR from zone %s: " "RR not found", zone_str, zone->name); return ODS_STATUS_UNCHANGED; } record->is_removed = 1; record->is_added = 0; /* unset is_added */ /* update stats */ if (do_stats && zone->stats) { zone->stats->sort_count -= 1; } return ODS_STATUS_OK; }
/** * Run engine, run!. * */ static void engine_run(engine_type* engine, int single_run) { if (!engine) { return; } engine_start_workers(engine); engine_start_drudgers(engine); lock_basic_lock(&engine->signal_lock); engine->signal = SIGNAL_RUN; lock_basic_unlock(&engine->signal_lock); while (!engine->need_to_exit && !engine->need_to_reload) { lock_basic_lock(&engine->signal_lock); engine->signal = signal_capture(engine->signal); switch (engine->signal) { case SIGNAL_RUN: ods_log_assert(1); break; case SIGNAL_RELOAD: engine->need_to_reload = 1; break; case SIGNAL_SHUTDOWN: engine->need_to_exit = 1; break; default: ods_log_warning("[%s] invalid signal %d captured, " "keep running", engine_str, signal); engine->signal = SIGNAL_RUN; break; } lock_basic_unlock(&engine->signal_lock); if (single_run) { engine->need_to_exit = engine_all_zones_processed(engine); } lock_basic_lock(&engine->signal_lock); if (engine->signal == SIGNAL_RUN && !single_run) { ods_log_debug("[%s] taking a break", engine_str); lock_basic_sleep(&engine->signal_cond, &engine->signal_lock, 3600); } lock_basic_unlock(&engine->signal_lock); } ods_log_debug("[%s] signer halted", engine_str); engine_stop_drudgers(engine); engine_stop_workers(engine); (void)lhsm_reopen(engine->config->cfg_filename); return; }
int ods_log_get_facility(const char* facility) { int length; if (!facility) { return LOG_DAEMON; } length = strlen(facility); if (length == 4 && strncasecmp(facility, "KERN", 4) == 0) return LOG_KERN; else if (length == 4 && strncasecmp(facility, "USER", 4) == 0) return LOG_USER; else if (length == 4 && strncasecmp(facility, "MAIL", 4) == 0) return LOG_MAIL; else if (length == 6 && strncasecmp(facility, "DAEMON", 6) == 0) return LOG_DAEMON; else if (length == 4 && strncasecmp(facility, "AUTH", 4) == 0) return LOG_AUTH; else if (length == 3 && strncasecmp(facility, "LPR", 3) == 0) return LOG_LPR; else if (length == 4 && strncasecmp(facility, "NEWS", 4) == 0) return LOG_NEWS; else if (length == 4 && strncasecmp(facility, "UUCP", 4) == 0) return LOG_UUCP; else if (length == 4 && strncasecmp(facility, "CRON", 4) == 0) return LOG_CRON; else if (length == 6 && strncasecmp(facility, "LOCAL0", 6) == 0) return LOG_LOCAL0; else if (length == 6 && strncasecmp(facility, "LOCAL1", 6) == 0) return LOG_LOCAL1; else if (length == 6 && strncasecmp(facility, "LOCAL2", 6) == 0) return LOG_LOCAL2; else if (length == 6 && strncasecmp(facility, "LOCAL3", 6) == 0) return LOG_LOCAL3; else if (length == 6 && strncasecmp(facility, "LOCAL4", 6) == 0) return LOG_LOCAL4; else if (length == 6 && strncasecmp(facility, "LOCAL5", 6) == 0) return LOG_LOCAL5; else if (length == 6 && strncasecmp(facility, "LOCAL6", 6) == 0) return LOG_LOCAL6; else if (length == 6 && strncasecmp(facility, "LOCAL7", 6) == 0) return LOG_LOCAL7; ods_log_warning("[%s] syslog facility %s not supported, logging to " "log_daemon", log_str, facility); return LOG_DAEMON; }
static int run(int sockfd, engine_type* engine, const char *cmd, ssize_t n, db_connection_t *dbconn) { #define NARGV 8 char buf[ODS_SE_MAXLINE]; const char *argv[NARGV]; int argc; const char *zone = NULL; (void)engine; ods_log_debug("[%s] %s command", module_str, rollover_list_funcblock()->cmdname); cmd = ods_check_command(cmd, n, rollover_list_funcblock()->cmdname); /* Use buf as an intermediate buffer for the command.*/ strncpy(buf, cmd,sizeof(buf)); buf[sizeof(buf)-1] = '\0'; /* separate the arguments*/ argc = ods_str_explode(buf, NARGV, argv); if (argc > NARGV) { ods_log_warning("[%s] too many arguments for %s command", module_str, rollover_list_funcblock()->cmdname); client_printf(sockfd,"too many arguments\n"); return -1; } (void)ods_find_arg_and_param(&argc,argv,"zone","z",&zone); if (argc) { ods_log_warning("[%s] unknown arguments for %s command", module_str, rollover_list_funcblock()->cmdname); client_printf(sockfd,"unknown arguments\n"); return -1; } return perform_rollover_list(sockfd, zone, dbconn); }
/** * Check the HSM connection, reload engine if necessary. * */ void lhsm_check_connection(void* engine) { engine_type* e = (engine_type*) engine; if (hsm_check_context(NULL) != HSM_OK) { ods_log_warning("[%s] idle libhsm connection, trying to reopen", hsm_str); engine_stop_drudgers(e); hsm_close(); (void)lhsm_open(e->config->cfg_filename); engine_start_drudgers((engine_type*) engine); } else { ods_log_debug("[%s] libhsm connection ok", hsm_str); } return; }
/** * Load signer configuration for zone. * */ ods_status zone_load_signconf(zone_type* zone, signconf_type** new_signconf) { ods_status status = ODS_STATUS_OK; signconf_type* signconf = NULL; char* datestamp = NULL; if (!zone || !zone->name || !zone->signconf) { return ODS_STATUS_ASSERT_ERR; } if (!zone->signconf_filename) { ods_log_warning("[%s] zone %s has no signconf filename, treat as " "insecure?", zone_str, zone->name); return ODS_STATUS_INSECURE; } status = signconf_update(&signconf, zone->signconf_filename, zone->signconf->last_modified); if (status == ODS_STATUS_OK) { if (!signconf) { /* this is unexpected */ ods_log_alert("[%s] unable to load signconf for zone %s: signconf " "status ok but no signconf stored", zone_str, zone->name); return ODS_STATUS_ASSERT_ERR; } (void)time_datestamp(signconf->last_modified, "%Y-%m-%d %T", &datestamp); ods_log_debug("[%s] zone %s signconf file %s is modified since %s", zone_str, zone->name, zone->signconf_filename, datestamp?datestamp:"Unknown"); free((void*)datestamp); *new_signconf = signconf; } else if (status == ODS_STATUS_UNCHANGED) { (void)time_datestamp(zone->signconf->last_modified, "%Y-%m-%d %T", &datestamp); ods_log_verbose("[%s] zone %s signconf file %s is unchanged since " "%s", zone_str, zone->name, zone->signconf_filename, datestamp?datestamp:"Unknown"); free((void*)datestamp); } else { ods_log_error("[%s] unable to load signconf for zone %s: signconf %s " "%s", zone_str, zone->name, zone->signconf_filename, ods_status2str(status)); } return status; }
/** * Initialize logging. * */ void ods_log_init(const char *filename, int use_syslog, int verbosity) { #ifdef HAVE_SYSLOG_H int facility; #endif /* HAVE_SYSLOG_H */ ods_log_verbose("[%s] switching log to %s verbosity %i (log level %i)", log_str, use_syslog?"syslog":(filename&&filename[0]?filename:"stderr"), verbosity, verbosity+2); if (logfile && logfile != stderr) { ods_fclose(logfile); } log_level = verbosity + 2; #ifdef HAVE_SYSLOG_H if(logging_to_syslog) { closelog(); logging_to_syslog = 0; } if(use_syslog) { facility = ods_log_get_facility(filename); openlog(MY_PACKAGE_TARNAME, LOG_NDELAY, facility); logging_to_syslog = 1; return; } #endif /* HAVE_SYSLOG_H */ if(filename && filename[0]) { logfile = ods_fopen(filename, NULL, "a"); if (logfile) { ods_log_debug("[%s] new logfile %s", log_str, filename); return; } logfile = stderr; ods_log_warning("[%s] cannot open %s for appending, logging to " "stderr", log_str, filename); } else { logfile = stderr; } return; }
/** * Delete zone. * */ zone_type* zonelist_del_zone(zonelist_type* zlist, zone_type* zone) { ldns_rbnode_t* old_node = LDNS_RBTREE_NULL; if (!zone) { return NULL; } if (!zlist || !zlist->zones) { goto zone_not_present; } old_node = ldns_rbtree_delete(zlist->zones, zone); if (!old_node) { goto zone_not_present; } free((void*) old_node); return zone; zone_not_present: ods_log_warning("[%s] unable to delete zone %s: not present", zl_str, zone->name); return zone; }
/** * Convert a duration to a time. * */ time_t duration2time(duration_type* duration) { time_t period = 0; if (duration) { period += (duration->seconds); period += (duration->minutes)*60; period += (duration->hours)*3600; period += (duration->days)*86400; period += (duration->weeks)*86400*7; period += (duration->months)*86400*31; period += (duration->years)*86400*365; if (duration->months || duration->years) { /* [TODO] calculate correct number of days in this month/year */ region_type* tmpregion = region_create(); char* dstr = duration2str(tmpregion, duration); ods_log_warning("[%s] converting duration %s to approximate value", logstr, dstr?dstr:"(null)"); region_cleanup(tmpregion); } } return period; }
/** * Try to recover from the backup files. * */ static ods_status engine_recover(engine_type* engine) { ldns_rbnode_t* node = LDNS_RBTREE_NULL; zone_type* zone = NULL; ods_status status = ODS_STATUS_OK; ods_status result = ODS_STATUS_UNCHANGED; if (!engine || !engine->zonelist || !engine->zonelist->zones) { ods_log_error("[%s] cannot recover zones: no engine or zonelist", engine_str); return ODS_STATUS_ERR; /* no need to update zones */ } ods_log_assert(engine); ods_log_assert(engine->zonelist); ods_log_assert(engine->zonelist->zones); lock_basic_lock(&engine->zonelist->zl_lock); /* [LOCK] zonelist */ node = ldns_rbtree_first(engine->zonelist->zones); while (node && node != LDNS_RBTREE_NULL) { zone = (zone_type*) node->data; ods_log_assert(zone->zl_status == ZONE_ZL_ADDED); status = zone_recover2(zone); if (status == ODS_STATUS_OK) { ods_log_assert(zone->task); ods_log_assert(zone->db); ods_log_assert(zone->signconf); /* notify nameserver */ if (engine->config->notify_command && !zone->notify_ns) { set_notify_ns(zone, engine->config->notify_command); } /* schedule task */ lock_basic_lock(&engine->taskq->schedule_lock); /* [LOCK] schedule */ status = schedule_task(engine->taskq, (task_type*) zone->task, 0); /* [UNLOCK] schedule */ lock_basic_unlock(&engine->taskq->schedule_lock); if (status != ODS_STATUS_OK) { ods_log_crit("[%s] unable to schedule task for zone %s: %s", engine_str, zone->name, ods_status2str(status)); task_cleanup((task_type*) zone->task); zone->task = NULL; result = ODS_STATUS_OK; /* will trigger update zones */ } else { ods_log_debug("[%s] recovered zone %s", engine_str, zone->name); /* recovery done */ zone->zl_status = ZONE_ZL_OK; } } else { if (status != ODS_STATUS_UNCHANGED) { ods_log_warning("[%s] unable to recover zone %s from backup," " performing full sign", engine_str, zone->name); } result = ODS_STATUS_OK; /* will trigger update zones */ } node = ldns_rbtree_next(node); } /* [UNLOCK] zonelist */ lock_basic_unlock(&engine->zonelist->zl_lock); return result; }
/** * Update zones. * */ void engine_update_zones(engine_type* engine, ods_status zl_changed) { ldns_rbnode_t* node = LDNS_RBTREE_NULL; zone_type* zone = NULL; zone_type* delzone = NULL; task_type* task = NULL; ods_status status = ODS_STATUS_OK; unsigned wake_up = 0; int warnings = 0; time_t now = 0; if (!engine || !engine->zonelist || !engine->zonelist->zones) { return; } now = time_now(); ods_log_debug("[%s] commit zone list changes", engine_str); lock_basic_lock(&engine->zonelist->zl_lock); node = ldns_rbtree_first(engine->zonelist->zones); while (node && node != LDNS_RBTREE_NULL) { zone = (zone_type*) node->data; task = NULL; /* reset task */ if (zone->zl_status == ZONE_ZL_REMOVED) { node = ldns_rbtree_next(node); lock_basic_lock(&zone->zone_lock); delzone = zonelist_del_zone(engine->zonelist, zone); if (delzone) { lock_basic_lock(&engine->taskq->schedule_lock); task = unschedule_task(engine->taskq, (task_type*) zone->task); lock_basic_unlock(&engine->taskq->schedule_lock); } task_cleanup(task); task = NULL; lock_basic_unlock(&zone->zone_lock); netio_remove_handler(engine->xfrhandler->netio, &zone->xfrd->handler); zone_cleanup(zone); zone = NULL; continue; } else if (zone->zl_status == ZONE_ZL_ADDED) { lock_basic_lock(&zone->zone_lock); ods_log_assert(!zone->task); /* set notify nameserver command */ if (engine->config->notify_command && !zone->notify_ns) { set_notify_ns(zone, engine->config->notify_command); } /* create task */ task = task_create(TASK_SIGNCONF, now, zone); lock_basic_unlock(&zone->zone_lock); if (!task) { ods_log_crit("[%s] unable to create task for zone %s: " "task_create() failed", engine_str, zone->name); node = ldns_rbtree_next(node); continue; } } /* load adapter config */ status = adapter_load_config(zone->adinbound); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to load config for inbound adapter " "for zone %s: %s", engine_str, zone->name, ods_status2str(status)); } status = adapter_load_config(zone->adoutbound); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to load config for outbound adapter " "for zone %s: %s", engine_str, zone->name, ods_status2str(status)); } /* for dns adapters */ warnings += dnsconfig_zone(engine, zone); if (zone->zl_status == ZONE_ZL_ADDED) { ods_log_assert(task); lock_basic_lock(&zone->zone_lock); zone->task = task; lock_basic_unlock(&zone->zone_lock); lock_basic_lock(&engine->taskq->schedule_lock); status = schedule_task(engine->taskq, task, 0); lock_basic_unlock(&engine->taskq->schedule_lock); } else if (zl_changed == ODS_STATUS_OK) { /* always try to update signconf */ lock_basic_lock(&zone->zone_lock); status = zone_reschedule_task(zone, engine->taskq, TASK_SIGNCONF); lock_basic_unlock(&zone->zone_lock); } if (status != ODS_STATUS_OK) { ods_log_crit("[%s] unable to schedule task for zone %s: %s", engine_str, zone->name, ods_status2str(status)); } else { wake_up = 1; zone->zl_status = ZONE_ZL_OK; } node = ldns_rbtree_next(node); } lock_basic_unlock(&engine->zonelist->zl_lock); if (engine->dnshandler) { dnshandler_fwd_notify(engine->dnshandler, (uint8_t*) ODS_SE_NOTIFY_CMD, strlen(ODS_SE_NOTIFY_CMD)); } else if (warnings) { ods_log_warning("[%s] no dnshandler/listener configured, but zones " "are configured with dns adapters: notify and zone transfer " "requests will not work properly", engine_str); } if (wake_up) { engine_wakeup_workers(engine); } return; }
/** * Process query. * */ query_state query_process(query_type* q, void* engine) { ldns_status status = LDNS_STATUS_OK; ldns_pkt* pkt = NULL; ldns_rr* rr = NULL; ldns_pkt_rcode rcode = LDNS_RCODE_NOERROR; ldns_pkt_opcode opcode = LDNS_PACKET_QUERY; ldns_rr_type qtype = LDNS_RR_TYPE_SOA; engine_type* e = (engine_type*) engine; ods_log_assert(e); ods_log_assert(q); ods_log_assert(q->buffer); if (!e || !q || !q->buffer) { ods_log_error("[%s] drop query: assertion error", query_str); return QUERY_DISCARDED; /* should not happen */ } if (buffer_limit(q->buffer) < BUFFER_PKT_HEADER_SIZE) { ods_log_debug("[%s] drop query: packet too small", query_str); return QUERY_DISCARDED; /* too small */ } if (buffer_pkt_qr(q->buffer)) { ods_log_debug("[%s] drop query: qr bit set", query_str); return QUERY_DISCARDED; /* not a query */ } /* parse packet */ status = ldns_wire2pkt(&pkt, buffer_current(q->buffer), buffer_remaining(q->buffer)); if (status != LDNS_STATUS_OK) { ods_log_debug("[%s] got bad packet: %s", query_str, ldns_get_errorstr_by_id(status)); return query_formerr(q); } rr = ldns_rr_list_rr(ldns_pkt_question(pkt), 0); lock_basic_lock(&e->zonelist->zl_lock); /* we can just lookup the zone, because we will only handle SOA queries, zone transfers, updates and notifies */ q->zone = zonelist_lookup_zone_by_dname(e->zonelist, ldns_rr_owner(rr), ldns_rr_get_class(rr)); /* don't answer for zones that are just added */ if (q->zone && q->zone->zl_status == ZONE_ZL_ADDED) { ods_log_warning("[%s] zone %s just added, don't answer for now", query_str, q->zone->name); q->zone = NULL; } lock_basic_unlock(&e->zonelist->zl_lock); if (!q->zone) { ods_log_debug("[%s] zone not found", query_str); return query_servfail(q); } /* see if it is tsig signed */ if (!query_find_tsig(q)) { return query_formerr(q); } /* else: valid tsig, or no tsig present */ ods_log_debug("[%s] tsig %s", query_str, tsig_status2str(q->tsig_rr->status)); rcode = query_process_tsig(q); if (rcode != LDNS_RCODE_NOERROR) { return query_error(q, rcode); } /* process edns */ rcode = query_process_edns(q); if (rcode != LDNS_RCODE_NOERROR) { /* We should not return FORMERR, but BADVERS (=16). * BADVERS is created with Ext. RCODE, followed by RCODE. * Ext. RCODE is set to 1, RCODE must be 0 (getting 0x10 = 16). * Thus RCODE = NOERROR = NSD_RC_OK. */ return query_error(q, LDNS_RCODE_NOERROR); } /* handle incoming request */ opcode = ldns_pkt_get_opcode(pkt); qtype = ldns_rr_get_type(rr); ldns_pkt_free(pkt); switch (opcode) { case LDNS_PACKET_NOTIFY: return query_process_notify(q, qtype, engine); case LDNS_PACKET_QUERY: return query_process_query(q, qtype, engine); case LDNS_PACKET_UPDATE: return query_process_update(q); default: break; } return query_notimpl(q); }
int handled_keystate_ds_seen_cmd(int sockfd, engine_type* engine, const char *cmd, ssize_t n) { char buf[ODS_SE_MAXLINE]; const char *argv[8]; const int NARGV = sizeof(argv)/sizeof(char*); int argc; const char *scmd = "key ds-seen"; cmd = ods_check_command(cmd,n,scmd); if (!cmd) return 0; // not handled ods_log_debug("[%s] %s command", module_str, scmd); // Use buf as an intermediate buffer for the command. strncpy(buf,cmd,sizeof(buf)); buf[sizeof(buf)-1] = '\0'; // separate the arguments argc = ods_str_explode(buf,NARGV,argv); if (argc > NARGV) { ods_log_warning("[%s] too many arguments for %s command", module_str,scmd); ods_printf(sockfd,"too many arguments\n"); return 1; // errors, but handled } const char *zone = NULL; const char *cka_id = NULL; const char *keytag = NULL; (void)ods_find_arg_and_param(&argc,argv,"zone","z",&zone); (void)ods_find_arg_and_param(&argc,argv,"cka_id","k",&cka_id); (void)ods_find_arg_and_param(&argc,argv,"keytag","x",&keytag); // Check for unknown parameters on the command line if (argc) { ods_log_warning("[%s] unknown arguments for %s command", module_str,scmd); ods_printf(sockfd,"unknown arguments\n"); help_keystate_ds_seen_cmd(sockfd); return 1; // errors, but handled } // Check for too many parameters on the command line if (argc > NARGV) { ods_log_warning("[%s] too many arguments for %s command", module_str,scmd); ods_printf(sockfd,"too many arguments\n"); help_keystate_ds_seen_cmd(sockfd); return 1; // errors, but handled } // Either no option or combi of zone & cka_id or zone & keytag needs to be // present. But not both cka_id and keytag uint16_t nkeytag = 0; if (zone || cka_id || keytag) { if (!zone) { ods_log_warning("[%s] expected option --zone <zone> for %s command", module_str,scmd); ods_printf(sockfd,"expected --zone <zone> option\n"); help_keystate_ds_seen_cmd(sockfd); return 1; // errors, but handled } if (!cka_id && !keytag) { ods_log_warning("[%s] expected option --cka_id <cka_id> or " "--keytag <keytag> for %s command", module_str,scmd); ods_printf(sockfd,"expected --cka_id <cka_id> or " "--keytag <keytag> option\n"); help_keystate_ds_seen_cmd(sockfd); return 1; // errors, but handled } else { if (cka_id && keytag) { ods_log_warning("[%s] both --cka_id <cka_id> and --keytag <keytag> given, " "please only specify one for %s command", module_str,scmd); ods_printf(sockfd, "both --cka_id <cka_id> and --keytag <keytag> given, " "please only specify one\n"); help_keystate_ds_seen_cmd(sockfd); return 1; // errors, but handled } } if (keytag) { int kt = atoi(keytag); if (kt<=0 || kt>=65536) { ods_log_warning("[%s] value \"%s\" for --keytag is invalid", module_str,keytag); ods_printf(sockfd, "value \"%s\" for --keytag is invalid\n", keytag); return 1; // errors, but handled } nkeytag = (uint16_t )kt; } } time_t tstart = time(NULL); perform_keystate_ds_seen(sockfd,engine->config,zone,cka_id,nkeytag); ods_printf(sockfd,"%s completed in %ld seconds.\n",scmd,time(NULL)-tstart); flush_enforce_task(engine); return 1; }
/** * 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; }
/** * Parse keys from the signer configuration file. * */ keylist_type* parse_sc_keys(void* sc, const char* cfgfile) { xmlDocPtr doc = NULL; xmlXPathContextPtr xpathCtx = NULL; xmlXPathObjectPtr xpathObj = NULL; xmlNode* curNode = NULL; xmlChar* xexpr = NULL; key_type* new_key = NULL; keylist_type* kl = NULL; char* resourcerecord; char* locator; char* flags; char* algorithm; int configerr; int ksk, zsk, publish, i; if (!cfgfile || !sc) { return NULL; } /* Load XML document */ doc = xmlParseFile(cfgfile); if (doc == NULL) { ods_log_error("[%s] unable to parse <Keys>: " "xmlParseFile() failed", parser_str); return NULL; } /* Create xpath evaluation context */ xpathCtx = xmlXPathNewContext(doc); if(xpathCtx == NULL) { xmlFreeDoc(doc); ods_log_error("[%s] unable to parse <Keys>: " "xmlXPathNewContext() failed", parser_str); return NULL; } /* Evaluate xpath expression */ xexpr = (xmlChar*) "//SignerConfiguration/Zone/Keys/Key"; xpathObj = xmlXPathEvalExpression(xexpr, xpathCtx); if(xpathObj == NULL) { xmlXPathFreeContext(xpathCtx); xmlFreeDoc(doc); ods_log_error("[%s] unable to parse <Keys>: " "xmlXPathEvalExpression() failed", parser_str); return NULL; } /* Parse keys */ kl = keylist_create(sc); ods_log_assert(kl); if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0) { for (i = 0; i < xpathObj->nodesetval->nodeNr; i++) { resourcerecord = NULL; locator = NULL; flags = NULL; algorithm = NULL; ksk = 0; zsk = 0; publish = 0; configerr = 0; curNode = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode; while (curNode) { if (xmlStrEqual(curNode->name, (const xmlChar *)"Locator")) { locator = (char *) xmlNodeGetContent(curNode); } else if (xmlStrEqual(curNode->name, (const xmlChar *)"Algorithm")) { algorithm = (char *) xmlNodeGetContent(curNode); } else if (xmlStrEqual(curNode->name, (const xmlChar *)"Flags")) { flags = (char *) xmlNodeGetContent(curNode); } else if (xmlStrEqual(curNode->name, (const xmlChar *)"KSK")) { ksk = 1; } else if (xmlStrEqual(curNode->name, (const xmlChar *)"ZSK")) { zsk = 1; } else if (xmlStrEqual(curNode->name, (const xmlChar *)"Publish")) { publish = 1; } else if (xmlStrEqual(curNode->name, (const xmlChar *)"ResourceRecord")) { resourcerecord = (char *) xmlNodeGetContent(curNode); } curNode = curNode->next; } if (!algorithm) configerr = 1; if (!flags) configerr = 1; if (!locator && !resourcerecord) configerr = 1; if (!configerr) { /* search for duplicates */ new_key = keylist_lookup_by_locator(kl, locator); if (new_key && new_key->algorithm == (uint8_t) atoi(algorithm) && new_key->flags == (uint32_t) atoi(flags) && new_key->publish == publish && new_key->ksk == ksk && new_key->zsk == zsk) { /* duplicate */ ods_log_warning("[%s] unable to push duplicate key %s " "to keylist, skipping", parser_str, locator); } else { (void) keylist_push(kl, locator, resourcerecord, (uint8_t) atoi(algorithm), (uint32_t) atoi(flags), publish, ksk, zsk); } } else { ods_log_error("[%s] unable to push key to keylist: <Key> " "is missing required elements, skipping", parser_str); } free((void*)algorithm); free((void*)flags); } } xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); if (doc) { xmlFreeDoc(doc); } return kl; }
/** * Interface. * */ static int interface_run(FILE* fp, int sockfd, char* cmd) { int maxfdp1 = 0; int stdineof = 0; int i = 0; int n = 0; int ret = 0; int cmd_written = 0; int cmd_response = 0; fd_set rset; char buf[ODS_SE_MAXLINE]; int written; stdineof = 0; FD_ZERO(&rset); for(;;) { /* prepare */ if (stdineof == 0) { FD_SET(fileno(fp), &rset); } FD_SET(sockfd, &rset); maxfdp1 = max(fileno(fp), sockfd) + 1; if (!cmd || cmd_written) { /* interactive mode */ ret = select(maxfdp1, &rset, NULL, NULL, NULL); if (ret < 0) { if (errno != EINTR && errno != EWOULDBLOCK) { ods_log_warning("[%s] interface select error: %s", cli_str, strerror(errno)); } continue; } } else if (cmd) { /* passive mode */ ods_writen(sockfd, cmd, strlen(cmd)); cmd_written = 1; stdineof = 1; /* Clear the interactive mode / stdin fd from the set */ FD_CLR(fileno(fp), &rset); continue; } if (cmd && cmd_written && cmd_response) { /* normal termination */ return 0; } if (FD_ISSET(sockfd, &rset)) { /* clear buffer */ for (i=0; i < ODS_SE_MAXLINE; i++) { buf[i] = 0; } buf[ODS_SE_MAXLINE-1] = '\0'; /* socket is readable */ if ((n = read(sockfd, buf, ODS_SE_MAXLINE)) <= 0) { if (n < 0) { /* error occurred */ fprintf(stderr, "error: %s\n", strerror(errno)); return 1; } else { /* n==0 */ if (stdineof == 1) { /* normal termination */ return 0; } else { /* weird termination */ fprintf(stderr, "enforcer engine terminated " "prematurely\n"); return 1; } } } if (cmd) { if (n < SE_CLI_CMDLEN) { /* not enough data received */ fprintf(stderr, "not enough response data received " "from daemon.\n"); return 1; } /* n >= SE_CLI_CMDLEN : and so it is safe to do buffer manipulations below. */ if (strncmp(buf+n-SE_CLI_CMDLEN,"\ncmd> ",SE_CLI_CMDLEN) == 0) { /* we have the full response */ n -= SE_CLI_CMDLEN; buf[n] = '\0'; cmd_response = 1; } } /* n > 0 : when we get to this line... */ for (written=0; written < n; written += ret) { /* write what we got to stdout */ ret = (int) write(fileno(stdout), &buf[written], n-written); /* error and shutdown handling */ if (ret == 0) { fprintf(stderr, "no write\n"); break; } if (ret < 0) { if (errno == EINTR || errno == EWOULDBLOCK) { ret = 0; continue; /* try again... */ } fprintf(stderr, "\n\nwrite error: %s\n", strerror(errno)); break; } /* ret > 0 : when we get here... */ if (written+ret > n) { fprintf(stderr, "\n\nwrite error: more bytes (%d) written than required (%d)\n", written+ret, n); break; } /* written+ret < n : means partial write, requires us to loop... */ } if (ods_strcmp(buf, ODS_SE_STOP_RESPONSE) == 0 ) { /* we do no further reading, flush */ write(fileno(stdout), "\n", 1); return 0; } else if (cmd_response) return 0; } if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */ if (cmd && cmd_written) { /* passive mode */ stdineof = 1; ret = shutdown(sockfd, SHUT_WR); if (ret != 0) { fprintf(stderr, "shutdown failed: %s\n", strerror(errno)); return 1; } FD_CLR(fileno(fp), &rset); continue; } /* clear buffer */ for (i=0; i< ODS_SE_MAXLINE; i++) { buf[i] = 0; } /* interactive mode */ if ((n = read(fileno(fp), buf, ODS_SE_MAXLINE)) == 0) { stdineof = 1; ret = shutdown(sockfd, SHUT_WR); if (ret != 0) { fprintf(stderr, "shutdown failed: %s\n", strerror(errno)); return 1; } FD_CLR(fileno(fp), &rset); continue; } if (strncmp(buf, "exit", 4) == 0 || strncmp(buf, "quit", 4) == 0) { return 0; } ods_str_trim(buf); n = strlen(buf); ods_writen(sockfd, buf, n); } } }
/** * Recover zone from backup. * */ ods_status zone_recover2(zone_type* zone) { char* filename = NULL; FILE* fd = NULL; const char* token = NULL; time_t when = 0; task_type* task = NULL; ods_status status = ODS_STATUS_OK; /* zone part */ int klass = 0; uint32_t inbound = 0, internal = 0, outbound = 0; /* signconf part */ time_t lastmod = 0; /* nsec3params part */ const char* salt = NULL; ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->signconf); ods_log_assert(zone->db); filename = ods_build_path(zone->name, ".backup2", 0, 1); if (!filename) { return ODS_STATUS_MALLOC_ERR; } fd = ods_fopen(filename, NULL, "r"); if (fd) { /* start recovery */ if (!backup_read_check_str(fd, ODS_SE_FILE_MAGIC_V3)) { ods_log_error("[%s] corrupted backup file zone %s: read magic " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, ";;Time:") | !backup_read_time_t(fd, &when)) { ods_log_error("[%s] corrupted backup file zone %s: read time " "error", zone_str, zone->name); goto recover_error2; } /* zone stuff */ if (!backup_read_check_str(fd, ";;Zone:") | !backup_read_check_str(fd, "name") | !backup_read_check_str(fd, zone->name)) { ods_log_error("[%s] corrupted backup file zone %s: read name " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, "class") | !backup_read_int(fd, &klass)) { ods_log_error("[%s] corrupted backup file zone %s: read class " "error", zone_str, zone->name); goto recover_error2; } if (!backup_read_check_str(fd, "inbound") | !backup_read_uint32_t(fd, &inbound) | !backup_read_check_str(fd, "internal") | !backup_read_uint32_t(fd, &internal) | !backup_read_check_str(fd, "outbound") | !backup_read_uint32_t(fd, &outbound)) { ods_log_error("[%s] corrupted backup file zone %s: read serial " "error", zone_str, zone->name); goto recover_error2; } zone->klass = (ldns_rr_class) klass; zone->db->inbserial = inbound; zone->db->intserial = internal; zone->db->outserial = outbound; /* signconf part */ if (!backup_read_check_str(fd, ";;Signconf:") | !backup_read_check_str(fd, "lastmod") | !backup_read_time_t(fd, &lastmod) | !backup_read_check_str(fd, "maxzonettl") | !backup_read_check_str(fd, "0") | !backup_read_check_str(fd, "resign") | !backup_read_duration(fd, &zone->signconf->sig_resign_interval) | !backup_read_check_str(fd, "refresh") | !backup_read_duration(fd, &zone->signconf->sig_refresh_interval) | !backup_read_check_str(fd, "valid") | !backup_read_duration(fd, &zone->signconf->sig_validity_default) | !backup_read_check_str(fd, "denial") | !backup_read_duration(fd,&zone->signconf->sig_validity_denial) | !backup_read_check_str(fd, "jitter") | !backup_read_duration(fd, &zone->signconf->sig_jitter) | !backup_read_check_str(fd, "offset") | !backup_read_duration(fd, &zone->signconf->sig_inception_offset) | !backup_read_check_str(fd, "nsec") | !backup_read_rr_type(fd, &zone->signconf->nsec_type) | !backup_read_check_str(fd, "dnskeyttl") | !backup_read_duration(fd, &zone->signconf->dnskey_ttl) | !backup_read_check_str(fd, "soattl") | !backup_read_duration(fd, &zone->signconf->soa_ttl) | !backup_read_check_str(fd, "soamin") | !backup_read_duration(fd, &zone->signconf->soa_min) | !backup_read_check_str(fd, "serial") | !backup_read_str(fd, &zone->signconf->soa_serial)) { ods_log_error("[%s] corrupted backup file zone %s: read signconf " "error", zone_str, zone->name); goto recover_error2; } /* nsec3params part */ if (zone->signconf->nsec_type == LDNS_RR_TYPE_NSEC3) { if (!backup_read_check_str(fd, ";;Nsec3parameters:") | !backup_read_check_str(fd, "salt") | !backup_read_str(fd, &salt) | !backup_read_check_str(fd, "algorithm") | !backup_read_uint32_t(fd, &zone->signconf->nsec3_algo) | !backup_read_check_str(fd, "optout") | !backup_read_int(fd, &zone->signconf->nsec3_optout) | !backup_read_check_str(fd, "iterations") | !backup_read_uint32_t(fd, &zone->signconf->nsec3_iterations)) { ods_log_error("[%s] corrupted backup file zone %s: read " "nsec3parameters error", zone_str, zone->name); goto recover_error2; } zone->signconf->nsec3_salt = allocator_strdup( zone->signconf->allocator, salt); free((void*) salt); salt = NULL; zone->signconf->nsec3params = nsec3params_create( (void*) zone->signconf, (uint8_t) zone->signconf->nsec3_algo, (uint8_t) zone->signconf->nsec3_optout, (uint16_t) zone->signconf->nsec3_iterations, zone->signconf->nsec3_salt); if (!zone->signconf->nsec3params) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "create nsec3param", zone_str, zone->name); goto recover_error2; } } zone->signconf->last_modified = lastmod; zone->default_ttl = (uint32_t) duration2time(zone->signconf->soa_min); /* keys part */ zone->signconf->keys = keylist_create((void*) zone->signconf); while (backup_read_str(fd, &token)) { if (ods_strcmp(token, ";;Key:") == 0) { if (!key_recover2(fd, zone->signconf->keys)) { ods_log_error("[%s] corrupted backup file zone %s: read " "key error", zone_str, zone->name); goto recover_error2; } } else if (ods_strcmp(token, ";;") == 0) { /* keylist done */ free((void*) token); token = NULL; break; } else { /* keylist corrupted */ goto recover_error2; } free((void*) token); token = NULL; } /* publish dnskeys */ status = zone_publish_dnskeys(zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "publish dnskeys (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* publish nsec3param */ status = zone_publish_nsec3param(zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "publish nsec3param (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* publish other records */ status = backup_read_namedb(fd, zone); if (status != ODS_STATUS_OK) { ods_log_error("[%s] corrupted backup file zone %s: unable to " "read resource records (%s)", zone_str, zone->name, ods_status2str(status)); goto recover_error2; } /* task */ task = task_create(TASK_SIGN, when, (void*) zone); if (!task) { ods_log_error("[%s] failed to restore zone %s: unable to " "create task", zone_str, zone->name); goto recover_error2; } zone->task = (void*) task; free((void*)filename); ods_fclose(fd); /* journal */ zone->db->is_initialized = 1; filename = ods_build_path(zone->name, ".ixfr", 0, 1); if (filename) { fd = ods_fopen(filename, NULL, "r"); } if (fd) { status = backup_read_ixfr(fd, zone); if (status != ODS_STATUS_OK) { ods_log_warning("[%s] corrupted journal file zone %s, " "skipping (%s)", zone_str, zone->name, ods_status2str(status)); ixfr_cleanup(zone->ixfr); zone->ixfr = ixfr_create((void*)zone); } } lock_basic_lock(&zone->ixfr->ixfr_lock); ixfr_purge(zone->ixfr); lock_basic_unlock(&zone->ixfr->ixfr_lock); /* all ok */ free((void*)filename); ods_fclose(fd); if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); stats_clear(zone->stats); lock_basic_unlock(&zone->stats->stats_lock); } return ODS_STATUS_OK; } return ODS_STATUS_UNCHANGED; recover_error2: free((void*)filename); ods_fclose(fd); /* signconf cleanup */ free((void*)salt); salt = NULL; signconf_cleanup(zone->signconf); zone->signconf = signconf_create(); ods_log_assert(zone->signconf); /* namedb cleanup */ namedb_cleanup(zone->db); zone->db = namedb_create((void*)zone); ods_log_assert(zone->db); /* stats reset */ if (zone->stats) { lock_basic_lock(&zone->stats->stats_lock); stats_clear(zone->stats); lock_basic_unlock(&zone->stats->stats_lock); } return ODS_STATUS_ERR; }
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; }
/** * Process RR. * */ static ods_status adapi_process_rr(zone_type* zone, ldns_rr* rr, int add, int backup) { ods_status status = ODS_STATUS_OK; uint32_t tmp = 0; ods_log_assert(rr); ods_log_assert(zone); ods_log_assert(zone->name); ods_log_assert(zone->db); ods_log_assert(zone->signconf); /* We only support IN class */ if (ldns_rr_get_class(rr) != LDNS_RR_CLASS_IN) { ods_log_warning("[%s] only class in is supported, changing class " "to in", adapi_str); ldns_rr_set_class(rr, LDNS_RR_CLASS_IN); } /* RR processing */ if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_SOA) { if (ldns_dname_compare(ldns_rr_owner(rr), zone->apex)) { ods_log_error("[%s] unable to %s rr to zone: soa record has " "invalid owner name", adapi_str, add?"add":"delete"); return ODS_STATUS_ERR; } status = adapi_process_soa(zone, rr, add, backup); if (status != ODS_STATUS_OK) { ods_log_error("[%s] unable to %s rr: failed to process soa " "record", adapi_str, add?"add":"delete"); return status; } } else { if (ldns_dname_compare(ldns_rr_owner(rr), zone->apex) && !ldns_dname_is_subdomain(ldns_rr_owner(rr), zone->apex)) { ods_log_warning("[%s] zone %s contains out-of-zone data, " "skipping", adapi_str, zone->name); return ODS_STATUS_UNCHANGED; } else if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY) { adapi_process_dnskey(zone, rr); } else if (util_is_dnssec_rr(rr) && !backup) { ods_log_warning("[%s] zone %s contains dnssec data (type=%u), " "skipping", adapi_str, zone->name, (unsigned) ldns_rr_get_type(rr)); return ODS_STATUS_UNCHANGED; } else if (zone->signconf->max_zone_ttl) { /* Convert MaxZoneTTL */ tmp = (uint32_t) duration2time(zone->signconf->max_zone_ttl); } } /* //MaxZoneTTL. Only set for RRtype != SOA && RRtype != DNSKEY */ if (tmp && tmp < ldns_rr_ttl(rr)) { char* str = ldns_rdf2str(ldns_rr_owner(rr)); if (str) { size_t i = 0; str[(strlen(str))-1] = '\0'; /* replace tabs with white space */ for (i=0; i < strlen(str); i++) { if (str[i] == '\t') { str[i] = ' '; } } ods_log_debug("[%s] capping ttl %u to MaxZoneTTL %u for rrset " "<%s,%s>", adapi_str, ldns_rr_ttl(rr), tmp, str, rrset_type2str(ldns_rr_get_type(rr))); } ldns_rr_set_ttl(rr, tmp); } /* TODO: DNAME and CNAME checks */ /* TODO: NS and DS checks */ if (add) { return zone_add_rr(zone, rr, 1); } else { return zone_del_rr(zone, rr, 1); } /* not reached */ return ODS_STATUS_ERR; }
/** * Run engine, run!. * */ static void engine_run(engine_type* engine, start_cb_t start, int single_run) { if (!engine) { return; } ods_log_assert(engine); engine_start_workers(engine); engine_start_drudgers(engine); lock_basic_lock(&engine->signal_lock); /* [LOCK] signal */ engine->signal = SIGNAL_RUN; /* [UNLOCK] signal */ lock_basic_unlock(&engine->signal_lock); /* call the external start callback function */ start(engine); while (!engine->need_to_exit && !engine->need_to_reload) { lock_basic_lock(&engine->signal_lock); /* [LOCK] signal */ engine->signal = signal_capture(engine->signal); switch (engine->signal) { case SIGNAL_RUN: ods_log_assert(1); break; case SIGNAL_RELOAD: engine->need_to_reload = 1; break; case SIGNAL_SHUTDOWN: engine->need_to_exit = 1; break; default: ods_log_warning("[%s] invalid signal captured: %d, " "keep running", engine_str, signal); engine->signal = SIGNAL_RUN; break; } /* [UNLOCK] signal */ lock_basic_unlock(&engine->signal_lock); if (single_run) { engine->need_to_exit = 1; /* FIXME: all tasks need to terminate, then set need_to_exit to 1 */ } lock_basic_lock(&engine->signal_lock); /* [LOCK] signal */ if (engine->signal == SIGNAL_RUN && !single_run) { ods_log_debug("[%s] taking a break", engine_str); lock_basic_sleep(&engine->signal_cond, &engine->signal_lock, 3600); } /* [UNLOCK] signal */ lock_basic_unlock(&engine->signal_lock); } ods_log_debug("[%s] enforcer halted", engine_str); engine_stop_drudgers(engine); engine_stop_workers(engine); return; }
static int run(int sockfd, engine_type* engine, const char *cmd, ssize_t n, db_connection_t *dbconn) { char buf[ODS_SE_MAXLINE]; #define NARGV 12 const char *argv[NARGV]; int success, argIndex; int argc, bVerbose, bDebug, bParsable, bAll; char* keytypeParam; char* keystateParam; const char* filterZone; /* NULL if no filtering on zone, otherwise zone to match */ char** filterKeytype; /* NULL if no filtering on key type, NULL terminated list of key types to filter */ char** filterKeystate; /* NULL if no filtering on key state, NULL terminated list of key states to filter */ (void) engine; ods_log_debug("[%s] %s command", module_str, key_list_funcblock()->cmdname); cmd = ods_check_command(cmd, n, key_list_funcblock()->cmdname); /* Use buf as an intermediate buffer for the command. */ strncpy(buf, cmd, sizeof (buf)); buf[sizeof (buf) - 1] = '\0'; /* separate the arguments */ argc = ods_str_explode(buf, NARGV, argv); if (argc > NARGV) { ods_log_warning("[%s] too many arguments for %s command", module_str, key_list_funcblock()->cmdname); client_printf(sockfd, "too many arguments\n"); return -1; } bVerbose = ods_find_arg(&argc, argv, "verbose", "v") != -1; bDebug = ods_find_arg(&argc, argv, "debug", "d") != -1; bParsable = ods_find_arg(&argc, argv, "parsable", "p") != -1; if ((argIndex = ods_find_arg_and_param(&argc, argv, "zone", "z", &filterZone)) == -1) { filterZone = NULL; } if (ods_find_arg_and_param(&argc, argv, "keytype", "k", (const char **)&keytypeParam) == -1) { keytypeParam = NULL; } if (ods_find_arg_and_param(&argc, argv, "keystate", "e", (const char **)&keystateParam) == -1) { keystateParam = NULL; } bAll = (ods_find_arg(&argc, argv, "all", "a") != -1); if (keystateParam != NULL && bAll) { client_printf(sockfd, "Error: --keystate and --all option cannot be given together\n"); return -1; } if (argc) { ods_log_warning("[%s] unknown arguments for %s command", module_str, key_list_funcblock()->cmdname); client_printf(sockfd, "unknown arguments\n"); return -1; } if (keytypeParam) filterKeytype = tokenizeparam(keytypeParam); else filterKeytype = NULL; if (keystateParam) { filterKeystate = tokenizeparam(keystateParam); } else filterKeystate = NULL; if (bAll) { if (filterKeystate != NULL) { free(filterKeystate); } filterKeystate = NULL; } else if(filterKeystate == NULL) { if ((filterKeystate = malloc(sizeof (char*) * 6))) { filterKeystate[0] = (char *)"publish"; filterKeystate[1] = (char *)"ready"; filterKeystate[2] = (char *)"active"; filterKeystate[3] = (char *)"retire"; filterKeystate[4] = (char *)"mixed"; filterKeystate[5] = NULL; } /* else emit error */ } if (bDebug) { if (bParsable) { success = perform_keystate_list(sockfd, dbconn, filterZone, filterKeytype, filterKeystate, NULL, &printdebugparsablekey); } else { success = perform_keystate_list(sockfd, dbconn, filterZone, filterKeytype, filterKeystate, &printdebugheader, &printdebugkey); } } else if (bVerbose) { if (bParsable) { success = perform_keystate_list(sockfd, dbconn, filterZone, filterKeytype, filterKeystate, NULL, &printverboseparsablekey); } else { success = perform_keystate_list(sockfd, dbconn, filterZone, filterKeytype, filterKeystate, &printverboseheader, &printverbosekey); } } else { success = perform_keystate_list(sockfd, dbconn, filterZone, filterKeytype, filterKeystate, &printcompatheader, &printcompatkey); } if (filterKeytype) free(filterKeytype); if (filterKeystate) free(filterKeystate); return success; }