1 /* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <string.h>
18 #include <unistd.h>
19 #include <sys/time.h>
20 #include <urcu.h>
21
22 #include "knot/common/log.h"
23 #include "knot/common/stats.h"
24 #include "knot/conf/confio.h"
25 #include "knot/ctl/commands.h"
26 #include "knot/dnssec/key-events.h"
27 #include "knot/events/events.h"
28 #include "knot/events/handlers.h"
29 #include "knot/journal/journal_metadata.h"
30 #include "knot/nameserver/query_module.h"
31 #include "knot/updates/zone-update.h"
32 #include "knot/zone/backup.h"
33 #include "knot/zone/digest.h"
34 #include "knot/zone/timers.h"
35 #include "knot/zone/zonedb-load.h"
36 #include "knot/zone/zonefile.h"
37 #include "libknot/libknot.h"
38 #include "libknot/yparser/yptrafo.h"
39 #include "contrib/files.h"
40 #include "contrib/string.h"
41 #include "contrib/strtonum.h"
42 #include "contrib/ucw/lists.h"
43 #include "libzscanner/scanner.h"
44
45 #define MATCH_OR_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] == NULL || \
46 strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL)
47
48 #define MATCH_AND_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] != NULL && \
49 strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL)
50
51 typedef struct {
52 ctl_args_t *args;
53 int type_filter; // -1: no specific type, [0, 2^16]: specific type.
54 knot_dump_style_t style;
55 knot_ctl_data_t data;
56 knot_dname_txt_storage_t zone;
57 knot_dname_txt_storage_t owner;
58 char ttl[16];
59 char type[32];
60 char rdata[2 * 65536];
61 } send_ctx_t;
62
63 static struct {
64 send_ctx_t send_ctx;
65 zs_scanner_t scanner;
66 char txt_rr[sizeof(((send_ctx_t *)0)->owner) +
67 sizeof(((send_ctx_t *)0)->ttl) +
68 sizeof(((send_ctx_t *)0)->type) +
69 sizeof(((send_ctx_t *)0)->rdata)];
70 } ctl_globals;
71
72 /*!
73 * Evaluates a filter pair and checks for conflicting filters.
74 *
75 * \param[in] args Command arguments.
76 * \param[out] param The filter to be set.
77 * \param[in] dflt Default filter value.
78 * \param[in] filter Name of the filter.
79 * \param[in] neg_filter Name of the negative filter.
80 *
81 * \return false if there is a filter conflict, true otherwise.
82 */
83
eval_opposite_filters(ctl_args_t * args,bool * param,bool dflt,int filter,int neg_filter)84 static bool eval_opposite_filters(ctl_args_t *args, bool *param, bool dflt,
85 int filter, int neg_filter)
86 {
87 bool set = MATCH_AND_FILTER(args, filter);
88 bool unset = MATCH_AND_FILTER(args, neg_filter);
89
90 *param = dflt ? (set || !unset) : (set && !unset);
91 return !(set && unset);
92 }
93
schedule_trigger(zone_t * zone,ctl_args_t * args,zone_event_type_t event,bool user)94 static int schedule_trigger(zone_t *zone, ctl_args_t *args, zone_event_type_t event,
95 bool user)
96 {
97 int ret = KNOT_EOK;
98
99 if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_BLOCKING)) {
100 ret = zone_events_schedule_blocking(zone, event, user);
101 } else if (user) {
102 zone_events_schedule_user(zone, event);
103 } else {
104 zone_events_schedule_now(zone, event);
105 }
106
107 return ret;
108 }
109
ctl_log_conf_data(knot_ctl_data_t * data)110 static void ctl_log_conf_data(knot_ctl_data_t *data)
111 {
112 if (data == NULL) {
113 return;
114 }
115
116 const char *section = (*data)[KNOT_CTL_IDX_SECTION];
117 const char *item = (*data)[KNOT_CTL_IDX_ITEM];
118 const char *id = (*data)[KNOT_CTL_IDX_ID];
119
120 if (section != NULL) {
121 log_ctl_debug("control, config item '%s%s%s%s%s%s'", section,
122 (id != NULL ? "[" : ""),
123 (id != NULL ? id : ""),
124 (id != NULL ? "]" : ""),
125 (item != NULL ? "." : ""),
126 (item != NULL ? item : ""));
127 }
128 }
129
send_error(ctl_args_t * args,const char * msg)130 static void send_error(ctl_args_t *args, const char *msg)
131 {
132 knot_ctl_data_t data;
133 memcpy(&data, args->data, sizeof(data));
134
135 data[KNOT_CTL_IDX_ERROR] = msg;
136
137 int ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data);
138 if (ret != KNOT_EOK) {
139 log_ctl_debug("control, failed to send error (%s)", knot_strerror(ret));
140 }
141 }
142
get_zone(ctl_args_t * args,zone_t ** zone)143 static int get_zone(ctl_args_t *args, zone_t **zone)
144 {
145 const char *name = args->data[KNOT_CTL_IDX_ZONE];
146 assert(name != NULL);
147
148 knot_dname_storage_t buff;
149 knot_dname_t *dname = knot_dname_from_str(buff, name, sizeof(buff));
150 if (dname == NULL) {
151 return KNOT_EINVAL;
152 }
153 knot_dname_to_lower(dname);
154
155 *zone = knot_zonedb_find(args->server->zone_db, dname);
156 if (*zone == NULL) {
157 return KNOT_ENOZONE;
158 }
159
160 return KNOT_EOK;
161 }
162
zones_apply(ctl_args_t * args,int (* fcn)(zone_t *,ctl_args_t *))163 static int zones_apply(ctl_args_t *args, int (*fcn)(zone_t *, ctl_args_t *))
164 {
165 int ret;
166
167 // Process all configured zones if none is specified.
168 if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
169 bool failed = false;
170 knot_zonedb_iter_t *it = knot_zonedb_iter_begin(args->server->zone_db);
171 while (!knot_zonedb_iter_finished(it)) {
172 args->suppress = false;
173 ret = fcn((zone_t *)knot_zonedb_iter_val(it), args);
174 if (ret != KNOT_EOK && !args->suppress) {
175 failed = true;
176 }
177 knot_zonedb_iter_next(it);
178 }
179 knot_zonedb_iter_free(it);
180
181 if (failed) {
182 ret = KNOT_CTL_EZONE;
183 log_ctl_error("control, error (%s)", knot_strerror(ret));
184 send_error(args, knot_strerror(ret));
185 }
186
187 return KNOT_EOK;
188 }
189
190 while (true) {
191 zone_t *zone;
192 ret = get_zone(args, &zone);
193 if (ret == KNOT_EOK) {
194 ret = fcn(zone, args);
195 }
196 if (ret != KNOT_EOK) {
197 log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
198 "control, error (%s)", knot_strerror(ret));
199 send_error(args, knot_strerror(ret));
200 }
201
202 // Get next zone name.
203 ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
204 if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
205 break;
206 }
207 strtolower((char *)args->data[KNOT_CTL_IDX_ZONE]);
208
209 // Log the other zones the same way as the first one from process.c.
210 log_ctl_zone_str_info(args->data[KNOT_CTL_IDX_ZONE],
211 "control, received command '%s'",
212 args->data[KNOT_CTL_IDX_CMD]);
213 }
214
215 return ret;
216 }
217
zone_status(zone_t * zone,ctl_args_t * args)218 static int zone_status(zone_t *zone, ctl_args_t *args)
219 {
220 knot_dname_txt_storage_t name;
221 if (knot_dname_to_str(name, zone->name, sizeof(name)) == NULL) {
222 return KNOT_EINVAL;
223 }
224
225 knot_ctl_data_t data = {
226 [KNOT_CTL_IDX_ZONE] = name
227 };
228
229 int ret;
230 char buff[128];
231 knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
232
233 if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_ROLE)) {
234 data[KNOT_CTL_IDX_TYPE] = "role";
235
236 if (zone_is_slave(conf(), zone)) {
237 data[KNOT_CTL_IDX_DATA] = "slave";
238 } else {
239 data[KNOT_CTL_IDX_DATA] = "master";
240 }
241
242 ret = knot_ctl_send(args->ctl, type, &data);
243 if (ret != KNOT_EOK) {
244 return ret;
245 } else {
246 type = KNOT_CTL_TYPE_EXTRA;
247 }
248 }
249
250 if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_SERIAL)) {
251 data[KNOT_CTL_IDX_TYPE] = "serial";
252
253 if (zone->contents != NULL) {
254 knot_rdataset_t *soa = node_rdataset(zone->contents->apex,
255 KNOT_RRTYPE_SOA);
256 ret = snprintf(buff, sizeof(buff), "%u", knot_soa_serial(soa->rdata));
257 } else {
258 ret = snprintf(buff, sizeof(buff), "none");
259 }
260 if (ret < 0 || ret >= sizeof(buff)) {
261 return KNOT_ESPACE;
262 }
263
264 data[KNOT_CTL_IDX_DATA] = buff;
265
266 ret = knot_ctl_send(args->ctl, type, &data);
267 if (ret != KNOT_EOK) {
268 return ret;
269 } else {
270 type = KNOT_CTL_TYPE_EXTRA;
271 }
272 }
273
274 if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_TRANSACTION)) {
275 data[KNOT_CTL_IDX_TYPE] = "transaction";
276 data[KNOT_CTL_IDX_DATA] = (zone->control_update != NULL) ? "open" : "none";
277 ret = knot_ctl_send(args->ctl, type, &data);
278 if (ret != KNOT_EOK) {
279 return ret;
280 } else {
281 type = KNOT_CTL_TYPE_EXTRA;
282 }
283 }
284
285 bool ufrozen = zone->events.ufrozen;
286 if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_FREEZE)) {
287 data[KNOT_CTL_IDX_TYPE] = "freeze";
288 if (ufrozen) {
289 if (zone_events_get_time(zone, ZONE_EVENT_UTHAW) < time(NULL)) {
290 data[KNOT_CTL_IDX_DATA] = "yes";
291 } else {
292 data[KNOT_CTL_IDX_DATA] = "thawing";
293 }
294 } else {
295 if (zone_events_get_time(zone, ZONE_EVENT_UFREEZE) < time(NULL)) {
296 data[KNOT_CTL_IDX_DATA] = "no";
297 } else {
298 data[KNOT_CTL_IDX_DATA] = "freezing";
299
300 }
301 }
302 ret = knot_ctl_send(args->ctl, type, &data);
303 if (ret != KNOT_EOK) {
304 return ret;
305 }
306 }
307
308 if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_EVENTS)) {
309 for (zone_event_type_t i = 0; i < ZONE_EVENT_COUNT; i++) {
310 // Events not worth showing or used elsewhere.
311 if (i == ZONE_EVENT_UFREEZE || i == ZONE_EVENT_UTHAW) {
312 continue;
313 }
314
315 // Skip events affected by freeze.
316 if (ufrozen && ufreeze_applies(i)) {
317 continue;
318 }
319
320 data[KNOT_CTL_IDX_TYPE] = zone_events_get_name(i);
321 time_t ev_time = zone_events_get_time(zone, i);
322 if (zone->events.running && zone->events.type == i) {
323 ret = snprintf(buff, sizeof(buff), "running");
324 } else if (ev_time <= 0) {
325 ret = snprintf(buff, sizeof(buff), "not scheduled");
326 } else if (ev_time <= time(NULL)) {
327 ret = snprintf(buff, sizeof(buff), "pending");
328 } else {
329 ret = knot_time_print(TIME_PRINT_HUMAN_MIXED,
330 ev_time, buff, sizeof(buff));
331 }
332 if (ret < 0 || ret >= sizeof(buff)) {
333 return KNOT_ESPACE;
334 }
335 data[KNOT_CTL_IDX_DATA] = buff;
336
337 ret = knot_ctl_send(args->ctl, type, &data);
338 if (ret != KNOT_EOK) {
339 return ret;
340 }
341 type = KNOT_CTL_TYPE_EXTRA;
342 }
343 }
344
345 return KNOT_EOK;
346 }
347
zone_reload(zone_t * zone,_unused_ ctl_args_t * args)348 static int zone_reload(zone_t *zone, _unused_ ctl_args_t *args)
349 {
350 if (zone_expired(zone)) {
351 args->suppress = true;
352 return KNOT_ENOTSUP;
353 }
354
355 if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) {
356 return zone_reload_modules(conf(), args->server, zone->name);
357 }
358
359 return schedule_trigger(zone, args, ZONE_EVENT_LOAD, true);
360 }
361
zone_refresh(zone_t * zone,_unused_ ctl_args_t * args)362 static int zone_refresh(zone_t *zone, _unused_ ctl_args_t *args)
363 {
364 if (!zone_is_slave(conf(), zone)) {
365 args->suppress = true;
366 return KNOT_ENOTSUP;
367 }
368
369 return schedule_trigger(zone, args, ZONE_EVENT_REFRESH, true);
370 }
371
zone_retransfer(zone_t * zone,_unused_ ctl_args_t * args)372 static int zone_retransfer(zone_t *zone, _unused_ ctl_args_t *args)
373 {
374 if (!zone_is_slave(conf(), zone)) {
375 args->suppress = true;
376 return KNOT_ENOTSUP;
377 }
378
379 zone_set_flag(zone, ZONE_FORCE_AXFR);
380 return schedule_trigger(zone, args, ZONE_EVENT_REFRESH, true);
381 }
382
zone_notify(zone_t * zone,_unused_ ctl_args_t * args)383 static int zone_notify(zone_t *zone, _unused_ ctl_args_t *args)
384 {
385 return schedule_trigger(zone, args, ZONE_EVENT_NOTIFY, true);
386 }
387
zone_flush(zone_t * zone,ctl_args_t * args)388 static int zone_flush(zone_t *zone, ctl_args_t *args)
389 {
390 if (MATCH_AND_FILTER(args, CTL_FILTER_FLUSH_OUTDIR)) {
391 rcu_read_lock();
392 int ret = zone_dump_to_dir(conf(), zone, args->data[KNOT_CTL_IDX_DATA]);
393 rcu_read_unlock();
394 if (ret != KNOT_EOK) {
395 log_zone_warning(zone->name, "failed to update zone file (%s)",
396 knot_strerror(ret));
397 }
398 return ret;
399 }
400
401 if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) {
402 zone_set_flag(zone, ZONE_FORCE_FLUSH);
403 }
404
405 return schedule_trigger(zone, args, ZONE_EVENT_FLUSH, true);
406 }
407
init_backup(ctl_args_t * args,bool restore_mode)408 static int init_backup(ctl_args_t *args, bool restore_mode)
409 {
410 if (!MATCH_AND_FILTER(args, CTL_FILTER_BACKUP_OUTDIR)) {
411 return KNOT_ENOPARAM;
412 }
413
414 // Make sure that the backup outdir is not the same as the server DB storage.
415 conf_val_t db_storage_val = conf_db_param(conf(), C_STORAGE);
416 const char *db_storage = conf_str(&db_storage_val);
417
418 const char *backup_dir = args->data[KNOT_CTL_IDX_DATA];
419
420 if (same_path(backup_dir, db_storage)) {
421 char *msg = sprintf_alloc("%s the database storage directory not allowed",
422 restore_mode ? "restore from" : "backup to");
423
424 if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
425 log_ctl_error("%s", msg);
426 } else {
427 log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE], "%s", msg);
428 }
429 free(msg);
430 return KNOT_EINVAL;
431 }
432
433 // Evaluate filters (and possibly fail) before writing to the filesystem.
434 bool filter_zonefile, filter_journal, filter_timers, filter_kaspdb, filter_catalog;
435
436 // The default filter values are set just in this paragraph.
437 if (!(eval_opposite_filters(args, &filter_zonefile, true,
438 CTL_FILTER_BACKUP_ZONEFILE, CTL_FILTER_BACKUP_NOZONEFILE) &&
439 eval_opposite_filters(args, &filter_journal, false,
440 CTL_FILTER_BACKUP_JOURNAL, CTL_FILTER_BACKUP_NOJOURNAL) &&
441 eval_opposite_filters(args, &filter_timers, true,
442 CTL_FILTER_BACKUP_TIMERS, CTL_FILTER_BACKUP_NOTIMERS) &&
443 eval_opposite_filters(args, &filter_kaspdb, true,
444 CTL_FILTER_BACKUP_KASPDB, CTL_FILTER_BACKUP_NOKASPDB) &&
445 eval_opposite_filters(args, &filter_catalog, true,
446 CTL_FILTER_BACKUP_CATALOG, CTL_FILTER_BACKUP_NOCATALOG))) {
447 return KNOT_EXPARAM;
448 }
449
450 bool forced = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE);
451
452 zone_backup_ctx_t *ctx;
453
454 // The present timer db size is not up-to-date, use the maximum one.
455 conf_val_t timer_db_size = conf_db_param(conf(), C_TIMER_DB_MAX_SIZE);
456
457 int ret = zone_backup_init(restore_mode, forced,
458 args->data[KNOT_CTL_IDX_DATA],
459 knot_lmdb_copy_size(&args->server->kaspdb),
460 conf_int(&timer_db_size),
461 knot_lmdb_copy_size(&args->server->journaldb),
462 knot_lmdb_copy_size(&args->server->catalog.db),
463 &ctx);
464 if (ret != KNOT_EOK) {
465 return ret;
466 }
467
468 assert(ctx != NULL);
469 ctx->backup_zonefile = filter_zonefile;
470 ctx->backup_journal = filter_journal;
471 ctx->backup_timers = filter_timers;
472 ctx->backup_kaspdb = filter_kaspdb;
473 ctx->backup_catalog = filter_catalog;
474
475 zone_backups_add(&args->server->backup_ctxs, ctx);
476
477 return ret;
478 }
479
latest_backup_ctx(ctl_args_t * args)480 static zone_backup_ctx_t *latest_backup_ctx(ctl_args_t *args)
481 {
482 // no need to mutex in this case
483 return (zone_backup_ctx_t *)TAIL(args->server->backup_ctxs.ctxs);
484 }
485
deinit_backup(ctl_args_t * args)486 static int deinit_backup(ctl_args_t *args)
487 {
488 return zone_backup_deinit(latest_backup_ctx(args));
489 }
490
zone_backup_cmd(zone_t * zone,ctl_args_t * args)491 static int zone_backup_cmd(zone_t *zone, ctl_args_t *args)
492 {
493 zone_backup_ctx_t *ctx = latest_backup_ctx(args);
494 if (!ctx->restore_mode && ctx->failed) {
495 // No need to proceed with already faulty backup.
496 return KNOT_EOK;
497 }
498
499 if (zone->backup_ctx != NULL) {
500 log_zone_warning(zone->name, "backup or restore already in progress, skipping zone");
501 ctx->failed = true;
502 return KNOT_EPROGRESS;
503 }
504
505 zone->backup_ctx = ctx;
506 pthread_mutex_lock(&ctx->readers_mutex);
507 ctx->readers++;
508 pthread_mutex_unlock(&ctx->readers_mutex);
509 ctx->zone_count++;
510
511 int ret = schedule_trigger(zone, args, ZONE_EVENT_BACKUP, true);
512
513 if (ret == KNOT_EOK && !ctx->backup_global && (ctx->restore_mode || !ctx->failed)) {
514 ret = global_backup(ctx, zone->catalog, zone->name);
515 }
516
517 return ret;
518 }
519
zones_apply_backup(ctl_args_t * args,bool restore_mode)520 static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
521 {
522 int ret_deinit;
523 int ret = init_backup(args, restore_mode);
524
525 if (ret != KNOT_EOK) {
526 char *msg = sprintf_alloc("%s init failed (%s)",
527 restore_mode ? "restore" : "backup",
528 knot_strerror(ret));
529
530 if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
531 log_ctl_error("%s", msg);
532 } else {
533 log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
534 "%s", msg);
535 }
536 free (msg);
537
538 /* Warning: zone name in the control command params discarded here. */
539 args->data[KNOT_CTL_IDX_ZONE] = NULL;
540 send_error(args, knot_strerror(ret));
541 return KNOT_CTL_EZONE;
542 }
543
544 /* Global catalog zones backup. */
545 if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
546 zone_backup_ctx_t *ctx = latest_backup_ctx(args);
547 ctx->backup_global = true;
548 ret = global_backup(ctx, &args->server->catalog, NULL);
549 if (ret != KNOT_EOK) {
550 log_ctl_error("control, error (%s)", knot_strerror(ret));
551 send_error(args, knot_strerror(ret));
552 ret = KNOT_EOK;
553 goto done;
554 }
555 }
556
557 ret = zones_apply(args, zone_backup_cmd);
558
559 done:
560 ret_deinit = deinit_backup(args);
561 return ret != KNOT_EOK ? ret : ret_deinit;
562 }
563
zone_sign(zone_t * zone,_unused_ ctl_args_t * args)564 static int zone_sign(zone_t *zone, _unused_ ctl_args_t *args)
565 {
566 conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
567 if (!conf_bool(&val)) {
568 args->suppress = true;
569 return KNOT_ENOTSUP;
570 }
571
572 zone_set_flag(zone, ZONE_FORCE_RESIGN);
573 return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
574 }
575
zone_keys_load(zone_t * zone,_unused_ ctl_args_t * args)576 static int zone_keys_load(zone_t *zone, _unused_ ctl_args_t *args)
577 {
578 conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
579 if (!conf_bool(&val)) {
580 args->suppress = true;
581 return KNOT_ENOTSUP;
582 }
583
584 return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
585 }
586
zone_key_roll(zone_t * zone,ctl_args_t * args)587 static int zone_key_roll(zone_t *zone, ctl_args_t *args)
588 {
589 conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
590 if (!conf_bool(&val)) {
591 args->suppress = true;
592 return KNOT_ENOTSUP;
593 }
594
595 const char *key_type = args->data[KNOT_CTL_IDX_TYPE];
596 if (strncasecmp(key_type, "ksk", 3) == 0) {
597 zone_set_flag(zone, ZONE_FORCE_KSK_ROLL);
598 } else if (strncasecmp(key_type, "zsk", 3) == 0) {
599 zone_set_flag(zone, ZONE_FORCE_ZSK_ROLL);
600 } else {
601 return KNOT_EINVAL;
602 }
603
604 return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
605 }
606
zone_ksk_sbm_confirm(zone_t * zone,_unused_ ctl_args_t * args)607 static int zone_ksk_sbm_confirm(zone_t *zone, _unused_ ctl_args_t *args)
608 {
609 kdnssec_ctx_t ctx = { 0 };
610
611 int ret = kdnssec_ctx_init(conf(), &ctx, zone->name, zone->kaspdb, NULL);
612 if (ret != KNOT_EOK) {
613 return ret;
614 }
615
616 ret = knot_dnssec_ksk_sbm_confirm(&ctx, 0);
617 kdnssec_ctx_deinit(&ctx);
618
619 conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
620 if (ret == KNOT_EOK && conf_bool(&val)) {
621 // NOT zone_events_schedule_user(), intentionally!
622 ret = schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, false);
623 }
624
625 return ret;
626 }
627
zone_freeze(zone_t * zone,_unused_ ctl_args_t * args)628 static int zone_freeze(zone_t *zone, _unused_ ctl_args_t *args)
629 {
630 return schedule_trigger(zone, args, ZONE_EVENT_UFREEZE, false);
631 }
632
zone_thaw(zone_t * zone,_unused_ ctl_args_t * args)633 static int zone_thaw(zone_t *zone, _unused_ ctl_args_t *args)
634 {
635 return schedule_trigger(zone, args, ZONE_EVENT_UTHAW, false);
636 }
637
zone_txn_begin(zone_t * zone,_unused_ ctl_args_t * args)638 static int zone_txn_begin(zone_t *zone, _unused_ ctl_args_t *args)
639 {
640 if (zone->control_update != NULL) {
641 return KNOT_TXN_EEXISTS;
642 }
643
644 zone->control_update = malloc(sizeof(zone_update_t));
645 if (zone->control_update == NULL) {
646 return KNOT_ENOMEM;
647 }
648
649 zone_update_flags_t type = (zone->contents == NULL) ? UPDATE_FULL : UPDATE_INCREMENTAL;
650 int ret = zone_update_init(zone->control_update, zone, type | UPDATE_STRICT);
651 if (ret != KNOT_EOK) {
652 free(zone->control_update);
653 zone->control_update = NULL;
654 }
655
656 return ret;
657 }
658
zone_txn_commit(zone_t * zone,_unused_ ctl_args_t * args)659 static int zone_txn_commit(zone_t *zone, _unused_ ctl_args_t *args)
660 {
661 if (zone->control_update == NULL) {
662 args->suppress = true;
663 return KNOT_TXN_ENOTEXISTS;
664 }
665
666 int ret = zone_update_semcheck(zone->control_update);
667 if (ret != KNOT_EOK) {
668 return ret; // Recoverable error.
669 }
670
671 // NOOP if empty changeset/contents.
672 if (((zone->control_update->flags & UPDATE_INCREMENTAL) &&
673 changeset_empty(&zone->control_update->change)) ||
674 ((zone->control_update->flags & UPDATE_FULL) &&
675 zone_contents_is_empty(zone->control_update->new_cont))) {
676 zone_control_clear(zone);
677 return KNOT_EOK;
678 }
679
680 // Sign update.
681 conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
682 bool dnssec_enable = conf_bool(&val);
683 val = conf_zone_get(conf(), C_ZONEMD_GENERATE, zone->name);
684 unsigned digest_alg = conf_opt(&val);
685 if (dnssec_enable) {
686 zone_sign_reschedule_t resch = { 0 };
687 bool full = (zone->control_update->flags & UPDATE_FULL);
688 zone_sign_roll_flags_t rflags = KEY_ROLL_ALLOW_ALL;
689 ret = (full ? knot_dnssec_zone_sign(zone->control_update, conf(), 0, rflags, 0, &resch) :
690 knot_dnssec_sign_update(zone->control_update, conf(), &resch));
691 event_dnssec_reschedule(conf(), zone, &resch, false);
692 } else if (digest_alg != ZONE_DIGEST_NONE) {
693 if (zone_update_to(zone->control_update) == NULL) {
694 ret = zone_update_increment_soa(zone->control_update, conf());
695 }
696 if (ret == KNOT_EOK) {
697 ret = zone_update_add_digest(zone->control_update, digest_alg, false);
698 }
699 }
700 if (ret != KNOT_EOK) {
701 zone_control_clear(zone);
702 return ret;
703 }
704
705 ret = zone_update_commit(conf(), zone->control_update);
706 if (ret != KNOT_EOK) {
707 zone_control_clear(zone);
708 return ret;
709 }
710
711 free(zone->control_update);
712 zone->control_update = NULL;
713
714 zone_events_schedule_now(zone, ZONE_EVENT_NOTIFY);
715
716 return KNOT_EOK;
717 }
718
zone_txn_abort(zone_t * zone,_unused_ ctl_args_t * args)719 static int zone_txn_abort(zone_t *zone, _unused_ ctl_args_t *args)
720 {
721 if (zone->control_update == NULL) {
722 args->suppress = true;
723 return KNOT_TXN_ENOTEXISTS;
724 }
725
726 zone_control_clear(zone);
727
728 return KNOT_EOK;
729 }
730
init_send_ctx(send_ctx_t * ctx,const knot_dname_t * zone_name,ctl_args_t * args)731 static int init_send_ctx(send_ctx_t *ctx, const knot_dname_t *zone_name,
732 ctl_args_t *args)
733 {
734 memset(ctx, 0, sizeof(*ctx));
735
736 ctx->args = args;
737
738 // Set the dump style.
739 ctx->style.show_ttl = true;
740 ctx->style.original_ttl = true;
741 ctx->style.human_tmstamp = true;
742
743 // Set the output data buffers.
744 ctx->data[KNOT_CTL_IDX_ZONE] = ctx->zone;
745 ctx->data[KNOT_CTL_IDX_OWNER] = ctx->owner;
746 ctx->data[KNOT_CTL_IDX_TTL] = ctx->ttl;
747 ctx->data[KNOT_CTL_IDX_TYPE] = ctx->type;
748 ctx->data[KNOT_CTL_IDX_DATA] = ctx->rdata;
749
750 // Set the ZONE.
751 if (knot_dname_to_str(ctx->zone, zone_name, sizeof(ctx->zone)) == NULL) {
752 return KNOT_EINVAL;
753 }
754
755 // Set the TYPE filter.
756 if (args->data[KNOT_CTL_IDX_TYPE] != NULL) {
757 uint16_t type;
758 if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) {
759 return KNOT_EINVAL;
760 }
761 ctx->type_filter = type;
762 } else {
763 ctx->type_filter = -1;
764 }
765
766 return KNOT_EOK;
767 }
768
send_rrset(knot_rrset_t * rrset,send_ctx_t * ctx)769 static int send_rrset(knot_rrset_t *rrset, send_ctx_t *ctx)
770 {
771 if (rrset->type != KNOT_RRTYPE_RRSIG) {
772 int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u", rrset->ttl);
773 if (ret <= 0 || ret >= sizeof(ctx->ttl)) {
774 return KNOT_ESPACE;
775 }
776 }
777
778 if (knot_rrtype_to_string(rrset->type, ctx->type, sizeof(ctx->type)) < 0) {
779 return KNOT_ESPACE;
780 }
781
782 for (size_t i = 0; i < rrset->rrs.count; ++i) {
783 if (rrset->type == KNOT_RRTYPE_RRSIG) {
784 int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u",
785 knot_rrsig_original_ttl(knot_rdataset_at(&rrset->rrs, i)));
786 if (ret <= 0 || ret >= sizeof(ctx->ttl)) {
787 return KNOT_ESPACE;
788 }
789 }
790
791 int ret = knot_rrset_txt_dump_data(rrset, i, ctx->rdata,
792 sizeof(ctx->rdata), &ctx->style);
793 if (ret < 0) {
794 return ret;
795 }
796
797 ret = knot_ctl_send(ctx->args->ctl, KNOT_CTL_TYPE_DATA, &ctx->data);
798 if (ret != KNOT_EOK) {
799 return ret;
800 }
801 }
802
803 return KNOT_EOK;
804 }
805
send_node(zone_node_t * node,void * ctx_void)806 static int send_node(zone_node_t *node, void *ctx_void)
807 {
808 send_ctx_t *ctx = ctx_void;
809 if (knot_dname_to_str(ctx->owner, node->owner, sizeof(ctx->owner)) == NULL) {
810 return KNOT_EINVAL;
811 }
812
813 for (size_t i = 0; i < node->rrset_count; ++i) {
814 knot_rrset_t rrset = node_rrset_at(node, i);
815
816 // Check for requested TYPE.
817 if (ctx->type_filter != -1 && rrset.type != ctx->type_filter) {
818 continue;
819 }
820
821 int ret = send_rrset(&rrset, ctx);
822 if (ret != KNOT_EOK) {
823 return ret;
824 }
825 }
826
827 return KNOT_EOK;
828 }
829
get_owner(uint8_t * out,size_t out_len,knot_dname_t * origin,ctl_args_t * args)830 static int get_owner(uint8_t *out, size_t out_len, knot_dname_t *origin,
831 ctl_args_t *args)
832 {
833 const char *owner = args->data[KNOT_CTL_IDX_OWNER];
834 assert(owner != NULL);
835
836 bool fqdn = false;
837 size_t prefix_len = 0;
838
839 size_t owner_len = strlen(owner);
840 if (owner_len > 0 && (owner_len != 1 || owner[0] != '@')) {
841 // Check if the owner is FQDN.
842 if (owner[owner_len - 1] == '.') {
843 fqdn = true;
844 }
845
846 if (knot_dname_from_str(out, owner, out_len) == NULL) {
847 return KNOT_EINVAL;
848 }
849 knot_dname_to_lower(out);
850
851 prefix_len = knot_dname_size(out);
852 if (prefix_len == 0) {
853 return KNOT_EINVAL;
854 }
855
856 // Ignore trailing dot.
857 prefix_len--;
858 }
859
860 // Append the origin.
861 if (!fqdn) {
862 size_t origin_len = knot_dname_size(origin);
863 if (origin_len == 0 || origin_len > out_len - prefix_len) {
864 return KNOT_EINVAL;
865 }
866 memcpy(out + prefix_len, origin, origin_len);
867 }
868
869 return KNOT_EOK;
870 }
871
zone_read(zone_t * zone,ctl_args_t * args)872 static int zone_read(zone_t *zone, ctl_args_t *args)
873 {
874 send_ctx_t *ctx = &ctl_globals.send_ctx;
875 int ret = init_send_ctx(ctx, zone->name, args);
876 if (ret != KNOT_EOK) {
877 return ret;
878 }
879
880 if (args->data[KNOT_CTL_IDX_OWNER] != NULL) {
881 knot_dname_storage_t owner;
882
883 ret = get_owner(owner, sizeof(owner), zone->name, args);
884 if (ret != KNOT_EOK) {
885 return ret;
886 }
887
888 const zone_node_t *node = zone_contents_node_or_nsec3(zone->contents, owner);
889 if (node == NULL) {
890 return KNOT_ENONODE;
891 }
892
893 ret = send_node((zone_node_t *)node, ctx);
894 } else if (zone->contents != NULL) {
895 ret = zone_contents_apply(zone->contents, send_node, ctx);
896 if (ret == KNOT_EOK) {
897 ret = zone_contents_nsec3_apply(zone->contents, send_node, ctx);
898 }
899 }
900
901 return ret;
902 }
903
zone_flag_txn_get(zone_t * zone,ctl_args_t * args,const char * flag)904 static int zone_flag_txn_get(zone_t *zone, ctl_args_t *args, const char *flag)
905 {
906 if (zone->control_update == NULL) {
907 args->suppress = true;
908 return KNOT_TXN_ENOTEXISTS;
909 }
910
911 send_ctx_t *ctx = &ctl_globals.send_ctx;
912 int ret = init_send_ctx(ctx, zone->name, args);
913 if (ret != KNOT_EOK) {
914 return ret;
915 }
916 ctx->data[KNOT_CTL_IDX_FLAGS] = flag;
917
918 if (args->data[KNOT_CTL_IDX_OWNER] != NULL) {
919 knot_dname_storage_t owner;
920
921 ret = get_owner(owner, sizeof(owner), zone->name, args);
922 if (ret != KNOT_EOK) {
923 return ret;
924 }
925
926 const zone_node_t *node = zone_contents_node_or_nsec3(zone->control_update->new_cont, owner);
927 if (node == NULL) {
928 return KNOT_ENONODE;
929
930 }
931
932 ret = send_node((zone_node_t *)node, ctx);
933 } else {
934 zone_tree_it_t it = { 0 };
935 ret = zone_tree_it_double_begin(zone->control_update->new_cont->nodes,
936 zone->control_update->new_cont->nsec3_nodes,
937 &it);
938 while (ret == KNOT_EOK && !zone_tree_it_finished(&it)) {
939 ret = send_node(zone_tree_it_val(&it), ctx);
940 zone_tree_it_next(&it);
941 }
942 zone_tree_it_free(&it);
943 }
944
945 return ret;
946 }
947
zone_txn_get(zone_t * zone,ctl_args_t * args)948 static int zone_txn_get(zone_t *zone, ctl_args_t *args)
949 {
950 return zone_flag_txn_get(zone, args, NULL);
951 }
952
send_changeset_part(changeset_t * ch,send_ctx_t * ctx,bool from)953 static int send_changeset_part(changeset_t *ch, send_ctx_t *ctx, bool from)
954 {
955 ctx->data[KNOT_CTL_IDX_FLAGS] = from ? CTL_FLAG_REM : CTL_FLAG_ADD;
956
957 // Send SOA only if explicitly changed.
958 if (ch->soa_to != NULL) {
959 knot_rrset_t *soa = from ? ch->soa_from : ch->soa_to;
960 assert(soa);
961
962 char *owner = knot_dname_to_str(ctx->owner, soa->owner, sizeof(ctx->owner));
963 if (owner == NULL) {
964 return KNOT_EINVAL;
965 }
966
967 int ret = send_rrset(soa, ctx);
968 if (ret != KNOT_EOK) {
969 return ret;
970 }
971 }
972
973 // Send other records.
974 changeset_iter_t it;
975 int ret = from ? changeset_iter_rem(&it, ch) : changeset_iter_add(&it, ch);
976 if (ret != KNOT_EOK) {
977 return ret;
978 }
979
980 knot_rrset_t rrset = changeset_iter_next(&it);
981 while (!knot_rrset_empty(&rrset)) {
982 char *owner = knot_dname_to_str(ctx->owner, rrset.owner, sizeof(ctx->owner));
983 if (owner == NULL) {
984 changeset_iter_clear(&it);
985 return KNOT_EINVAL;
986 }
987
988 ret = send_rrset(&rrset, ctx);
989 if (ret != KNOT_EOK) {
990 changeset_iter_clear(&it);
991 return ret;
992 }
993
994 rrset = changeset_iter_next(&it);
995 }
996 changeset_iter_clear(&it);
997
998 return KNOT_EOK;
999 }
1000
send_changeset(changeset_t * ch,send_ctx_t * ctx)1001 static int send_changeset(changeset_t *ch, send_ctx_t *ctx)
1002 {
1003 // First send 'from' changeset part.
1004 int ret = send_changeset_part(ch, ctx, true);
1005 if (ret != KNOT_EOK) {
1006 return ret;
1007 }
1008
1009 // Second send 'to' changeset part.
1010 return send_changeset_part(ch, ctx, false);
1011 }
1012
zone_txn_diff(zone_t * zone,ctl_args_t * args)1013 static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
1014 {
1015 if (zone->control_update == NULL) {
1016 args->suppress = true;
1017 return KNOT_TXN_ENOTEXISTS;
1018 }
1019
1020 // FULL update has no changeset to print, do a 'get' instead.
1021 if (zone->control_update->flags & UPDATE_FULL) {
1022 return zone_flag_txn_get(zone, args, CTL_FLAG_ADD);
1023 }
1024
1025 send_ctx_t *ctx = &ctl_globals.send_ctx;
1026 int ret = init_send_ctx(ctx, zone->name, args);
1027 if (ret != KNOT_EOK) {
1028 return ret;
1029 }
1030
1031 return send_changeset(&zone->control_update->change, ctx);
1032 }
1033
get_ttl(zone_t * zone,ctl_args_t * args,uint32_t * ttl)1034 static int get_ttl(zone_t *zone, ctl_args_t *args, uint32_t *ttl)
1035 {
1036 knot_dname_storage_t owner;
1037
1038 int ret = get_owner(owner, sizeof(owner), zone->name, args);
1039 if (ret != KNOT_EOK) {
1040 return ret;
1041 }
1042
1043 const zone_node_t *node = zone_contents_node_or_nsec3(zone->control_update->new_cont, owner);
1044 if (node == NULL) {
1045 return KNOT_EINVAL;
1046 }
1047
1048 uint16_t type;
1049 if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) {
1050 return KNOT_EINVAL;
1051 }
1052
1053 *ttl = node_rrset(node, type).ttl;
1054
1055 return KNOT_EOK;
1056 }
1057
create_rrset(knot_rrset_t ** rrset,zone_t * zone,ctl_args_t * args,bool need_ttl)1058 static int create_rrset(knot_rrset_t **rrset, zone_t *zone, ctl_args_t *args,
1059 bool need_ttl)
1060 {
1061 knot_dname_txt_storage_t origin_buff;
1062 char *origin = knot_dname_to_str(origin_buff, zone->name, sizeof(origin_buff));
1063 if (origin == NULL) {
1064 return KNOT_EINVAL;
1065 }
1066
1067 const char *owner = args->data[KNOT_CTL_IDX_OWNER];
1068 const char *type = args->data[KNOT_CTL_IDX_TYPE];
1069 const char *data = args->data[KNOT_CTL_IDX_DATA];
1070 const char *ttl = need_ttl ? args->data[KNOT_CTL_IDX_TTL] : NULL;
1071
1072 // Prepare a buffer for a reconstructed record.
1073 const size_t buff_len = sizeof(ctl_globals.txt_rr);
1074 char *buff = ctl_globals.txt_rr;
1075
1076 uint32_t default_ttl = 0;
1077 if (ttl == NULL) {
1078 int ret = get_ttl(zone, args, &default_ttl);
1079 if (need_ttl && ret != KNOT_EOK) {
1080 return ret;
1081 }
1082 }
1083
1084 // Reconstruct the record.
1085 int ret = snprintf(buff, buff_len, "%s %s %s %s\n",
1086 (owner != NULL ? owner : ""),
1087 (ttl != NULL ? ttl : ""),
1088 (type != NULL ? type : ""),
1089 (data != NULL ? data : ""));
1090 if (ret <= 0 || ret >= buff_len) {
1091 return KNOT_ESPACE;
1092 }
1093 size_t rdata_len = ret;
1094
1095 // Parse the record.
1096 zs_scanner_t *scanner = &ctl_globals.scanner;
1097 if (zs_init(scanner, origin, KNOT_CLASS_IN, default_ttl) != 0 ||
1098 zs_set_input_string(scanner, buff, rdata_len) != 0 ||
1099 zs_parse_record(scanner) != 0 ||
1100 scanner->state != ZS_STATE_DATA) {
1101 ret = KNOT_EPARSEFAIL;
1102 goto parser_failed;
1103 }
1104 knot_dname_to_lower(scanner->r_owner);
1105
1106 // Create output rrset.
1107 *rrset = knot_rrset_new(scanner->r_owner, scanner->r_type,
1108 scanner->r_class, scanner->r_ttl, NULL);
1109 if (*rrset == NULL) {
1110 ret = KNOT_ENOMEM;
1111 goto parser_failed;
1112 }
1113
1114 ret = knot_rrset_add_rdata(*rrset, scanner->r_data, scanner->r_data_length,
1115 NULL);
1116 parser_failed:
1117 zs_deinit(scanner);
1118
1119 return ret;
1120 }
1121
zone_txn_set(zone_t * zone,ctl_args_t * args)1122 static int zone_txn_set(zone_t *zone, ctl_args_t *args)
1123 {
1124 if (zone->control_update == NULL) {
1125 args->suppress = true;
1126 return KNOT_TXN_ENOTEXISTS;
1127 }
1128
1129 if (args->data[KNOT_CTL_IDX_OWNER] == NULL ||
1130 args->data[KNOT_CTL_IDX_TYPE] == NULL) {
1131 return KNOT_EINVAL;
1132 }
1133
1134 knot_rrset_t *rrset;
1135 int ret = create_rrset(&rrset, zone, args, true);
1136 if (ret != KNOT_EOK) {
1137 return ret;
1138 }
1139
1140 ret = zone_update_add(zone->control_update, rrset);
1141 knot_rrset_free(rrset, NULL);
1142
1143 return ret;
1144 }
1145
zone_txn_unset(zone_t * zone,ctl_args_t * args)1146 static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
1147 {
1148 if (zone->control_update == NULL) {
1149 args->suppress = true;
1150 return KNOT_TXN_ENOTEXISTS;
1151 }
1152
1153 if (args->data[KNOT_CTL_IDX_OWNER] == NULL) {
1154 return KNOT_EINVAL;
1155 }
1156
1157 // Remove specific record.
1158 if (args->data[KNOT_CTL_IDX_DATA] != NULL) {
1159 if (args->data[KNOT_CTL_IDX_TYPE] == NULL) {
1160 return KNOT_EINVAL;
1161 }
1162
1163 knot_rrset_t *rrset;
1164 int ret = create_rrset(&rrset, zone, args, false);
1165 if (ret != KNOT_EOK) {
1166 return ret;
1167 }
1168
1169 ret = zone_update_remove(zone->control_update, rrset);
1170 knot_rrset_free(rrset, NULL);
1171 return ret;
1172 } else {
1173 knot_dname_storage_t owner;
1174
1175 int ret = get_owner(owner, sizeof(owner), zone->name, args);
1176 if (ret != KNOT_EOK) {
1177 return ret;
1178 }
1179
1180 // Remove whole rrset.
1181 if (args->data[KNOT_CTL_IDX_TYPE] != NULL) {
1182 uint16_t type;
1183 if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE],
1184 &type) != 0) {
1185 return KNOT_EINVAL;
1186 }
1187
1188 return zone_update_remove_rrset(zone->control_update, owner, type);
1189 // Remove whole node.
1190 } else {
1191 return zone_update_remove_node(zone->control_update, owner);
1192 }
1193 }
1194 }
1195
zone_exists(const knot_dname_t * zone,void * data)1196 static bool zone_exists(const knot_dname_t *zone, void *data)
1197 {
1198 assert(zone);
1199 assert(data);
1200
1201 knot_zonedb_t *db = data;
1202
1203 return knot_zonedb_find(db, zone) != NULL;
1204 }
1205
zone_names_distinct(const knot_dname_t * zone,void * data)1206 static bool zone_names_distinct(const knot_dname_t *zone, void *data)
1207 {
1208 assert(zone);
1209 assert(data);
1210
1211 knot_dname_t *zone_to_purge = data;
1212
1213 return !knot_dname_is_equal(zone, zone_to_purge);
1214 }
1215
drop_journal_if_orphan(const knot_dname_t * for_zone,void * ctx)1216 static int drop_journal_if_orphan(const knot_dname_t *for_zone, void *ctx)
1217 {
1218 server_t *server = ctx;
1219 zone_journal_t j = { &server->journaldb, for_zone };
1220 if (!zone_exists(for_zone, server->zone_db)) {
1221 return journal_scrape_with_md(j, false);
1222 }
1223 return KNOT_EOK;
1224 }
1225
log_if_orphans_error(knot_dname_t * zone_name,int err,char * db_type)1226 static void log_if_orphans_error(knot_dname_t *zone_name, int err, char *db_type)
1227 {
1228 if (err == KNOT_EOK || err == KNOT_ENOENT || err == KNOT_EFILE) {
1229 return;
1230 }
1231
1232 char *msg = sprintf_alloc("failed to purge orphan from %s database (%s)",
1233 db_type, knot_strerror(err));
1234 if (msg == NULL) {
1235 return;
1236 }
1237
1238 if (zone_name == NULL) {
1239 log_error("%s", msg);
1240 } else {
1241 log_zone_error(zone_name, "%s", msg);
1242 }
1243 free(msg);
1244 }
1245
orphans_purge(ctl_args_t * args)1246 static int orphans_purge(ctl_args_t *args)
1247 {
1248 assert(args->data[KNOT_CTL_IDX_FILTER] != NULL);
1249 bool only_orphan = (strlen(args->data[KNOT_CTL_IDX_FILTER]) == 1);
1250 int ret;
1251
1252 if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
1253 // Purge KASP DB.
1254 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
1255 ret = kasp_db_sweep(&args->server->kaspdb,
1256 zone_exists, args->server->zone_db);
1257 log_if_orphans_error(NULL, ret, "KASP");
1258 }
1259
1260 // Purge zone journals of unconfigured zones.
1261 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
1262 ret = journals_walk(&args->server->journaldb,
1263 drop_journal_if_orphan, args->server);
1264 log_if_orphans_error(NULL, ret, "journal");
1265 }
1266
1267 // Purge timers of unconfigured zones.
1268 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
1269 ret = zone_timers_sweep(&args->server->timerdb,
1270 zone_exists, args->server->zone_db);
1271 log_if_orphans_error(NULL, ret, "timer");
1272 }
1273 } else {
1274 knot_dname_storage_t buff;
1275 while (true) {
1276 knot_dname_t *zone_name =
1277 knot_dname_from_str(buff, args->data[KNOT_CTL_IDX_ZONE],
1278 sizeof(buff));
1279 if (zone_name == NULL) {
1280 log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
1281 "control, error (%s)",
1282 knot_strerror(KNOT_EINVAL));
1283 send_error(args, knot_strerror(KNOT_EINVAL));
1284 return KNOT_EINVAL;
1285 }
1286 knot_dname_to_lower(zone_name);
1287
1288 if (!zone_exists(zone_name, args->server->zone_db)) {
1289 // Purge KASP DB.
1290 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
1291 if (knot_lmdb_open(&args->server->kaspdb) == KNOT_EOK) {
1292 ret = kasp_db_delete_all(&args->server->kaspdb, zone_name);
1293 log_if_orphans_error(zone_name, ret, "KASP");
1294 }
1295 }
1296
1297 // Purge zone journal.
1298 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
1299 zone_journal_t j = { &args->server->journaldb, zone_name };
1300 ret = journal_scrape_with_md(j, true);
1301 log_if_orphans_error(zone_name, ret, "journal");
1302 }
1303
1304 // Purge zone timers.
1305 if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
1306 ret = zone_timers_sweep(&args->server->timerdb,
1307 zone_names_distinct, zone_name);
1308 log_if_orphans_error(zone_name, ret, "timer");
1309 }
1310 }
1311
1312 // Get next zone name.
1313 ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
1314 if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
1315 break;
1316 }
1317 strtolower((char *)args->data[KNOT_CTL_IDX_ZONE]);
1318
1319 // Log the other zones the same way as the first one from process.c.
1320 log_ctl_zone_str_info(args->data[KNOT_CTL_IDX_ZONE],
1321 "control, received command '%s'",
1322 args->data[KNOT_CTL_IDX_CMD]);
1323 }
1324 }
1325
1326 return KNOT_EOK;
1327 }
1328
1329 #define RETURN_IF_FAILED(str, exception) \
1330 { \
1331 if (ret != KNOT_EOK && ret != (exception)) { \
1332 log_zone_error(zone->name, \
1333 "failed to %s (%s)", (str), knot_strerror(ret)); \
1334 return ret; \
1335 } \
1336 }
1337
zone_purge(zone_t * zone,ctl_args_t * args)1338 static int zone_purge(zone_t *zone, ctl_args_t *args)
1339 {
1340 int ret = KNOT_EOK;
1341
1342 // Abort possible editing transaction.
1343 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_EXPIRE)) {
1344 ret = zone_txn_abort(zone, args);
1345 RETURN_IF_FAILED("abort pending transaction", KNOT_TXN_ENOTEXISTS);
1346 }
1347
1348 // Purge the zone timers.
1349 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
1350 memset(&zone->timers, 0, sizeof(zone->timers));
1351 ret = zone_timers_sweep(&args->server->timerdb,
1352 zone_names_distinct, zone->name);
1353 RETURN_IF_FAILED("purge timers", KNOT_ENOENT);
1354 }
1355
1356 // Expire the zone.
1357 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_EXPIRE)) {
1358 // KNOT_EOK is the only return value from event_expire().
1359 (void)schedule_trigger(zone, args, ZONE_EVENT_EXPIRE, true);
1360 }
1361
1362 // Purge the zone file.
1363 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_ZONEFILE)) {
1364 char *zonefile = conf_zonefile(conf(), zone->name);
1365 ret = (unlink(zonefile) == -1 ? knot_map_errno() : KNOT_EOK);
1366 free(zonefile);
1367 RETURN_IF_FAILED("purge zone file", KNOT_ENOENT);
1368 }
1369
1370 // Purge the zone journal.
1371 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
1372 ret = journal_scrape_with_md(zone_journal(zone), true);
1373 RETURN_IF_FAILED("purge journal", KNOT_ENOENT);
1374 }
1375
1376 // Purge KASP DB.
1377 if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
1378 ret = knot_lmdb_open(zone->kaspdb);
1379 if (ret == KNOT_EOK) {
1380 ret = kasp_db_delete_all(zone->kaspdb, zone->name);
1381 }
1382 RETURN_IF_FAILED("purge KASP DB", KNOT_ENOENT);
1383 }
1384
1385 return KNOT_EOK;
1386 }
1387
send_stats_ctr(mod_ctr_t * ctr,uint64_t ** stats_vals,unsigned threads,ctl_args_t * args,knot_ctl_data_t * data)1388 static int send_stats_ctr(mod_ctr_t *ctr, uint64_t **stats_vals, unsigned threads,
1389 ctl_args_t *args, knot_ctl_data_t *data)
1390 {
1391 char index[128];
1392 char value[32];
1393
1394 if (ctr->count == 1) {
1395 uint64_t counter = stats_get_counter(stats_vals, ctr->offset, threads);
1396 int ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
1397 if (ret <= 0 || ret >= sizeof(value)) {
1398 return KNOT_ESPACE;
1399 }
1400
1401 (*data)[KNOT_CTL_IDX_ID] = NULL;
1402 (*data)[KNOT_CTL_IDX_DATA] = value;
1403
1404 ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, data);
1405 if (ret != KNOT_EOK) {
1406 return ret;
1407 }
1408 } else {
1409 bool force = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS],
1410 CTL_FLAG_FORCE);
1411
1412 for (uint32_t i = 0; i < ctr->count; i++) {
1413 uint64_t counter = stats_get_counter(stats_vals, ctr->offset + i, threads);
1414
1415 // Skip empty counters.
1416 if (counter == 0 && !force) {
1417 continue;
1418 }
1419
1420 int ret;
1421 if (ctr->idx_to_str) {
1422 char *str = ctr->idx_to_str(i, ctr->count);
1423 if (str == NULL) {
1424 continue;
1425 }
1426 ret = snprintf(index, sizeof(index), "%s", str);
1427 free(str);
1428 } else {
1429 ret = snprintf(index, sizeof(index), "%u", i);
1430 }
1431 if (ret <= 0 || ret >= sizeof(index)) {
1432 return KNOT_ESPACE;
1433 }
1434
1435 ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
1436 if (ret <= 0 || ret >= sizeof(value)) {
1437 return KNOT_ESPACE;
1438 }
1439
1440 (*data)[KNOT_CTL_IDX_ID] = index;
1441 (*data)[KNOT_CTL_IDX_DATA] = value;
1442
1443 knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA :
1444 KNOT_CTL_TYPE_EXTRA;
1445 ret = knot_ctl_send(args->ctl, type, data);
1446 if (ret != KNOT_EOK) {
1447 return ret;
1448 }
1449 }
1450 }
1451
1452 return KNOT_EOK;
1453 }
1454
modules_stats(list_t * query_modules,ctl_args_t * args,knot_dname_t * zone)1455 static int modules_stats(list_t *query_modules, ctl_args_t *args, knot_dname_t *zone)
1456 {
1457 if (query_modules == NULL) {
1458 return KNOT_EOK;
1459 }
1460
1461 const char *section = args->data[KNOT_CTL_IDX_SECTION];
1462 const char *item = args->data[KNOT_CTL_IDX_ITEM];
1463
1464 knot_dname_txt_storage_t name = "";
1465 knot_ctl_data_t data = { 0 };
1466
1467 bool section_found = (section == NULL) ? true : false;
1468 bool item_found = (item == NULL) ? true : false;
1469
1470 knotd_mod_t *mod;
1471 WALK_LIST(mod, *query_modules) {
1472 // Skip modules without statistics.
1473 if (mod->stats_count == 0) {
1474 continue;
1475 }
1476
1477 // Check for specific module.
1478 if (section != NULL) {
1479 if (section_found) {
1480 break;
1481 } else if (strcasecmp(mod->id->name + 1, section) == 0) {
1482 section_found = true;
1483 } else {
1484 continue;
1485 }
1486 }
1487
1488 data[KNOT_CTL_IDX_SECTION] = mod->id->name + 1;
1489
1490 unsigned threads = knotd_mod_threads(mod);
1491
1492 for (int i = 0; i < mod->stats_count; i++) {
1493 mod_ctr_t *ctr = mod->stats_info + i;
1494
1495 // Skip empty counter.
1496 if (ctr->name == NULL) {
1497 continue;
1498 }
1499
1500 // Check for specific counter.
1501 if (item != NULL) {
1502 if (item_found) {
1503 break;
1504 } else if (strcasecmp(ctr->name, item) == 0) {
1505 item_found = true;
1506 } else {
1507 continue;
1508 }
1509 }
1510
1511 // Prepare zone name if not already prepared.
1512 if (zone != NULL && name[0] == '\0') {
1513 if (knot_dname_to_str(name, zone, sizeof(name)) == NULL) {
1514 return KNOT_EINVAL;
1515 }
1516 data[KNOT_CTL_IDX_ZONE] = name;
1517 }
1518
1519 data[KNOT_CTL_IDX_ITEM] = ctr->name;
1520
1521 // Send the counters.
1522 int ret = send_stats_ctr(ctr, mod->stats_vals, threads, args, &data);
1523 if (ret != KNOT_EOK) {
1524 return ret;
1525 }
1526 }
1527 }
1528
1529 return (section_found && item_found) ? KNOT_EOK : KNOT_ENOENT;
1530 }
1531
zone_stats(zone_t * zone,ctl_args_t * args)1532 static int zone_stats(zone_t *zone, ctl_args_t *args)
1533 {
1534 return modules_stats(&zone->query_modules, args, zone->name);
1535 }
1536
ctl_zone(ctl_args_t * args,ctl_cmd_t cmd)1537 static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
1538 {
1539 switch (cmd) {
1540 case CTL_ZONE_STATUS:
1541 return zones_apply(args, zone_status);
1542 case CTL_ZONE_RELOAD:
1543 return zones_apply(args, zone_reload);
1544 case CTL_ZONE_REFRESH:
1545 return zones_apply(args, zone_refresh);
1546 case CTL_ZONE_RETRANSFER:
1547 return zones_apply(args, zone_retransfer);
1548 case CTL_ZONE_NOTIFY:
1549 return zones_apply(args, zone_notify);
1550 case CTL_ZONE_FLUSH:
1551 return zones_apply(args, zone_flush);
1552 case CTL_ZONE_BACKUP:
1553 return zones_apply_backup(args, false);
1554 case CTL_ZONE_RESTORE:
1555 return zones_apply_backup(args, true);
1556 case CTL_ZONE_SIGN:
1557 return zones_apply(args, zone_sign);
1558 case CTL_ZONE_KEYS_LOAD:
1559 return zones_apply(args, zone_keys_load);
1560 case CTL_ZONE_KEY_ROLL:
1561 return zones_apply(args, zone_key_roll);
1562 case CTL_ZONE_KSK_SBM:
1563 return zones_apply(args, zone_ksk_sbm_confirm);
1564 case CTL_ZONE_FREEZE:
1565 return zones_apply(args, zone_freeze);
1566 case CTL_ZONE_THAW:
1567 return zones_apply(args, zone_thaw);
1568 case CTL_ZONE_READ:
1569 return zones_apply(args, zone_read);
1570 case CTL_ZONE_BEGIN:
1571 return zones_apply(args, zone_txn_begin);
1572 case CTL_ZONE_COMMIT:
1573 return zones_apply(args, zone_txn_commit);
1574 case CTL_ZONE_ABORT:
1575 return zones_apply(args, zone_txn_abort);
1576 case CTL_ZONE_DIFF:
1577 return zones_apply(args, zone_txn_diff);
1578 case CTL_ZONE_GET:
1579 return zones_apply(args, zone_txn_get);
1580 case CTL_ZONE_SET:
1581 return zones_apply(args, zone_txn_set);
1582 case CTL_ZONE_UNSET:
1583 return zones_apply(args, zone_txn_unset);
1584 case CTL_ZONE_PURGE:
1585 if (MATCH_AND_FILTER(args, CTL_FILTER_PURGE_ORPHAN)) {
1586 return orphans_purge(args);
1587 } else {
1588 return zones_apply(args, zone_purge);
1589 }
1590 case CTL_ZONE_STATS:
1591 return zones_apply(args, zone_stats);
1592 default:
1593 assert(0);
1594 return KNOT_EINVAL;
1595 }
1596 }
1597
server_status(ctl_args_t * args)1598 static int server_status(ctl_args_t *args)
1599 {
1600 const char *type = args->data[KNOT_CTL_IDX_TYPE];
1601
1602 if (type == NULL || strlen(type) == 0) {
1603 return KNOT_EOK;
1604 }
1605
1606 char buff[2048] = "";
1607
1608 int ret;
1609 if (strcasecmp(type, "version") == 0) {
1610 ret = snprintf(buff, sizeof(buff), "Version: %s", PACKAGE_VERSION);
1611 } else if (strcasecmp(type, "workers") == 0) {
1612 int running_bkg_wrk, wrk_queue;
1613 worker_pool_status(args->server->workers, false, &running_bkg_wrk, &wrk_queue);
1614 ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers: %zu, "
1615 "XDP workers: %zu, background workers: %zu (running: %d, pending: %d)",
1616 conf()->cache.srv_udp_threads, conf()->cache.srv_tcp_threads,
1617 conf()->cache.srv_xdp_threads, conf()->cache.srv_bg_threads,
1618 running_bkg_wrk, wrk_queue);
1619 } else if (strcasecmp(type, "configure") == 0) {
1620 ret = snprintf(buff, sizeof(buff), "%s", CONFIGURE_SUMMARY);
1621 } else {
1622 return KNOT_EINVAL;
1623 }
1624 if (ret <= 0 || ret >= sizeof(buff)) {
1625 return KNOT_ESPACE;
1626 }
1627
1628 args->data[KNOT_CTL_IDX_DATA] = buff;
1629
1630 return knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &args->data);
1631 }
1632
ctl_server(ctl_args_t * args,ctl_cmd_t cmd)1633 static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd)
1634 {
1635 int ret = KNOT_EOK;
1636
1637 switch (cmd) {
1638 case CTL_STATUS:
1639 ret = server_status(args);
1640 if (ret != KNOT_EOK) {
1641 send_error(args, knot_strerror(ret));
1642 }
1643 break;
1644 case CTL_STOP:
1645 ret = KNOT_CTL_ESTOP;
1646 break;
1647 case CTL_RELOAD:
1648 ret = server_reload(args->server);
1649 if (ret != KNOT_EOK) {
1650 send_error(args, knot_strerror(ret));
1651 }
1652 break;
1653 default:
1654 assert(0);
1655 ret = KNOT_EINVAL;
1656 }
1657
1658 return ret;
1659 }
1660
ctl_stats(ctl_args_t * args,ctl_cmd_t cmd)1661 static int ctl_stats(ctl_args_t *args, ctl_cmd_t cmd)
1662 {
1663 const char *section = args->data[KNOT_CTL_IDX_SECTION];
1664 const char *item = args->data[KNOT_CTL_IDX_ITEM];
1665
1666 bool found = (section == NULL) ? true : false;
1667
1668 // Process server metrics.
1669 if (section == NULL || strcasecmp(section, "server") == 0) {
1670 char value[32];
1671 knot_ctl_data_t data = {
1672 [KNOT_CTL_IDX_SECTION] = "server",
1673 [KNOT_CTL_IDX_DATA] = value
1674 };
1675
1676 for (const stats_item_t *i = server_stats; i->name != NULL; i++) {
1677 if (item != NULL) {
1678 if (found) {
1679 break;
1680 } else if (strcmp(i->name, item) == 0) {
1681 found = true;
1682 } else {
1683 continue;
1684 }
1685 } else {
1686 found = true;
1687 }
1688
1689 data[KNOT_CTL_IDX_ITEM] = i->name;
1690 int ret = snprintf(value, sizeof(value), "%"PRIu64,
1691 i->val(args->server));
1692 if (ret <= 0 || ret >= sizeof(value)) {
1693 ret = KNOT_ESPACE;
1694 send_error(args, knot_strerror(ret));
1695 return ret;
1696 }
1697
1698 ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data);
1699 if (ret != KNOT_EOK) {
1700 send_error(args, knot_strerror(ret));
1701 return ret;
1702 }
1703 }
1704 }
1705
1706 // Process modules metrics.
1707 if (section == NULL || strncasecmp(section, "mod-", strlen("mod-")) == 0) {
1708 int ret = modules_stats(conf()->query_modules, args, NULL);
1709 if (ret != KNOT_EOK) {
1710 send_error(args, knot_strerror(ret));
1711 return ret;
1712 }
1713
1714 found = true;
1715 }
1716
1717 if (!found) {
1718 send_error(args, knot_strerror(KNOT_EINVAL));
1719 return KNOT_EINVAL;
1720 }
1721
1722 return KNOT_EOK;
1723 }
1724
send_block_data(conf_io_t * io,knot_ctl_data_t * data)1725 static int send_block_data(conf_io_t *io, knot_ctl_data_t *data)
1726 {
1727 knot_ctl_t *ctl = (knot_ctl_t *)io->misc;
1728
1729 const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0;
1730 assert(item != NULL);
1731
1732 char buff[YP_MAX_TXT_DATA_LEN + 1] = "\0";
1733
1734 (*data)[KNOT_CTL_IDX_DATA] = buff;
1735
1736 // Format explicit binary data value.
1737 if (io->data.bin != NULL) {
1738 size_t buff_len = sizeof(buff);
1739 int ret = yp_item_to_txt(item, io->data.bin, io->data.bin_len, buff,
1740 &buff_len, YP_SNOQUOTE);
1741 if (ret != KNOT_EOK) {
1742 return ret;
1743 }
1744 return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data);
1745 // Format all multivalued item data if no specified index.
1746 } else if ((item->flags & YP_FMULTI) && io->data.index == 0) {
1747 size_t values = conf_val_count(io->data.val);
1748 for (size_t i = 0; i < values; i++) {
1749 conf_val(io->data.val);
1750 size_t buff_len = sizeof(buff);
1751 int ret = yp_item_to_txt(item, io->data.val->data,
1752 io->data.val->len, buff,&buff_len,
1753 YP_SNOQUOTE);
1754 if (ret != KNOT_EOK) {
1755 return ret;
1756 }
1757
1758 knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA :
1759 KNOT_CTL_TYPE_EXTRA;
1760 ret = knot_ctl_send(ctl, type, data);
1761 if (ret != KNOT_EOK) {
1762 return ret;
1763 }
1764
1765 conf_val_next(io->data.val);
1766 }
1767 return KNOT_EOK;
1768 // Format singlevalued item data or a specified one from multivalued.
1769 } else {
1770 conf_val(io->data.val);
1771 size_t buff_len = sizeof(buff);
1772 int ret = yp_item_to_txt(item, io->data.val->data, io->data.val->len,
1773 buff, &buff_len, YP_SNOQUOTE);
1774 if (ret != KNOT_EOK) {
1775 return ret;
1776 }
1777 return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data);
1778 }
1779 }
1780
send_block(conf_io_t * io)1781 static int send_block(conf_io_t *io)
1782 {
1783 knot_ctl_t *ctl = (knot_ctl_t *)io->misc;
1784
1785 // Get possible error message.
1786 const char *err = io->error.str;
1787 if (err == NULL && io->error.code != KNOT_EOK) {
1788 err = knot_strerror(io->error.code);
1789 }
1790
1791 knot_ctl_data_t data = {
1792 [KNOT_CTL_IDX_ERROR] = err,
1793 };
1794
1795 if (io->key0 != NULL) {
1796 data[KNOT_CTL_IDX_SECTION] = io->key0->name + 1;
1797 }
1798 if (io->key1 != NULL) {
1799 data[KNOT_CTL_IDX_ITEM] = io->key1->name + 1;
1800 }
1801
1802 // Get the item prefix.
1803 switch (io->type) {
1804 case NEW: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_ADD; break;
1805 case OLD: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_REM; break;
1806 default: break;
1807 }
1808
1809 knot_dname_txt_storage_t id;
1810
1811 // Get the textual item id.
1812 if (io->id_len > 0 && io->key0 != NULL) {
1813 size_t id_len = sizeof(id);
1814 int ret = yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len,
1815 id, &id_len, YP_SNOQUOTE);
1816 if (ret != KNOT_EOK) {
1817 return ret;
1818 }
1819 if (io->id_as_data) {
1820 data[KNOT_CTL_IDX_DATA] = id;
1821 } else {
1822 data[KNOT_CTL_IDX_ID] = id;
1823 }
1824 }
1825
1826 if (io->data.val == NULL && io->data.bin == NULL) {
1827 return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &data);
1828 } else {
1829 return send_block_data(io, &data);
1830 }
1831 }
1832
ctl_conf_txn(ctl_args_t * args,ctl_cmd_t cmd)1833 static int ctl_conf_txn(ctl_args_t *args, ctl_cmd_t cmd)
1834 {
1835 conf_io_t io = {
1836 .fcn = send_block,
1837 .misc = args->ctl
1838 };
1839
1840 int ret = KNOT_EOK;
1841
1842 switch (cmd) {
1843 case CTL_CONF_BEGIN:
1844 ret = conf_io_begin(false);
1845 break;
1846 case CTL_CONF_ABORT:
1847 conf_io_abort(false);
1848 ret = KNOT_EOK;
1849 break;
1850 case CTL_CONF_COMMIT:
1851 // First check the database.
1852 ret = conf_io_check(&io);
1853 if (ret != KNOT_EOK) {
1854 // A semantic error is already sent by the check function.
1855 if (io.error.code != KNOT_EOK) {
1856 return KNOT_EOK;
1857 }
1858 // No transaction abort!
1859 break;
1860 }
1861
1862 ret = conf_io_commit(false);
1863 if (ret != KNOT_EOK) {
1864 conf_io_abort(false);
1865 break;
1866 }
1867
1868 ret = server_reload(args->server);
1869 break;
1870 default:
1871 assert(0);
1872 ret = KNOT_EINVAL;
1873 }
1874
1875 if (ret != KNOT_EOK) {
1876 send_error(args, knot_strerror(ret));
1877 }
1878
1879 return ret;
1880 }
1881
ctl_conf_read(ctl_args_t * args,ctl_cmd_t cmd)1882 static int ctl_conf_read(ctl_args_t *args, ctl_cmd_t cmd)
1883 {
1884 conf_io_t io = {
1885 .fcn = send_block,
1886 .misc = args->ctl
1887 };
1888
1889 int ret = KNOT_EOK;
1890
1891 while (true) {
1892 const char *key0 = args->data[KNOT_CTL_IDX_SECTION];
1893 const char *key1 = args->data[KNOT_CTL_IDX_ITEM];
1894 const char *id = args->data[KNOT_CTL_IDX_ID];
1895
1896 ctl_log_conf_data(&args->data);
1897
1898 switch (cmd) {
1899 case CTL_CONF_LIST:
1900 ret = conf_io_list(key0, &io);
1901 break;
1902 case CTL_CONF_READ:
1903 ret = conf_io_get(key0, key1, id, true, &io);
1904 break;
1905 case CTL_CONF_DIFF:
1906 ret = conf_io_diff(key0, key1, id, &io);
1907 break;
1908 case CTL_CONF_GET:
1909 ret = conf_io_get(key0, key1, id, false, &io);
1910 break;
1911 default:
1912 assert(0);
1913 ret = KNOT_EINVAL;
1914 }
1915 if (ret != KNOT_EOK) {
1916 send_error(args, knot_strerror(ret));
1917 break;
1918 }
1919
1920 // Get next data unit.
1921 ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
1922 if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
1923 break;
1924 }
1925 }
1926
1927 return ret;
1928 }
1929
ctl_conf_modify(ctl_args_t * args,ctl_cmd_t cmd)1930 static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
1931 {
1932 // Start child transaction.
1933 int ret = conf_io_begin(true);
1934 if (ret != KNOT_EOK) {
1935 send_error(args, knot_strerror(ret));
1936 return ret;
1937 }
1938
1939 while (true) {
1940 const char *key0 = args->data[KNOT_CTL_IDX_SECTION];
1941 const char *key1 = args->data[KNOT_CTL_IDX_ITEM];
1942 const char *id = args->data[KNOT_CTL_IDX_ID];
1943 const char *data = args->data[KNOT_CTL_IDX_DATA];
1944
1945 ctl_log_conf_data(&args->data);
1946
1947 switch (cmd) {
1948 case CTL_CONF_SET:
1949 ret = conf_io_set(key0, key1, id, data);
1950 break;
1951 case CTL_CONF_UNSET:
1952 ret = conf_io_unset(key0, key1, id, data);
1953 break;
1954 default:
1955 assert(0);
1956 ret = KNOT_EINVAL;
1957 }
1958 if (ret != KNOT_EOK) {
1959 send_error(args, knot_strerror(ret));
1960 break;
1961 }
1962
1963 // Get next data unit.
1964 ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
1965 if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
1966 break;
1967 }
1968 }
1969
1970 // Finish child transaction.
1971 if (ret == KNOT_EOK) {
1972 ret = conf_io_commit(true);
1973 if (ret != KNOT_EOK) {
1974 send_error(args, knot_strerror(ret));
1975 }
1976 } else {
1977 conf_io_abort(true);
1978 }
1979
1980 return ret;
1981 }
1982
1983 typedef struct {
1984 const char *name;
1985 int (*fcn)(ctl_args_t *, ctl_cmd_t);
1986 } desc_t;
1987
1988 static const desc_t cmd_table[] = {
1989 [CTL_NONE] = { "" },
1990
1991 [CTL_STATUS] = { "status", ctl_server },
1992 [CTL_STOP] = { "stop", ctl_server },
1993 [CTL_RELOAD] = { "reload", ctl_server },
1994 [CTL_STATS] = { "stats", ctl_stats },
1995
1996 [CTL_ZONE_STATUS] = { "zone-status", ctl_zone },
1997 [CTL_ZONE_RELOAD] = { "zone-reload", ctl_zone },
1998 [CTL_ZONE_REFRESH] = { "zone-refresh", ctl_zone },
1999 [CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone },
2000 [CTL_ZONE_NOTIFY] = { "zone-notify", ctl_zone },
2001 [CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone },
2002 [CTL_ZONE_BACKUP] = { "zone-backup", ctl_zone },
2003 [CTL_ZONE_RESTORE] = { "zone-restore", ctl_zone },
2004 [CTL_ZONE_SIGN] = { "zone-sign", ctl_zone },
2005 [CTL_ZONE_KEYS_LOAD] = { "zone-keys-load", ctl_zone },
2006 [CTL_ZONE_KEY_ROLL] = { "zone-key-rollover", ctl_zone },
2007 [CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone },
2008 [CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone },
2009 [CTL_ZONE_THAW] = { "zone-thaw", ctl_zone },
2010
2011 [CTL_ZONE_READ] = { "zone-read", ctl_zone },
2012 [CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone },
2013 [CTL_ZONE_COMMIT] = { "zone-commit", ctl_zone },
2014 [CTL_ZONE_ABORT] = { "zone-abort", ctl_zone },
2015 [CTL_ZONE_DIFF] = { "zone-diff", ctl_zone },
2016 [CTL_ZONE_GET] = { "zone-get", ctl_zone },
2017 [CTL_ZONE_SET] = { "zone-set", ctl_zone },
2018 [CTL_ZONE_UNSET] = { "zone-unset", ctl_zone },
2019 [CTL_ZONE_PURGE] = { "zone-purge", ctl_zone },
2020 [CTL_ZONE_STATS] = { "zone-stats", ctl_zone },
2021
2022 [CTL_CONF_LIST] = { "conf-list", ctl_conf_read },
2023 [CTL_CONF_READ] = { "conf-read", ctl_conf_read },
2024 [CTL_CONF_BEGIN] = { "conf-begin", ctl_conf_txn },
2025 [CTL_CONF_COMMIT] = { "conf-commit", ctl_conf_txn },
2026 [CTL_CONF_ABORT] = { "conf-abort", ctl_conf_txn },
2027 [CTL_CONF_DIFF] = { "conf-diff", ctl_conf_read },
2028 [CTL_CONF_GET] = { "conf-get", ctl_conf_read },
2029 [CTL_CONF_SET] = { "conf-set", ctl_conf_modify },
2030 [CTL_CONF_UNSET] = { "conf-unset", ctl_conf_modify },
2031 };
2032
2033 #define MAX_CTL_CODE (sizeof(cmd_table) / sizeof(desc_t) - 1)
2034
ctl_cmd_to_str(ctl_cmd_t cmd)2035 const char *ctl_cmd_to_str(ctl_cmd_t cmd)
2036 {
2037 if (cmd <= CTL_NONE || cmd > MAX_CTL_CODE) {
2038 return NULL;
2039 }
2040
2041 return cmd_table[cmd].name;
2042 }
2043
ctl_str_to_cmd(const char * cmd_str)2044 ctl_cmd_t ctl_str_to_cmd(const char *cmd_str)
2045 {
2046 if (cmd_str == NULL) {
2047 return CTL_NONE;
2048 }
2049
2050 for (ctl_cmd_t cmd = CTL_NONE + 1; cmd <= MAX_CTL_CODE; cmd++) {
2051 if (strcmp(cmd_str, cmd_table[cmd].name) == 0) {
2052 return cmd;
2053 }
2054 }
2055
2056 return CTL_NONE;
2057 }
2058
ctl_exec(ctl_cmd_t cmd,ctl_args_t * args)2059 int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args)
2060 {
2061 if (args == NULL) {
2062 return KNOT_EINVAL;
2063 }
2064
2065 return cmd_table[cmd].fcn(args, cmd);
2066 }
2067
ctl_has_flag(const char * flags,const char * flag)2068 bool ctl_has_flag(const char *flags, const char *flag)
2069 {
2070 if (flags == NULL || flag == NULL) {
2071 return false;
2072 }
2073
2074 return strstr(flags, flag) != NULL;
2075 }
2076