/** * Merge zones. * */ void zone_merge(zone_type* z1, zone_type* z2) { const char* str; adapter_type* adtmp = NULL; if (!z1 || !z2) { return; } /* policy name */ if (ods_strcmp(z2->policy_name, z1->policy_name) != 0) { if (z2->policy_name) { str = strdup(z2->policy_name); if (!str) { ods_log_error("[%s] failed to merge policy %s name to zone " "%s", zone_str, z2->policy_name, z1->name); } else { free((void*)z1->policy_name); z1->policy_name = str; z1->zl_status = ZONE_ZL_UPDATED; } } else { free((void*)z1->policy_name); z1->policy_name = NULL; z1->zl_status = ZONE_ZL_UPDATED; } } /* signconf filename */ if (ods_strcmp(z2->signconf_filename, z1->signconf_filename) != 0) { if (z2->signconf_filename) { str = strdup(z2->signconf_filename); if (!str) { ods_log_error("[%s] failed to merge signconf filename %s to " "zone %s", zone_str, z2->policy_name, z1->name); } else { free((void*)z1->signconf_filename); z1->signconf_filename = str; z1->zl_status = ZONE_ZL_UPDATED; } } else { free((void*)z1->signconf_filename); z1->signconf_filename = NULL; z1->zl_status = ZONE_ZL_UPDATED; } } /* adapters */ if (adapter_compare(z2->adinbound, z1->adinbound) != 0) { adtmp = z2->adinbound; z2->adinbound = z1->adinbound; z1->adinbound = adtmp; adtmp = NULL; } if (adapter_compare(z2->adoutbound, z1->adoutbound) != 0) { adtmp = z2->adoutbound; z2->adoutbound = z1->adoutbound; z1->adoutbound = adtmp; adtmp = NULL; } return; }
/** * Convert file mode to readable string. * */ const char* ods_file_mode2str(const char* mode) { if (!mode) { return "no mode"; } if (ods_strcmp(mode, "a") == 0) { return "appending"; } else if (ods_strcmp(mode, "r") == 0) { return "reading"; } else if (ods_strcmp(mode, "w") == 0) { return "writing"; } return "unknown mode"; }
/** * Main. start interface tool. * */ int main(int argc, char* argv[]) { char* cmd = NULL; int ret = 0; allocator_type* clialloc = allocator_create(malloc, free); if (!clialloc) { fprintf(stderr,"error, malloc failed for client\n"); exit(1); } /* argv[0] is always the executable name so argc is always 1 or higher */ ods_log_assert(argc >= 1); /* concat arguments an add 1 extra char for adding '\n' char later on, but only when argc > 1 */ cmd = ods_str_join(clialloc,argc-1,&argv[1],' '); if (argc > 1 && !cmd) { fprintf(stderr, "memory allocation failed\n"); exit(1); } /* main stuff */ if (!cmd) { ret = interface_start(cmd); } else { if (ods_strcmp(cmd, "-h") == 0 || ods_strcmp(cmd, "--help") == 0) { usage(stdout); ret = 1; } else { strcat(cmd,"\n"); ret = interface_start(cmd); } } /* done */ allocator_deallocate(clialloc, (void*) cmd); allocator_cleanup(clialloc); return ret; }
/** * Read and match a string from backup file. * */ int backup_read_check_str(FILE* in, const char* str) { char *p = backup_read_token(in); if (!p) { ods_log_debug("[%s] cannot read check string \'%s\'", backup_str, str); return 0; } if (ods_strcmp(p, str) != 0) { ods_log_debug("[%s] \'%s\' does not match \'%s\'", backup_str, p, str); return 0; } return 1; }
/** * Lookup a key in the key list by locator. * */ key_type* keylist_lookup_by_locator(keylist_type* kl, const char* locator) { uint16_t i = 0; if (!kl || !locator || kl->count <= 0) { return NULL; } for (i=0; i < kl->count; i++) { if (&kl->keys[i] && kl->keys[i].locator) { if (ods_strcmp(kl->keys[i].locator, locator) == 0) { return &kl->keys[i]; } } } return NULL; }
/** * 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); } } }
/** * Start interface * */ static int interface_start(char* cmd) { int sockfd, ret, flags; struct sockaddr_un servaddr; const char* servsock_filename = OPENDNSSEC_ENFORCER_SOCKETFILE; ods_log_init(NULL, 0, 0); /* new socket */ sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sockfd <= 0) { fprintf(stderr, "Unable to connect to engine. " "socket() failed: %s\n", strerror(errno)); return 1; } /* no suprises */ bzero(&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; strncpy(servaddr.sun_path, servsock_filename, sizeof(servaddr.sun_path) - 1); /* connect */ ret = connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)); if (ret != 0) { if (cmd && ods_strcmp(cmd, "start\n") == 0) { return system(ODS_EN_ENGINE); } if (cmd && ods_strcmp(cmd, "running\n") == 0) { fprintf(stderr, "Engine not running.\n"); } else { fprintf(stderr, "Unable to connect to engine: " "connect() failed: %s\n", strerror(errno)); } close(sockfd); return 1; } /* set socket to non-blocking */ flags = fcntl(sockfd, F_GETFL, 0); if (flags < 0) { ods_log_error("[%s] unable to start interface, fcntl(F_GETFL) " "failed: %s", cli_str, strerror(errno)); close(sockfd); return 1; } flags |= O_NONBLOCK; if (fcntl(sockfd, F_SETFL, flags) < 0) { ods_log_error("[%s] unable to start interface, fcntl(F_SETFL) " "failed: %s", cli_str, strerror(errno)); close(sockfd); return 1; } /* some sort of interface */ if (!cmd) { fprintf(stderr, "cmd> "); } /* run */ ret = interface_run(stdin, sockfd, cmd); close(sockfd); return ret; }
/** * 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; }