1 1.10 christos /* $NetBSD: control.c,v 1.11 2025/05/21 14:47:35 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.8 christos * SPDX-License-Identifier: MPL-2.0 7 1.8 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.7 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos /*! \file */ 17 1.1 christos 18 1.3 christos #include <stdbool.h> 19 1.1 christos 20 1.1 christos #include <isc/lex.h> 21 1.1 christos #include <isc/mem.h> 22 1.9 christos #include <isc/result.h> 23 1.1 christos #include <isc/string.h> 24 1.1 christos #include <isc/timer.h> 25 1.1 christos #include <isc/util.h> 26 1.1 christos 27 1.1 christos #include <isccc/alist.h> 28 1.1 christos #include <isccc/cc.h> 29 1.1 christos 30 1.1 christos #include <named/control.h> 31 1.1 christos #include <named/globals.h> 32 1.1 christos #include <named/log.h> 33 1.1 christos #include <named/os.h> 34 1.1 christos #include <named/server.h> 35 1.1 christos #ifdef HAVE_LIBSCF 36 1.1 christos #include <named/smf_globals.h> 37 1.5 christos #endif /* ifdef HAVE_LIBSCF */ 38 1.1 christos 39 1.1 christos static isc_result_t 40 1.1 christos getcommand(isc_lex_t *lex, char **cmdp) { 41 1.1 christos isc_result_t result; 42 1.1 christos isc_token_t token; 43 1.1 christos 44 1.1 christos REQUIRE(cmdp != NULL && *cmdp == NULL); 45 1.1 christos 46 1.1 christos result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF, &token); 47 1.5 christos if (result != ISC_R_SUCCESS) { 48 1.10 christos return result; 49 1.5 christos } 50 1.1 christos 51 1.1 christos isc_lex_ungettoken(lex, &token); 52 1.1 christos 53 1.5 christos if (token.type != isc_tokentype_string) { 54 1.10 christos return ISC_R_FAILURE; 55 1.5 christos } 56 1.1 christos 57 1.1 christos *cmdp = token.value.as_textregion.base; 58 1.1 christos 59 1.10 christos return ISC_R_SUCCESS; 60 1.1 christos } 61 1.1 christos 62 1.8 christos static bool 63 1.1 christos command_compare(const char *str, const char *command) { 64 1.10 christos return strcasecmp(str, command) == 0; 65 1.1 christos } 66 1.1 christos 67 1.1 christos /*% 68 1.1 christos * This function is called to process the incoming command 69 1.1 christos * when a control channel message is received. 70 1.1 christos */ 71 1.1 christos isc_result_t 72 1.3 christos named_control_docommand(isccc_sexpr_t *message, bool readonly, 73 1.5 christos isc_buffer_t **text) { 74 1.1 christos isccc_sexpr_t *data; 75 1.1 christos char *cmdline = NULL; 76 1.1 christos char *command = NULL; 77 1.1 christos isc_result_t result; 78 1.1 christos int log_level; 79 1.1 christos isc_buffer_t src; 80 1.1 christos isc_lex_t *lex = NULL; 81 1.1 christos #ifdef HAVE_LIBSCF 82 1.1 christos named_smf_want_disable = 0; 83 1.5 christos #endif /* ifdef HAVE_LIBSCF */ 84 1.1 christos 85 1.1 christos data = isccc_alist_lookup(message, "_data"); 86 1.1 christos if (!isccc_alist_alistp(data)) { 87 1.1 christos /* 88 1.1 christos * No data section. 89 1.1 christos */ 90 1.10 christos return ISC_R_FAILURE; 91 1.1 christos } 92 1.1 christos 93 1.1 christos result = isccc_cc_lookupstring(data, "type", &cmdline); 94 1.1 christos if (result != ISC_R_SUCCESS) { 95 1.1 christos /* 96 1.1 christos * We have no idea what this is. 97 1.1 christos */ 98 1.10 christos return result; 99 1.1 christos } 100 1.1 christos 101 1.10 christos isc_lex_create(named_g_mctx, strlen(cmdline), &lex); 102 1.1 christos 103 1.1 christos isc_buffer_init(&src, cmdline, strlen(cmdline)); 104 1.1 christos isc_buffer_add(&src, strlen(cmdline)); 105 1.1 christos result = isc_lex_openbuffer(lex, &src); 106 1.5 christos if (result != ISC_R_SUCCESS) { 107 1.1 christos goto cleanup; 108 1.5 christos } 109 1.1 christos 110 1.1 christos result = getcommand(lex, &command); 111 1.5 christos if (result != ISC_R_SUCCESS) { 112 1.1 christos goto cleanup; 113 1.5 christos } 114 1.1 christos 115 1.1 christos /* 116 1.1 christos * Compare the 'command' parameter against all known control commands. 117 1.1 christos */ 118 1.1 christos if ((command_compare(command, NAMED_COMMAND_NULL) && 119 1.1 christos strlen(cmdline) == 4) || 120 1.1 christos command_compare(command, NAMED_COMMAND_STATUS)) 121 1.1 christos { 122 1.1 christos log_level = ISC_LOG_DEBUG(1); 123 1.1 christos } else { 124 1.1 christos log_level = ISC_LOG_INFO; 125 1.1 christos } 126 1.1 christos 127 1.1 christos /* 128 1.1 christos * If this listener should have read-only access, reject 129 1.1 christos * restricted commands here. rndc nta is handled specially 130 1.1 christos * below. 131 1.1 christos */ 132 1.5 christos if (readonly && !command_compare(command, NAMED_COMMAND_NTA) && 133 1.1 christos !command_compare(command, NAMED_COMMAND_NULL) && 134 1.1 christos !command_compare(command, NAMED_COMMAND_STATUS) && 135 1.1 christos !command_compare(command, NAMED_COMMAND_SHOWZONE) && 136 1.1 christos !command_compare(command, NAMED_COMMAND_TESTGEN) && 137 1.1 christos !command_compare(command, NAMED_COMMAND_ZONESTATUS)) 138 1.1 christos { 139 1.1 christos isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 140 1.1 christos NAMED_LOGMODULE_CONTROL, log_level, 141 1.1 christos "rejecting restricted control channel " 142 1.5 christos "command '%s'", 143 1.5 christos cmdline); 144 1.1 christos result = ISC_R_FAILURE; 145 1.1 christos goto cleanup; 146 1.1 christos } 147 1.1 christos 148 1.1 christos isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 149 1.1 christos NAMED_LOGMODULE_CONTROL, log_level, 150 1.5 christos "received control channel command '%s'", cmdline); 151 1.1 christos 152 1.4 christos /* 153 1.4 christos * After the lengthy "halt" and "stop", the commands are 154 1.4 christos * handled in alphabetical order of the NAMED_COMMAND_ macros. 155 1.4 christos */ 156 1.4 christos if (command_compare(command, NAMED_COMMAND_HALT)) { 157 1.1 christos #ifdef HAVE_LIBSCF 158 1.1 christos /* 159 1.1 christos * If we are managed by smf(5), AND in chroot, then 160 1.1 christos * we cannot connect to the smf repository, so just 161 1.1 christos * return with an appropriate message back to rndc. 162 1.1 christos */ 163 1.1 christos if (named_smf_got_instance == 1 && named_smf_chroot == 1) { 164 1.1 christos result = named_smf_add_message(text); 165 1.1 christos goto cleanup; 166 1.1 christos } 167 1.1 christos /* 168 1.1 christos * If we are managed by smf(5) but not in chroot, 169 1.1 christos * try to disable ourselves the smf way. 170 1.1 christos */ 171 1.5 christos if (named_smf_got_instance == 1 && named_smf_chroot == 0) { 172 1.1 christos named_smf_want_disable = 1; 173 1.5 christos } 174 1.1 christos /* 175 1.10 christos * If named_smf_got_instance = 0, named_smf_chroot is 176 1.10 christos * not relevant and we fall through to shutdown below. 177 1.1 christos */ 178 1.5 christos #endif /* ifdef HAVE_LIBSCF */ 179 1.1 christos /* Do not flush master files */ 180 1.3 christos named_server_flushonshutdown(named_g_server, false); 181 1.1 christos named_os_shutdownmsg(cmdline, *text); 182 1.10 christos isc_loopmgr_shutdown(named_g_loopmgr); 183 1.9 christos result = ISC_R_SHUTTINGDOWN; 184 1.1 christos } else if (command_compare(command, NAMED_COMMAND_STOP)) { 185 1.1 christos /* 186 1.1 christos * "stop" is the same as "halt" except it does 187 1.1 christos * flush master files. 188 1.1 christos */ 189 1.1 christos #ifdef HAVE_LIBSCF 190 1.1 christos if (named_smf_got_instance == 1 && named_smf_chroot == 1) { 191 1.1 christos result = named_smf_add_message(text); 192 1.1 christos goto cleanup; 193 1.1 christos } 194 1.5 christos if (named_smf_got_instance == 1 && named_smf_chroot == 0) { 195 1.1 christos named_smf_want_disable = 1; 196 1.5 christos } 197 1.5 christos #endif /* ifdef HAVE_LIBSCF */ 198 1.3 christos named_server_flushonshutdown(named_g_server, true); 199 1.1 christos named_os_shutdownmsg(cmdline, *text); 200 1.10 christos isc_loopmgr_shutdown(named_g_loopmgr); 201 1.9 christos result = ISC_R_SHUTTINGDOWN; 202 1.4 christos } else if (command_compare(command, NAMED_COMMAND_ADDZONE) || 203 1.5 christos command_compare(command, NAMED_COMMAND_MODZONE)) 204 1.5 christos { 205 1.4 christos result = named_server_changezone(named_g_server, cmdline, text); 206 1.4 christos } else if (command_compare(command, NAMED_COMMAND_DELZONE)) { 207 1.4 christos result = named_server_delzone(named_g_server, lex, text); 208 1.6 christos } else if (command_compare(command, NAMED_COMMAND_DNSSEC)) { 209 1.6 christos result = named_server_dnssec(named_g_server, lex, text); 210 1.4 christos } else if (command_compare(command, NAMED_COMMAND_DNSTAP) || 211 1.5 christos command_compare(command, NAMED_COMMAND_DNSTAPREOPEN)) 212 1.5 christos { 213 1.4 christos result = named_server_dnstap(named_g_server, lex, text); 214 1.1 christos } else if (command_compare(command, NAMED_COMMAND_DUMPDB)) { 215 1.10 christos result = named_server_dumpdb(named_g_server, lex, text); 216 1.4 christos } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) { 217 1.4 christos result = named_server_dumpstats(named_g_server); 218 1.10 christos } else if (command_compare(command, NAMED_COMMAND_FETCHLIMIT)) { 219 1.10 christos result = named_server_fetchlimit(named_g_server, lex, text); 220 1.1 christos } else if (command_compare(command, NAMED_COMMAND_FLUSH)) { 221 1.1 christos result = named_server_flushcache(named_g_server, lex); 222 1.1 christos } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) { 223 1.3 christos result = named_server_flushnode(named_g_server, lex, false); 224 1.1 christos } else if (command_compare(command, NAMED_COMMAND_FLUSHTREE)) { 225 1.3 christos result = named_server_flushnode(named_g_server, lex, true); 226 1.1 christos } else if (command_compare(command, NAMED_COMMAND_FREEZE)) { 227 1.4 christos result = named_server_freeze(named_g_server, true, lex, text); 228 1.10 christos } else if (command_compare(command, NAMED_COMMAND_SKR)) { 229 1.10 christos result = named_server_skr(named_g_server, lex, text); 230 1.4 christos } else if (command_compare(command, NAMED_COMMAND_LOADKEYS) || 231 1.5 christos command_compare(command, NAMED_COMMAND_SIGN)) 232 1.5 christos { 233 1.4 christos result = named_server_rekey(named_g_server, lex, text); 234 1.11 christos } else if (command_compare(command, NAMED_COMMAND_MEMPROF)) { 235 1.11 christos result = named_server_togglememprof(lex); 236 1.4 christos } else if (command_compare(command, NAMED_COMMAND_MKEYS)) { 237 1.4 christos result = named_server_mkeys(named_g_server, lex, text); 238 1.4 christos } else if (command_compare(command, NAMED_COMMAND_NOTIFY)) { 239 1.4 christos result = named_server_notifycommand(named_g_server, lex, text); 240 1.4 christos } else if (command_compare(command, NAMED_COMMAND_NOTRACE)) { 241 1.4 christos named_g_debuglevel = 0; 242 1.4 christos isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); 243 1.4 christos result = ISC_R_SUCCESS; 244 1.4 christos } else if (command_compare(command, NAMED_COMMAND_NTA)) { 245 1.4 christos result = named_server_nta(named_g_server, lex, readonly, text); 246 1.4 christos } else if (command_compare(command, NAMED_COMMAND_NULL)) { 247 1.4 christos result = ISC_R_SUCCESS; 248 1.4 christos } else if (command_compare(command, NAMED_COMMAND_QUERYLOG)) { 249 1.10 christos result = named_server_setortoggle(named_g_server, 250 1.10 christos "query logging", 251 1.10 christos NS_SERVER_LOGQUERIES, lex); 252 1.4 christos } else if (command_compare(command, NAMED_COMMAND_RECONFIG)) { 253 1.4 christos result = named_server_reconfigcommand(named_g_server); 254 1.4 christos } else if (command_compare(command, NAMED_COMMAND_RECURSING)) { 255 1.4 christos result = named_server_dumprecursing(named_g_server); 256 1.4 christos } else if (command_compare(command, NAMED_COMMAND_REFRESH)) { 257 1.4 christos result = named_server_refreshcommand(named_g_server, lex, text); 258 1.4 christos } else if (command_compare(command, NAMED_COMMAND_RELOAD)) { 259 1.4 christos result = named_server_reloadcommand(named_g_server, lex, text); 260 1.11 christos } else if (command_compare(command, NAMED_COMMAND_RESETSTATS)) { 261 1.11 christos result = named_server_resetstatscommand(named_g_server, lex, 262 1.11 christos text); 263 1.10 christos } else if (command_compare(command, NAMED_COMMAND_RESPONSELOG)) { 264 1.10 christos result = named_server_setortoggle(named_g_server, 265 1.10 christos "response logging", 266 1.10 christos NS_SERVER_LOGRESPONSES, lex); 267 1.4 christos } else if (command_compare(command, NAMED_COMMAND_RETRANSFER)) { 268 1.5 christos result = named_server_retransfercommand(named_g_server, lex, 269 1.5 christos text); 270 1.1 christos } else if (command_compare(command, NAMED_COMMAND_SCAN)) { 271 1.4 christos named_server_scan_interfaces(named_g_server); 272 1.1 christos result = ISC_R_SUCCESS; 273 1.4 christos } else if (command_compare(command, NAMED_COMMAND_SECROOTS)) { 274 1.4 christos result = named_server_dumpsecroots(named_g_server, lex, text); 275 1.4 christos } else if (command_compare(command, NAMED_COMMAND_SERVESTALE)) { 276 1.4 christos result = named_server_servestale(named_g_server, lex, text); 277 1.4 christos } else if (command_compare(command, NAMED_COMMAND_SHOWZONE)) { 278 1.4 christos result = named_server_showzone(named_g_server, lex, text); 279 1.4 christos } else if (command_compare(command, NAMED_COMMAND_SIGNING)) { 280 1.4 christos result = named_server_signing(named_g_server, lex, text); 281 1.4 christos } else if (command_compare(command, NAMED_COMMAND_STATUS)) { 282 1.4 christos result = named_server_status(named_g_server, text); 283 1.1 christos } else if (command_compare(command, NAMED_COMMAND_SYNC)) { 284 1.1 christos result = named_server_sync(named_g_server, lex, text); 285 1.4 christos } else if (command_compare(command, NAMED_COMMAND_TCPTIMEOUTS)) { 286 1.4 christos result = named_server_tcptimeouts(lex, text); 287 1.4 christos } else if (command_compare(command, NAMED_COMMAND_TESTGEN)) { 288 1.4 christos result = named_server_testgen(lex, text); 289 1.4 christos } else if (command_compare(command, NAMED_COMMAND_THAW) || 290 1.5 christos command_compare(command, NAMED_COMMAND_UNFREEZE)) 291 1.5 christos { 292 1.4 christos result = named_server_freeze(named_g_server, false, lex, text); 293 1.4 christos } else if (command_compare(command, NAMED_COMMAND_TRACE)) { 294 1.4 christos result = named_server_setdebuglevel(named_g_server, lex); 295 1.1 christos } else if (command_compare(command, NAMED_COMMAND_VALIDATION)) { 296 1.1 christos result = named_server_validation(named_g_server, lex, text); 297 1.1 christos } else if (command_compare(command, NAMED_COMMAND_ZONESTATUS)) { 298 1.1 christos result = named_server_zonestatus(named_g_server, lex, text); 299 1.1 christos } else { 300 1.1 christos isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 301 1.1 christos NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 302 1.5 christos "unknown control channel command '%s'", command); 303 1.1 christos result = DNS_R_UNKNOWNCOMMAND; 304 1.1 christos } 305 1.1 christos 306 1.5 christos cleanup: 307 1.5 christos if (lex != NULL) { 308 1.1 christos isc_lex_destroy(&lex); 309 1.5 christos } 310 1.1 christos 311 1.10 christos return result; 312 1.1 christos } 313