1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup test
36 * @ingroup test
37 * @brief skeleton file
38 *
39 *
40 * Query for all DNSKEYs
41 *
42 * Generate KSK (Publish: none, Active: now, Inactive: never, Delete: never) + ZSK (Publish none: Active +60s, Inactive +180s, Delete: never)
43 * Remove all DNSKEYs and add KSK and ZSK
44 *
45 * loop:
46 * Generate ZSK (Publish none: Active +60s, Inactive +180s, Delete: never)
47 * Wait for update time + 60 seconds.
48 * Remove previous ZSK and add new ZSK
49 *
50 */
51
52 #include <dnscore/dnscore.h>
53 #include <dnscore/dnskey.h>
54 #include <dnscore/format.h>
55 #include <dnscore/config-cmdline.h>
56 #include <dnscore/packet_writer.h>
57 #include <dnscore/signals.h>
58
59 #include <dnscore/timems.h>
60 #include <dnscore/thread_pool.h>
61
62 #include <dnscore/dnskey-signature.h>
63 #include <dnscore/packet_reader.h>
64 #include <dnscore/dnskey-keyring.h>
65 #include <dnscore/parsing.h>
66 #include <dnscore/zone_reader_text.h>
67 #include <dnscore/server-setup.h>
68 #include <dnscore/logger_channel_stream.h>
69 #include <dnscore/buffer_output_stream.h>
70 #include <dnscore/logger.h>
71 #include <dnscore/file_output_stream.h>
72 #include <dnscore/pid.h>
73 #include <dnscore/dnscore-release-date.h>
74
75 #include <sys/stat.h>
76
77 #include "keyroll.h"
78 #include "keyroll-config.h"
79
80 #include "config-dnssec-policy.h"
81 #include "dnssec-policy.h"
82 #include "buildinfo.h"
83
84 #define PURGE_QUESTION "YES"
85
86 extern logger_handle *g_dnssec_logger;
87 extern logger_handle *g_keyroll_logger;
88
89 #define MODULE_MSG_HANDLE g_keyroll_logger
90
91 #define FIRST_JANUARY_2019_00_00_00 1546300800
92 #define FIRST_JANUARY_2021_00_00_00 1609459200
93
94 #define SERVER_FAILURE_RETRY_DELAY 30
95 #define CONSECUTIVE_ERRORS_BEFORE_RESTART 60
96
97 #define WAIT_MARGIN (ONE_SECOND_US * 10)
98
99 #define PROGRAM_NAME "yakeyrolld"
100 #define KEYROLL_CONFIG_SECTION "yakeyrolld"
101 #define RELEASEDATE YADIFA_DNSCORE_RELEASE_DATE
102
103 // mount -t tmpfs -o size=16384 tmpfs /registry/yadifa/var/log/yakeyrolld
104
105 static random_ctx rnd;
106
107 enum PROGRAM_MODE
108 {
109 NONE = 0,
110 GENERATE,
111 PLAY,
112 PLAYLOOP,
113 PRINT,
114 PRINT_JSON,
115 TEST
116 };
117
118 static value_name_table program_mode_enum_table[]=
119 {
120 {NONE, "none"},
121 {PLAY, "play"},
122 {PLAYLOOP, "playloop"},
123 {GENERATE, "generate"},
124 {PRINT, "print"},
125 {PRINT_JSON, "print-json"},
126 {TEST, "test"},
127 {0, NULL}
128 };
129
130 struct main_args
131 {
132 ptr_vector domains;
133 ptr_vector fqdns;
134 char *configuration_file_path;
135 char *log_path;
136 char *keys_path;
137 char *plan_path;
138 char *pid_path;
139 char *pid_file;
140 char *generate_from;
141 char *generate_until;
142 char *policy_name;
143 host_address *server;
144
145 uid_t uid;
146 gid_t gid;
147 u32 timeout;
148 u32 ttl;
149
150 u32 update_apply_verify_retries; // if an update wasn't applied successfully, retry CHECKING this amount of times
151 u32 update_apply_verify_retries_delay; // time between the above retries
152
153 u32 match_verify_retries; // if there is not match, retry checking this amount of times
154 u32 match_verify_retries_delay; // time between the above retries
155
156 int program_mode;
157 bool reset;
158 bool purge;
159 bool dryrun;
160 bool wait_for_yadifad;
161 bool daemonise;
162 bool print_plan;
163 bool user_confirmation;
164 #if DEBUG
165 bool with_secret_keys;
166 #endif
167 };
168
169 typedef struct main_args main_args;
170
171 struct testing_args
172 {
173 s64 timeus_offset;
174 };
175
176 typedef struct testing_args testing_args;
177
178 static ya_result
directory_writable(const char * path)179 directory_writable(const char *path)
180 {
181 if(path == NULL)
182 {
183 return UNEXPECTED_NULL_ARGUMENT_ERROR;
184 }
185
186 struct stat ds;
187
188 if(stat(path, &ds) < 0)
189 {
190 int err = errno;
191 log_err("error: '%s': %s", path, strerror(err));
192 formatln("error: '%s': %s", path, strerror(err));
193
194 return MAKE_ERRNO_ERROR(err);
195 }
196
197 if((ds.st_mode & S_IFMT) != S_IFDIR)
198 {
199 log_err("error: '%s' is not a directory", path);
200 formatln("error: '%s' is not a directory", path);
201
202 return INVALID_PATH;
203 }
204
205 char tempfile[PATH_MAX];
206
207 ya_result ret = snformat(tempfile, sizeof(tempfile), "%s/ydf.XXXXXX", path);
208
209 if(FAIL(ret))
210 {
211 log_err("error: '%s' temp file name creation failed: %r", path, ret);
212 formatln("error: '%s' temp file name creation failed: %r", path, ret);
213 return ret;
214 }
215
216 if(ret >= PATH_MAX)
217 {
218 log_err("error: '%s' path is too big", path);
219 formatln("error: '%s' path is too big", path);
220 return INVALID_PATH;
221 }
222
223 int tempfd;
224 if((tempfd = mkstemp_ex(tempfile)) < 0)
225 {
226 int ret = ERRNO_ERROR;
227 #ifndef WIN32
228 formatln("error: '%s' is not writable as (%d:%d): %r", path, getuid(), getgid(), ret);
229 #else
230 log_err("error: '%s' is not writable: %r", fullpath, ret);
231 formatln("error: '%s' is not writable: %r", fullpath, ret);
232 #endif
233 return ret;
234 }
235
236 unlink(tempfile);
237 close_ex(tempfd);
238
239 return SUCCESS;
240 }
241
242
243 #ifndef PREFIX
244 #define PREFIX "/usr/local"
245 #endif
246
247 #ifndef LOCALSTATEDIR
248 #define LOCALSTATEDIR PREFIX "/var"
249 #endif
250
251 #ifndef SYSCONFDIR
252 #define SYSCONFDIR PREFIX "/etc"
253 #endif
254
255 #define CONFIGURATION_FILE_PATH_DEFAULT SYSCONFDIR "/yakeyrolld.conf"
256
257 #define CONFIG_TYPE main_args
258
259 CONFIG_BEGIN(main_args_desc)
260 CONFIG_STRING_ARRAY(domains,NULL, 200) // I'm using a thread-pool for this, it cannot go beyond THREAD_POOL_SIZE_LIMIT_MAX threads.
261 CONFIG_PATH(log_path, LOCALSTATEDIR "/log/yakeyrolld")
262 CONFIG_FILE(configuration_file_path, CONFIGURATION_FILE_PATH_DEFAULT)
263 CONFIG_PATH(keys_path, LOCALSTATEDIR "/zones/keys")
264 CONFIG_PATH(plan_path, LOCALSTATEDIR "/plans")
265 CONFIG_PATH(pid_path, LOCALSTATEDIR "/run")
266 CONFIG_STRING(pid_file, "yakeyrolld.pid")
267 CONFIG_HOST_LIST(server, "127.0.0.1")
268 CONFIG_U32(timeout, "3")
269 CONFIG_U32(ttl, "600")
270
271 CONFIG_U32_RANGE(update_apply_verify_retries, "60", 0, 3600) // if an update wasn't applied successfully, retry CHECKING this amount of times
272 CONFIG_U32_RANGE(update_apply_verify_retries_delay, "1", 1, 60) // time between the above retries
273
274 CONFIG_U32_RANGE(match_verify_retries, "60", 0, 3600) // if there is not match, retry checking this amount of times
275 CONFIG_U32_RANGE(match_verify_retries_delay, "1", 1, 60) // time between the above retries
276
277 CONFIG_STRING(generate_from, "now")
278 CONFIG_STRING(generate_until, "+1y")
279 CONFIG_STRING(policy_name, "")
280 CONFIG_UID(uid, "0")
281 CONFIG_GID(gid, "0")
282 CONFIG_BOOL(reset, "0")
283 CONFIG_BOOL(dryrun, "0")
284 CONFIG_BOOL(wait_for_yadifad, "1")
285 CONFIG_BOOL(daemonise, "0")
286 CONFIG_BOOL(print_plan, "0")
287 CONFIG_BOOL(user_confirmation, "1")
288 #if DEBUG
289 CONFIG_BOOL(with_secret_keys, "0")
290 #endif
291 CONFIG_ENUM(program_mode, "none", program_mode_enum_table)
292 CONFIG_ALIAS(policy, policy_name)
293 CONFIG_ALIAS(domain, domains)
294 CONFIG_ALIAS(daemon, daemonise)
295 CONFIG_ALIAS(plans_path, plan_path)
296 CONFIG_END(main_args_desc)
297 #undef CONFIG_TYPE
298
299 #define CONFIG_TYPE testing_args
300
301 CONFIG_BEGIN(testing_args_desc)
302 CONFIG_U64(timeus_offset, "0")
303 CONFIG_END(testing_args_desc)
304
305 CMDLINE_BEGIN(keyroll_cmdline)
306 CMDLINE_VERSION_HELP(keyroll_cmdline)
307 CMDLINE_SECTION(KEYROLL_CONFIG_SECTION)
308 CMDLINE_OPT("config", 'c', "configuration_file_path")
309 CMDLINE_HELP("", "sets the configuration file to use (default: " CONFIGURATION_FILE_PATH_DEFAULT ")")
310 CMDLINE_OPT("mode", 'm', "program_mode")
311 CMDLINE_HELP("", "sets the program mode (generate,play,playloop,print,json)")
312 CMDLINE_OPT("domain",0,"domain")
313 CMDLINE_HELP("fqdn", "the domain name, overrides the domains from the configuration file")
314 CMDLINE_OPT("path",'p',"keys_path")
315 CMDLINE_HELP("directory", "the directory where to store the keys")
316 CMDLINE_OPT("server",'s',"server")
317 CMDLINE_HELP("address", "the address of the server")
318 CMDLINE_OPT("ttl",'t',"ttl")
319 CMDLINE_HELP("seconds", "the TTL to use for both DNSKEY and RRSIG records")
320 CMDLINE_BOOL("reset",0,"reset")
321 CMDLINE_HELP("", "start by removing all the keys, create a new KSK and a new ZSK")
322 CMDLINE_OPT("policy",0,"policy_name")
323 CMDLINE_HELP("", "name of the policy to use")
324 CMDLINE_OPT("from",0,"generate_from")
325 CMDLINE_HELP("time", "at what time the plan starts (e.g. : now, -1y, YYYYMMDDHHSS in UTC).")
326 CMDLINE_OPT("until",0,"generate_until")
327 CMDLINE_HELP("time", "the upper time limit covered by the plan (+1y, YYYYMMDDHHSS in UTC).")
328 CMDLINE_BLANK()
329 CMDLINE_INDENT(4)
330 CMDLINE_IMSG("", "time values can be:")
331 CMDLINE_BLANK()
332 CMDLINE_INDENT(4)
333 CMDLINE_IMSG("", "now")
334 CMDLINE_IMSG("", "tomorrow")
335 CMDLINE_IMSG("", "yesterday")
336 CMDLINE_IMSG("", "[+-]#{years|months|weeks|days|seconds} where # is an integer")
337 CMDLINE_IMSG("", "YYYY-MM-DD")
338 CMDLINE_IMSG("", "YYYYMMDD")
339 CMDLINE_IMSG("", "YYYYMMDDHHMMSSUUUUUU")
340 CMDLINE_BLANK()
341 CMDLINE_INDENT(-8)
342 CMDLINE_BOOL("dryrun",0,"dryrun")
343 CMDLINE_HELP("", "do not send the update to the server")
344 CMDLINE_BOOL("wait", 0, "wait_for_yadifad")
345 CMDLINE_HELP("", "wait for yadifad to answer before starting to work (default)")
346 CMDLINE_BOOL_NOT("nowait", 0, "wait_for_yadifad")
347 CMDLINE_HELP("", "do not wait for yadifad to answer before starting to work")
348 CMDLINE_BOOL("daemon", 0, "daemonise")
349 CMDLINE_HELP("", "daemonise the program for supported modes (default)")
350 CMDLINE_BOOL_NOT("nodaemon", 0, "daemonise")
351 CMDLINE_HELP("", "do not daemonise the program (needed for systemd)")
352 CMDLINE_BOOL_NOT("noconfirm",'Y', "user_confirmation")
353 CMDLINE_HELP("", "do not ask for confirmation before destroying steps and .key and .private files")
354 CMDLINE_BOOL("print-plan", 0, "print_plan")
355 CMDLINE_HELP("", "prints the complete plan after generation or after loading")
356 #if DEBUG
357 CMDLINE_BOOL("with-secret-keys", 0, "with_secret_keys")
358 #endif
359 #if DEBUG
360 CMDLINE_SECTION("testing")
361 CMDLINE_OPT("timeus-offset", 0, "timeus_offset")
362 CMDLINE_HELP("", "fakes the current time changing the time by that many seconds (testing)")
363 #endif
364 CMDLINE_BLANK()
365 CMDLINE_END(keyroll_cmdline)
366
367 static main_args g_config; // initilised in main_config(argc, argv)
368 static testing_args g_testing = {0};
369
370 static void
help_print(const char * name)371 help_print(const char *name)
372 {
373 formatln("%s [-c configurationfile] [...]\n\n", name);
374 cmdline_print_help(keyroll_cmdline, 16, 28, " : ", 48, termout);
375 }
376
377 /**
378 * To abstract key generation or reading from storage
379 */
380
381 static ya_result
main_config_main_postprocess(struct config_section_descriptor_s * csd)382 main_config_main_postprocess(struct config_section_descriptor_s *csd)
383 {
384 (void)csd;
385 // no logger if help is requested
386
387 if(cmdline_help_get() || cmdline_version_get())
388 {
389 return SUCCESS;
390 }
391
392 config_set_log_base_path(g_config.log_path);
393 keyroll_set_dryrun_mode(g_config.dryrun);
394
395 logger_start();
396
397 logger_handle_create("keyroll", &g_keyroll_logger);
398 logger_handle_create("dnssec", &g_dnssec_logger);
399
400 timeus_set_offset(g_testing.timeus_offset);
401
402 logger_flush();
403
404 return SUCCESS;
405 }
406
407 static void
yakeyrolld_print_authors()408 yakeyrolld_print_authors()
409 {
410 print("\n"
411 "\t\tYADIFAD authors:\n"
412 "\t\t---------------\n"
413 "\t\t\n"
414 "\t\tGery Van Emelen\n"
415 "\t\tEric Diaz Fernandez\n"
416 "\n"
417 "\t\tContact: " PACKAGE_BUGREPORT "\n"
418 );
419 flushout();
420 }
421
422 static void
yakeyrolld_show_version(u8 level)423 yakeyrolld_show_version(u8 level)
424 {
425 switch(level)
426 {
427 case 1:
428 osformatln(termout, "%s %s (%s)\n", YKEYROLL_NAME, YKEYROLL_VERSION, RELEASEDATE);
429 break;
430 case 2:
431 #if HAS_BUILD_TIMESTAMP && defined(__DATE__)
432 osformatln(termout, "%s %s (released %s, compiled %s)\n\nbuild settings: %s\n", YKEYROLL_NAME, YKEYROLL_VERSION, RELEASEDATE, __DATE__, BUILD_OPTIONS);
433 #else
434 osformatln(termout, "%s %s (released %s)\n\nbuild settings: %s\n", YKEYROLL_NAME, YKEYROLL_VERSION, RELEASEDATE, BUILD_OPTIONS);
435 #endif
436 break;
437 case 3:
438 default:
439 #if HAS_BUILD_TIMESTAMP && defined(__DATE__)
440 osformatln(termout, "%s %s (released %s, compiled %s)\n", PROGRAM_NAME, YKEYROLL_VERSION, RELEASEDATE, __DATE__);
441 #else
442 osformatln(termout, "%s %s (released %s)\n", YKEYROLL_NAME, YKEYROLL_VERSION, RELEASEDATE);
443 #endif
444 yakeyrolld_print_authors();
445 break;
446 }
447
448 flushout();
449 }
450
451 /**
452 * Reads the configuration.
453 * It's only a command line but extending to a file is relatively trivial.
454 */
455
456 static ya_result
main_config(int argc,char * argv[])457 main_config(int argc, char *argv[])
458 {
459 config_error_s cfg_error;
460 ya_result ret;
461
462 config_init();
463
464 memset(&g_config, 0, sizeof(g_config));
465
466 ptr_vector_init(&g_config.domains);
467 ptr_vector_init(&g_config.fqdns);
468
469 int priority = 0;
470
471 if(FAIL(ret = config_register_cmdline(priority++))) // without this line, the help will not work
472 {
473 return ret;
474 }
475
476 if(FAIL(ret = config_register_struct(KEYROLL_CONFIG_SECTION, main_args_desc, &g_config, priority++)))
477 {
478 return ret;
479 }
480
481 if(FAIL(ret = config_register_struct("testing", testing_args_desc, &g_testing, priority++)))
482 {
483 return ret;
484 }
485
486 // hook the post-processing to know what to do with the logger
487 // this is a bit dirty but the vtbl is a copy of an original and the const is a safeguard here
488 // I'll need to add a registration function that allows to overwrite these.
489
490 // also registers key-roll denial key-template and key-suite (hence the + 5)
491
492 if(FAIL(ret = config_register_dnssec_policy(NULL, priority)))
493 {
494 return ret;
495 }
496
497 priority += 5;
498
499 if(FAIL(ret = config_register_logger(NULL, NULL, priority)))
500 {
501 return ret;
502 }
503
504 // priority += 2;
505
506 // shouldn't this be 2 sources instead of twice one ?
507
508 struct config_source_s sources[1];
509
510 if(FAIL(ret = config_source_set_commandline(&sources[0], keyroll_cmdline, argc, argv)))
511 {
512 formatln("command line definition: %r", ret);
513 return ret;
514 }
515
516 if(FAIL(ret = config_read_from_sources(sources, 1, &cfg_error)))
517 {
518 if(cmdline_help_get())
519 {
520 help_print(argv[0]);
521 ret = SUCCESS;
522 }
523 else if(cmdline_version_get())
524 {
525 yakeyrolld_show_version(cmdline_version_get());
526 ret = SUCCESS;
527 }
528 else
529 {
530 formatln("settings: (%s:%i) %s: %s: %r", cfg_error.file, cfg_error.line_number, cfg_error.line, cfg_error.variable_name, ret);
531 }
532 flushout();
533 return ret;
534 }
535
536 if(cmdline_help_get())
537 {
538 help_print(argv[0]);
539 return SUCCESS;
540 }
541
542 if(cmdline_version_get())
543 {
544 yakeyrolld_show_version(cmdline_version_get());
545 return SUCCESS;
546 }
547
548 config_source_set_file(&sources[0], g_config.configuration_file_path, CONFIG_SOURCE_FILE);
549
550 config_section_descriptor_s *main_desc = config_section_get_descriptor(KEYROLL_CONFIG_SECTION);
551 config_section_descriptor_vtbl_s *vtbl = (config_section_descriptor_vtbl_s*)main_desc->vtbl;
552 vtbl->postprocess = main_config_main_postprocess;
553
554 if(FAIL(ret = config_read_from_sources(sources, 1, &cfg_error)))
555 {
556 formatln("settings: (%s:%i) %s %s: %r", cfg_error.file, cfg_error.line_number, cfg_error.line, cfg_error.variable_name, ret);
557 flushout();
558 return ret;
559 }
560
561 if(g_config.server->port == 0)
562 {
563 g_config.server->port = NU16(DNS_DEFAULT_PORT);
564 }
565
566 for(int i = 0; i <= ptr_vector_last_index(&g_config.domains); ++i)
567 {
568 const char *name = (const char*)ptr_vector_get(&g_config.domains, i);
569 u8 *fqdn = dnsname_zdup_from_name(name);
570 if(fqdn == NULL)
571 {
572 formatln("cannot parse domain name: %s", name);
573 ret = PARSESTRING_ERROR;
574 return ret;
575 }
576 ptr_vector_append(&g_config.fqdns, fqdn);
577 }
578
579 // no stdout channel in daemon mode
580
581 if(!((g_config.program_mode == PLAYLOOP) && g_config.daemonise))
582 {
583 if(!config_logger_isconfigured())
584 {
585 output_stream stdout_os;
586 logger_channel *stdout_channel;
587
588 fd_output_stream_attach(&stdout_os, dup_ex(1));
589 buffer_output_stream_init(&stdout_os, &stdout_os, 65536);
590 stdout_channel = logger_channel_alloc();
591 if(stdout_channel == NULL)
592 {
593 return INVALID_STATE_ERROR;
594 }
595 logger_channel_stream_open(&stdout_os, FALSE, stdout_channel);
596
597 logger_channel_register("stdout", stdout_channel);
598
599 #if !DEBUG
600 logger_handle_add_channel("keyroll", MSG_PROD_MASK, "stdout");
601 #else
602 logger_handle_add_channel("keyroll", MSG_ALL_MASK, "stdout");
603 logger_handle_add_channel("dnssec", MSG_ALL_MASK, "stdout");
604 #endif
605 }
606 }
607
608 if(FAIL(ret = directory_writable(g_config.log_path)))
609 {
610 return ret;
611 }
612
613 return ret;
614 }
615
616 static ya_result
get_user_confirmation()617 get_user_confirmation()
618 {
619 ya_result ret = SUCCESS;
620
621 log_notice("Asking user to confirm by typing '" PURGE_QUESTION "'");
622 print("Please confirm by typing '" PURGE_QUESTION "' (without the '') followed by the ENTER key: ");
623 flushout();
624
625 char *line_buffer = NULL;
626 size_t line_buffer_size = 0;
627 ssize_t n = getline(&line_buffer, &line_buffer_size, stdin);
628
629 if(n < 0)
630 {
631 ret = ERRNO_ERROR;
632 formatln("getline failed: %r", ret);
633 flushout();
634 free(line_buffer);
635 return ret;
636 }
637
638 while((n > 0) && isspace(line_buffer[--n]))
639 {
640 line_buffer[n] = '\0';
641 }
642
643 if(strcmp(line_buffer, PURGE_QUESTION) != 0)
644 {
645 log_err("expected: '" PURGE_QUESTION "', got '%s': stopping", line_buffer);
646 formatln("expected: '" PURGE_QUESTION "', got '%s': stopping", line_buffer);
647 flushout();
648 free(line_buffer);
649 return PARSEWORD_NOMATCH_ERROR;
650 }
651 else
652 {
653 log_notice("Got user confirmation");
654 }
655
656 return ret;
657 }
658
659 static ya_result
program_mode_generate(const u8 * domain)660 program_mode_generate(const u8 *domain)
661 {
662 ya_result ret;
663
664 rnd = random_init(0);
665
666 if(dirent_get_file_type(g_config.plan_path, ".") == DT_UNKNOWN)
667 {
668 formatln("%{dnsname}: having trouble with directory '%s'", domain, g_config.plan_path);
669 return INVALID_PATH;
670 }
671
672 keyroll_t keyroll;
673
674 if(FAIL(ret = keyroll_init(&keyroll, domain, g_config.plan_path, g_config.keys_path, g_config.server, TRUE)))
675 {
676 return ret;
677 }
678
679 if(FAIL(ret = keyroll_update_apply_verify_retries_set(&keyroll, g_config.update_apply_verify_retries, g_config.update_apply_verify_retries_delay)))
680 {
681 log_err("%{dnsname}: update apply retry combination out of acceptable range", domain);
682 formatln("%{dnsname}: update apply retry combination out of acceptable range", domain);
683 keyroll_finalize(&keyroll);
684 return ret;
685 }
686
687 if(FAIL(ret = keyroll_match_verify_retries_set(&keyroll, g_config.match_verify_retries, g_config.match_verify_retries_delay)))
688 {
689 log_err("%{dnsname}: match retry combination out of acceptable range", domain);
690 formatln("%{dnsname}: match retry combination out of acceptable range", domain);
691 keyroll_finalize(&keyroll);
692 return ret;
693 }
694
695 // at this point:
696 // _ we know the present state on the server
697 // _ we know the plan folder for this domain exists
698
699 if(g_config.reset)
700 {
701 // delete the content of the plan folder
702
703 if(!g_config.dryrun)
704 {
705 log_info("%{dnsname}: deleting the plan and private keys for domain", keyroll.domain);
706 formatln("%{dnsname}: deleting the plan and private keys for domain", keyroll.domain);
707 keyroll_plan_purge(&keyroll);
708 }
709 else
710 {
711 log_info("%{dnsname}: dryrun: not really deleting the plan and private keys for domain", keyroll.domain);
712 formatln("%{dnsname}: dryrun: not really deleting the plan and private keys for domain", keyroll.domain);
713 }
714 }
715 else
716 {
717 if(FAIL(ret = keyroll_plan_load(&keyroll)))
718 {
719 if(ret != MAKE_ERRNO_ERROR(ENOENT))
720 {
721 log_err("%{dnsname}: plan loading failed: %r", keyroll.domain, ret);
722 formatln("%{dnsname}: plan loading failed: %r", keyroll.domain, ret);
723
724 keyroll_finalize(&keyroll);
725 return ret;
726 }
727
728 log_info("%{dnsname}: there are no plans for the domain in the directory '%s'", keyroll.domain, g_config.plan_path);
729 formatln("%{dnsname}: there are no plans for the domain in the directory '%s'", keyroll.domain, g_config.plan_path);
730 }
731 }
732
733 s64 generate_from = timeus_from_smarttime(g_config.generate_from);
734
735 if(generate_from < 0)
736 {
737 log_err("%{dnsname}: cannot parse '%s'", domain, g_config.generate_from);
738 formatln("%{dnsname}: cannot parse '%s'", domain, g_config.generate_from);
739 keyroll_finalize(&keyroll);
740 return ret;
741 }
742
743 generate_from /= ONE_SECOND_US;
744 generate_from *= ONE_SECOND_US;
745
746 s64 generate_until = timeus_from_smarttime_ex(g_config.generate_until, generate_from);
747
748 if(generate_until < 0)
749 {
750 log_err("%{dnsname}: cannot parse '%s'", domain, g_config.generate_until);
751 formatln("%{dnsname}: cannot parse '%s'", domain, g_config.generate_until);
752 return ret;
753 }
754
755 log_info("%{dnsname}: covering %llU to %llU", domain, generate_from, generate_until);
756 formatln("%{dnsname}: covering %llU to %llU", domain, generate_from, generate_until);
757
758 if(FAIL(ret = keyroll_plan_with_policy(&keyroll, generate_from, generate_until, g_config.policy_name)))
759 {
760 log_err("%{dnsname}: policy-based planning failed: %r", domain, ret);
761 formatln("%{dnsname}: policy-based planning failed: %r", domain, ret);
762 return ret;
763 }
764
765 if(g_config.print_plan)
766 {
767 if(FAIL(ret = keyroll_print(&keyroll, termout)))
768 {
769 log_err("%{dnsname}: the plan is not perfect", domain);
770 formatln("%{dnsname}: the plan is not perfect", domain);
771 }
772 }
773
774 if(g_config.dryrun)
775 {
776 log_info("%{dnsname}: dryrun: not storing the plan", domain);
777 formatln("%{dnsname}: dryrun: not storing the plan", domain);
778 ret = SUCCESS;
779 }
780 else
781 {
782 if(ISOK(ret = keyroll_store(&keyroll)))
783 {
784 log_info("%{dnsname}: plan stored", domain);
785 formatln("%{dnsname}: plan stored", domain);
786 }
787 else
788 {
789 log_err("%{dnsname}: failed to store the plan: %r", domain, ret);
790 formatln("%{dnsname}: failed to store the plan: %r", domain, ret);
791 }
792 }
793
794 keyroll_finalize(&keyroll);
795
796 return ret;
797 }
798
799 static ya_result
program_mode_generate_all()800 program_mode_generate_all()
801 {
802 pid_t pid;
803 ya_result ret;
804 char pid_file_path_buffer[PATH_MAX];
805 char *pid_file_path = &pid_file_path_buffer[0];
806
807 snformat(pid_file_path_buffer, sizeof(pid_file_path_buffer), "%s/%s", g_config.pid_path, g_config.pid_file);
808
809 if(FAIL(ret = pid_check_running_program(pid_file_path, &pid)))
810 {
811 log_err("already running with pid: %lu (%s)", pid, pid_file_path);
812 return ret;
813 }
814
815 if(FAIL(ret = directory_writable(g_config.plan_path)))
816 {
817 return ret;
818 }
819
820 if(ISOK(ret = server_setup_env(&pid, &pid_file_path, g_config.uid, g_config.gid, SETUP_CREATE_PID_FILE|SETUP_ID_CHANGE|SETUP_CORE_LIMITS)))
821 {
822 if(g_config.reset && g_config.user_confirmation)
823 {
824 println("WARNING: A full data reset has been required for the following domains:");
825 // delete the content of the plan folder
826 for(int i = 0; i <= ptr_vector_last_index(&g_config.fqdns); ++i)
827 {
828 const u8 *domain = (const u8*)ptr_vector_get(&g_config.fqdns, i);
829 formatln(" %{dnsname}", domain);
830 }
831 println("All currently stored steps and private keys for the above domains will be erased.\nThis operation cannot be undone.");
832 if(FAIL(ret = get_user_confirmation()))
833 {
834 return ret;
835 }
836 }
837
838 for(int i = 0; i <= ptr_vector_last_index(&g_config.fqdns); ++i)
839 {
840 const u8 *domain = (const u8*)ptr_vector_get(&g_config.fqdns, i);
841 log_info("zone generate: %{dnsname}", domain);
842 if(FAIL(ret = program_mode_generate(domain)))
843 {
844 log_err("zone generate: %{dnsname} failed: %r", domain, ret);
845 break;
846 }
847 }
848
849 unlink(pid_file_path);
850 }
851
852 return ret;
853 }
854
855 static void
signal_int(u8 signum)856 signal_int(u8 signum)
857 {
858 (void)signum;
859
860 if(!dnscore_shuttingdown())
861 {
862 dnscore_shutdown();
863 }
864
865 signal_handler_stop();
866 }
867
868 static void
signal_hup(u8 signum)869 signal_hup(u8 signum)
870 {
871 (void)signum;
872
873 logger_reopen();
874 }
875
876 static ya_result
program_mode_play(const u8 * domain,bool does_loop)877 program_mode_play(const u8 *domain, bool does_loop)
878 {
879 ya_result ret;
880 int consecutive_errors = 0;
881
882 rnd = random_init(0);
883
884 if(dirent_get_file_type(g_config.plan_path, ".") == DT_UNKNOWN)
885 {
886 log_info("play: %{dnsname}: having trouble with directory '%s'", domain, g_config.plan_path);
887 return INVALID_PATH;
888 }
889
890 keyroll_t keyroll;
891
892 // start from an empty state
893 if(FAIL(ret = keyroll_init(&keyroll, domain, g_config.plan_path, g_config.keys_path, g_config.server, FALSE)))
894 {
895 return ret;
896 }
897
898 // at this point:
899 // _ we know the present state on the server
900 // _ we know the plan folder for this domain exists
901
902 if(does_loop)
903 {
904 log_info("play: %{dnsname}: loading plan", domain);
905 logger_flush();
906 }
907
908 if(FAIL(ret = keyroll_plan_load(&keyroll)))
909 {
910 if(ret != MAKE_ERRNO_ERROR(ENOENT))
911 {
912 log_info("play: %{dnsname}: plan loading failed: %r", domain, ret);
913 }
914 else
915 {
916 log_info("play: %{dnsname}: there are no plans on storage (%s)", domain, g_config.plan_path);
917 }
918
919 logger_flush();
920
921 keyroll_finalize(&keyroll);
922
923 return ret;
924 }
925
926 s64 step_time;
927
928 do
929 {
930 step_time = timeus_with_offset();
931
932 log_info("play: %{dnsname}: now is %llU (%lli)", domain, step_time, step_time);
933
934 keyroll_step_t *current_step = keyroll_get_current_step_at(&keyroll, step_time);
935
936 if(current_step == NULL)
937 {
938 log_info("play: %{dnsname}: there are no steps registered for this time", domain);
939
940 keyroll_finalize(&keyroll);
941
942 return INVALID_STATE_ERROR;
943 }
944
945 log_info("play: %{dnsname}: the current step happened at %llU (%lli)", domain, current_step->epochus, current_step->epochus);
946
947 keyroll_step_t *next_step = keyroll_get_next_step_from(&keyroll, step_time + 1);
948
949 s64 next_step_time;
950
951 if(next_step != NULL)
952 {
953 next_step_time = next_step->epochus;
954 log_info("play: %{dnsname}: the step that will follow will happen at %llU (%lli)", domain, next_step_time, next_step_time);
955 }
956 else
957 {
958 next_step_time = ONE_SECOND_US * MAX_U32;
959 }
960
961 /*
962 // check the expected set with the server
963 // do a query for all DNSKEY + RRSIG and compare with the step
964
965 ptr_vector current_dnskey_rrsig_rr;
966 ptr_vector_init_ex(¤t_dnskey_rrsig_rr, 32);
967 */
968 const keyroll_step_t *matched_step = NULL;
969
970 ret = keyroll_get_state_find_match_and_play(&keyroll, step_time, current_step, &matched_step);
971
972 if(ISOK(ret))
973 {
974 log_info("play: %{dnsname}: first loop ended (%u)", domain, ret);
975 break;
976 }
977
978 if(ret != STOPPED_BY_APPLICATION_SHUTDOWN)
979 {
980 log_info("play: %{dnsname}: keyroll_get_state_find_match returned %r (retrying in " TOSTRING(SERVER_FAILURE_RETRY_DELAY) " seconds)", domain, ret);
981
982 s64 now = timeus();
983
984 if(now + ONE_SECOND_US * SERVER_FAILURE_RETRY_DELAY < next_step_time)
985 {
986 for(int i = 0; (i < SERVER_FAILURE_RETRY_DELAY) && !dnscore_shuttingdown(); ++i)
987 {
988 sleep(1);
989 }
990 }
991 else
992 {
993 // we have gone through a step, current computations are invalid : restart the roll for this domain
994 ret = KEYROLL_MUST_REINITIALIZE;
995
996 keyroll_finalize(&keyroll);
997
998 return ret;
999 }
1000 }
1001 }
1002 while(g_config.wait_for_yadifad && !dnscore_shuttingdown());
1003
1004 if(FAIL(ret))
1005 {
1006 log_notice("play: %{dnsname}: keyroll_get_state_find_match returned %r", domain, ret);
1007
1008 keyroll_finalize(&keyroll);
1009
1010 return ret;
1011 }
1012
1013 keyroll_step_t *next_step = keyroll_get_next_step_from(&keyroll, step_time);
1014
1015 if(next_step != NULL)
1016 {
1017 log_info("play: %{dnsname}: the next step happens at %llU (%lli)", domain, next_step->epochus, next_step->epochus);
1018
1019 // find the interval for now
1020
1021 s64 last_warning_us = 0;
1022
1023 // wait until the next event
1024
1025 while(!dnscore_shuttingdown())
1026 {
1027 log_info("play: %{dnsname}: waiting until %llU (%lli)", domain, next_step->epochus, next_step->epochus);
1028
1029 do
1030 {
1031 s64 now = timeus_with_offset();
1032
1033 if(now - WAIT_MARGIN >= next_step->epochus)
1034 {
1035 break;
1036 }
1037 else
1038 {
1039 usleep(MAX(MIN(next_step->epochus - (now - WAIT_MARGIN), WAIT_MARGIN), 1000));
1040 }
1041 }
1042 while(!dnscore_shuttingdown());
1043
1044 if(dnscore_shuttingdown())
1045 {
1046 log_info("play: %{dnsname}: shutting down", domain);
1047 logger_flush();
1048 break;
1049 }
1050
1051 step_time = timeus_with_offset();
1052
1053 keyroll_step_t *current_step = keyroll_get_current_step_at(&keyroll, step_time);
1054
1055 if(current_step == NULL)
1056 {
1057 log_err("play: %{dnsname}: there are no steps registered for this time: shutting down", domain);
1058 break;
1059 }
1060
1061 ret = keyroll_get_state_find_match_and_play(&keyroll, step_time, current_step, NULL);
1062
1063 log_warn("play: %{dnsname}: match and play returned: %r (%x)", domain, ret, ret);
1064
1065 if(ISOK(ret))
1066 {
1067 consecutive_errors = 0;
1068 }
1069 else
1070 {
1071 // test for error conditions warranting a retry
1072
1073 switch(ret)
1074 {
1075 case MAKE_ERRNO_ERROR(ETIMEDOUT):
1076 case MAKE_ERRNO_ERROR(EADDRNOTAVAIL):
1077 case MAKE_ERRNO_ERROR(EAGAIN):
1078 case MAKE_DNSMSG_ERROR(RCODE_SERVFAIL):
1079 case MAKE_DNSMSG_ERROR(RCODE_REFUSED):
1080 case UNABLE_TO_COMPLETE_FULL_READ:
1081 {
1082 ++consecutive_errors;
1083
1084 if(consecutive_errors < CONSECUTIVE_ERRORS_BEFORE_RESTART)
1085 {
1086 step_time = timeus_with_offset();
1087 if(step_time - last_warning_us >= ONE_SECOND_US * 60)
1088 {
1089 log_warn("play: %{dnsname}: step play failure: %r: trying again (this message will only be printed every minute)", domain, ret);
1090 last_warning_us = step_time;
1091 }
1092
1093 sleep(1);
1094 continue;
1095 }
1096 else
1097 {
1098 log_warn("play: %{dnsname}: step play failure: %r: restarting", domain, ret);
1099 ret = KEYROLL_MUST_REINITIALIZE;
1100 break;
1101 }
1102 }
1103 default:
1104 {
1105 // unrecoverable error
1106 log_err("play: %{dnsname}: step play failure: %r (%x): shutting down. Please restart after fixing the issue.", domain, ret, ret);
1107 break;
1108 }
1109 }
1110 break;
1111 }
1112
1113 if(!does_loop)
1114 {
1115 break;
1116 }
1117
1118 next_step = keyroll_get_next_step_from(&keyroll, next_step->epochus + 1);
1119 }
1120 }
1121 else
1122 {
1123 log_info("play: %{dnsname}: there is no next step recorded after %llU", domain, step_time);
1124 }
1125
1126 keyroll_finalize(&keyroll);
1127
1128 return ret;
1129 }
1130
1131 struct program_mode_play_thread_args
1132 {
1133 const u8 *fqdn;
1134 bool does_loop;
1135 };
1136
1137 typedef struct program_mode_play_thread_args program_mode_play_thread_args;
1138
1139 static void*
program_mode_play_thread(void * args_)1140 program_mode_play_thread(void *args_)
1141 {
1142 program_mode_play_thread_args *args = (program_mode_play_thread_args*)args_;
1143 while(!dnscore_shuttingdown())
1144 {
1145 ya_result ret = program_mode_play(args->fqdn, args->does_loop);
1146
1147 if(ISOK(ret))
1148 {
1149 log_info("%{dnsname}: key roll stopped", args->fqdn);
1150 break;
1151 }
1152 else
1153 {
1154 if(ret == KEYROLL_MUST_REINITIALIZE)
1155 {
1156 log_warn("%{dnsname}: trying again from the start", args->fqdn);
1157 }
1158 else if(ret == STOPPED_BY_APPLICATION_SHUTDOWN)
1159 {
1160 log_warn("%{dnsname}: keyroll is shutting down", args->fqdn);
1161 break;
1162 }
1163 else
1164 {
1165 log_err("%{dnsname}: shutting down (%r)", args->fqdn, ret);
1166 logger_flush();
1167 dnscore_shutdown();
1168 break;
1169 }
1170 }
1171 }
1172 return NULL;
1173 }
1174
1175 static ya_result
program_mode_play_all(bool does_loop,bool daemonise)1176 program_mode_play_all(bool does_loop, bool daemonise)
1177 {
1178 ya_result ret;
1179
1180 pid_t pid;
1181 char pid_file_path_buffer[PATH_MAX];
1182 char *pid_file_path = &pid_file_path_buffer[0];
1183 snformat(pid_file_path_buffer, sizeof(pid_file_path_buffer), "%s/%s", g_config.pid_path, g_config.pid_file);
1184
1185 if(FAIL(ret = pid_check_running_program(pid_file_path, &pid)))
1186 {
1187 log_err("already running with pid: %lu (%s)", pid, pid_file_path);
1188 return ret;
1189 }
1190
1191 if(FAIL(ret = directory_writable(g_config.keys_path)))
1192 {
1193 return ret;
1194 }
1195
1196 if(ISOK(ret = server_setup_env(&pid, &pid_file_path, g_config.uid, g_config.gid, SETUP_CREATE_PID_FILE|SETUP_ID_CHANGE|SETUP_CORE_LIMITS)))
1197 {
1198 if(daemonise)
1199 {
1200 if(!does_loop)
1201 {
1202 log_warn("daemonise requires to enable loops");
1203 does_loop = true;
1204 }
1205
1206 signal_handler_finalize();
1207
1208 server_setup_daemon_go();
1209
1210 u32 setup_flags = SETUP_CORE_LIMITS | SETUP_ID_CHANGE | SETUP_CREATE_PID_FILE;
1211
1212 if(FAIL(ret = server_setup_env(NULL, &pid_file_path, g_config.uid, g_config.gid, setup_flags)))
1213 {
1214 log_err("server setup failed: %r", ret);
1215 return EXIT_FAILURE;
1216 }
1217
1218 if(FAIL(ret = signal_handler_init()))
1219 {
1220 log_err("failed to setup the signal handler: %r", ret);
1221
1222 osformatln(termerr, "error: failed to setup the signal handler: %r", ret);
1223 flusherr();
1224
1225 logger_flush();
1226
1227 return ret;
1228 }
1229 }
1230 }
1231 else
1232 {
1233 log_err("server setup failed: %r", ret);
1234 return ret;
1235 }
1236
1237 struct thread_pool_s *tp = thread_pool_init(ptr_vector_size(&g_config.fqdns), ptr_vector_size(&g_config.fqdns) * 2);
1238
1239 if(tp != NULL)
1240 {
1241 program_mode_play_thread_args *args;
1242 MALLOC_OBJECT_ARRAY_OR_DIE(args, program_mode_play_thread_args, ptr_vector_size(&g_config.fqdns), GENERIC_TAG);
1243
1244 thread_pool_task_counter counter;
1245 thread_pool_counter_init(&counter, 0);
1246
1247 for(int i = 0; i <= ptr_vector_last_index(&g_config.fqdns); ++i)
1248 {
1249 char *domain = (char*)ptr_vector_get(&g_config.domains, i);
1250 u8 *fqdn = (u8*)ptr_vector_get(&g_config.fqdns, i);
1251 log_info("zone play: %{dnsname}", fqdn);
1252
1253 args[i].fqdn = fqdn; // VS false positive (nonsense)
1254 args[i].does_loop = does_loop;
1255
1256 thread_pool_enqueue_call(tp, program_mode_play_thread, &args[i], &counter, domain);
1257 }
1258
1259 // ensure the counter was incremented
1260
1261 thread_pool_wait_queue_empty(tp);
1262
1263 // wait for the shutdown or for workers to stop
1264
1265 for(;;)
1266 {
1267 ret = thread_pool_counter_wait_equal_with_timeout(&counter, 0, ONE_SECOND_US * 30);
1268
1269 log_debug("keyroll: waiting for the threads to stop (%r)", ret);
1270
1271 if(dnscore_shuttingdown())
1272 {
1273 break;
1274 }
1275 }
1276
1277 s64 wait_stop_begin = timeus();
1278 bool wait_stop_error_message = FALSE;
1279 for(u32 wait_count = 0; thread_pool_counter_get_value(&counter) > 0; ++wait_count)
1280 {
1281 sleep(1);
1282 s64 wait_stop_now = timeus();
1283 s64 wait_stop_duration = wait_stop_now - wait_stop_begin;
1284 if(dnscore_shuttingdown() && (wait_stop_duration > (ONE_SECOND_US * 30)) )
1285 {
1286 if(!wait_stop_error_message)
1287 {
1288 log_err("keyroll workers aren't stopping");
1289 logger_flush();
1290 wait_stop_error_message = TRUE;
1291 }
1292 if(wait_stop_duration > (ONE_SECOND_US * 60))
1293 {
1294 log_err("not waiting anymore");
1295 logger_flush();
1296 }
1297 }
1298 }
1299
1300 thread_pool_destroy(tp);
1301 tp = NULL;
1302
1303 free(args);
1304 }
1305
1306 if(does_loop)
1307 {
1308 log_info("keyroll stopped");
1309 }
1310
1311 unlink(pid_file_path);
1312
1313 return SUCCESS;
1314 }
1315
1316 static ya_result
program_mode_print(const u8 * domain)1317 program_mode_print(const u8 *domain)
1318 {
1319 ya_result ret;
1320 if(dirent_get_file_type(g_config.plan_path, ".") == DT_UNKNOWN)
1321 {
1322 log_info("print: %{dnsname}: having trouble with directory '%s'", domain, g_config.plan_path);
1323 return INVALID_PATH;
1324 }
1325
1326 keyroll_t keyroll;
1327
1328 // start from an empty state
1329 if(FAIL(ret = keyroll_init(&keyroll, domain, g_config.plan_path, g_config.keys_path, g_config.server, FALSE)))
1330 {
1331 return ret;
1332 }
1333
1334 // at this point:
1335 // _ we know the present state on the server
1336 // _ we know the plan folder for this domain exists
1337
1338 if(FAIL(ret = keyroll_plan_load(&keyroll)))
1339 {
1340 if(ret != MAKE_ERRNO_ERROR(ENOENT))
1341 {
1342 log_info("print: %{dnsname}: plan loading failed: %r", domain, ret);
1343 formatln("print: %{dnsname}: plan loading failed: %r", domain, ret);
1344 }
1345 else
1346 {
1347 log_info("print: %{dnsname}: there are no plans on storage (%s)", domain, g_config.plan_path);
1348 formatln("print: %{dnsname}: there are no plans on storage (%s)", domain, g_config.plan_path);
1349 }
1350
1351 return ret;
1352 }
1353
1354 ret = keyroll_print(&keyroll, termout);
1355
1356 return ret;
1357 }
1358
1359 static ya_result
program_mode_print_json(const u8 * domain)1360 program_mode_print_json(const u8 *domain)
1361 {
1362 ya_result ret;
1363 if(dirent_get_file_type(g_config.plan_path, ".") == DT_UNKNOWN)
1364 {
1365 log_info("print: %{dnsname}: having trouble with directory '%s'", domain, g_config.plan_path);
1366 return INVALID_PATH;
1367 }
1368
1369 keyroll_t keyroll;
1370
1371 // start from an empty state
1372 if(FAIL(ret = keyroll_init(&keyroll, domain, g_config.plan_path, g_config.keys_path, g_config.server, FALSE)))
1373 {
1374 return ret;
1375 }
1376
1377 // at this point:
1378 // _ we know the present state on the server
1379 // _ we know the plan folder for this domain exists
1380
1381 if(FAIL(ret = keyroll_plan_load(&keyroll)))
1382 {
1383 if(ret != MAKE_ERRNO_ERROR(ENOENT))
1384 {
1385 log_info("print: %{dnsname}: plan loading failed: %r", domain, ret);
1386 formatln("print: %{dnsname}: plan loading failed: %r", domain, ret);
1387 }
1388 else
1389 {
1390 log_info("print: %{dnsname}: there are no plans on storage (%s)", domain, g_config.plan_path);
1391 formatln("print: %{dnsname}: there are no plans on storage (%s)", domain, g_config.plan_path);
1392 }
1393
1394 return ret;
1395 }
1396
1397 ret = keyroll_print_json(&keyroll, termout);
1398
1399 return ret;
1400 }
1401
1402 static ya_result
program_mode_test()1403 program_mode_test()
1404 {
1405 return SUCCESS;
1406 }
1407
1408 int
main(int argc,char * argv[])1409 main(int argc, char *argv[])
1410 {
1411 /* initializes the core library */
1412 dnscore_init();
1413 keyroll_errors_register();
1414
1415 ya_result ret = main_config(argc, argv);
1416
1417 if(FAIL(ret) || cmdline_help_get() || cmdline_version_get())
1418 {
1419 return EXIT_FAILURE;
1420 }
1421
1422 if(g_config.dryrun)
1423 {
1424 println("dryrun mode");
1425 log_notice("dryrun mode");
1426 }
1427
1428 if(ptr_vector_size(&g_config.fqdns) == 0)
1429 {
1430 log_err("No domain has been configured.");
1431 println("No domain has been configured.");
1432 flushout();
1433 return EXIT_FAILURE;
1434 }
1435
1436 signal_handler_init();
1437 signal_handler_set(SIGINT, signal_int);
1438 signal_handler_set(SIGTERM, signal_int);
1439 signal_handler_set(SIGHUP, signal_hup);
1440
1441 flushout();
1442 flusherr();
1443 logger_flush();
1444
1445 switch(g_config.program_mode)
1446 {
1447 case NONE:
1448 {
1449 println("\nno -m option given\n");
1450 help_print(argv[0]);
1451 break;
1452 }
1453 case GENERATE:
1454 {
1455 ret = program_mode_generate_all();
1456 break;
1457 }
1458 case PLAY:
1459 {
1460 ret = program_mode_play_all(FALSE, FALSE);
1461 break;
1462 }
1463 case PLAYLOOP:
1464 {
1465 ret = program_mode_play_all(TRUE, g_config.daemonise);
1466 break;
1467 }
1468 case PRINT:
1469 {
1470 for(int i = 0; i <= ptr_vector_last_index(&g_config.fqdns); ++i)
1471 {
1472 u8 *fqdn = (u8*)ptr_vector_get(&g_config.fqdns, i);
1473 program_mode_print(fqdn);
1474 }
1475 break;
1476 }
1477 case PRINT_JSON:
1478 {
1479 formatln("{\"version\": \"" YKEYROLL_VERSION "\", \"plans\": [");
1480 for(int i = 0; i <= ptr_vector_last_index(&g_config.fqdns); ++i)
1481 {
1482 if(i > 0)
1483 {
1484 println(",");
1485 }
1486 u8 *fqdn = (u8*)ptr_vector_get(&g_config.fqdns, i);
1487 program_mode_print_json(fqdn);
1488 }
1489 println("]}");
1490 break;
1491 }
1492
1493 case TEST:
1494 {
1495 ret = program_mode_test();
1496 break;
1497 }
1498 default:
1499 {
1500 ret = INVALID_STATE_ERROR;
1501 break;
1502 }
1503 }
1504
1505 if(FAIL(ret))
1506 {
1507 log_err("failed with: %r", ret);
1508 osformatln(termerr, "failed with: %r", ret);
1509 }
1510
1511 flushout();
1512 flusherr();
1513 fflush(NULL);
1514
1515 signal_handler_finalize();
1516 dnscore_finalize();
1517
1518 return ISOK(ret)?EXIT_SUCCESS:EXIT_FAILURE;
1519 }
1520