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