1 /* $NetBSD: control.c,v 1.4 2019/02/24 20:01:27 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 http://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 15 /*! \file */ 16 17 #include <config.h> 18 19 #include <stdbool.h> 20 21 #include <isc/app.h> 22 #include <isc/event.h> 23 #include <isc/lex.h> 24 #include <isc/mem.h> 25 #include <isc/string.h> 26 #include <isc/timer.h> 27 #include <isc/util.h> 28 29 #include <dns/result.h> 30 31 #include <isccc/alist.h> 32 #include <isccc/cc.h> 33 #include <isccc/result.h> 34 35 #include <named/control.h> 36 #include <named/globals.h> 37 #include <named/log.h> 38 #include <named/os.h> 39 #include <named/server.h> 40 #ifdef HAVE_LIBSCF 41 #include <named/smf_globals.h> 42 #endif 43 44 static isc_result_t 45 getcommand(isc_lex_t *lex, char **cmdp) { 46 isc_result_t result; 47 isc_token_t token; 48 49 REQUIRE(cmdp != NULL && *cmdp == NULL); 50 51 result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF, &token); 52 if (result != ISC_R_SUCCESS) 53 return (result); 54 55 isc_lex_ungettoken(lex, &token); 56 57 if (token.type != isc_tokentype_string) 58 return (ISC_R_FAILURE); 59 60 *cmdp = token.value.as_textregion.base; 61 62 return (ISC_R_SUCCESS); 63 } 64 65 static inline bool 66 command_compare(const char *str, const char *command) { 67 return (strcasecmp(str, command) == 0); 68 } 69 70 /*% 71 * This function is called to process the incoming command 72 * when a control channel message is received. 73 */ 74 isc_result_t 75 named_control_docommand(isccc_sexpr_t *message, bool readonly, 76 isc_buffer_t **text) 77 { 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 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 isc_buffer_init(&src, cmdline, strlen(cmdline)); 110 isc_buffer_add(&src, strlen(cmdline)); 111 result = isc_lex_openbuffer(lex, &src); 112 if (result != ISC_R_SUCCESS) 113 goto cleanup; 114 115 result = getcommand(lex, &command); 116 if (result != ISC_R_SUCCESS) 117 goto cleanup; 118 119 /* 120 * Compare the 'command' parameter against all known control commands. 121 */ 122 if ((command_compare(command, NAMED_COMMAND_NULL) && 123 strlen(cmdline) == 4) || 124 command_compare(command, NAMED_COMMAND_STATUS)) 125 { 126 log_level = ISC_LOG_DEBUG(1); 127 } else { 128 log_level = ISC_LOG_INFO; 129 } 130 131 /* 132 * If this listener should have read-only access, reject 133 * restricted commands here. rndc nta is handled specially 134 * below. 135 */ 136 if (readonly && 137 !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'", cmdline); 148 result = ISC_R_FAILURE; 149 goto cleanup; 150 } 151 152 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 153 NAMED_LOGMODULE_CONTROL, log_level, 154 "received control channel command '%s'", 155 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 * If named_smf_got_instance = 0, named_smf_chroot 180 * is not relevant and we fall through to 181 * isc_app_shutdown below. 182 */ 183 #endif 184 /* Do not flush master files */ 185 named_server_flushonshutdown(named_g_server, false); 186 named_os_shutdownmsg(cmdline, *text); 187 isc_app_shutdown(); 188 result = ISC_R_SUCCESS; 189 } else if (command_compare(command, NAMED_COMMAND_STOP)) { 190 /* 191 * "stop" is the same as "halt" except it does 192 * flush master files. 193 */ 194 #ifdef HAVE_LIBSCF 195 if (named_smf_got_instance == 1 && named_smf_chroot == 1) { 196 result = named_smf_add_message(text); 197 goto cleanup; 198 } 199 if (named_smf_got_instance == 1 && named_smf_chroot == 0) 200 named_smf_want_disable = 1; 201 #endif 202 named_server_flushonshutdown(named_g_server, true); 203 named_os_shutdownmsg(cmdline, *text); 204 isc_app_shutdown(); 205 result = ISC_R_SUCCESS; 206 } else if (command_compare(command, NAMED_COMMAND_ADDZONE) || 207 command_compare(command, NAMED_COMMAND_MODZONE)) { 208 result = named_server_changezone(named_g_server, cmdline, text); 209 } else if (command_compare(command, NAMED_COMMAND_DELZONE)) { 210 result = named_server_delzone(named_g_server, lex, text); 211 } else if (command_compare(command, NAMED_COMMAND_DNSTAP) || 212 command_compare(command, NAMED_COMMAND_DNSTAPREOPEN)) { 213 result = named_server_dnstap(named_g_server, lex, text); 214 } else if (command_compare(command, NAMED_COMMAND_DUMPDB)) { 215 named_server_dumpdb(named_g_server, lex, text); 216 result = ISC_R_SUCCESS; 217 } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) { 218 result = named_server_dumpstats(named_g_server); 219 } else if (command_compare(command, NAMED_COMMAND_FLUSH)) { 220 result = named_server_flushcache(named_g_server, lex); 221 } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) { 222 result = named_server_flushnode(named_g_server, lex, false); 223 } else if (command_compare(command, NAMED_COMMAND_FLUSHTREE)) { 224 result = named_server_flushnode(named_g_server, lex, true); 225 } else if (command_compare(command, NAMED_COMMAND_FREEZE)) { 226 result = named_server_freeze(named_g_server, true, lex, text); 227 } else if (command_compare(command, NAMED_COMMAND_LOADKEYS) || 228 command_compare(command, NAMED_COMMAND_SIGN)) { 229 result = named_server_rekey(named_g_server, lex, text); 230 } else if (command_compare(command, NAMED_COMMAND_MKEYS)) { 231 result = named_server_mkeys(named_g_server, lex, text); 232 } else if (command_compare(command, NAMED_COMMAND_NOTIFY)) { 233 result = named_server_notifycommand(named_g_server, lex, text); 234 } else if (command_compare(command, NAMED_COMMAND_NOTRACE)) { 235 named_g_debuglevel = 0; 236 isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); 237 result = ISC_R_SUCCESS; 238 } else if (command_compare(command, NAMED_COMMAND_NTA)) { 239 result = named_server_nta(named_g_server, lex, readonly, text); 240 } else if (command_compare(command, NAMED_COMMAND_NULL)) { 241 result = ISC_R_SUCCESS; 242 } else if (command_compare(command, NAMED_COMMAND_QUERYLOG)) { 243 result = named_server_togglequerylog(named_g_server, lex); 244 } else if (command_compare(command, NAMED_COMMAND_RECONFIG)) { 245 result = named_server_reconfigcommand(named_g_server); 246 } else if (command_compare(command, NAMED_COMMAND_RECURSING)) { 247 result = named_server_dumprecursing(named_g_server); 248 } else if (command_compare(command, NAMED_COMMAND_REFRESH)) { 249 result = named_server_refreshcommand(named_g_server, lex, text); 250 } else if (command_compare(command, NAMED_COMMAND_RELOAD)) { 251 result = named_server_reloadcommand(named_g_server, lex, text); 252 } else if (command_compare(command, NAMED_COMMAND_RETRANSFER)) { 253 result = named_server_retransfercommand(named_g_server, 254 lex, text); 255 } else if (command_compare(command, NAMED_COMMAND_SCAN)) { 256 named_server_scan_interfaces(named_g_server); 257 result = ISC_R_SUCCESS; 258 } else if (command_compare(command, NAMED_COMMAND_SECROOTS)) { 259 result = named_server_dumpsecroots(named_g_server, lex, text); 260 } else if (command_compare(command, NAMED_COMMAND_SERVESTALE)) { 261 result = named_server_servestale(named_g_server, lex, text); 262 } else if (command_compare(command, NAMED_COMMAND_SHOWZONE)) { 263 result = named_server_showzone(named_g_server, lex, text); 264 } else if (command_compare(command, NAMED_COMMAND_SIGNING)) { 265 result = named_server_signing(named_g_server, lex, text); 266 } else if (command_compare(command, NAMED_COMMAND_STATUS)) { 267 result = named_server_status(named_g_server, text); 268 } else if (command_compare(command, NAMED_COMMAND_SYNC)) { 269 result = named_server_sync(named_g_server, lex, text); 270 } else if (command_compare(command, NAMED_COMMAND_TCPTIMEOUTS)) { 271 result = named_server_tcptimeouts(lex, text); 272 } else if (command_compare(command, NAMED_COMMAND_TESTGEN)) { 273 result = named_server_testgen(lex, text); 274 } else if (command_compare(command, NAMED_COMMAND_THAW) || 275 command_compare(command, NAMED_COMMAND_UNFREEZE)) { 276 result = named_server_freeze(named_g_server, false, lex, text); 277 } else if (command_compare(command, NAMED_COMMAND_TIMERPOKE)) { 278 isc_timermgr_poke(named_g_timermgr); 279 result = ISC_R_SUCCESS; 280 } else if (command_compare(command, NAMED_COMMAND_TRACE)) { 281 result = named_server_setdebuglevel(named_g_server, lex); 282 } else if (command_compare(command, NAMED_COMMAND_TSIGDELETE)) { 283 result = named_server_tsigdelete(named_g_server, lex, text); 284 } else if (command_compare(command, NAMED_COMMAND_TSIGLIST)) { 285 result = named_server_tsiglist(named_g_server, text); 286 } else if (command_compare(command, NAMED_COMMAND_VALIDATION)) { 287 result = named_server_validation(named_g_server, lex, text); 288 } else if (command_compare(command, NAMED_COMMAND_ZONESTATUS)) { 289 result = named_server_zonestatus(named_g_server, lex, text); 290 } else { 291 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 292 NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 293 "unknown control channel command '%s'", 294 command); 295 result = DNS_R_UNKNOWNCOMMAND; 296 } 297 298 cleanup: 299 if (lex != NULL) 300 isc_lex_destroy(&lex); 301 302 return (result); 303 } 304