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