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