| File: | src/usr.sbin/nsd/remote.c |
| Warning: | line 429, column 5 Array access (from variable 'ip') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * remote.c - remote control for the NSD daemon. | |||
| 3 | * | |||
| 4 | * Copyright (c) 2008, NLnet Labs. All rights reserved. | |||
| 5 | * | |||
| 6 | * This software is open source. | |||
| 7 | * | |||
| 8 | * Redistribution and use in source and binary forms, with or without | |||
| 9 | * modification, are permitted provided that the following conditions | |||
| 10 | * are met: | |||
| 11 | * | |||
| 12 | * Redistributions of source code must retain the above copyright notice, | |||
| 13 | * this list of conditions and the following disclaimer. | |||
| 14 | * | |||
| 15 | * Redistributions in binary form must reproduce the above copyright notice, | |||
| 16 | * this list of conditions and the following disclaimer in the documentation | |||
| 17 | * and/or other materials provided with the distribution. | |||
| 18 | * | |||
| 19 | * Neither the name of the NLNET LABS nor the names of its contributors may | |||
| 20 | * be used to endorse or promote products derived from this software without | |||
| 21 | * specific prior written permission. | |||
| 22 | * | |||
| 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| 26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| 27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| 28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |||
| 29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| 30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| 31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| 32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| 33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| 34 | */ | |||
| 35 | ||||
| 36 | /** | |||
| 37 | * \file | |||
| 38 | * | |||
| 39 | * This file contains the remote control functionality for the daemon. | |||
| 40 | * The remote control can be performed using either the commandline | |||
| 41 | * nsd-control tool, or a TLS capable web browser. | |||
| 42 | * The channel is secured using TLSv1, and certificates. | |||
| 43 | * Both the server and the client(control tool) have their own keys. | |||
| 44 | */ | |||
| 45 | #include "config.h" | |||
| 46 | #ifdef HAVE_SSL | |||
| 47 | ||||
| 48 | #ifdef HAVE_OPENSSL_SSL_H1 | |||
| 49 | #include "openssl/ssl.h" | |||
| 50 | #endif | |||
| 51 | #ifdef HAVE_OPENSSL_ERR_H1 | |||
| 52 | #include <openssl/err.h> | |||
| 53 | #endif | |||
| 54 | #ifdef HAVE_OPENSSL_RAND_H1 | |||
| 55 | #include <openssl/rand.h> | |||
| 56 | #endif | |||
| 57 | #include <ctype.h> | |||
| 58 | #include <unistd.h> | |||
| 59 | #include <assert.h> | |||
| 60 | #include <fcntl.h> | |||
| 61 | #ifndef USE_MINI_EVENT | |||
| 62 | # ifdef HAVE_EVENT_H1 | |||
| 63 | # include <event.h> | |||
| 64 | # else | |||
| 65 | # include <event2/event.h> | |||
| 66 | # include "event2/event_struct.h" | |||
| 67 | # include "event2/event_compat.h" | |||
| 68 | # endif | |||
| 69 | #else | |||
| 70 | # include "mini_event.h" | |||
| 71 | #endif | |||
| 72 | #include "remote.h" | |||
| 73 | #include "util.h" | |||
| 74 | #include "xfrd.h" | |||
| 75 | #include "xfrd-notify.h" | |||
| 76 | #include "xfrd-tcp.h" | |||
| 77 | #include "nsd.h" | |||
| 78 | #include "options.h" | |||
| 79 | #include "difffile.h" | |||
| 80 | #include "ipc.h" | |||
| 81 | ||||
| 82 | #ifdef HAVE_SYS_TYPES_H1 | |||
| 83 | # include <sys/types.h> | |||
| 84 | #endif | |||
| 85 | #ifdef HAVE_SYS_STAT_H1 | |||
| 86 | # include <sys/stat.h> | |||
| 87 | #endif | |||
| 88 | #ifdef HAVE_NETDB_H1 | |||
| 89 | # include <netdb.h> | |||
| 90 | #endif | |||
| 91 | #ifdef HAVE_SYS_UN_H1 | |||
| 92 | # include <sys/un.h> | |||
| 93 | #endif | |||
| 94 | #ifndef AF_LOCAL1 | |||
| 95 | #define AF_LOCAL1 AF_UNIX1 | |||
| 96 | #endif | |||
| 97 | ||||
| 98 | /** number of seconds timeout on incoming remote control handshake */ | |||
| 99 | #define REMOTE_CONTROL_TCP_TIMEOUT120 120 | |||
| 100 | ||||
| 101 | /** repattern to master or slave */ | |||
| 102 | #define REPAT_SLAVE1 1 | |||
| 103 | #define REPAT_MASTER2 2 | |||
| 104 | ||||
| 105 | /** if you want zero to be inhibited in stats output. | |||
| 106 | * it omits zeroes for types that have no acronym and unused-rcodes */ | |||
| 107 | const int inhibit_zero = 1; | |||
| 108 | ||||
| 109 | /** | |||
| 110 | * a busy control command connection, SSL state | |||
| 111 | * Defined here to keep the definition private, and keep SSL out of the .h | |||
| 112 | */ | |||
| 113 | struct rc_state { | |||
| 114 | /** the next item in list */ | |||
| 115 | struct rc_state* next, *prev; | |||
| 116 | /* if the event was added to the event_base */ | |||
| 117 | int event_added; | |||
| 118 | /** the commpoint */ | |||
| 119 | struct event c; | |||
| 120 | /** timeout for this state */ | |||
| 121 | struct timeval tval; | |||
| 122 | /** in the handshake part */ | |||
| 123 | enum { rc_none, rc_hs_read, rc_hs_write } shake_state; | |||
| 124 | /** the ssl state */ | |||
| 125 | SSL* ssl; | |||
| 126 | /** file descriptor */ | |||
| 127 | int fd; | |||
| 128 | /** the rc this is part of */ | |||
| 129 | struct daemon_remote* rc; | |||
| 130 | /** stats list next item */ | |||
| 131 | struct rc_state* stats_next; | |||
| 132 | /** stats list indicator (0 is not part of stats list, 1 is stats, | |||
| 133 | * 2 is stats_noreset. */ | |||
| 134 | int in_stats_list; | |||
| 135 | }; | |||
| 136 | ||||
| 137 | /** | |||
| 138 | * list of events for accepting connections | |||
| 139 | */ | |||
| 140 | struct acceptlist { | |||
| 141 | struct acceptlist* next; | |||
| 142 | int event_added; | |||
| 143 | struct event c; | |||
| 144 | char* ident; | |||
| 145 | struct daemon_remote* rc; | |||
| 146 | }; | |||
| 147 | ||||
| 148 | /** | |||
| 149 | * The remote control state. | |||
| 150 | */ | |||
| 151 | struct daemon_remote { | |||
| 152 | /** the master process for this remote control */ | |||
| 153 | struct xfrd_state* xfrd; | |||
| 154 | /** commpoints for accepting remote control connections */ | |||
| 155 | struct acceptlist* accept_list; | |||
| 156 | /* if certificates are used */ | |||
| 157 | int use_cert; | |||
| 158 | /** number of active commpoints that are handling remote control */ | |||
| 159 | int active; | |||
| 160 | /** max active commpoints */ | |||
| 161 | int max_active; | |||
| 162 | /** current commpoints busy; double linked, malloced */ | |||
| 163 | struct rc_state* busy_list; | |||
| 164 | /** commpoints waiting for stats to complete (also in busy_list) */ | |||
| 165 | struct rc_state* stats_list; | |||
| 166 | /** last time stats was reported */ | |||
| 167 | struct timeval stats_time, boot_time; | |||
| 168 | /** the SSL context for creating new SSL streams */ | |||
| 169 | SSL_CTX* ctx; | |||
| 170 | }; | |||
| 171 | ||||
| 172 | /** | |||
| 173 | * Connection to print to, either SSL or plain over fd | |||
| 174 | */ | |||
| 175 | struct remote_stream { | |||
| 176 | /** SSL structure, nonNULL if using SSL */ | |||
| 177 | SSL* ssl; | |||
| 178 | /** file descriptor for plain transfer */ | |||
| 179 | int fd; | |||
| 180 | }; | |||
| 181 | typedef struct remote_stream RES; | |||
| 182 | ||||
| 183 | /** | |||
| 184 | * Print fixed line of text over ssl connection in blocking mode | |||
| 185 | * @param res: print to | |||
| 186 | * @param text: the text. | |||
| 187 | * @return false on connection failure. | |||
| 188 | */ | |||
| 189 | static int ssl_print_text(RES* res, const char* text); | |||
| 190 | ||||
| 191 | /** | |||
| 192 | * printf style printing to the ssl connection | |||
| 193 | * @param res: the RES connection to print to. Blocking. | |||
| 194 | * @param format: printf style format string. | |||
| 195 | * @return success or false on a network failure. | |||
| 196 | */ | |||
| 197 | static int ssl_printf(RES* res, const char* format, ...) | |||
| 198 | ATTR_FORMAT(printf, 2, 3)__attribute__ ((format (printf, 2, 3))); | |||
| 199 | ||||
| 200 | /** | |||
| 201 | * Read until \n is encountered | |||
| 202 | * If stream signals EOF, the string up to then is returned (without \n). | |||
| 203 | * @param res: the RES connection to read from. blocking. | |||
| 204 | * @param buf: buffer to read to. | |||
| 205 | * @param max: size of buffer. | |||
| 206 | * @return false on connection failure. | |||
| 207 | */ | |||
| 208 | static int ssl_read_line(RES* res, char* buf, size_t max); | |||
| 209 | ||||
| 210 | /** perform the accept of a new remote control connection */ | |||
| 211 | static void | |||
| 212 | remote_accept_callback(int fd, short event, void* arg); | |||
| 213 | ||||
| 214 | /** perform remote control */ | |||
| 215 | static void | |||
| 216 | remote_control_callback(int fd, short event, void* arg); | |||
| 217 | ||||
| 218 | ||||
| 219 | /** ---- end of private defines ---- **/ | |||
| 220 | ||||
| 221 | ||||
| 222 | /** log ssl crypto err */ | |||
| 223 | static void | |||
| 224 | log_crypto_err(const char* str) | |||
| 225 | { | |||
| 226 | /* error:[error code]:[library name]:[function name]:[reason string] */ | |||
| 227 | char buf[128]; | |||
| 228 | unsigned long e; | |||
| 229 | ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); | |||
| 230 | log_msg(LOG_ERR3, "%s crypto %s", str, buf); | |||
| 231 | while( (e=ERR_get_error()) ) { | |||
| 232 | ERR_error_string_n(e, buf, sizeof(buf)); | |||
| 233 | log_msg(LOG_ERR3, "and additionally crypto %s", buf); | |||
| 234 | } | |||
| 235 | } | |||
| 236 | ||||
| 237 | #ifdef BIND8_STATS | |||
| 238 | /** subtract timers and the values do not overflow or become negative */ | |||
| 239 | static void | |||
| 240 | timeval_subtract(struct timeval* d, const struct timeval* end, | |||
| 241 | const struct timeval* start) | |||
| 242 | { | |||
| 243 | #ifndef S_SPLINT_S | |||
| 244 | time_t end_usec = end->tv_usec; | |||
| 245 | d->tv_sec = end->tv_sec - start->tv_sec; | |||
| 246 | if(end_usec < start->tv_usec) { | |||
| 247 | end_usec += 1000000; | |||
| 248 | d->tv_sec--; | |||
| 249 | } | |||
| 250 | d->tv_usec = end_usec - start->tv_usec; | |||
| 251 | #endif | |||
| 252 | } | |||
| 253 | #endif /* BIND8_STATS */ | |||
| 254 | ||||
| 255 | static int | |||
| 256 | remote_setup_ctx(struct daemon_remote* rc, struct nsd_options* cfg) | |||
| 257 | { | |||
| 258 | char* s_cert = cfg->server_cert_file; | |||
| 259 | char* s_key = cfg->server_key_file; | |||
| 260 | rc->ctx = server_tls_ctx_setup(s_key, s_cert, s_cert); | |||
| 261 | if(!rc->ctx) { | |||
| 262 | log_msg(LOG_ERR3, "could not setup remote control TLS context"); | |||
| 263 | return 0; | |||
| 264 | } | |||
| 265 | return 1; | |||
| 266 | } | |||
| 267 | ||||
| 268 | struct daemon_remote* | |||
| 269 | daemon_remote_create(struct nsd_options* cfg) | |||
| 270 | { | |||
| 271 | struct daemon_remote* rc = (struct daemon_remote*)xalloc_zero( | |||
| 272 | sizeof(*rc)); | |||
| 273 | rc->max_active = 10; | |||
| 274 | assert(cfg->control_enable)((void)0); | |||
| 275 | ||||
| 276 | if(options_remote_is_address(cfg)) { | |||
| ||||
| 277 | if(!remote_setup_ctx(rc, cfg)) { | |||
| 278 | daemon_remote_delete(rc); | |||
| 279 | return NULL((void*)0); | |||
| 280 | } | |||
| 281 | rc->use_cert = 1; | |||
| 282 | } else { | |||
| 283 | struct ip_address_option* o; | |||
| 284 | rc->ctx = NULL((void*)0); | |||
| 285 | rc->use_cert = 0; | |||
| 286 | for(o = cfg->control_interface; o; o = o->next) { | |||
| 287 | if(o->address && o->address[0] != '/') | |||
| 288 | log_msg(LOG_WARNING4, "control-interface %s is not using TLS, but plain transfer, because first control-interface in config file is a local socket (starts with a /).", o->address); | |||
| 289 | } | |||
| 290 | } | |||
| 291 | ||||
| 292 | /* and try to open the ports */ | |||
| 293 | if(!daemon_remote_open_ports(rc, cfg)) { | |||
| 294 | log_msg(LOG_ERR3, "could not open remote control port"); | |||
| 295 | daemon_remote_delete(rc); | |||
| 296 | return NULL((void*)0); | |||
| 297 | } | |||
| 298 | ||||
| 299 | if(gettimeofday(&rc->boot_time, NULL((void*)0)) == -1) | |||
| 300 | log_msg(LOG_ERR3, "gettimeofday: %s", strerror(errno(*__errno()))); | |||
| 301 | rc->stats_time = rc->boot_time; | |||
| 302 | ||||
| 303 | return rc; | |||
| 304 | } | |||
| 305 | ||||
| 306 | void daemon_remote_close(struct daemon_remote* rc) | |||
| 307 | { | |||
| 308 | struct rc_state* p, *np; | |||
| 309 | struct acceptlist* h, *nh; | |||
| 310 | if(!rc) return; | |||
| 311 | ||||
| 312 | /* close listen sockets */ | |||
| 313 | h = rc->accept_list; | |||
| 314 | while(h) { | |||
| 315 | nh = h->next; | |||
| 316 | if(h->event_added) | |||
| 317 | event_del(&h->c); | |||
| 318 | close(h->c.ev_fd); | |||
| 319 | free(h->ident); | |||
| 320 | free(h); | |||
| 321 | h = nh; | |||
| 322 | } | |||
| 323 | rc->accept_list = NULL((void*)0); | |||
| 324 | ||||
| 325 | /* close busy connection sockets */ | |||
| 326 | p = rc->busy_list; | |||
| 327 | while(p) { | |||
| 328 | np = p->next; | |||
| 329 | if(p->event_added) | |||
| 330 | event_del(&p->c); | |||
| 331 | if(p->ssl) | |||
| 332 | SSL_free(p->ssl); | |||
| 333 | close(p->c.ev_fd); | |||
| 334 | free(p); | |||
| 335 | p = np; | |||
| 336 | } | |||
| 337 | rc->busy_list = NULL((void*)0); | |||
| 338 | rc->active = 0; | |||
| 339 | } | |||
| 340 | ||||
| 341 | void daemon_remote_delete(struct daemon_remote* rc) | |||
| 342 | { | |||
| 343 | if(!rc) return; | |||
| 344 | daemon_remote_close(rc); | |||
| 345 | if(rc->ctx) { | |||
| 346 | SSL_CTX_free(rc->ctx); | |||
| 347 | } | |||
| 348 | free(rc); | |||
| 349 | } | |||
| 350 | ||||
| 351 | static int | |||
| 352 | create_tcp_accept_sock(struct addrinfo* addr, int* noproto) | |||
| 353 | { | |||
| 354 | #if defined(SO_REUSEADDR0x0004) || (defined(INET6) && (defined(IPV6_V6ONLY27) || defined(IPV6_USE_MIN_MTU42) || defined(IPV6_MTU))) | |||
| 355 | int on = 1; | |||
| 356 | #endif | |||
| 357 | int s; | |||
| 358 | *noproto = 0; | |||
| 359 | if ((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) { | |||
| 360 | #if defined(INET6) | |||
| 361 | if (addr->ai_family == AF_INET624 && | |||
| 362 | errno(*__errno()) == EAFNOSUPPORT47) { | |||
| 363 | *noproto = 1; | |||
| 364 | log_msg(LOG_WARNING4, "fallback to TCP4, no IPv6: not supported"); | |||
| 365 | return -1; | |||
| 366 | } | |||
| 367 | #endif /* INET6 */ | |||
| 368 | log_msg(LOG_ERR3, "can't create a socket: %s", strerror(errno(*__errno()))); | |||
| 369 | return -1; | |||
| 370 | } | |||
| 371 | #ifdef SO_REUSEADDR0x0004 | |||
| 372 | if (setsockopt(s, SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &on, sizeof(on)) < 0) { | |||
| 373 | log_msg(LOG_ERR3, "setsockopt(..., SO_REUSEADDR, ...) failed: %s", strerror(errno(*__errno()))); | |||
| 374 | } | |||
| 375 | #endif /* SO_REUSEADDR */ | |||
| 376 | #if defined(INET6) && defined(IPV6_V6ONLY27) | |||
| 377 | if (addr->ai_family == AF_INET624 && | |||
| 378 | setsockopt(s, IPPROTO_IPV641, IPV6_V6ONLY27, &on, sizeof(on)) < 0) | |||
| 379 | { | |||
| 380 | log_msg(LOG_ERR3, "setsockopt(..., IPV6_V6ONLY, ...) failed: %s", strerror(errno(*__errno()))); | |||
| 381 | close(s); | |||
| 382 | return -1; | |||
| 383 | } | |||
| 384 | #endif | |||
| 385 | /* set it nonblocking */ | |||
| 386 | /* (StevensUNP p463), if tcp listening socket is blocking, then | |||
| 387 | it may block in accept, even if select() says readable. */ | |||
| 388 | if (fcntl(s, F_SETFL4, O_NONBLOCK0x0004) == -1) { | |||
| 389 | log_msg(LOG_ERR3, "cannot fcntl tcp: %s", strerror(errno(*__errno()))); | |||
| 390 | } | |||
| 391 | /* Bind it... */ | |||
| 392 | if (bind(s, (struct sockaddr *)addr->ai_addr, addr->ai_addrlen) != 0) { | |||
| 393 | log_msg(LOG_ERR3, "can't bind tcp socket: %s", strerror(errno(*__errno()))); | |||
| 394 | close(s); | |||
| 395 | return -1; | |||
| 396 | } | |||
| 397 | /* Listen to it... */ | |||
| 398 | if (listen(s, TCP_BACKLOG_REMOTE16) == -1) { | |||
| 399 | log_msg(LOG_ERR3, "can't listen: %s", strerror(errno(*__errno()))); | |||
| 400 | close(s); | |||
| 401 | return -1; | |||
| 402 | } | |||
| 403 | return s; | |||
| 404 | } | |||
| 405 | ||||
| 406 | /** | |||
| 407 | * Add and open a new control port | |||
| 408 | * @param rc: rc with result list. | |||
| 409 | * @param ip: ip str | |||
| 410 | * @param nr: port nr | |||
| 411 | * @param noproto_is_err: if lack of protocol support is an error. | |||
| 412 | * @return false on failure. | |||
| 413 | */ | |||
| 414 | static int | |||
| 415 | add_open(struct daemon_remote* rc, struct nsd_options* cfg, const char* ip, | |||
| 416 | int nr, int noproto_is_err) | |||
| 417 | { | |||
| 418 | struct addrinfo hints; | |||
| 419 | struct addrinfo* res; | |||
| 420 | struct acceptlist* hl; | |||
| 421 | int noproto = 0; | |||
| 422 | int fd, r; | |||
| 423 | char port[15]; | |||
| 424 | snprintf(port, sizeof(port), "%d", nr); | |||
| 425 | port[sizeof(port)-1]=0; | |||
| 426 | memset(&hints, 0, sizeof(hints)); | |||
| 427 | assert(ip)((void)0); | |||
| 428 | ||||
| 429 | if(ip[0] == '/') { | |||
| ||||
| 430 | /* This looks like a local socket */ | |||
| 431 | fd = create_local_accept_sock(ip, &noproto); | |||
| 432 | /* | |||
| 433 | * Change socket ownership and permissions so users other | |||
| 434 | * than root can access it provided they are in the same | |||
| 435 | * group as the user we run as. | |||
| 436 | */ | |||
| 437 | if(fd != -1) { | |||
| 438 | #ifdef HAVE_CHOWN1 | |||
| 439 | if (cfg->username && cfg->username[0] && | |||
| 440 | nsd.uid != (uid_t)-1) { | |||
| 441 | if(chown(ip, nsd.uid, nsd.gid) == -1) | |||
| 442 | VERBOSITY(2, (LOG_INFO, "cannot chown %u.%u %s: %s",do { if ((2) <= verbosity) { log_msg (6, "cannot chown %u.%u %s: %s" , (unsigned)nsd.uid, (unsigned)nsd.gid, ip, strerror((*__errno ()))) ; } } while (0) | |||
| 443 | (unsigned)nsd.uid, (unsigned)nsd.gid,do { if ((2) <= verbosity) { log_msg (6, "cannot chown %u.%u %s: %s" , (unsigned)nsd.uid, (unsigned)nsd.gid, ip, strerror((*__errno ()))) ; } } while (0) | |||
| 444 | ip, strerror(errno)))do { if ((2) <= verbosity) { log_msg (6, "cannot chown %u.%u %s: %s" , (unsigned)nsd.uid, (unsigned)nsd.gid, ip, strerror((*__errno ()))) ; } } while (0); | |||
| 445 | } | |||
| 446 | if(chmod(ip, (mode_t)(S_IRUSR0000400 | S_IWUSR0000200 | S_IRGRP0000040 | S_IWGRP0000020)) == -1) { | |||
| 447 | VERBOSITY(3, (LOG_INFO, "cannot chmod control socket %s: %s", ip, strerror(errno)))do { if ((3) <= verbosity) { log_msg (6, "cannot chmod control socket %s: %s" , ip, strerror((*__errno()))) ; } } while (0); | |||
| 448 | } | |||
| 449 | #else | |||
| 450 | (void)cfg; | |||
| 451 | #endif | |||
| 452 | } | |||
| 453 | } else { | |||
| 454 | hints.ai_socktype = SOCK_STREAM1; | |||
| 455 | hints.ai_flags = AI_PASSIVE1 | AI_NUMERICHOST4; | |||
| 456 | /* if we had no interface ip name, "default" is what we | |||
| 457 | * would do getaddrinfo for. */ | |||
| 458 | if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) { | |||
| 459 | log_msg(LOG_ERR3, "control interface %s:%s getaddrinfo: %s %s", | |||
| 460 | ip, port, gai_strerror(r), | |||
| 461 | #ifdef EAI_SYSTEM-11 | |||
| 462 | r==EAI_SYSTEM-11?(char*)strerror(errno(*__errno())):"" | |||
| 463 | #else | |||
| 464 | "" | |||
| 465 | #endif | |||
| 466 | ); | |||
| 467 | return 0; | |||
| 468 | } | |||
| 469 | ||||
| 470 | /* open fd */ | |||
| 471 | fd = create_tcp_accept_sock(res, &noproto); | |||
| 472 | freeaddrinfo(res); | |||
| 473 | } | |||
| 474 | ||||
| 475 | if(fd == -1 && noproto) { | |||
| 476 | if(!noproto_is_err) | |||
| 477 | return 1; /* return success, but do nothing */ | |||
| 478 | log_msg(LOG_ERR3, "cannot open control interface %s %d : " | |||
| 479 | "protocol not supported", ip, nr); | |||
| 480 | return 0; | |||
| 481 | } | |||
| 482 | if(fd == -1) { | |||
| 483 | log_msg(LOG_ERR3, "cannot open control interface %s %d", ip, nr); | |||
| 484 | return 0; | |||
| 485 | } | |||
| 486 | ||||
| 487 | /* alloc */ | |||
| 488 | hl = (struct acceptlist*)xalloc_zero(sizeof(*hl)); | |||
| 489 | hl->rc = rc; | |||
| 490 | hl->ident = strdup(ip); | |||
| 491 | if(!hl->ident) { | |||
| 492 | log_msg(LOG_ERR3, "malloc failure"); | |||
| 493 | close(fd); | |||
| 494 | free(hl); | |||
| 495 | return 0; | |||
| 496 | } | |||
| 497 | hl->next = rc->accept_list; | |||
| 498 | rc->accept_list = hl; | |||
| 499 | ||||
| 500 | hl->c.ev_fd = fd; | |||
| 501 | hl->event_added = 0; | |||
| 502 | return 1; | |||
| 503 | } | |||
| 504 | ||||
| 505 | int | |||
| 506 | daemon_remote_open_ports(struct daemon_remote* rc, struct nsd_options* cfg) | |||
| 507 | { | |||
| 508 | assert(cfg->control_enable && cfg->control_port)((void)0); | |||
| 509 | if(cfg->control_interface
| |||
| 510 | ip_address_option_type* p; | |||
| 511 | for(p = cfg->control_interface; p; p = p->next) { | |||
| 512 | if(!add_open(rc, cfg, p->address, cfg->control_port, 1)) { | |||
| 513 | return 0; | |||
| 514 | } | |||
| 515 | } | |||
| 516 | } else { | |||
| 517 | /* defaults */ | |||
| 518 | if(cfg->do_ip6 && !add_open(rc, cfg, "::1", cfg->control_port, 0)) { | |||
| 519 | return 0; | |||
| 520 | } | |||
| 521 | if(cfg->do_ip4 && | |||
| 522 | !add_open(rc, cfg, "127.0.0.1", cfg->control_port, 1)) { | |||
| 523 | return 0; | |||
| 524 | } | |||
| 525 | } | |||
| 526 | return 1; | |||
| 527 | } | |||
| 528 | ||||
| 529 | void | |||
| 530 | daemon_remote_attach(struct daemon_remote* rc, struct xfrd_state* xfrd) | |||
| 531 | { | |||
| 532 | int fd; | |||
| 533 | struct acceptlist* p; | |||
| 534 | if(!rc) return; | |||
| 535 | rc->xfrd = xfrd; | |||
| 536 | for(p = rc->accept_list; p; p = p->next) { | |||
| 537 | /* add event */ | |||
| 538 | fd = p->c.ev_fd; | |||
| 539 | memset(&p->c, 0, sizeof(p->c)); | |||
| 540 | event_set(&p->c, fd, EV_PERSIST0x10|EV_READ0x02, remote_accept_callback, | |||
| 541 | p); | |||
| 542 | if(event_base_set(xfrd->event_base, &p->c) != 0) | |||
| 543 | log_msg(LOG_ERR3, "remote: cannot set event_base"); | |||
| 544 | if(event_add(&p->c, NULL((void*)0)) != 0) | |||
| 545 | log_msg(LOG_ERR3, "remote: cannot add event"); | |||
| 546 | p->event_added = 1; | |||
| 547 | } | |||
| 548 | } | |||
| 549 | ||||
| 550 | static void | |||
| 551 | remote_accept_callback(int fd, short event, void* arg) | |||
| 552 | { | |||
| 553 | struct acceptlist *hl = (struct acceptlist*)arg; | |||
| 554 | struct daemon_remote *rc = hl->rc; | |||
| 555 | #ifdef INET6 | |||
| 556 | struct sockaddr_storage addr; | |||
| 557 | #else | |||
| 558 | struct sockaddr_in addr; | |||
| 559 | #endif | |||
| 560 | socklen_t addrlen; | |||
| 561 | int newfd; | |||
| 562 | struct rc_state* n; | |||
| 563 | ||||
| 564 | if (!(event & EV_READ0x02)) { | |||
| 565 | return; | |||
| 566 | } | |||
| 567 | ||||
| 568 | /* perform the accept */ | |||
| 569 | addrlen = sizeof(addr); | |||
| 570 | #ifndef HAVE_ACCEPT41 | |||
| 571 | newfd = accept(fd, (struct sockaddr*)&addr, &addrlen); | |||
| 572 | #else | |||
| 573 | newfd = accept4(fd, (struct sockaddr*)&addr, &addrlen, SOCK_NONBLOCK0x4000); | |||
| 574 | #endif | |||
| 575 | if(newfd == -1) { | |||
| 576 | if ( errno(*__errno()) != EINTR4 | |||
| 577 | && errno(*__errno()) != EWOULDBLOCK35 | |||
| 578 | #ifdef ECONNABORTED53 | |||
| 579 | && errno(*__errno()) != ECONNABORTED53 | |||
| 580 | #endif /* ECONNABORTED */ | |||
| 581 | #ifdef EPROTO95 | |||
| 582 | && errno(*__errno()) != EPROTO95 | |||
| 583 | #endif /* EPROTO */ | |||
| 584 | ) { | |||
| 585 | log_msg(LOG_ERR3, "accept failed: %s", strerror(errno(*__errno()))); | |||
| 586 | } | |||
| 587 | return; | |||
| 588 | } | |||
| 589 | ||||
| 590 | /* create new commpoint unless we are servicing already */ | |||
| 591 | if(rc->active >= rc->max_active) { | |||
| 592 | log_msg(LOG_WARNING4, "drop incoming remote control: " | |||
| 593 | "too many connections"); | |||
| 594 | close_exit: | |||
| 595 | close(newfd); | |||
| 596 | return; | |||
| 597 | } | |||
| 598 | ||||
| 599 | #ifndef HAVE_ACCEPT41 | |||
| 600 | if (fcntl(newfd, F_SETFL4, O_NONBLOCK0x0004) == -1) { | |||
| 601 | log_msg(LOG_ERR3, "fcntl failed: %s", strerror(errno(*__errno()))); | |||
| 602 | goto close_exit; | |||
| 603 | } | |||
| 604 | #endif | |||
| 605 | ||||
| 606 | /* setup state to service the remote control command */ | |||
| 607 | n = (struct rc_state*)calloc(1, sizeof(*n)); | |||
| 608 | if(!n) { | |||
| 609 | log_msg(LOG_ERR3, "out of memory"); | |||
| 610 | goto close_exit; | |||
| 611 | } | |||
| 612 | ||||
| 613 | n->tval.tv_sec = REMOTE_CONTROL_TCP_TIMEOUT120; | |||
| 614 | n->tval.tv_usec = 0L; | |||
| 615 | n->fd = newfd; | |||
| 616 | ||||
| 617 | memset(&n->c, 0, sizeof(n->c)); | |||
| 618 | event_set(&n->c, newfd, EV_PERSIST0x10|EV_TIMEOUT0x01|EV_READ0x02, | |||
| 619 | remote_control_callback, n); | |||
| 620 | if(event_base_set(xfrd->event_base, &n->c) != 0) { | |||
| 621 | log_msg(LOG_ERR3, "remote_accept: cannot set event_base"); | |||
| 622 | free(n); | |||
| 623 | goto close_exit; | |||
| 624 | } | |||
| 625 | if(event_add(&n->c, &n->tval) != 0) { | |||
| 626 | log_msg(LOG_ERR3, "remote_accept: cannot add event"); | |||
| 627 | free(n); | |||
| 628 | goto close_exit; | |||
| 629 | } | |||
| 630 | n->event_added = 1; | |||
| 631 | ||||
| 632 | if(2 <= verbosity) { | |||
| 633 | if(hl->ident && hl->ident[0] == '/') { | |||
| 634 | VERBOSITY(2, (LOG_INFO, "new control connection from %s", hl->ident))do { if ((2) <= verbosity) { log_msg (6, "new control connection from %s" , hl->ident) ; } } while (0); | |||
| 635 | } else { | |||
| 636 | char s[128]; | |||
| 637 | addr2str(&addr, s, sizeof(s)); | |||
| 638 | VERBOSITY(2, (LOG_INFO, "new control connection from %s", s))do { if ((2) <= verbosity) { log_msg (6, "new control connection from %s" , s) ; } } while (0); | |||
| 639 | } | |||
| 640 | } | |||
| 641 | ||||
| 642 | if(rc->ctx) { | |||
| 643 | n->shake_state = rc_hs_read; | |||
| 644 | n->ssl = SSL_new(rc->ctx); | |||
| 645 | if(!n->ssl) { | |||
| 646 | log_crypto_err("could not SSL_new"); | |||
| 647 | event_del(&n->c); | |||
| 648 | free(n); | |||
| 649 | goto close_exit; | |||
| 650 | } | |||
| 651 | SSL_set_accept_state(n->ssl); | |||
| 652 | (void)SSL_set_mode(n->ssl, SSL_MODE_AUTO_RETRY)SSL_ctrl((n->ssl),33,(0x00000004L),((void*)0)); | |||
| 653 | if(!SSL_set_fd(n->ssl, newfd)) { | |||
| 654 | log_crypto_err("could not SSL_set_fd"); | |||
| 655 | event_del(&n->c); | |||
| 656 | SSL_free(n->ssl); | |||
| 657 | free(n); | |||
| 658 | goto close_exit; | |||
| 659 | } | |||
| 660 | } else { | |||
| 661 | n->ssl = NULL((void*)0); | |||
| 662 | } | |||
| 663 | ||||
| 664 | n->rc = rc; | |||
| 665 | n->stats_next = NULL((void*)0); | |||
| 666 | n->in_stats_list = 0; | |||
| 667 | n->prev = NULL((void*)0); | |||
| 668 | n->next = rc->busy_list; | |||
| 669 | if(n->next) n->next->prev = n; | |||
| 670 | rc->busy_list = n; | |||
| 671 | rc->active ++; | |||
| 672 | ||||
| 673 | /* perform the first nonblocking read already, for windows, | |||
| 674 | * so it can return wouldblock. could be faster too. */ | |||
| 675 | remote_control_callback(newfd, EV_READ0x02, n); | |||
| 676 | } | |||
| 677 | ||||
| 678 | /** delete from list */ | |||
| 679 | static void | |||
| 680 | state_list_remove_elem(struct rc_state** list, struct rc_state* todel) | |||
| 681 | { | |||
| 682 | if(todel->prev) todel->prev->next = todel->next; | |||
| 683 | else *list = todel->next; | |||
| 684 | if(todel->next) todel->next->prev = todel->prev; | |||
| 685 | } | |||
| 686 | ||||
| 687 | /** delete from stats list */ | |||
| 688 | static void | |||
| 689 | stats_list_remove_elem(struct rc_state** list, struct rc_state* todel) | |||
| 690 | { | |||
| 691 | struct rc_state* prev = NULL((void*)0); | |||
| 692 | struct rc_state* n = *list; | |||
| 693 | while(n) { | |||
| 694 | /* delete this one? */ | |||
| 695 | if(n == todel) { | |||
| 696 | if(prev) prev->next = n->next; | |||
| 697 | else (*list) = n->next; | |||
| 698 | /* go on and delete further elements */ | |||
| 699 | /* prev = prev; */ | |||
| 700 | n = n->next; | |||
| 701 | continue; | |||
| 702 | } | |||
| 703 | ||||
| 704 | /* go to the next element */ | |||
| 705 | prev = n; | |||
| 706 | n = n->next; | |||
| 707 | } | |||
| 708 | } | |||
| 709 | ||||
| 710 | /** decrease active count and remove commpoint from busy list */ | |||
| 711 | static void | |||
| 712 | clean_point(struct daemon_remote* rc, struct rc_state* s) | |||
| 713 | { | |||
| 714 | if(s->in_stats_list) | |||
| 715 | stats_list_remove_elem(&rc->stats_list, s); | |||
| 716 | state_list_remove_elem(&rc->busy_list, s); | |||
| 717 | rc->active --; | |||
| 718 | if(s->event_added) | |||
| 719 | event_del(&s->c); | |||
| 720 | if(s->ssl) { | |||
| 721 | SSL_shutdown(s->ssl); | |||
| 722 | SSL_free(s->ssl); | |||
| 723 | } | |||
| 724 | close(s->c.ev_fd); | |||
| 725 | free(s); | |||
| 726 | } | |||
| 727 | ||||
| 728 | static int | |||
| 729 | ssl_print_text(RES* res, const char* text) | |||
| 730 | { | |||
| 731 | int r; | |||
| 732 | if(!res) | |||
| 733 | return 0; | |||
| 734 | if(res->ssl) { | |||
| 735 | ERR_clear_error(); | |||
| 736 | if((r=SSL_write(res->ssl, text, (int)strlen(text))) <= 0) { | |||
| 737 | if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN6) { | |||
| 738 | VERBOSITY(2, (LOG_WARNING, "in SSL_write, peer "do { if ((2) <= verbosity) { log_msg (4, "in SSL_write, peer " "closed connection") ; } } while (0) | |||
| 739 | "closed connection"))do { if ((2) <= verbosity) { log_msg (4, "in SSL_write, peer " "closed connection") ; } } while (0); | |||
| 740 | return 0; | |||
| 741 | } | |||
| 742 | log_crypto_err("could not SSL_write"); | |||
| 743 | return 0; | |||
| 744 | } | |||
| 745 | } else { | |||
| 746 | if(write_socket(res->fd, text, strlen(text)) <= 0) { | |||
| 747 | log_msg(LOG_ERR3, "could not write: %s", | |||
| 748 | strerror(errno(*__errno()))); | |||
| 749 | return 0; | |||
| 750 | } | |||
| 751 | } | |||
| 752 | return 1; | |||
| 753 | } | |||
| 754 | ||||
| 755 | /** print text over the ssl connection */ | |||
| 756 | static int | |||
| 757 | ssl_print_vmsg(RES* ssl, const char* format, va_list args) | |||
| 758 | { | |||
| 759 | char msg[1024]; | |||
| 760 | vsnprintf(msg, sizeof(msg), format, args); | |||
| 761 | return ssl_print_text(ssl, msg); | |||
| 762 | } | |||
| 763 | ||||
| 764 | /** printf style printing to the ssl connection */ | |||
| 765 | static int | |||
| 766 | ssl_printf(RES* ssl, const char* format, ...) | |||
| 767 | { | |||
| 768 | va_list args; | |||
| 769 | int ret; | |||
| 770 | va_start(args, format)__builtin_va_start(args, format); | |||
| 771 | ret = ssl_print_vmsg(ssl, format, args); | |||
| 772 | va_end(args)__builtin_va_end(args); | |||
| 773 | return ret; | |||
| 774 | } | |||
| 775 | ||||
| 776 | static int | |||
| 777 | ssl_read_line(RES* res, char* buf, size_t max) | |||
| 778 | { | |||
| 779 | int r; | |||
| 780 | size_t len = 0; | |||
| 781 | if(!res) | |||
| 782 | return 0; | |||
| 783 | while(len < max) { | |||
| 784 | buf[len] = 0; /* terminate for safety and please checkers */ | |||
| 785 | /* this byte is written if we read a byte from the input */ | |||
| 786 | if(res->ssl) { | |||
| 787 | ERR_clear_error(); | |||
| 788 | if((r=SSL_read(res->ssl, buf+len, 1)) <= 0) { | |||
| 789 | if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN6) { | |||
| 790 | buf[len] = 0; | |||
| 791 | return 1; | |||
| 792 | } | |||
| 793 | log_crypto_err("could not SSL_read"); | |||
| 794 | return 0; | |||
| 795 | } | |||
| 796 | } else { | |||
| 797 | while(1) { | |||
| 798 | ssize_t rr = read(res->fd, buf+len, 1); | |||
| 799 | if(rr <= 0) { | |||
| 800 | if(rr == 0) { | |||
| 801 | buf[len] = 0; | |||
| 802 | return 1; | |||
| 803 | } | |||
| 804 | if(errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35) | |||
| 805 | continue; | |||
| 806 | log_msg(LOG_ERR3, "could not read: %s", | |||
| 807 | strerror(errno(*__errno()))); | |||
| 808 | return 0; | |||
| 809 | } | |||
| 810 | break; | |||
| 811 | } | |||
| 812 | } | |||
| 813 | if(buf[len] == '\n') { | |||
| 814 | /* return string without \n */ | |||
| 815 | buf[len] = 0; | |||
| 816 | return 1; | |||
| 817 | } | |||
| 818 | len++; | |||
| 819 | } | |||
| 820 | buf[max-1] = 0; | |||
| 821 | log_msg(LOG_ERR3, "control line too long (%d): %s", (int)max, buf); | |||
| 822 | return 0; | |||
| 823 | } | |||
| 824 | ||||
| 825 | /** skip whitespace, return new pointer into string */ | |||
| 826 | static char* | |||
| 827 | skipwhite(char* str) | |||
| 828 | { | |||
| 829 | /* EOS \0 is not a space */ | |||
| 830 | while( isspace((unsigned char)*str) ) | |||
| 831 | str++; | |||
| 832 | return str; | |||
| 833 | } | |||
| 834 | ||||
| 835 | /** send the OK to the control client */ | |||
| 836 | static void | |||
| 837 | send_ok(RES* ssl) | |||
| 838 | { | |||
| 839 | (void)ssl_printf(ssl, "ok\n"); | |||
| 840 | } | |||
| 841 | ||||
| 842 | /** get zone argument (if any) or NULL, false on error */ | |||
| 843 | static int | |||
| 844 | get_zone_arg(RES* ssl, xfrd_state_type* xfrd, char* arg, | |||
| 845 | struct zone_options** zo) | |||
| 846 | { | |||
| 847 | const dname_type* dname; | |||
| 848 | if(!arg[0]) { | |||
| 849 | /* no argument present, return NULL */ | |||
| 850 | *zo = NULL((void*)0); | |||
| 851 | return 1; | |||
| 852 | } | |||
| 853 | dname = dname_parse(xfrd->region, arg); | |||
| 854 | if(!dname) { | |||
| 855 | (void)ssl_printf(ssl, "error cannot parse zone name '%s'\n", arg); | |||
| 856 | *zo = NULL((void*)0); | |||
| 857 | return 0; | |||
| 858 | } | |||
| 859 | *zo = zone_options_find(xfrd->nsd->options, dname); | |||
| 860 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 861 | if(!*zo) { | |||
| 862 | (void)ssl_printf(ssl, "error zone %s not configured\n", arg); | |||
| 863 | return 0; | |||
| 864 | } | |||
| 865 | return 1; | |||
| 866 | } | |||
| 867 | ||||
| 868 | /** do the stop command */ | |||
| 869 | static void | |||
| 870 | do_stop(RES* ssl, xfrd_state_type* xfrd) | |||
| 871 | { | |||
| 872 | xfrd->need_to_send_shutdown = 1; | |||
| 873 | ||||
| 874 | if(!(xfrd->ipc_handler_flags&EV_WRITE0x04)) { | |||
| 875 | ipc_xfrd_set_listening(xfrd, EV_PERSIST0x10|EV_READ0x02|EV_WRITE0x04); | |||
| 876 | } | |||
| 877 | ||||
| 878 | send_ok(ssl); | |||
| 879 | } | |||
| 880 | ||||
| 881 | /** do the log_reopen command, it only needs reload_now */ | |||
| 882 | static void | |||
| 883 | do_log_reopen(RES* ssl, xfrd_state_type* xfrd) | |||
| 884 | { | |||
| 885 | xfrd_set_reload_now(xfrd); | |||
| 886 | send_ok(ssl); | |||
| 887 | } | |||
| 888 | ||||
| 889 | /** do the reload command */ | |||
| 890 | static void | |||
| 891 | do_reload(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 892 | { | |||
| 893 | struct zone_options* zo; | |||
| 894 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 895 | return; | |||
| 896 | task_new_check_zonefiles(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 897 | xfrd->last_task, zo?(const dname_type*)zo->node.key:NULL((void*)0)); | |||
| 898 | xfrd_set_reload_now(xfrd); | |||
| 899 | send_ok(ssl); | |||
| 900 | } | |||
| 901 | ||||
| 902 | /** do the write command */ | |||
| 903 | static void | |||
| 904 | do_write(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 905 | { | |||
| 906 | struct zone_options* zo; | |||
| 907 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 908 | return; | |||
| 909 | task_new_write_zonefiles(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 910 | xfrd->last_task, zo?(const dname_type*)zo->node.key:NULL((void*)0)); | |||
| 911 | xfrd_set_reload_now(xfrd); | |||
| 912 | send_ok(ssl); | |||
| 913 | } | |||
| 914 | ||||
| 915 | /** do the notify command */ | |||
| 916 | static void | |||
| 917 | do_notify(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 918 | { | |||
| 919 | struct zone_options* zo; | |||
| 920 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 921 | return; | |||
| 922 | if(zo) { | |||
| 923 | struct notify_zone* n = (struct notify_zone*)rbtree_search( | |||
| 924 | xfrd->notify_zones, (const dname_type*)zo->node.key); | |||
| 925 | if(n) { | |||
| 926 | xfrd_notify_start(n, xfrd); | |||
| 927 | send_ok(ssl); | |||
| 928 | } else { | |||
| 929 | (void)ssl_printf(ssl, "error zone does not have notify\n"); | |||
| 930 | } | |||
| 931 | } else { | |||
| 932 | struct notify_zone* n; | |||
| 933 | RBTREE_FOR(n, struct notify_zone*, xfrd->notify_zones)for(n=(struct notify_zone*)rbtree_first(xfrd->notify_zones ); (rbnode_type*)n != &rbtree_null_node; n = (struct notify_zone *)rbtree_next((rbnode_type*)n)) { | |||
| 934 | xfrd_notify_start(n, xfrd); | |||
| 935 | } | |||
| 936 | send_ok(ssl); | |||
| 937 | } | |||
| 938 | } | |||
| 939 | ||||
| 940 | /** do the transfer command */ | |||
| 941 | static void | |||
| 942 | do_transfer(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 943 | { | |||
| 944 | struct zone_options* zo; | |||
| 945 | xfrd_zone_type* zone; | |||
| 946 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 947 | return; | |||
| 948 | if(zo) { | |||
| 949 | zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, (const | |||
| 950 | dname_type*)zo->node.key); | |||
| 951 | if(zone) { | |||
| 952 | xfrd_handle_notify_and_start_xfr(zone, NULL((void*)0)); | |||
| 953 | send_ok(ssl); | |||
| 954 | } else { | |||
| 955 | (void)ssl_printf(ssl, "error zone not slave\n"); | |||
| 956 | } | |||
| 957 | } else { | |||
| 958 | RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)for(zone=(xfrd_zone_type*)rbtree_first(xfrd->zones); (rbnode_type *)zone != &rbtree_null_node; zone = (xfrd_zone_type*)rbtree_next ((rbnode_type*)zone)) { | |||
| 959 | xfrd_handle_notify_and_start_xfr(zone, NULL((void*)0)); | |||
| 960 | } | |||
| 961 | (void)ssl_printf(ssl, "ok, %lu zones\n", (unsigned long)xfrd->zones->count); | |||
| 962 | } | |||
| 963 | } | |||
| 964 | ||||
| 965 | /** force transfer a zone */ | |||
| 966 | static void | |||
| 967 | force_transfer_zone(xfrd_zone_type* zone) | |||
| 968 | { | |||
| 969 | /* if in TCP transaction, stop it immediately. */ | |||
| 970 | if(zone->tcp_conn != -1) | |||
| 971 | xfrd_tcp_release(xfrd->tcp_set, zone); | |||
| 972 | else if(zone->zone_handler.ev_fd != -1) | |||
| 973 | xfrd_udp_release(zone); | |||
| 974 | /* pretend we not longer have it and force any | |||
| 975 | * zone to be downloaded (even same serial, w AXFR) */ | |||
| 976 | zone->soa_disk_acquired = 0; | |||
| 977 | zone->soa_nsd_acquired = 0; | |||
| 978 | xfrd_handle_notify_and_start_xfr(zone, NULL((void*)0)); | |||
| 979 | } | |||
| 980 | ||||
| 981 | /** do the force transfer command */ | |||
| 982 | static void | |||
| 983 | do_force_transfer(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 984 | { | |||
| 985 | struct zone_options* zo; | |||
| 986 | xfrd_zone_type* zone; | |||
| 987 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 988 | return; | |||
| 989 | if(zo) { | |||
| 990 | zone = (xfrd_zone_type*)rbtree_search(xfrd->zones, (const | |||
| 991 | dname_type*)zo->node.key); | |||
| 992 | if(zone) { | |||
| 993 | force_transfer_zone(zone); | |||
| 994 | send_ok(ssl); | |||
| 995 | } else { | |||
| 996 | (void)ssl_printf(ssl, "error zone not slave\n"); | |||
| 997 | } | |||
| 998 | } else { | |||
| 999 | RBTREE_FOR(zone, xfrd_zone_type*, xfrd->zones)for(zone=(xfrd_zone_type*)rbtree_first(xfrd->zones); (rbnode_type *)zone != &rbtree_null_node; zone = (xfrd_zone_type*)rbtree_next ((rbnode_type*)zone)) { | |||
| 1000 | force_transfer_zone(zone); | |||
| 1001 | } | |||
| 1002 | (void)ssl_printf(ssl, "ok, %lu zones\n", (unsigned long)xfrd->zones->count); | |||
| 1003 | } | |||
| 1004 | } | |||
| 1005 | ||||
| 1006 | static int | |||
| 1007 | print_soa_status(RES* ssl, const char* str, xfrd_soa_type* soa, time_t acq) | |||
| 1008 | { | |||
| 1009 | if(acq) { | |||
| 1010 | if(!ssl_printf(ssl, " %s: \"%u since %s\"\n", str, | |||
| 1011 | (unsigned)ntohl(soa->serial)(__uint32_t)(__builtin_constant_p(soa->serial) ? (__uint32_t )(((__uint32_t)(soa->serial) & 0xff) << 24 | ((__uint32_t )(soa->serial) & 0xff00) << 8 | ((__uint32_t)(soa ->serial) & 0xff0000) >> 8 | ((__uint32_t)(soa-> serial) & 0xff000000) >> 24) : __swap32md(soa->serial )), xfrd_pretty_time(acq))) | |||
| 1012 | return 0; | |||
| 1013 | } else { | |||
| 1014 | if(!ssl_printf(ssl, " %s: none\n", str)) | |||
| 1015 | return 0; | |||
| 1016 | } | |||
| 1017 | return 1; | |||
| 1018 | } | |||
| 1019 | ||||
| 1020 | /** print zonestatus for one domain */ | |||
| 1021 | static int | |||
| 1022 | print_zonestatus(RES* ssl, xfrd_state_type* xfrd, struct zone_options* zo) | |||
| 1023 | { | |||
| 1024 | xfrd_zone_type* xz = (xfrd_zone_type*)rbtree_search(xfrd->zones, | |||
| 1025 | (const dname_type*)zo->node.key); | |||
| 1026 | struct notify_zone* nz = (struct notify_zone*)rbtree_search( | |||
| 1027 | xfrd->notify_zones, (const dname_type*)zo->node.key); | |||
| 1028 | if(!ssl_printf(ssl, "zone: %s\n", zo->name)) | |||
| 1029 | return 0; | |||
| 1030 | if(!zo->part_of_config) { | |||
| 1031 | if(!ssl_printf(ssl, " pattern: %s\n", zo->pattern->pname)) | |||
| 1032 | return 0; | |||
| 1033 | } | |||
| 1034 | if(nz) { | |||
| 1035 | if(nz->is_waiting) { | |||
| 1036 | if(!ssl_printf(ssl, " notify: \"waiting-for-fd\"\n")) | |||
| 1037 | return 0; | |||
| 1038 | } else if(nz->notify_send_enable || nz->notify_send6_enable) { | |||
| 1039 | int i; | |||
| 1040 | if(!ssl_printf(ssl, " notify: \"send")) | |||
| 1041 | return 0; | |||
| 1042 | for(i=0; i<NOTIFY_CONCURRENT_MAX16; i++) { | |||
| 1043 | if(!nz->pkts[i].dest) continue; | |||
| 1044 | if(!ssl_printf(ssl, " %s", | |||
| 1045 | nz->pkts[i].dest->ip_address_spec)) | |||
| 1046 | return 0; | |||
| 1047 | } | |||
| 1048 | if(!ssl_printf(ssl, " with serial %u\"\n", | |||
| 1049 | (unsigned)ntohl(nz->current_soa->serial)(__uint32_t)(__builtin_constant_p(nz->current_soa->serial ) ? (__uint32_t)(((__uint32_t)(nz->current_soa->serial) & 0xff) << 24 | ((__uint32_t)(nz->current_soa-> serial) & 0xff00) << 8 | ((__uint32_t)(nz->current_soa ->serial) & 0xff0000) >> 8 | ((__uint32_t)(nz-> current_soa->serial) & 0xff000000) >> 24) : __swap32md (nz->current_soa->serial)))) | |||
| 1050 | return 0; | |||
| 1051 | } | |||
| 1052 | } | |||
| 1053 | if(!xz) { | |||
| 1054 | if(!ssl_printf(ssl, " state: master\n")) | |||
| 1055 | return 0; | |||
| 1056 | return 1; | |||
| 1057 | } | |||
| 1058 | if(!ssl_printf(ssl, " state: %s\n", | |||
| 1059 | (xz->state == xfrd_zone_ok)?"ok":( | |||
| 1060 | (xz->state == xfrd_zone_expired)?"expired":"refreshing"))) | |||
| 1061 | return 0; | |||
| 1062 | if(!print_soa_status(ssl, "served-serial", &xz->soa_nsd, | |||
| 1063 | xz->soa_nsd_acquired)) | |||
| 1064 | return 0; | |||
| 1065 | if(!print_soa_status(ssl, "commit-serial", &xz->soa_disk, | |||
| 1066 | xz->soa_disk_acquired)) | |||
| 1067 | return 0; | |||
| 1068 | if(xz->round_num != -1) { | |||
| 1069 | if(!print_soa_status(ssl, "notified-serial", &xz->soa_notified, | |||
| 1070 | xz->soa_notified_acquired)) | |||
| 1071 | return 0; | |||
| 1072 | } else if(xz->event_added) { | |||
| 1073 | if(!ssl_printf(ssl, "\twait: \"%lu sec between attempts\"\n", | |||
| 1074 | (unsigned long)xz->timeout.tv_sec)) | |||
| 1075 | return 0; | |||
| 1076 | } | |||
| 1077 | ||||
| 1078 | /* UDP */ | |||
| 1079 | if(xz->udp_waiting) { | |||
| 1080 | if(!ssl_printf(ssl, " transfer: \"waiting-for-UDP-fd\"\n")) | |||
| 1081 | return 0; | |||
| 1082 | } else if(xz->zone_handler.ev_fd != -1 && xz->tcp_conn == -1) { | |||
| 1083 | if(!ssl_printf(ssl, " transfer: \"sent UDP to %s\"\n", | |||
| 1084 | xz->master->ip_address_spec)) | |||
| 1085 | return 0; | |||
| 1086 | } | |||
| 1087 | ||||
| 1088 | /* TCP */ | |||
| 1089 | if(xz->tcp_waiting) { | |||
| 1090 | if(!ssl_printf(ssl, " transfer: \"waiting-for-TCP-fd\"\n")) | |||
| 1091 | return 0; | |||
| 1092 | } else if(xz->tcp_conn != -1) { | |||
| 1093 | if(!ssl_printf(ssl, " transfer: \"TCP connected to %s\"\n", | |||
| 1094 | xz->master->ip_address_spec)) | |||
| 1095 | return 0; | |||
| 1096 | } | |||
| 1097 | ||||
| 1098 | return 1; | |||
| 1099 | } | |||
| 1100 | ||||
| 1101 | /** do the zonestatus command */ | |||
| 1102 | static void | |||
| 1103 | do_zonestatus(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1104 | { | |||
| 1105 | struct zone_options* zo; | |||
| 1106 | if(!get_zone_arg(ssl, xfrd, arg, &zo)) | |||
| 1107 | return; | |||
| 1108 | if(zo) (void)print_zonestatus(ssl, xfrd, zo); | |||
| 1109 | else { | |||
| 1110 | RBTREE_FOR(zo, struct zone_options*,for(zo=(struct zone_options*)rbtree_first(xfrd->nsd->options ->zone_options); (rbnode_type*)zo != &rbtree_null_node ; zo = (struct zone_options*)rbtree_next((rbnode_type*)zo)) | |||
| 1111 | xfrd->nsd->options->zone_options)for(zo=(struct zone_options*)rbtree_first(xfrd->nsd->options ->zone_options); (rbnode_type*)zo != &rbtree_null_node ; zo = (struct zone_options*)rbtree_next((rbnode_type*)zo)) { | |||
| 1112 | if(!print_zonestatus(ssl, xfrd, zo)) | |||
| 1113 | return; | |||
| 1114 | } | |||
| 1115 | } | |||
| 1116 | } | |||
| 1117 | ||||
| 1118 | /** do the verbosity command */ | |||
| 1119 | static void | |||
| 1120 | do_verbosity(RES* ssl, char* str) | |||
| 1121 | { | |||
| 1122 | int val = atoi(str); | |||
| 1123 | if(strcmp(str, "") == 0) { | |||
| 1124 | (void)ssl_printf(ssl, "verbosity %d\n", verbosity); | |||
| 1125 | return; | |||
| 1126 | } | |||
| 1127 | if(val == 0 && strcmp(str, "0") != 0) { | |||
| 1128 | (void)ssl_printf(ssl, "error in verbosity number syntax: %s\n", str); | |||
| 1129 | return; | |||
| 1130 | } | |||
| 1131 | verbosity = val; | |||
| 1132 | task_new_set_verbosity(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1133 | xfrd->last_task, val); | |||
| 1134 | xfrd_set_reload_now(xfrd); | |||
| 1135 | send_ok(ssl); | |||
| 1136 | } | |||
| 1137 | ||||
| 1138 | /** find second argument, modifies string */ | |||
| 1139 | static int | |||
| 1140 | find_arg2(RES* ssl, char* arg, char** arg2) | |||
| 1141 | { | |||
| 1142 | char* as = strrchr(arg, ' '); | |||
| 1143 | if(as) { | |||
| 1144 | as[0]=0; | |||
| 1145 | *arg2 = as+1; | |||
| 1146 | while(isspace((unsigned char)*as) && as > arg) | |||
| 1147 | as--; | |||
| 1148 | as[0]=0; | |||
| 1149 | return 1; | |||
| 1150 | } | |||
| 1151 | *arg2 = NULL((void*)0); | |||
| 1152 | (void)ssl_printf(ssl, "error could not find next argument " | |||
| 1153 | "after %s\n", arg); | |||
| 1154 | return 0; | |||
| 1155 | } | |||
| 1156 | ||||
| 1157 | /** find second and third arguments, modifies string, | |||
| 1158 | * does not print error for missing arg3 so that if it does not find an | |||
| 1159 | * arg3, the caller can use two arguments. */ | |||
| 1160 | static int | |||
| 1161 | find_arg3(RES* ssl, char* arg, char** arg2, char** arg3) | |||
| 1162 | { | |||
| 1163 | if(find_arg2(ssl, arg, arg2)) { | |||
| 1164 | char* as; | |||
| 1165 | *arg3 = *arg2; | |||
| 1166 | as = strrchr(arg, ' '); | |||
| 1167 | if(as) { | |||
| 1168 | as[0]=0; | |||
| 1169 | *arg2 = as+1; | |||
| 1170 | while(isspace((unsigned char)*as) && as > arg) | |||
| 1171 | as--; | |||
| 1172 | as[0]=0; | |||
| 1173 | return 1; | |||
| 1174 | } | |||
| 1175 | } | |||
| 1176 | *arg3 = NULL((void*)0); | |||
| 1177 | return 0; | |||
| 1178 | } | |||
| 1179 | ||||
| 1180 | /** do the status command */ | |||
| 1181 | static void | |||
| 1182 | do_status(RES* ssl, xfrd_state_type* xfrd) | |||
| 1183 | { | |||
| 1184 | if(!ssl_printf(ssl, "version: %s\n", PACKAGE_VERSION"4.3.9")) | |||
| 1185 | return; | |||
| 1186 | if(!ssl_printf(ssl, "verbosity: %d\n", verbosity)) | |||
| 1187 | return; | |||
| 1188 | #ifdef RATELIMIT | |||
| 1189 | if(!ssl_printf(ssl, "ratelimit: %d\n", | |||
| 1190 | (int)xfrd->nsd->options->rrl_ratelimit)) | |||
| 1191 | return; | |||
| 1192 | #else | |||
| 1193 | (void)xfrd; | |||
| 1194 | #endif | |||
| 1195 | } | |||
| 1196 | ||||
| 1197 | /** do the stats command */ | |||
| 1198 | static void | |||
| 1199 | do_stats(struct daemon_remote* rc, int peek, struct rc_state* rs) | |||
| 1200 | { | |||
| 1201 | #ifdef BIND8_STATS | |||
| 1202 | /* queue up to get stats after a reload is done (to gather statistics | |||
| 1203 | * from the servers) */ | |||
| 1204 | assert(!rs->in_stats_list)((void)0); | |||
| 1205 | if(peek) rs->in_stats_list = 2; | |||
| 1206 | else rs->in_stats_list = 1; | |||
| 1207 | rs->stats_next = rc->stats_list; | |||
| 1208 | rc->stats_list = rs; | |||
| 1209 | /* block the tcp waiting for the reload */ | |||
| 1210 | event_del(&rs->c); | |||
| 1211 | rs->event_added = 0; | |||
| 1212 | /* force a reload */ | |||
| 1213 | xfrd_set_reload_now(xfrd); | |||
| 1214 | #else | |||
| 1215 | (void)rc; (void)peek; | |||
| 1216 | (void)ssl_printf(rs->ssl, "error no stats enabled at compile time\n"); | |||
| 1217 | #endif /* BIND8_STATS */ | |||
| 1218 | } | |||
| 1219 | ||||
| 1220 | /** see if we have more zonestatistics entries and it has to be incremented */ | |||
| 1221 | static void | |||
| 1222 | zonestat_inc_ifneeded(xfrd_state_type* xfrd) | |||
| 1223 | { | |||
| 1224 | #ifdef USE_ZONE_STATS | |||
| 1225 | if(xfrd->nsd->options->zonestatnames->count != xfrd->zonestat_safe) | |||
| 1226 | task_new_zonestat_inc(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1227 | xfrd->last_task, | |||
| 1228 | xfrd->nsd->options->zonestatnames->count); | |||
| 1229 | #else | |||
| 1230 | (void)xfrd; | |||
| 1231 | #endif /* USE_ZONE_STATS */ | |||
| 1232 | } | |||
| 1233 | ||||
| 1234 | /** perform the changezone command for one zone */ | |||
| 1235 | static int | |||
| 1236 | perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1237 | { | |||
| 1238 | const dname_type* dname; | |||
| 1239 | struct zone_options* zopt; | |||
| 1240 | char* arg2 = NULL((void*)0); | |||
| 1241 | if(!find_arg2(ssl, arg, &arg2)) | |||
| 1242 | return 0; | |||
| 1243 | ||||
| 1244 | /* if we add it to the xfrd now, then xfrd could download AXFR and | |||
| 1245 | * store it and the NSD-reload would see it in the difffile before | |||
| 1246 | * it sees the add-config task. | |||
| 1247 | */ | |||
| 1248 | /* thus: AXFRs and IXFRs must store the pattern name in the | |||
| 1249 | * difffile, so that it can be added when the AXFR or IXFR is seen. | |||
| 1250 | */ | |||
| 1251 | ||||
| 1252 | /* check that the pattern exists */ | |||
| 1253 | if(!rbtree_search(xfrd->nsd->options->patterns, arg2)) { | |||
| 1254 | (void)ssl_printf(ssl, "error pattern %s does not exist\n", | |||
| 1255 | arg2); | |||
| 1256 | return 0; | |||
| 1257 | } | |||
| 1258 | ||||
| 1259 | dname = dname_parse(xfrd->region, arg); | |||
| 1260 | if(!dname) { | |||
| 1261 | (void)ssl_printf(ssl, "error cannot parse zone name\n"); | |||
| 1262 | return 0; | |||
| 1263 | } | |||
| 1264 | ||||
| 1265 | /* see if zone is a duplicate */ | |||
| 1266 | if( (zopt=zone_options_find(xfrd->nsd->options, dname)) ) { | |||
| 1267 | if(zopt->part_of_config) { | |||
| 1268 | (void)ssl_printf(ssl, "error zone defined in nsd.conf, " | |||
| 1269 | "cannot delete it in this manner: remove it from " | |||
| 1270 | "nsd.conf yourself and repattern\n"); | |||
| 1271 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 1272 | dname = NULL((void*)0); | |||
| 1273 | return 0; | |||
| 1274 | } | |||
| 1275 | /* found the zone, now delete it */ | |||
| 1276 | /* create deletion task */ | |||
| 1277 | /* this deletion task is processed before the addition task, | |||
| 1278 | * that is created below, in the same reload process, causing | |||
| 1279 | * a seamless change from one to the other, with no downtime | |||
| 1280 | * for the zone. */ | |||
| 1281 | task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1282 | xfrd->last_task, dname); | |||
| 1283 | xfrd_set_reload_now(xfrd); | |||
| 1284 | /* delete it in xfrd */ | |||
| 1285 | if(zone_is_slave(zopt)) { | |||
| 1286 | xfrd_del_slave_zone(xfrd, dname); | |||
| 1287 | } | |||
| 1288 | xfrd_del_notify(xfrd, dname); | |||
| 1289 | /* delete from config */ | |||
| 1290 | zone_list_del(xfrd->nsd->options, zopt); | |||
| 1291 | } else { | |||
| 1292 | (void)ssl_printf(ssl, "zone %s did not exist, creating", arg); | |||
| 1293 | } | |||
| 1294 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 1295 | dname = NULL((void*)0); | |||
| 1296 | ||||
| 1297 | /* add to zonelist and adds to config in memory */ | |||
| 1298 | zopt = zone_list_add(xfrd->nsd->options, arg, arg2); | |||
| 1299 | if(!zopt) { | |||
| 1300 | /* also dname parse error here */ | |||
| 1301 | (void)ssl_printf(ssl, "error could not add zonelist entry\n"); | |||
| 1302 | return 0; | |||
| 1303 | } | |||
| 1304 | /* make addzone task and schedule reload */ | |||
| 1305 | task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1306 | xfrd->last_task, arg, arg2, | |||
| 1307 | getzonestatid(xfrd->nsd->options, zopt)); | |||
| 1308 | zonestat_inc_ifneeded(xfrd); | |||
| 1309 | xfrd_set_reload_now(xfrd); | |||
| 1310 | /* add to xfrd - notify (for master and slaves) */ | |||
| 1311 | init_notify_send(xfrd->notify_zones, xfrd->region, zopt); | |||
| 1312 | /* add to xfrd - slave */ | |||
| 1313 | if(zone_is_slave(zopt)) { | |||
| 1314 | xfrd_init_slave_zone(xfrd, zopt); | |||
| 1315 | } | |||
| 1316 | return 1; | |||
| 1317 | } | |||
| 1318 | ||||
| 1319 | /** perform the addzone command for one zone */ | |||
| 1320 | static int | |||
| 1321 | perform_addzone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1322 | { | |||
| 1323 | const dname_type* dname; | |||
| 1324 | struct zone_options* zopt; | |||
| 1325 | char* arg2 = NULL((void*)0); | |||
| 1326 | if(!find_arg2(ssl, arg, &arg2)) | |||
| 1327 | return 0; | |||
| 1328 | ||||
| 1329 | /* if we add it to the xfrd now, then xfrd could download AXFR and | |||
| 1330 | * store it and the NSD-reload would see it in the difffile before | |||
| 1331 | * it sees the add-config task. | |||
| 1332 | */ | |||
| 1333 | /* thus: AXFRs and IXFRs must store the pattern name in the | |||
| 1334 | * difffile, so that it can be added when the AXFR or IXFR is seen. | |||
| 1335 | */ | |||
| 1336 | ||||
| 1337 | /* check that the pattern exists */ | |||
| 1338 | if(!rbtree_search(xfrd->nsd->options->patterns, arg2)) { | |||
| 1339 | (void)ssl_printf(ssl, "error pattern %s does not exist\n", | |||
| 1340 | arg2); | |||
| 1341 | return 0; | |||
| 1342 | } | |||
| 1343 | ||||
| 1344 | dname = dname_parse(xfrd->region, arg); | |||
| 1345 | if(!dname) { | |||
| 1346 | (void)ssl_printf(ssl, "error cannot parse zone name\n"); | |||
| 1347 | return 0; | |||
| 1348 | } | |||
| 1349 | ||||
| 1350 | /* see if zone is a duplicate */ | |||
| 1351 | if( zone_options_find(xfrd->nsd->options, dname) ) { | |||
| 1352 | region_recycle(xfrd->region, (void*)dname, | |||
| 1353 | dname_total_size(dname)); | |||
| 1354 | (void)ssl_printf(ssl, "zone %s already exists\n", arg); | |||
| 1355 | return 1; | |||
| 1356 | } | |||
| 1357 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 1358 | dname = NULL((void*)0); | |||
| 1359 | ||||
| 1360 | /* add to zonelist and adds to config in memory */ | |||
| 1361 | zopt = zone_list_add(xfrd->nsd->options, arg, arg2); | |||
| 1362 | if(!zopt) { | |||
| 1363 | /* also dname parse error here */ | |||
| 1364 | (void)ssl_printf(ssl, "error could not add zonelist entry\n"); | |||
| 1365 | return 0; | |||
| 1366 | } | |||
| 1367 | /* make addzone task and schedule reload */ | |||
| 1368 | task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1369 | xfrd->last_task, arg, arg2, | |||
| 1370 | getzonestatid(xfrd->nsd->options, zopt)); | |||
| 1371 | zonestat_inc_ifneeded(xfrd); | |||
| 1372 | xfrd_set_reload_now(xfrd); | |||
| 1373 | /* add to xfrd - notify (for master and slaves) */ | |||
| 1374 | init_notify_send(xfrd->notify_zones, xfrd->region, zopt); | |||
| 1375 | /* add to xfrd - slave */ | |||
| 1376 | if(zone_is_slave(zopt)) { | |||
| 1377 | xfrd_init_slave_zone(xfrd, zopt); | |||
| 1378 | } | |||
| 1379 | return 1; | |||
| 1380 | } | |||
| 1381 | ||||
| 1382 | /** perform the delzone command for one zone */ | |||
| 1383 | static int | |||
| 1384 | perform_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1385 | { | |||
| 1386 | const dname_type* dname; | |||
| 1387 | struct zone_options* zopt; | |||
| 1388 | ||||
| 1389 | dname = dname_parse(xfrd->region, arg); | |||
| 1390 | if(!dname) { | |||
| 1391 | (void)ssl_printf(ssl, "error cannot parse zone name\n"); | |||
| 1392 | return 0; | |||
| 1393 | } | |||
| 1394 | ||||
| 1395 | /* see if we have the zone in question */ | |||
| 1396 | zopt = zone_options_find(xfrd->nsd->options, dname); | |||
| 1397 | if(!zopt) { | |||
| 1398 | region_recycle(xfrd->region, (void*)dname, | |||
| 1399 | dname_total_size(dname)); | |||
| 1400 | /* nothing to do */ | |||
| 1401 | (void)ssl_printf(ssl, "warning zone %s not present\n", arg); | |||
| 1402 | return 0; | |||
| 1403 | } | |||
| 1404 | ||||
| 1405 | /* see if it can be deleted */ | |||
| 1406 | if(zopt->part_of_config) { | |||
| 1407 | region_recycle(xfrd->region, (void*)dname, | |||
| 1408 | dname_total_size(dname)); | |||
| 1409 | (void)ssl_printf(ssl, "error zone defined in nsd.conf, " | |||
| 1410 | "cannot delete it in this manner: remove it from " | |||
| 1411 | "nsd.conf yourself and repattern\n"); | |||
| 1412 | return 0; | |||
| 1413 | } | |||
| 1414 | ||||
| 1415 | /* create deletion task */ | |||
| 1416 | task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1417 | xfrd->last_task, dname); | |||
| 1418 | xfrd_set_reload_now(xfrd); | |||
| 1419 | /* delete it in xfrd */ | |||
| 1420 | if(zone_is_slave(zopt)) { | |||
| 1421 | xfrd_del_slave_zone(xfrd, dname); | |||
| 1422 | } | |||
| 1423 | xfrd_del_notify(xfrd, dname); | |||
| 1424 | /* delete from config */ | |||
| 1425 | zone_list_del(xfrd->nsd->options, zopt); | |||
| 1426 | ||||
| 1427 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 1428 | return 1; | |||
| 1429 | } | |||
| 1430 | ||||
| 1431 | /** do the addzone command */ | |||
| 1432 | static void | |||
| 1433 | do_addzone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1434 | { | |||
| 1435 | if(!perform_addzone(ssl, xfrd, arg)) | |||
| 1436 | return; | |||
| 1437 | send_ok(ssl); | |||
| 1438 | } | |||
| 1439 | ||||
| 1440 | /** do the delzone command */ | |||
| 1441 | static void | |||
| 1442 | do_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1443 | { | |||
| 1444 | if(!perform_delzone(ssl, xfrd, arg)) | |||
| 1445 | return; | |||
| 1446 | send_ok(ssl); | |||
| 1447 | } | |||
| 1448 | ||||
| 1449 | /** do the changezone command */ | |||
| 1450 | static void | |||
| 1451 | do_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1452 | { | |||
| 1453 | if(!perform_changezone(ssl, xfrd, arg)) | |||
| 1454 | return; | |||
| 1455 | send_ok(ssl); | |||
| 1456 | } | |||
| 1457 | ||||
| 1458 | /** do the addzones command */ | |||
| 1459 | static void | |||
| 1460 | do_addzones(RES* ssl, xfrd_state_type* xfrd) | |||
| 1461 | { | |||
| 1462 | char buf[2048]; | |||
| 1463 | int num = 0; | |||
| 1464 | while(ssl_read_line(ssl, buf, sizeof(buf))) { | |||
| 1465 | if(buf[0] == 0x04 && buf[1] == 0) | |||
| 1466 | break; /* end of transmission */ | |||
| 1467 | if(!perform_addzone(ssl, xfrd, buf)) { | |||
| 1468 | if(!ssl_printf(ssl, "error for input line '%s'\n", | |||
| 1469 | buf)) | |||
| 1470 | return; | |||
| 1471 | } else { | |||
| 1472 | if(!ssl_printf(ssl, "added: %s\n", buf)) | |||
| 1473 | return; | |||
| 1474 | num++; | |||
| 1475 | } | |||
| 1476 | } | |||
| 1477 | (void)ssl_printf(ssl, "added %d zones\n", num); | |||
| 1478 | } | |||
| 1479 | ||||
| 1480 | /** do the delzones command */ | |||
| 1481 | static void | |||
| 1482 | do_delzones(RES* ssl, xfrd_state_type* xfrd) | |||
| 1483 | { | |||
| 1484 | char buf[2048]; | |||
| 1485 | int num = 0; | |||
| 1486 | while(ssl_read_line(ssl, buf, sizeof(buf))) { | |||
| 1487 | if(buf[0] == 0x04 && buf[1] == 0) | |||
| 1488 | break; /* end of transmission */ | |||
| 1489 | if(!perform_delzone(ssl, xfrd, buf)) { | |||
| 1490 | if(!ssl_printf(ssl, "error for input line '%s'\n", | |||
| 1491 | buf)) | |||
| 1492 | return; | |||
| 1493 | } else { | |||
| 1494 | if(!ssl_printf(ssl, "removed: %s\n", buf)) | |||
| 1495 | return; | |||
| 1496 | num++; | |||
| 1497 | } | |||
| 1498 | } | |||
| 1499 | (void)ssl_printf(ssl, "deleted %d zones\n", num); | |||
| 1500 | } | |||
| 1501 | ||||
| 1502 | ||||
| 1503 | /** remove TSIG key from config and add task so that reload does too */ | |||
| 1504 | static void remove_key(xfrd_state_type* xfrd, const char* kname) | |||
| 1505 | { | |||
| 1506 | /* add task before deletion because the name string could be deleted */ | |||
| 1507 | task_new_del_key(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, | |||
| 1508 | kname); | |||
| 1509 | key_options_remove(xfrd->nsd->options, kname); | |||
| 1510 | xfrd_set_reload_now(xfrd); /* this is executed when the current control | |||
| 1511 | command ends, thus the entire config changes are bunched up */ | |||
| 1512 | } | |||
| 1513 | ||||
| 1514 | /** add TSIG key to config and add task so that reload does too */ | |||
| 1515 | static void add_key(xfrd_state_type* xfrd, struct key_options* k) | |||
| 1516 | { | |||
| 1517 | key_options_add_modify(xfrd->nsd->options, k); | |||
| 1518 | task_new_add_key(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, | |||
| 1519 | k); | |||
| 1520 | xfrd_set_reload_now(xfrd); | |||
| 1521 | } | |||
| 1522 | ||||
| 1523 | /** check if keys have changed */ | |||
| 1524 | static void repat_keys(xfrd_state_type* xfrd, struct nsd_options* newopt) | |||
| 1525 | { | |||
| 1526 | struct nsd_options* oldopt = xfrd->nsd->options; | |||
| 1527 | struct key_options* k; | |||
| 1528 | /* find deleted keys */ | |||
| 1529 | k = (struct key_options*)rbtree_first(oldopt->keys); | |||
| 1530 | while((rbnode_type*)k != RBTREE_NULL&rbtree_null_node) { | |||
| 1531 | struct key_options* next = (struct key_options*)rbtree_next( | |||
| 1532 | (rbnode_type*)k); | |||
| 1533 | if(!key_options_find(newopt, k->name)) | |||
| 1534 | remove_key(xfrd, k->name); | |||
| 1535 | k = next; | |||
| 1536 | } | |||
| 1537 | /* find added or changed keys */ | |||
| 1538 | RBTREE_FOR(k, struct key_options*, newopt->keys)for(k=(struct key_options*)rbtree_first(newopt->keys); (rbnode_type *)k != &rbtree_null_node; k = (struct key_options*)rbtree_next ((rbnode_type*)k)) { | |||
| 1539 | struct key_options* origk = key_options_find(oldopt, k->name); | |||
| 1540 | if(!origk) | |||
| 1541 | add_key(xfrd, k); | |||
| 1542 | else if(!key_options_equal(k, origk)) | |||
| 1543 | add_key(xfrd, k); | |||
| 1544 | } | |||
| 1545 | } | |||
| 1546 | ||||
| 1547 | /** find zone given the implicit pattern */ | |||
| 1548 | static const dname_type* | |||
| 1549 | parse_implicit_name(xfrd_state_type* xfrd,const char* pname) | |||
| 1550 | { | |||
| 1551 | if(strncmp(pname, PATTERN_IMPLICIT_MARKER"_implicit_", | |||
| 1552 | strlen(PATTERN_IMPLICIT_MARKER"_implicit_")) != 0) | |||
| 1553 | return NULL((void*)0); | |||
| 1554 | return dname_parse(xfrd->region, pname + | |||
| 1555 | strlen(PATTERN_IMPLICIT_MARKER"_implicit_")); | |||
| 1556 | } | |||
| 1557 | ||||
| 1558 | /** remove cfgzone and add task so that reload does too */ | |||
| 1559 | static void | |||
| 1560 | remove_cfgzone(xfrd_state_type* xfrd, const char* pname) | |||
| 1561 | { | |||
| 1562 | /* dname and find the zone for the implicit pattern */ | |||
| 1563 | struct zone_options* zopt = NULL((void*)0); | |||
| 1564 | const dname_type* dname = parse_implicit_name(xfrd, pname); | |||
| 1565 | if(!dname) { | |||
| 1566 | /* should have a parseable name, but it did not */ | |||
| 1567 | return; | |||
| 1568 | } | |||
| 1569 | ||||
| 1570 | /* find the zone entry for the implicit pattern */ | |||
| 1571 | zopt = zone_options_find(xfrd->nsd->options, dname); | |||
| 1572 | if(!zopt) { | |||
| 1573 | /* this should not happen; implicit pattern has zone entry */ | |||
| 1574 | region_recycle(xfrd->region, (void*)dname, | |||
| 1575 | dname_total_size(dname)); | |||
| 1576 | return; | |||
| 1577 | } | |||
| 1578 | ||||
| 1579 | /* create deletion task */ | |||
| 1580 | task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1581 | xfrd->last_task, dname); | |||
| 1582 | xfrd_set_reload_now(xfrd); | |||
| 1583 | /* delete it in xfrd */ | |||
| 1584 | if(zone_is_slave(zopt)) { | |||
| 1585 | xfrd_del_slave_zone(xfrd, dname); | |||
| 1586 | } | |||
| 1587 | xfrd_del_notify(xfrd, dname); | |||
| 1588 | ||||
| 1589 | /* delete from zoneoptions */ | |||
| 1590 | zone_options_delete(xfrd->nsd->options, zopt); | |||
| 1591 | ||||
| 1592 | /* recycle parsed dname */ | |||
| 1593 | region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); | |||
| 1594 | } | |||
| 1595 | ||||
| 1596 | /** add cfgzone and add task so that reload does too */ | |||
| 1597 | static void | |||
| 1598 | add_cfgzone(xfrd_state_type* xfrd, const char* pname) | |||
| 1599 | { | |||
| 1600 | /* add to our zonelist */ | |||
| 1601 | struct zone_options* zopt = zone_options_create( | |||
| 1602 | xfrd->nsd->options->region); | |||
| 1603 | if(!zopt) | |||
| 1604 | return; | |||
| 1605 | zopt->part_of_config = 1; | |||
| 1606 | zopt->name = region_strdup(xfrd->nsd->options->region, | |||
| 1607 | pname + strlen(PATTERN_IMPLICIT_MARKER"_implicit_")); | |||
| 1608 | zopt->pattern = pattern_options_find(xfrd->nsd->options, pname); | |||
| 1609 | if(!zopt->name || !zopt->pattern) | |||
| 1610 | return; | |||
| 1611 | if(!nsd_options_insert_zone(xfrd->nsd->options, zopt)) { | |||
| 1612 | log_msg(LOG_ERR3, "bad domain name or duplicate zone '%s' " | |||
| 1613 | "pattern %s", zopt->name, pname); | |||
| 1614 | } | |||
| 1615 | ||||
| 1616 | /* make addzone task and schedule reload */ | |||
| 1617 | task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1618 | xfrd->last_task, zopt->name, pname, | |||
| 1619 | getzonestatid(xfrd->nsd->options, zopt)); | |||
| 1620 | /* zonestat_inc is done after the entire config file has been done */ | |||
| 1621 | xfrd_set_reload_now(xfrd); | |||
| 1622 | /* add to xfrd - notify (for master and slaves) */ | |||
| 1623 | init_notify_send(xfrd->notify_zones, xfrd->region, zopt); | |||
| 1624 | /* add to xfrd - slave */ | |||
| 1625 | if(zone_is_slave(zopt)) { | |||
| 1626 | xfrd_init_slave_zone(xfrd, zopt); | |||
| 1627 | } | |||
| 1628 | } | |||
| 1629 | ||||
| 1630 | /** remove pattern and add task so that reload does too */ | |||
| 1631 | static void | |||
| 1632 | remove_pat(xfrd_state_type* xfrd, const char* name) | |||
| 1633 | { | |||
| 1634 | /* add task before deletion, because name-string could be deleted */ | |||
| 1635 | task_new_del_pattern(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1636 | xfrd->last_task, name); | |||
| 1637 | pattern_options_remove(xfrd->nsd->options, name); | |||
| 1638 | xfrd_set_reload_now(xfrd); | |||
| 1639 | } | |||
| 1640 | ||||
| 1641 | /** add pattern and add task so that reload does too */ | |||
| 1642 | static void | |||
| 1643 | add_pat(xfrd_state_type* xfrd, struct pattern_options* p) | |||
| 1644 | { | |||
| 1645 | pattern_options_add_modify(xfrd->nsd->options, p); | |||
| 1646 | task_new_add_pattern(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1647 | xfrd->last_task, p); | |||
| 1648 | xfrd_set_reload_now(xfrd); | |||
| 1649 | } | |||
| 1650 | ||||
| 1651 | /** interrupt zones that are using changed or removed patterns */ | |||
| 1652 | static void | |||
| 1653 | repat_interrupt_zones(xfrd_state_type* xfrd, struct nsd_options* newopt) | |||
| 1654 | { | |||
| 1655 | /* if masterlist changed: | |||
| 1656 | * interrupt slave zone (UDP or TCP) transfers. | |||
| 1657 | * slave zones reset master to start of list. | |||
| 1658 | */ | |||
| 1659 | xfrd_zone_type* xz; | |||
| 1660 | struct notify_zone* nz; | |||
| 1661 | RBTREE_FOR(xz, xfrd_zone_type*, xfrd->zones)for(xz=(xfrd_zone_type*)rbtree_first(xfrd->zones); (rbnode_type *)xz != &rbtree_null_node; xz = (xfrd_zone_type*)rbtree_next ((rbnode_type*)xz)) { | |||
| 1662 | struct pattern_options* oldp = xz->zone_options->pattern; | |||
| 1663 | struct pattern_options* newp = pattern_options_find(newopt, | |||
| 1664 | oldp->pname); | |||
| 1665 | if(!newp || !acl_list_equal(oldp->request_xfr, | |||
| 1666 | newp->request_xfr)) { | |||
| 1667 | /* interrupt transfer */ | |||
| 1668 | if(xz->tcp_conn != -1) { | |||
| 1669 | xfrd_tcp_release(xfrd->tcp_set, xz); | |||
| 1670 | xfrd_set_refresh_now(xz); | |||
| 1671 | } else if(xz->zone_handler.ev_fd != -1) { | |||
| 1672 | xfrd_udp_release(xz); | |||
| 1673 | xfrd_set_refresh_now(xz); | |||
| 1674 | } | |||
| 1675 | xz->master = 0; | |||
| 1676 | xz->master_num = 0; | |||
| 1677 | xz->next_master = -1; | |||
| 1678 | xz->round_num = -1; /* fresh set of retries */ | |||
| 1679 | } | |||
| 1680 | } | |||
| 1681 | /* if notify list changed: | |||
| 1682 | * interrupt notify that is busy. | |||
| 1683 | * reset notify to start of list. (clear all other reset_notify) | |||
| 1684 | */ | |||
| 1685 | RBTREE_FOR(nz, struct notify_zone*, xfrd->notify_zones)for(nz=(struct notify_zone*)rbtree_first(xfrd->notify_zones ); (rbnode_type*)nz != &rbtree_null_node; nz = (struct notify_zone *)rbtree_next((rbnode_type*)nz)) { | |||
| 1686 | struct pattern_options* oldp = nz->options->pattern; | |||
| 1687 | struct pattern_options* newp = pattern_options_find(newopt, | |||
| 1688 | oldp->pname); | |||
| 1689 | if(!newp || !acl_list_equal(oldp->notify, newp->notify)) { | |||
| 1690 | /* interrupt notify */ | |||
| 1691 | if(nz->notify_send_enable) { | |||
| 1692 | notify_disable(nz); | |||
| 1693 | /* set to restart the notify after the | |||
| 1694 | * pattern has been changed. */ | |||
| 1695 | nz->notify_restart = 2; | |||
| 1696 | } else { | |||
| 1697 | nz->notify_restart = 1; | |||
| 1698 | } | |||
| 1699 | } else { | |||
| 1700 | nz->notify_restart = 0; | |||
| 1701 | } | |||
| 1702 | } | |||
| 1703 | } | |||
| 1704 | ||||
| 1705 | /** for notify, after the pattern changes, restart the affected notifies */ | |||
| 1706 | static void | |||
| 1707 | repat_interrupt_notify_start(xfrd_state_type* xfrd) | |||
| 1708 | { | |||
| 1709 | struct notify_zone* nz; | |||
| 1710 | RBTREE_FOR(nz, struct notify_zone*, xfrd->notify_zones)for(nz=(struct notify_zone*)rbtree_first(xfrd->notify_zones ); (rbnode_type*)nz != &rbtree_null_node; nz = (struct notify_zone *)rbtree_next((rbnode_type*)nz)) { | |||
| 1711 | if(nz->notify_restart) { | |||
| 1712 | if(nz->notify_current) | |||
| 1713 | nz->notify_current = nz->options->pattern->notify; | |||
| 1714 | if(nz->notify_restart == 2) { | |||
| 1715 | if(nz->notify_restart) | |||
| 1716 | xfrd_notify_start(nz, xfrd); | |||
| 1717 | } | |||
| 1718 | } | |||
| 1719 | } | |||
| 1720 | } | |||
| 1721 | ||||
| 1722 | /** check if patterns have changed */ | |||
| 1723 | static void | |||
| 1724 | repat_patterns(xfrd_state_type* xfrd, struct nsd_options* newopt) | |||
| 1725 | { | |||
| 1726 | /* zones that use changed patterns must have: | |||
| 1727 | * - their AXFR/IXFR interrupted: try again, acl may have changed. | |||
| 1728 | * if the old master/key still exists, OK, fix master-numptrs and | |||
| 1729 | * keep going. Otherwise, stop xfer and reset TSIG. | |||
| 1730 | * - send NOTIFY reset to start of NOTIFY list (and TSIG reset). | |||
| 1731 | */ | |||
| 1732 | struct nsd_options* oldopt = xfrd->nsd->options; | |||
| 1733 | struct pattern_options* p; | |||
| 1734 | int search_zones = 0; | |||
| 1735 | ||||
| 1736 | repat_interrupt_zones(xfrd, newopt); | |||
| 1737 | /* find deleted patterns */ | |||
| 1738 | p = (struct pattern_options*)rbtree_first(oldopt->patterns); | |||
| 1739 | while((rbnode_type*)p != RBTREE_NULL&rbtree_null_node) { | |||
| 1740 | struct pattern_options* next = (struct pattern_options*) | |||
| 1741 | rbtree_next((rbnode_type*)p); | |||
| 1742 | if(!pattern_options_find(newopt, p->pname)) { | |||
| 1743 | if(p->implicit) { | |||
| 1744 | /* first remove its zone */ | |||
| 1745 | VERBOSITY(1, (LOG_INFO, "zone removed from config: %s", p->pname + strlen(PATTERN_IMPLICIT_MARKER)))do { if ((1) <= verbosity) { log_msg (6, "zone removed from config: %s" , p->pname + strlen("_implicit_")) ; } } while (0); | |||
| 1746 | remove_cfgzone(xfrd, p->pname); | |||
| 1747 | } | |||
| 1748 | remove_pat(xfrd, p->pname); | |||
| 1749 | } | |||
| 1750 | p = next; | |||
| 1751 | } | |||
| 1752 | /* find added or changed patterns */ | |||
| 1753 | RBTREE_FOR(p, struct pattern_options*, newopt->patterns)for(p=(struct pattern_options*)rbtree_first(newopt->patterns ); (rbnode_type*)p != &rbtree_null_node; p = (struct pattern_options *)rbtree_next((rbnode_type*)p)) { | |||
| 1754 | struct pattern_options* origp = pattern_options_find(oldopt, | |||
| 1755 | p->pname); | |||
| 1756 | if(!origp) { | |||
| 1757 | /* no zones can use it, no zone_interrupt needed */ | |||
| 1758 | add_pat(xfrd, p); | |||
| 1759 | if(p->implicit) { | |||
| 1760 | VERBOSITY(1, (LOG_INFO, "zone added to config: %s", p->pname + strlen(PATTERN_IMPLICIT_MARKER)))do { if ((1) <= verbosity) { log_msg (6, "zone added to config: %s" , p->pname + strlen("_implicit_")) ; } } while (0); | |||
| 1761 | add_cfgzone(xfrd, p->pname); | |||
| 1762 | } | |||
| 1763 | } else if(!pattern_options_equal(p, origp)) { | |||
| 1764 | uint8_t newstate = 0; | |||
| 1765 | if (p->request_xfr && !origp->request_xfr) { | |||
| 1766 | newstate = REPAT_SLAVE1; | |||
| 1767 | } else if (!p->request_xfr && origp->request_xfr) { | |||
| 1768 | newstate = REPAT_MASTER2; | |||
| 1769 | } | |||
| 1770 | add_pat(xfrd, p); | |||
| 1771 | if (p->implicit && newstate) { | |||
| 1772 | const dname_type* dname = | |||
| 1773 | parse_implicit_name(xfrd, p->pname); | |||
| 1774 | if (dname) { | |||
| 1775 | if (newstate == REPAT_SLAVE1) { | |||
| 1776 | struct zone_options* zopt = | |||
| 1777 | zone_options_find( | |||
| 1778 | oldopt, dname); | |||
| 1779 | if (zopt) { | |||
| 1780 | xfrd_init_slave_zone( | |||
| 1781 | xfrd, zopt); | |||
| 1782 | } | |||
| 1783 | } else if (newstate == REPAT_MASTER2) { | |||
| 1784 | xfrd_del_slave_zone(xfrd, | |||
| 1785 | dname); | |||
| 1786 | } | |||
| 1787 | region_recycle(xfrd->region, | |||
| 1788 | (void*)dname, | |||
| 1789 | dname_total_size(dname)); | |||
| 1790 | } | |||
| 1791 | } else if(!p->implicit && newstate) { | |||
| 1792 | /* search all zones with this pattern */ | |||
| 1793 | search_zones = 1; | |||
| 1794 | origp->xfrd_flags = newstate; | |||
| 1795 | } | |||
| 1796 | } | |||
| 1797 | } | |||
| 1798 | if (search_zones) { | |||
| 1799 | struct zone_options* zone_opt; | |||
| 1800 | /* search in oldopt because 1) it contains zonelist zones, | |||
| 1801 | * and 2) you need oldopt(existing) to call xfrd_init */ | |||
| 1802 | RBTREE_FOR(zone_opt, struct zone_options*, oldopt->zone_options)for(zone_opt=(struct zone_options*)rbtree_first(oldopt->zone_options ); (rbnode_type*)zone_opt != &rbtree_null_node; zone_opt = (struct zone_options*)rbtree_next((rbnode_type*)zone_opt)) { | |||
| 1803 | struct pattern_options* oldp = zone_opt->pattern; | |||
| 1804 | if (!oldp->implicit) { | |||
| 1805 | if (oldp->xfrd_flags == REPAT_SLAVE1) { | |||
| 1806 | /* xfrd needs stable reference so get | |||
| 1807 | * it from the oldopt(modified) tree */ | |||
| 1808 | xfrd_init_slave_zone(xfrd, zone_opt); | |||
| 1809 | } else if (oldp->xfrd_flags == REPAT_MASTER2) { | |||
| 1810 | xfrd_del_slave_zone(xfrd, | |||
| 1811 | (const dname_type*) | |||
| 1812 | zone_opt->node.key); | |||
| 1813 | } | |||
| 1814 | oldp->xfrd_flags = 0; | |||
| 1815 | } | |||
| 1816 | } | |||
| 1817 | } | |||
| 1818 | repat_interrupt_notify_start(xfrd); | |||
| 1819 | } | |||
| 1820 | ||||
| 1821 | /** true if options are different that can be set via repat. */ | |||
| 1822 | static int | |||
| 1823 | repat_options_changed(xfrd_state_type* xfrd, struct nsd_options* newopt) | |||
| 1824 | { | |||
| 1825 | #ifdef RATELIMIT | |||
| 1826 | if(xfrd->nsd->options->rrl_ratelimit != newopt->rrl_ratelimit) | |||
| 1827 | return 1; | |||
| 1828 | if(xfrd->nsd->options->rrl_whitelist_ratelimit != newopt->rrl_whitelist_ratelimit) | |||
| 1829 | return 1; | |||
| 1830 | if(xfrd->nsd->options->rrl_slip != newopt->rrl_slip) | |||
| 1831 | return 1; | |||
| 1832 | #else | |||
| 1833 | (void)xfrd; (void)newopt; | |||
| 1834 | #endif | |||
| 1835 | return 0; | |||
| 1836 | } | |||
| 1837 | ||||
| 1838 | /** check if global options have changed */ | |||
| 1839 | static void | |||
| 1840 | repat_options(xfrd_state_type* xfrd, struct nsd_options* newopt) | |||
| 1841 | { | |||
| 1842 | if(repat_options_changed(xfrd, newopt)) { | |||
| 1843 | /* update our options */ | |||
| 1844 | #ifdef RATELIMIT | |||
| 1845 | xfrd->nsd->options->rrl_ratelimit = newopt->rrl_ratelimit; | |||
| 1846 | xfrd->nsd->options->rrl_whitelist_ratelimit = newopt->rrl_whitelist_ratelimit; | |||
| 1847 | xfrd->nsd->options->rrl_slip = newopt->rrl_slip; | |||
| 1848 | #endif | |||
| 1849 | task_new_opt_change(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 1850 | xfrd->last_task, newopt); | |||
| 1851 | xfrd_set_reload_now(xfrd); | |||
| 1852 | } | |||
| 1853 | } | |||
| 1854 | ||||
| 1855 | /** print errors over ssl, gets pointer-to-pointer to ssl, so it can set | |||
| 1856 | * the pointer to NULL on failure and stop printing */ | |||
| 1857 | static void | |||
| 1858 | print_ssl_cfg_err(void* arg, const char* str) | |||
| 1859 | { | |||
| 1860 | RES** ssl = (RES**)arg; | |||
| 1861 | if(!*ssl) return; | |||
| 1862 | if(!ssl_printf(*ssl, "%s", str)) | |||
| 1863 | *ssl = NULL((void*)0); /* failed, stop printing */ | |||
| 1864 | } | |||
| 1865 | ||||
| 1866 | /** do the repattern command: reread config file and apply keys, patterns */ | |||
| 1867 | static void | |||
| 1868 | do_repattern(RES* ssl, xfrd_state_type* xfrd) | |||
| 1869 | { | |||
| 1870 | region_type* region = region_create(xalloc, free); | |||
| 1871 | struct nsd_options* opt; | |||
| 1872 | const char* cfgfile = xfrd->nsd->options->configfile; | |||
| 1873 | ||||
| 1874 | /* check chroot and configfile, if possible to reread */ | |||
| 1875 | if(xfrd->nsd->chrootdir) { | |||
| 1876 | size_t l = strlen(xfrd->nsd->chrootdir); | |||
| 1877 | while(l>0 && xfrd->nsd->chrootdir[l-1] == '/') | |||
| 1878 | --l; | |||
| 1879 | if(strncmp(xfrd->nsd->chrootdir, cfgfile, l) != 0) { | |||
| 1880 | (void)ssl_printf(ssl, "error %s is not relative to %s: " | |||
| 1881 | "chroot prevents reread of config\n", | |||
| 1882 | cfgfile, xfrd->nsd->chrootdir); | |||
| 1883 | region_destroy(region); | |||
| 1884 | return; | |||
| 1885 | } | |||
| 1886 | cfgfile += l; | |||
| 1887 | } | |||
| 1888 | ||||
| 1889 | (void)ssl_printf(ssl, "reconfig start, read %s\n", cfgfile); | |||
| 1890 | opt = nsd_options_create(region); | |||
| 1891 | if(!parse_options_file(opt, cfgfile, &print_ssl_cfg_err, &ssl)) { | |||
| 1892 | /* error already printed */ | |||
| 1893 | region_destroy(region); | |||
| 1894 | return; | |||
| 1895 | } | |||
| 1896 | /* check for differences in TSIG keys and patterns, and apply, | |||
| 1897 | * first the keys, so that pattern->keyptr can be set right. */ | |||
| 1898 | repat_keys(xfrd, opt); | |||
| 1899 | repat_patterns(xfrd, opt); | |||
| 1900 | repat_options(xfrd, opt); | |||
| 1901 | zonestat_inc_ifneeded(xfrd); | |||
| 1902 | send_ok(ssl); | |||
| 1903 | region_destroy(region); | |||
| 1904 | } | |||
| 1905 | ||||
| 1906 | /** do the serverpid command: printout pid of server process */ | |||
| 1907 | static void | |||
| 1908 | do_serverpid(RES* ssl, xfrd_state_type* xfrd) | |||
| 1909 | { | |||
| 1910 | (void)ssl_printf(ssl, "%u\n", (unsigned)xfrd->reload_pid); | |||
| 1911 | } | |||
| 1912 | ||||
| 1913 | /** do the print_tsig command: printout tsig info */ | |||
| 1914 | static void | |||
| 1915 | do_print_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1916 | { | |||
| 1917 | if(*arg == '\0') { | |||
| 1918 | struct key_options* key; | |||
| 1919 | RBTREE_FOR(key, struct key_options*, xfrd->nsd->options->keys)for(key=(struct key_options*)rbtree_first(xfrd->nsd->options ->keys); (rbnode_type*)key != &rbtree_null_node; key = (struct key_options*)rbtree_next((rbnode_type*)key)) { | |||
| 1920 | if(!ssl_printf(ssl, "key: name: \"%s\" secret: \"%s\" algorithm: %s\n", key->name, key->secret, key->algorithm)) | |||
| 1921 | return; | |||
| 1922 | } | |||
| 1923 | return; | |||
| 1924 | } else { | |||
| 1925 | struct key_options* key_opts = key_options_find(xfrd->nsd->options, arg); | |||
| 1926 | if(!key_opts) { | |||
| 1927 | (void)ssl_printf(ssl, "error: no such key with name: %s\n", arg); | |||
| 1928 | return; | |||
| 1929 | } else { | |||
| 1930 | (void)ssl_printf(ssl, "key: name: \"%s\" secret: \"%s\" algorithm: %s\n", arg, key_opts->secret, key_opts->algorithm); | |||
| 1931 | } | |||
| 1932 | } | |||
| 1933 | } | |||
| 1934 | ||||
| 1935 | /** do the update_tsig command: change existing tsig to new secret */ | |||
| 1936 | static void | |||
| 1937 | do_update_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1938 | { | |||
| 1939 | struct region* region = xfrd->nsd->options->region; | |||
| 1940 | char* arg2 = NULL((void*)0); | |||
| 1941 | uint8_t data[65536]; /* 64K */ | |||
| 1942 | struct key_options* key_opt; | |||
| 1943 | ||||
| 1944 | if(*arg == '\0') { | |||
| 1945 | (void)ssl_printf(ssl, "error: missing argument (keyname)\n"); | |||
| 1946 | return; | |||
| 1947 | } | |||
| 1948 | if(!find_arg2(ssl, arg, &arg2)) { | |||
| 1949 | (void)ssl_printf(ssl, "error: missing argument (secret)\n"); | |||
| 1950 | return; | |||
| 1951 | } | |||
| 1952 | key_opt = key_options_find(xfrd->nsd->options, arg); | |||
| 1953 | if(!key_opt) { | |||
| 1954 | (void)ssl_printf(ssl, "error: no such key with name: %s\n", arg); | |||
| 1955 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 1956 | return; | |||
| 1957 | } | |||
| 1958 | if(__b64_pton(arg2, data, sizeof(data)) == -1) { | |||
| 1959 | (void)ssl_printf(ssl, "error: the secret: %s is not in b64 format\n", arg2); | |||
| 1960 | memset(data, 0xdd, sizeof(data)); /* wipe secret */ | |||
| 1961 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 1962 | return; | |||
| 1963 | } | |||
| 1964 | log_msg(LOG_INFO6, "changing secret provided with the key: %s with old secret %s and algo: %s\n", arg, key_opt->secret, key_opt->algorithm); | |||
| 1965 | if(key_opt->secret) { | |||
| 1966 | /* wipe old secret */ | |||
| 1967 | memset(key_opt->secret, 0xdd, strlen(key_opt->secret)); | |||
| 1968 | region_recycle(region, key_opt->secret, | |||
| 1969 | strlen(key_opt->secret)+1); | |||
| 1970 | } | |||
| 1971 | key_opt->secret = region_strdup(region, arg2); | |||
| 1972 | log_msg(LOG_INFO6, "the key: %s has new secret %s and algorithm: %s\n", arg, key_opt->secret, key_opt->algorithm); | |||
| 1973 | /* wipe secret from temp parse buffer */ | |||
| 1974 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 1975 | memset(data, 0xdd, sizeof(data)); | |||
| 1976 | ||||
| 1977 | key_options_desetup(region, key_opt); | |||
| 1978 | key_options_setup(region, key_opt); | |||
| 1979 | task_new_add_key(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, | |||
| 1980 | key_opt); | |||
| 1981 | xfrd_set_reload_now(xfrd); | |||
| 1982 | ||||
| 1983 | send_ok(ssl); | |||
| 1984 | } | |||
| 1985 | ||||
| 1986 | /** do the add tsig command, add new key with name, secret and algo given */ | |||
| 1987 | static void | |||
| 1988 | do_add_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 1989 | { | |||
| 1990 | char* arg2 = NULL((void*)0); | |||
| 1991 | char* arg3 = NULL((void*)0); | |||
| 1992 | uint8_t data[65536]; /* 64KB */ | |||
| 1993 | uint8_t dname[MAXDOMAINLEN255+1]; | |||
| 1994 | char algo[256]; | |||
| 1995 | region_type* region = xfrd->nsd->options->region; | |||
| 1996 | struct key_options* new_key_opt; | |||
| 1997 | ||||
| 1998 | if(*arg == '\0') { | |||
| 1999 | (void)ssl_printf(ssl, "error: missing argument (keyname)\n"); | |||
| 2000 | return; | |||
| 2001 | } | |||
| 2002 | if(!find_arg3(ssl, arg, &arg2, &arg3)) { | |||
| 2003 | strlcpy(algo, "hmac-sha256", sizeof(algo)); | |||
| 2004 | } else { | |||
| 2005 | strlcpy(algo, arg3, sizeof(algo)); | |||
| 2006 | } | |||
| 2007 | if(!arg2) { | |||
| 2008 | (void)ssl_printf(ssl, "error: missing argument (secret)\n"); | |||
| 2009 | return; | |||
| 2010 | } | |||
| 2011 | if(key_options_find(xfrd->nsd->options, arg)) { | |||
| 2012 | (void)ssl_printf(ssl, "error: key %s already exists\n", arg); | |||
| 2013 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 2014 | return; | |||
| 2015 | } | |||
| 2016 | if(__b64_pton(arg2, data, sizeof(data)) == -1) { | |||
| 2017 | (void)ssl_printf(ssl, "error: the secret: %s is not in b64 format\n", arg2); | |||
| 2018 | memset(data, 0xdd, sizeof(data)); /* wipe secret */ | |||
| 2019 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 2020 | return; | |||
| 2021 | } | |||
| 2022 | memset(data, 0xdd, sizeof(data)); /* wipe secret from temp buffer */ | |||
| 2023 | if(!dname_parse_wire(dname, arg)) { | |||
| 2024 | (void)ssl_printf(ssl, "error: could not parse key name: %s\n", arg); | |||
| 2025 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 2026 | return; | |||
| 2027 | } | |||
| 2028 | if(tsig_get_algorithm_by_name(algo) == NULL((void*)0)) { | |||
| 2029 | (void)ssl_printf(ssl, "error: unknown algorithm: %s\n", algo); | |||
| 2030 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 2031 | return; | |||
| 2032 | } | |||
| 2033 | log_msg(LOG_INFO6, "adding key with name: %s and secret: %s with algo: %s\n", arg, arg2, algo); | |||
| 2034 | new_key_opt = key_options_create(region); | |||
| 2035 | new_key_opt->name = region_strdup(region, arg); | |||
| 2036 | new_key_opt->secret = region_strdup(region, arg2); | |||
| 2037 | new_key_opt->algorithm = region_strdup(region, algo); | |||
| 2038 | add_key(xfrd, new_key_opt); | |||
| 2039 | ||||
| 2040 | /* wipe secret from temp buffer */ | |||
| 2041 | memset(arg2, 0xdd, strlen(arg2)); | |||
| 2042 | send_ok(ssl); | |||
| 2043 | } | |||
| 2044 | ||||
| 2045 | /** set acl entries to use the given TSIG key */ | |||
| 2046 | static void | |||
| 2047 | zopt_set_acl_to_tsig(struct acl_options* acl, struct region* region, | |||
| 2048 | const char* key_name, struct key_options* key_opt) | |||
| 2049 | { | |||
| 2050 | while(acl) { | |||
| 2051 | if(acl->blocked) { | |||
| 2052 | acl = acl->next; | |||
| 2053 | continue; | |||
| 2054 | } | |||
| 2055 | acl->nokey = 0; | |||
| 2056 | if(acl->key_name) | |||
| 2057 | region_recycle(region, (void*)acl->key_name, | |||
| 2058 | strlen(acl->key_name)+1); | |||
| 2059 | acl->key_name = region_strdup(region, key_name); | |||
| 2060 | acl->key_options = key_opt; | |||
| 2061 | acl = acl->next; | |||
| 2062 | } | |||
| 2063 | } | |||
| 2064 | ||||
| 2065 | /** do the assoc_tsig command: associate the zone to use the tsig name */ | |||
| 2066 | static void | |||
| 2067 | do_assoc_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) | |||
| 2068 | { | |||
| 2069 | region_type* region = xfrd->nsd->options->region; | |||
| 2070 | char* arg2 = NULL((void*)0); | |||
| 2071 | struct zone_options* zone; | |||
| 2072 | struct key_options* key_opt; | |||
| 2073 | ||||
| 2074 | if(*arg == '\0') { | |||
| 2075 | (void)ssl_printf(ssl, "error: missing argument (zonename)\n"); | |||
| 2076 | return; | |||
| 2077 | } | |||
| 2078 | if(!find_arg2(ssl, arg, &arg2)) { | |||
| 2079 | (void)ssl_printf(ssl, "error: missing argument (keyname)\n"); | |||
| 2080 | return; | |||
| 2081 | } | |||
| 2082 | ||||
| 2083 | if(!get_zone_arg(ssl, xfrd, arg, &zone)) | |||
| 2084 | return; | |||
| 2085 | if(!zone) { | |||
| 2086 | (void)ssl_printf(ssl, "error: missing argument (zone)\n"); | |||
| 2087 | return; | |||
| 2088 | } | |||
| 2089 | key_opt = key_options_find(xfrd->nsd->options, arg2); | |||
| 2090 | if(!key_opt) { | |||
| 2091 | (void)ssl_printf(ssl, "error: key: %s does not exist\n", arg2); | |||
| 2092 | return; | |||
| 2093 | } | |||
| 2094 | ||||
| 2095 | zopt_set_acl_to_tsig(zone->pattern->allow_notify, region, arg2, | |||
| 2096 | key_opt); | |||
| 2097 | zopt_set_acl_to_tsig(zone->pattern->notify, region, arg2, key_opt); | |||
| 2098 | zopt_set_acl_to_tsig(zone->pattern->request_xfr, region, arg2, | |||
| 2099 | key_opt); | |||
| 2100 | zopt_set_acl_to_tsig(zone->pattern->provide_xfr, region, arg2, | |||
| 2101 | key_opt); | |||
| 2102 | zopt_set_acl_to_tsig(zone->pattern->allow_query, region, arg2, | |||
| 2103 | key_opt); | |||
| 2104 | ||||
| 2105 | task_new_add_pattern(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 2106 | xfrd->last_task, zone->pattern); | |||
| 2107 | xfrd_set_reload_now(xfrd); | |||
| 2108 | ||||
| 2109 | send_ok(ssl); | |||
| 2110 | } | |||
| 2111 | ||||
| 2112 | /** see if TSIG key is used in the acl */ | |||
| 2113 | static int | |||
| 2114 | acl_contains_tsig_key(struct acl_options* acl, const char* name) | |||
| 2115 | { | |||
| 2116 | while(acl) { | |||
| 2117 | if(acl->key_name && strcmp(acl->key_name, name) == 0) | |||
| 2118 | return 1; | |||
| 2119 | acl = acl->next; | |||
| 2120 | } | |||
| 2121 | return 0; | |||
| 2122 | } | |||
| 2123 | ||||
| 2124 | /** do the del_tsig command, remove an (unused) tsig */ | |||
| 2125 | static void | |||
| 2126 | do_del_tsig(RES* ssl, xfrd_state_type* xfrd, char* arg) { | |||
| 2127 | int used_key = 0; | |||
| 2128 | struct zone_options* zone; | |||
| 2129 | struct key_options* key_opt; | |||
| 2130 | ||||
| 2131 | if(*arg == '\0') { | |||
| 2132 | (void)ssl_printf(ssl, "error: missing argument (keyname)\n"); | |||
| 2133 | return; | |||
| 2134 | } | |||
| 2135 | key_opt = key_options_find(xfrd->nsd->options, arg); | |||
| 2136 | if(!key_opt) { | |||
| 2137 | (void)ssl_printf(ssl, "key %s does not exist, nothing to be deleted\n", arg); | |||
| 2138 | return; | |||
| 2139 | } | |||
| 2140 | RBTREE_FOR(zone, struct zone_options*, xfrd->nsd->options->zone_options)for(zone=(struct zone_options*)rbtree_first(xfrd->nsd-> options->zone_options); (rbnode_type*)zone != &rbtree_null_node ; zone = (struct zone_options*)rbtree_next((rbnode_type*)zone )) | |||
| 2141 | { | |||
| 2142 | if(acl_contains_tsig_key(zone->pattern->allow_notify, arg) || | |||
| 2143 | acl_contains_tsig_key(zone->pattern->notify, arg) || | |||
| 2144 | acl_contains_tsig_key(zone->pattern->request_xfr, arg) || | |||
| 2145 | acl_contains_tsig_key(zone->pattern->provide_xfr, arg) || | |||
| 2146 | acl_contains_tsig_key(zone->pattern->allow_query, arg)) { | |||
| 2147 | if(!ssl_printf(ssl, "zone %s uses key %s\n", | |||
| 2148 | zone->name, arg)) | |||
| 2149 | return; | |||
| 2150 | used_key = 1; | |||
| 2151 | break; | |||
| 2152 | } | |||
| 2153 | } | |||
| 2154 | ||||
| 2155 | if(used_key) { | |||
| 2156 | (void)ssl_printf(ssl, "error: key: %s is in use and cannot be deleted\n", arg); | |||
| 2157 | return; | |||
| 2158 | } else { | |||
| 2159 | remove_key(xfrd, arg); | |||
| 2160 | log_msg(LOG_INFO6, "key: %s is successfully deleted\n", arg); | |||
| 2161 | } | |||
| 2162 | ||||
| 2163 | send_ok(ssl); | |||
| 2164 | } | |||
| 2165 | ||||
| 2166 | /* returns `0` on failure */ | |||
| 2167 | static int | |||
| 2168 | cookie_secret_file_dump(RES* ssl, nsd_type const* nsd) { | |||
| 2169 | char const* secret_file = nsd->options->cookie_secret_file; | |||
| 2170 | char secret_hex[NSD_COOKIE_SECRET_SIZE16 * 2 + 1]; | |||
| 2171 | FILE* f; | |||
| 2172 | size_t i; | |||
| 2173 | assert( secret_file != NULL )((void)0); | |||
| 2174 | ||||
| 2175 | /* open write only and truncate */ | |||
| 2176 | if((f = fopen(secret_file, "w")) == NULL((void*)0) ) { | |||
| 2177 | (void)ssl_printf(ssl, "unable to open cookie secret file %s: %s", | |||
| 2178 | secret_file, strerror(errno(*__errno()))); | |||
| 2179 | return 0; | |||
| 2180 | } | |||
| 2181 | for(i = 0; i < nsd->cookie_count; i++) { | |||
| 2182 | struct cookie_secret const* cs = &nsd->cookie_secrets[i]; | |||
| 2183 | ssize_t const len = hex_ntop(cs->cookie_secret, NSD_COOKIE_SECRET_SIZE16, | |||
| 2184 | secret_hex, sizeof(secret_hex)); | |||
| 2185 | (void)len; /* silence unused variable warning with -DNDEBUG */ | |||
| 2186 | assert( len == NSD_COOKIE_SECRET_SIZE * 2 )((void)0); | |||
| 2187 | secret_hex[NSD_COOKIE_SECRET_SIZE16 * 2] = '\0'; | |||
| 2188 | fprintf(f, "%s\n", secret_hex); | |||
| 2189 | } | |||
| 2190 | explicit_bzero(secret_hex, sizeof(secret_hex)); | |||
| 2191 | fclose(f); | |||
| 2192 | return 1; | |||
| 2193 | } | |||
| 2194 | ||||
| 2195 | static void | |||
| 2196 | do_activate_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { | |||
| 2197 | nsd_type* nsd = xrfd->nsd; | |||
| 2198 | (void)arg; | |||
| 2199 | ||||
| 2200 | if(nsd->cookie_count <= 1 ) { | |||
| 2201 | (void)ssl_printf(ssl, "error: no staging cookie secret to activate\n"); | |||
| 2202 | return; | |||
| 2203 | } | |||
| 2204 | if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { | |||
| 2205 | (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); | |||
| 2206 | return; | |||
| 2207 | } | |||
| 2208 | if(!cookie_secret_file_dump(ssl, nsd)) { | |||
| 2209 | (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", | |||
| 2210 | nsd->options->cookie_secret_file); | |||
| 2211 | return; | |||
| 2212 | } | |||
| 2213 | activate_cookie_secret(nsd); | |||
| 2214 | (void)cookie_secret_file_dump(ssl, nsd); | |||
| 2215 | task_new_activate_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 2216 | xfrd->last_task); | |||
| 2217 | xfrd_set_reload_now(xfrd); | |||
| 2218 | send_ok(ssl); | |||
| 2219 | } | |||
| 2220 | ||||
| 2221 | static void | |||
| 2222 | do_drop_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { | |||
| 2223 | nsd_type* nsd = xrfd->nsd; | |||
| 2224 | (void)arg; | |||
| 2225 | ||||
| 2226 | if(nsd->cookie_count <= 1 ) { | |||
| 2227 | (void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n"); | |||
| 2228 | return; | |||
| 2229 | } | |||
| 2230 | if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { | |||
| 2231 | (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); | |||
| 2232 | return; | |||
| 2233 | } | |||
| 2234 | if(!cookie_secret_file_dump(ssl, nsd)) { | |||
| 2235 | (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", | |||
| 2236 | nsd->options->cookie_secret_file); | |||
| 2237 | return; | |||
| 2238 | } | |||
| 2239 | drop_cookie_secret(nsd); | |||
| 2240 | (void)cookie_secret_file_dump(ssl, nsd); | |||
| 2241 | task_new_drop_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 2242 | xfrd->last_task); | |||
| 2243 | xfrd_set_reload_now(xfrd); | |||
| 2244 | send_ok(ssl); | |||
| 2245 | } | |||
| 2246 | ||||
| 2247 | static void | |||
| 2248 | do_add_cookie_secret(RES* ssl, xfrd_state_type* xrfd, char* arg) { | |||
| 2249 | nsd_type* nsd = xrfd->nsd; | |||
| 2250 | uint8_t secret[NSD_COOKIE_SECRET_SIZE16]; | |||
| 2251 | ||||
| 2252 | if(*arg == '\0') { | |||
| 2253 | (void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n"); | |||
| 2254 | return; | |||
| 2255 | } | |||
| 2256 | if(strlen(arg) != 32) { | |||
| 2257 | explicit_bzero(arg, strlen(arg)); | |||
| 2258 | (void)ssl_printf(ssl, "invalid cookie secret: invalid argument length\n"); | |||
| 2259 | (void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n"); | |||
| 2260 | return; | |||
| 2261 | } | |||
| 2262 | if(hex_pton(arg, secret, NSD_COOKIE_SECRET_SIZE16) != NSD_COOKIE_SECRET_SIZE16 ) { | |||
| 2263 | explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE16); | |||
| 2264 | explicit_bzero(arg, strlen(arg)); | |||
| 2265 | (void)ssl_printf(ssl, "invalid cookie secret: parse error\n"); | |||
| 2266 | (void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n"); | |||
| 2267 | return; | |||
| 2268 | } | |||
| 2269 | if(!nsd->options->cookie_secret_file || !nsd->options->cookie_secret_file[0]) { | |||
| 2270 | explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE16); | |||
| 2271 | explicit_bzero(arg, strlen(arg)); | |||
| 2272 | (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); | |||
| 2273 | return; | |||
| 2274 | } | |||
| 2275 | if(!cookie_secret_file_dump(ssl, nsd)) { | |||
| 2276 | explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE16); | |||
| 2277 | explicit_bzero(arg, strlen(arg)); | |||
| 2278 | (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", | |||
| 2279 | nsd->options->cookie_secret_file); | |||
| 2280 | return; | |||
| 2281 | } | |||
| 2282 | add_cookie_secret(nsd, secret); | |||
| 2283 | explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE16); | |||
| 2284 | (void)cookie_secret_file_dump(ssl, nsd); | |||
| 2285 | task_new_add_cookie_secret(xfrd->nsd->task[xfrd->nsd->mytask], | |||
| 2286 | xfrd->last_task, arg); | |||
| 2287 | explicit_bzero(arg, strlen(arg)); | |||
| 2288 | xfrd_set_reload_now(xfrd); | |||
| 2289 | send_ok(ssl); | |||
| 2290 | } | |||
| 2291 | ||||
| 2292 | static void | |||
| 2293 | do_print_cookie_secrets(RES* ssl, xfrd_state_type* xrfd, char* arg) { | |||
| 2294 | nsd_type* nsd = xrfd->nsd; | |||
| 2295 | char secret_hex[NSD_COOKIE_SECRET_SIZE16 * 2 + 1]; | |||
| 2296 | int i; | |||
| 2297 | (void)arg; | |||
| 2298 | ||||
| 2299 | /* (void)ssl_printf(ssl, "cookie_secret_count=%zu\n", nsd->cookie_count); */ | |||
| 2300 | for(i = 0; (size_t)i < nsd->cookie_count; i++) { | |||
| 2301 | struct cookie_secret const* cs = &nsd->cookie_secrets[i]; | |||
| 2302 | ssize_t const len = hex_ntop(cs->cookie_secret, NSD_COOKIE_SECRET_SIZE16, | |||
| 2303 | secret_hex, sizeof(secret_hex)); | |||
| 2304 | (void)len; /* silence unused variable warning with -DNDEBUG */ | |||
| 2305 | assert( len == NSD_COOKIE_SECRET_SIZE * 2 )((void)0); | |||
| 2306 | secret_hex[NSD_COOKIE_SECRET_SIZE16 * 2] = '\0'; | |||
| 2307 | if (i == 0) | |||
| 2308 | (void)ssl_printf(ssl, "active : %s\n", secret_hex); | |||
| 2309 | else if (nsd->cookie_count == 2) | |||
| 2310 | (void)ssl_printf(ssl, "staging: %s\n", secret_hex); | |||
| 2311 | else | |||
| 2312 | (void)ssl_printf(ssl, "staging[%d]: %s\n", i, secret_hex); | |||
| 2313 | } | |||
| 2314 | explicit_bzero(secret_hex, sizeof(secret_hex)); | |||
| 2315 | } | |||
| 2316 | ||||
| 2317 | /** check for name with end-of-string, space or tab after it */ | |||
| 2318 | static int | |||
| 2319 | cmdcmp(char* p, const char* cmd, size_t len) | |||
| 2320 | { | |||
| 2321 | return strncmp(p,cmd,len)==0 && (p[len]==0||p[len]==' '||p[len]=='\t'); | |||
| 2322 | } | |||
| 2323 | ||||
| 2324 | /** execute a remote control command */ | |||
| 2325 | static void | |||
| 2326 | execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, struct rc_state* rs) | |||
| 2327 | { | |||
| 2328 | char* p = skipwhite(cmd); | |||
| 2329 | /* compare command */ | |||
| 2330 | if(cmdcmp(p, "stop", 4)) { | |||
| 2331 | do_stop(ssl, rc->xfrd); | |||
| 2332 | } else if(cmdcmp(p, "reload", 6)) { | |||
| 2333 | do_reload(ssl, rc->xfrd, skipwhite(p+6)); | |||
| 2334 | } else if(cmdcmp(p, "write", 5)) { | |||
| 2335 | do_write(ssl, rc->xfrd, skipwhite(p+5)); | |||
| 2336 | } else if(cmdcmp(p, "status", 6)) { | |||
| 2337 | do_status(ssl, rc->xfrd); | |||
| 2338 | } else if(cmdcmp(p, "stats_noreset", 13)) { | |||
| 2339 | do_stats(rc, 1, rs); | |||
| 2340 | } else if(cmdcmp(p, "stats", 5)) { | |||
| 2341 | do_stats(rc, 0, rs); | |||
| 2342 | } else if(cmdcmp(p, "log_reopen", 10)) { | |||
| 2343 | do_log_reopen(ssl, rc->xfrd); | |||
| 2344 | } else if(cmdcmp(p, "addzone", 7)) { | |||
| 2345 | do_addzone(ssl, rc->xfrd, skipwhite(p+7)); | |||
| 2346 | } else if(cmdcmp(p, "delzone", 7)) { | |||
| 2347 | do_delzone(ssl, rc->xfrd, skipwhite(p+7)); | |||
| 2348 | } else if(cmdcmp(p, "changezone", 10)) { | |||
| 2349 | do_changezone(ssl, rc->xfrd, skipwhite(p+10)); | |||
| 2350 | } else if(cmdcmp(p, "addzones", 8)) { | |||
| 2351 | do_addzones(ssl, rc->xfrd); | |||
| 2352 | } else if(cmdcmp(p, "delzones", 8)) { | |||
| 2353 | do_delzones(ssl, rc->xfrd); | |||
| 2354 | } else if(cmdcmp(p, "notify", 6)) { | |||
| 2355 | do_notify(ssl, rc->xfrd, skipwhite(p+6)); | |||
| 2356 | } else if(cmdcmp(p, "transfer", 8)) { | |||
| 2357 | do_transfer(ssl, rc->xfrd, skipwhite(p+8)); | |||
| 2358 | } else if(cmdcmp(p, "force_transfer", 14)) { | |||
| 2359 | do_force_transfer(ssl, rc->xfrd, skipwhite(p+14)); | |||
| 2360 | } else if(cmdcmp(p, "zonestatus", 10)) { | |||
| 2361 | do_zonestatus(ssl, rc->xfrd, skipwhite(p+10)); | |||
| 2362 | } else if(cmdcmp(p, "verbosity", 9)) { | |||
| 2363 | do_verbosity(ssl, skipwhite(p+9)); | |||
| 2364 | } else if(cmdcmp(p, "repattern", 9)) { | |||
| 2365 | do_repattern(ssl, rc->xfrd); | |||
| 2366 | } else if(cmdcmp(p, "reconfig", 8)) { | |||
| 2367 | do_repattern(ssl, rc->xfrd); | |||
| 2368 | } else if(cmdcmp(p, "serverpid", 9)) { | |||
| 2369 | do_serverpid(ssl, rc->xfrd); | |||
| 2370 | } else if(cmdcmp(p, "print_tsig", 10)) { | |||
| 2371 | do_print_tsig(ssl, rc->xfrd, skipwhite(p+10)); | |||
| 2372 | } else if(cmdcmp(p, "update_tsig", 11)) { | |||
| 2373 | do_update_tsig(ssl, rc->xfrd, skipwhite(p+11)); | |||
| 2374 | } else if(cmdcmp(p, "add_tsig", 8)) { | |||
| 2375 | do_add_tsig(ssl, rc->xfrd, skipwhite(p+8)); | |||
| 2376 | } else if(cmdcmp(p, "assoc_tsig", 10)) { | |||
| 2377 | do_assoc_tsig(ssl, rc->xfrd, skipwhite(p+10)); | |||
| 2378 | } else if(cmdcmp(p, "del_tsig", 8)) { | |||
| 2379 | do_del_tsig(ssl, rc->xfrd, skipwhite(p+8)); | |||
| 2380 | } else if(cmdcmp(p, "add_cookie_secret", 17)) { | |||
| 2381 | do_add_cookie_secret(ssl, rc->xfrd, skipwhite(p+17)); | |||
| 2382 | } else if(cmdcmp(p, "drop_cookie_secret", 18)) { | |||
| 2383 | do_drop_cookie_secret(ssl, rc->xfrd, skipwhite(p+18)); | |||
| 2384 | } else if(cmdcmp(p, "print_cookie_secrets", 20)) { | |||
| 2385 | do_print_cookie_secrets(ssl, rc->xfrd, skipwhite(p+20)); | |||
| 2386 | } else if(cmdcmp(p, "activate_cookie_secret", 22)) { | |||
| 2387 | do_activate_cookie_secret(ssl, rc->xfrd, skipwhite(p+22)); | |||
| 2388 | } else { | |||
| 2389 | (void)ssl_printf(ssl, "error unknown command '%s'\n", p); | |||
| 2390 | } | |||
| 2391 | } | |||
| 2392 | ||||
| 2393 | /** handle remote control request */ | |||
| 2394 | static void | |||
| 2395 | handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res) | |||
| 2396 | { | |||
| 2397 | int r; | |||
| 2398 | char pre[10]; | |||
| 2399 | char magic[8]; | |||
| 2400 | char buf[1024]; | |||
| 2401 | if (fcntl(s->c.ev_fd, F_SETFL4, 0) == -1) { /* set blocking */ | |||
| 2402 | log_msg(LOG_ERR3, "cannot fcntl rc: %s", strerror(errno(*__errno()))); | |||
| 2403 | } | |||
| 2404 | ||||
| 2405 | /* try to read magic UBCT[version]_space_ string */ | |||
| 2406 | if(res->ssl) { | |||
| 2407 | ERR_clear_error(); | |||
| 2408 | if((r=SSL_read(res->ssl, magic, (int)sizeof(magic)-1)) <= 0) { | |||
| 2409 | if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN6) | |||
| 2410 | return; | |||
| 2411 | log_crypto_err("could not SSL_read"); | |||
| 2412 | return; | |||
| 2413 | } | |||
| 2414 | } else { | |||
| 2415 | while(1) { | |||
| 2416 | ssize_t rr = read(res->fd, magic, sizeof(magic)-1); | |||
| 2417 | if(rr <= 0) { | |||
| 2418 | if(rr == 0) return; | |||
| 2419 | if(errno(*__errno()) == EINTR4 || errno(*__errno()) == EAGAIN35) | |||
| 2420 | continue; | |||
| 2421 | log_msg(LOG_ERR3, "could not read: %s", strerror(errno(*__errno()))); | |||
| 2422 | return; | |||
| 2423 | } | |||
| 2424 | r = (int)rr; | |||
| 2425 | break; | |||
| 2426 | } | |||
| 2427 | } | |||
| 2428 | magic[7] = 0; | |||
| 2429 | if( r != 7 || strncmp(magic, "NSDCT", 5) != 0) { | |||
| 2430 | VERBOSITY(2, (LOG_INFO, "control connection has bad header"))do { if ((2) <= verbosity) { log_msg (6, "control connection has bad header" ) ; } } while (0); | |||
| 2431 | /* probably wrong tool connected, ignore it completely */ | |||
| 2432 | return; | |||
| 2433 | } | |||
| 2434 | ||||
| 2435 | /* read the command line */ | |||
| 2436 | if(!ssl_read_line(res, buf, sizeof(buf))) { | |||
| 2437 | return; | |||
| 2438 | } | |||
| 2439 | snprintf(pre, sizeof(pre), "NSDCT%d ", NSD_CONTROL_VERSION1); | |||
| 2440 | if(strcmp(magic, pre) != 0) { | |||
| 2441 | VERBOSITY(2, (LOG_INFO, "control connection had bad "do { if ((2) <= verbosity) { log_msg (6, "control connection had bad " "version %s, cmd: %s", magic, buf) ; } } while (0) | |||
| 2442 | "version %s, cmd: %s", magic, buf))do { if ((2) <= verbosity) { log_msg (6, "control connection had bad " "version %s, cmd: %s", magic, buf) ; } } while (0); | |||
| 2443 | (void)ssl_printf(res, "error version mismatch\n"); | |||
| 2444 | return; | |||
| 2445 | } | |||
| 2446 | VERBOSITY(2, (LOG_INFO, "control cmd: %s", buf))do { if ((2) <= verbosity) { log_msg (6, "control cmd: %s" , buf) ; } } while (0); | |||
| 2447 | ||||
| 2448 | /* figure out what to do */ | |||
| 2449 | execute_cmd(rc, res, buf, s); | |||
| 2450 | } | |||
| 2451 | ||||
| 2452 | /** handle SSL_do_handshake changes to the file descriptor to wait for later */ | |||
| 2453 | static void | |||
| 2454 | remote_handshake_later(struct daemon_remote* rc, struct rc_state* s, int fd, | |||
| 2455 | int r, int r2) | |||
| 2456 | { | |||
| 2457 | if(r2 == SSL_ERROR_WANT_READ2) { | |||
| 2458 | if(s->shake_state == rc_hs_read) { | |||
| 2459 | /* try again later */ | |||
| 2460 | return; | |||
| 2461 | } | |||
| 2462 | s->shake_state = rc_hs_read; | |||
| 2463 | event_del(&s->c); | |||
| 2464 | memset(&s->c, 0, sizeof(s->c)); | |||
| 2465 | event_set(&s->c, fd, EV_PERSIST0x10|EV_TIMEOUT0x01|EV_READ0x02, | |||
| 2466 | remote_control_callback, s); | |||
| 2467 | if(event_base_set(xfrd->event_base, &s->c) != 0) | |||
| 2468 | log_msg(LOG_ERR3, "remote_accept: cannot set event_base"); | |||
| 2469 | if(event_add(&s->c, &s->tval) != 0) | |||
| 2470 | log_msg(LOG_ERR3, "remote_accept: cannot add event"); | |||
| 2471 | return; | |||
| 2472 | } else if(r2 == SSL_ERROR_WANT_WRITE3) { | |||
| 2473 | if(s->shake_state == rc_hs_write) { | |||
| 2474 | /* try again later */ | |||
| 2475 | return; | |||
| 2476 | } | |||
| 2477 | s->shake_state = rc_hs_write; | |||
| 2478 | event_del(&s->c); | |||
| 2479 | memset(&s->c, 0, sizeof(s->c)); | |||
| 2480 | event_set(&s->c, fd, EV_PERSIST0x10|EV_TIMEOUT0x01|EV_WRITE0x04, | |||
| 2481 | remote_control_callback, s); | |||
| 2482 | if(event_base_set(xfrd->event_base, &s->c) != 0) | |||
| 2483 | log_msg(LOG_ERR3, "remote_accept: cannot set event_base"); | |||
| 2484 | if(event_add(&s->c, &s->tval) != 0) | |||
| 2485 | log_msg(LOG_ERR3, "remote_accept: cannot add event"); | |||
| 2486 | return; | |||
| 2487 | } else { | |||
| 2488 | if(r == 0) | |||
| 2489 | log_msg(LOG_ERR3, "remote control connection closed prematurely"); | |||
| 2490 | log_crypto_err("remote control failed ssl"); | |||
| 2491 | clean_point(rc, s); | |||
| 2492 | } | |||
| 2493 | } | |||
| 2494 | ||||
| 2495 | static void | |||
| 2496 | remote_control_callback(int fd, short event, void* arg) | |||
| 2497 | { | |||
| 2498 | RES res; | |||
| 2499 | struct rc_state* s = (struct rc_state*)arg; | |||
| 2500 | struct daemon_remote* rc = s->rc; | |||
| 2501 | int r; | |||
| 2502 | if( (event&EV_TIMEOUT0x01) ) { | |||
| 2503 | log_msg(LOG_ERR3, "remote control timed out"); | |||
| 2504 | clean_point(rc, s); | |||
| 2505 | return; | |||
| 2506 | } | |||
| 2507 | if(s->ssl) { | |||
| 2508 | /* (continue to) setup the SSL connection */ | |||
| 2509 | ERR_clear_error(); | |||
| 2510 | r = SSL_do_handshake(s->ssl); | |||
| 2511 | if(r != 1) { | |||
| 2512 | int r2 = SSL_get_error(s->ssl, r); | |||
| 2513 | remote_handshake_later(rc, s, fd, r, r2); | |||
| 2514 | return; | |||
| 2515 | } | |||
| 2516 | s->shake_state = rc_none; | |||
| 2517 | } | |||
| 2518 | ||||
| 2519 | /* once handshake has completed, check authentication */ | |||
| 2520 | if (!rc->use_cert) { | |||
| 2521 | VERBOSITY(3, (LOG_INFO, "unauthenticated remote control connection"))do { if ((3) <= verbosity) { log_msg (6, "unauthenticated remote control connection" ) ; } } while (0); | |||
| 2522 | } else if(SSL_get_verify_result(s->ssl) == X509_V_OK0) { | |||
| 2523 | X509* x = SSL_get_peer_certificate(s->ssl); | |||
| 2524 | if(!x) { | |||
| 2525 | VERBOSITY(2, (LOG_INFO, "remote control connection "do { if ((2) <= verbosity) { log_msg (6, "remote control connection " "provided no client certificate") ; } } while (0) | |||
| 2526 | "provided no client certificate"))do { if ((2) <= verbosity) { log_msg (6, "remote control connection " "provided no client certificate") ; } } while (0); | |||
| 2527 | clean_point(rc, s); | |||
| 2528 | return; | |||
| 2529 | } | |||
| 2530 | VERBOSITY(3, (LOG_INFO, "remote control connection authenticated"))do { if ((3) <= verbosity) { log_msg (6, "remote control connection authenticated" ) ; } } while (0); | |||
| 2531 | X509_free(x); | |||
| 2532 | } else { | |||
| 2533 | VERBOSITY(2, (LOG_INFO, "remote control connection failed to "do { if ((2) <= verbosity) { log_msg (6, "remote control connection failed to " "authenticate with client certificate") ; } } while (0) | |||
| 2534 | "authenticate with client certificate"))do { if ((2) <= verbosity) { log_msg (6, "remote control connection failed to " "authenticate with client certificate") ; } } while (0); | |||
| 2535 | clean_point(rc, s); | |||
| 2536 | return; | |||
| 2537 | } | |||
| 2538 | ||||
| 2539 | /* if OK start to actually handle the request */ | |||
| 2540 | res.ssl = s->ssl; | |||
| 2541 | res.fd = fd; | |||
| 2542 | handle_req(rc, s, &res); | |||
| 2543 | ||||
| 2544 | if(!s->in_stats_list) { | |||
| 2545 | VERBOSITY(3, (LOG_INFO, "remote control operation completed"))do { if ((3) <= verbosity) { log_msg (6, "remote control operation completed" ) ; } } while (0); | |||
| 2546 | clean_point(rc, s); | |||
| 2547 | } | |||
| 2548 | } | |||
| 2549 | ||||
| 2550 | #ifdef BIND8_STATS | |||
| 2551 | static const char* | |||
| 2552 | opcode2str(int o) | |||
| 2553 | { | |||
| 2554 | switch(o) { | |||
| 2555 | case OPCODE_QUERY0: return "QUERY"; | |||
| 2556 | case OPCODE_IQUERY1: return "IQUERY"; | |||
| 2557 | case OPCODE_STATUS2: return "STATUS"; | |||
| 2558 | case OPCODE_NOTIFY4: return "NOTIFY"; | |||
| 2559 | case OPCODE_UPDATE5: return "UPDATE"; | |||
| 2560 | default: return "OTHER"; | |||
| 2561 | } | |||
| 2562 | } | |||
| 2563 | ||||
| 2564 | /** print long number */ | |||
| 2565 | static int | |||
| 2566 | print_longnum(RES* ssl, char* desc, uint64_t x) | |||
| 2567 | { | |||
| 2568 | if(x > (uint64_t)1024*1024*1024) { | |||
| 2569 | /* more than a Gb */ | |||
| 2570 | size_t front = (size_t)(x / (uint64_t)1000000); | |||
| 2571 | size_t back = (size_t)(x % (uint64_t)1000000); | |||
| 2572 | return ssl_printf(ssl, "%s%lu%6.6lu\n", desc, | |||
| 2573 | (unsigned long)front, (unsigned long)back); | |||
| 2574 | } else { | |||
| 2575 | return ssl_printf(ssl, "%s%lu\n", desc, (unsigned long)x); | |||
| 2576 | } | |||
| 2577 | } | |||
| 2578 | ||||
| 2579 | /* print one block of statistics. n is name and d is delimiter */ | |||
| 2580 | static void | |||
| 2581 | print_stat_block(RES* ssl, char* n, char* d, struct nsdst* st) | |||
| 2582 | { | |||
| 2583 | const char* rcstr[] = {"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", | |||
| 2584 | "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH", | |||
| 2585 | "NOTZONE", "RCODE11", "RCODE12", "RCODE13", "RCODE14", "RCODE15", | |||
| 2586 | "BADVERS" | |||
| 2587 | }; | |||
| 2588 | size_t i; | |||
| 2589 | for(i=0; i<= 255; i++) { | |||
| 2590 | if(inhibit_zero && st->qtype[i] == 0 && | |||
| 2591 | strncmp(rrtype_to_string(i), "TYPE", 4) == 0) | |||
| 2592 | continue; | |||
| 2593 | if(!ssl_printf(ssl, "%s%snum.type.%s=%lu\n", n, d, | |||
| 2594 | rrtype_to_string(i), (unsigned long)st->qtype[i])) | |||
| 2595 | return; | |||
| 2596 | } | |||
| 2597 | ||||
| 2598 | /* opcode */ | |||
| 2599 | for(i=0; i<6; i++) { | |||
| 2600 | if(inhibit_zero && st->opcode[i] == 0 && i != OPCODE_QUERY0) | |||
| 2601 | continue; | |||
| 2602 | if(!ssl_printf(ssl, "%s%snum.opcode.%s=%lu\n", n, d, | |||
| 2603 | opcode2str(i), (unsigned long)st->opcode[i])) | |||
| 2604 | return; | |||
| 2605 | } | |||
| 2606 | ||||
| 2607 | /* qclass */ | |||
| 2608 | for(i=0; i<4; i++) { | |||
| 2609 | if(inhibit_zero && st->qclass[i] == 0 && i != CLASS_IN1) | |||
| 2610 | continue; | |||
| 2611 | if(!ssl_printf(ssl, "%s%snum.class.%s=%lu\n", n, d, | |||
| 2612 | rrclass_to_string(i), (unsigned long)st->qclass[i])) | |||
| 2613 | return; | |||
| 2614 | } | |||
| 2615 | ||||
| 2616 | /* rcode */ | |||
| 2617 | for(i=0; i<17; i++) { | |||
| 2618 | if(inhibit_zero && st->rcode[i] == 0 && | |||
| 2619 | i > RCODE_YXDOMAIN6) /* NSD does not use larger */ | |||
| 2620 | continue; | |||
| 2621 | if(!ssl_printf(ssl, "%s%snum.rcode.%s=%lu\n", n, d, rcstr[i], | |||
| 2622 | (unsigned long)st->rcode[i])) | |||
| 2623 | return; | |||
| 2624 | } | |||
| 2625 | ||||
| 2626 | /* edns */ | |||
| 2627 | if(!ssl_printf(ssl, "%s%snum.edns=%lu\n", n, d, (unsigned long)st->edns)) | |||
| 2628 | return; | |||
| 2629 | ||||
| 2630 | /* ednserr */ | |||
| 2631 | if(!ssl_printf(ssl, "%s%snum.ednserr=%lu\n", n, d, | |||
| 2632 | (unsigned long)st->ednserr)) | |||
| 2633 | return; | |||
| 2634 | ||||
| 2635 | /* qudp */ | |||
| 2636 | if(!ssl_printf(ssl, "%s%snum.udp=%lu\n", n, d, (unsigned long)st->qudp)) | |||
| 2637 | return; | |||
| 2638 | /* qudp6 */ | |||
| 2639 | if(!ssl_printf(ssl, "%s%snum.udp6=%lu\n", n, d, (unsigned long)st->qudp6)) | |||
| 2640 | return; | |||
| 2641 | /* ctcp */ | |||
| 2642 | if(!ssl_printf(ssl, "%s%snum.tcp=%lu\n", n, d, (unsigned long)st->ctcp)) | |||
| 2643 | return; | |||
| 2644 | /* ctcp6 */ | |||
| 2645 | if(!ssl_printf(ssl, "%s%snum.tcp6=%lu\n", n, d, (unsigned long)st->ctcp6)) | |||
| 2646 | return; | |||
| 2647 | /* ctls */ | |||
| 2648 | if(!ssl_printf(ssl, "%s%snum.tls=%lu\n", n, d, (unsigned long)st->ctls)) | |||
| 2649 | return; | |||
| 2650 | /* ctls6 */ | |||
| 2651 | if(!ssl_printf(ssl, "%s%snum.tls6=%lu\n", n, d, (unsigned long)st->ctls6)) | |||
| 2652 | return; | |||
| 2653 | ||||
| 2654 | /* nona */ | |||
| 2655 | if(!ssl_printf(ssl, "%s%snum.answer_wo_aa=%lu\n", n, d, | |||
| 2656 | (unsigned long)st->nona)) | |||
| 2657 | return; | |||
| 2658 | ||||
| 2659 | /* rxerr */ | |||
| 2660 | if(!ssl_printf(ssl, "%s%snum.rxerr=%lu\n", n, d, (unsigned long)st->rxerr)) | |||
| 2661 | return; | |||
| 2662 | ||||
| 2663 | /* txerr */ | |||
| 2664 | if(!ssl_printf(ssl, "%s%snum.txerr=%lu\n", n, d, (unsigned long)st->txerr)) | |||
| 2665 | return; | |||
| 2666 | ||||
| 2667 | /* number of requested-axfr, number of times axfr served to clients */ | |||
| 2668 | if(!ssl_printf(ssl, "%s%snum.raxfr=%lu\n", n, d, (unsigned long)st->raxfr)) | |||
| 2669 | return; | |||
| 2670 | ||||
| 2671 | /* truncated */ | |||
| 2672 | if(!ssl_printf(ssl, "%s%snum.truncated=%lu\n", n, d, | |||
| 2673 | (unsigned long)st->truncated)) | |||
| 2674 | return; | |||
| 2675 | ||||
| 2676 | /* dropped */ | |||
| 2677 | if(!ssl_printf(ssl, "%s%snum.dropped=%lu\n", n, d, | |||
| 2678 | (unsigned long)st->dropped)) | |||
| 2679 | return; | |||
| 2680 | } | |||
| 2681 | ||||
| 2682 | #ifdef USE_ZONE_STATS | |||
| 2683 | static void | |||
| 2684 | resize_zonestat(xfrd_state_type* xfrd, size_t num) | |||
| 2685 | { | |||
| 2686 | struct nsdst** a = xalloc_array_zero(num, sizeof(struct nsdst*)); | |||
| 2687 | if(xfrd->zonestat_clear_num != 0) | |||
| 2688 | memcpy(a, xfrd->zonestat_clear, xfrd->zonestat_clear_num | |||
| 2689 | * sizeof(struct nsdst*)); | |||
| 2690 | free(xfrd->zonestat_clear); | |||
| 2691 | xfrd->zonestat_clear = a; | |||
| 2692 | xfrd->zonestat_clear_num = num; | |||
| 2693 | } | |||
| 2694 | ||||
| 2695 | static void | |||
| 2696 | zonestat_print(RES* ssl, xfrd_state_type* xfrd, int clear) | |||
| 2697 | { | |||
| 2698 | struct zonestatname* n; | |||
| 2699 | struct nsdst stat0, stat1; | |||
| 2700 | RBTREE_FOR(n, struct zonestatname*, xfrd->nsd->options->zonestatnames)for(n=(struct zonestatname*)rbtree_first(xfrd->nsd->options ->zonestatnames); (rbnode_type*)n != &rbtree_null_node ; n = (struct zonestatname*)rbtree_next((rbnode_type*)n)){ | |||
| 2701 | char* name = (char*)n->node.key; | |||
| 2702 | if(n->id >= xfrd->zonestat_safe) | |||
| 2703 | continue; /* newly allocated and reload has not yet | |||
| 2704 | done and replied with new size */ | |||
| 2705 | if(name == NULL((void*)0) || name[0]==0) | |||
| 2706 | continue; /* empty name, do not output */ | |||
| 2707 | /* the statistics are stored in two blocks, during reload | |||
| 2708 | * the newly forked processes get the other block to use, | |||
| 2709 | * these blocks are mmapped and are currently in use to | |||
| 2710 | * add statistics to */ | |||
| 2711 | memcpy(&stat0, &xfrd->nsd->zonestat[0][n->id], sizeof(stat0)); | |||
| 2712 | memcpy(&stat1, &xfrd->nsd->zonestat[1][n->id], sizeof(stat1)); | |||
| 2713 | stats_add(&stat0, &stat1); | |||
| 2714 | ||||
| 2715 | /* save a copy of current (cumulative) stats in stat1 */ | |||
| 2716 | memcpy(&stat1, &stat0, sizeof(stat1)); | |||
| 2717 | /* subtract last total of stats that was 'cleared' */ | |||
| 2718 | if(n->id < xfrd->zonestat_clear_num && | |||
| 2719 | xfrd->zonestat_clear[n->id]) | |||
| 2720 | stats_subtract(&stat0, xfrd->zonestat_clear[n->id]); | |||
| 2721 | if(clear) { | |||
| 2722 | /* extend storage array if needed */ | |||
| 2723 | if(n->id >= xfrd->zonestat_clear_num) { | |||
| 2724 | if(n->id+1 < xfrd->nsd->options->zonestatnames->count) | |||
| 2725 | resize_zonestat(xfrd, xfrd->nsd->options->zonestatnames->count); | |||
| 2726 | else | |||
| 2727 | resize_zonestat(xfrd, n->id+1); | |||
| 2728 | } | |||
| 2729 | if(!xfrd->zonestat_clear[n->id]) | |||
| 2730 | xfrd->zonestat_clear[n->id] = xalloc( | |||
| 2731 | sizeof(struct nsdst)); | |||
| 2732 | /* store last total of stats */ | |||
| 2733 | memcpy(xfrd->zonestat_clear[n->id], &stat1, | |||
| 2734 | sizeof(struct nsdst)); | |||
| 2735 | } | |||
| 2736 | ||||
| 2737 | /* stat0 contains the details that we want to print */ | |||
| 2738 | if(!ssl_printf(ssl, "%s%snum.queries=%lu\n", name, ".", | |||
| 2739 | (unsigned long)(stat0.qudp + stat0.qudp6 + stat0.ctcp + | |||
| 2740 | stat0.ctcp6 + stat0.ctls + stat0.ctls6))) | |||
| 2741 | return; | |||
| 2742 | print_stat_block(ssl, name, ".", &stat0); | |||
| 2743 | } | |||
| 2744 | } | |||
| 2745 | #endif /* USE_ZONE_STATS */ | |||
| 2746 | ||||
| 2747 | static void | |||
| 2748 | print_stats(RES* ssl, xfrd_state_type* xfrd, struct timeval* now, int clear) | |||
| 2749 | { | |||
| 2750 | size_t i; | |||
| 2751 | stc_type total = 0; | |||
| 2752 | struct timeval elapsed, uptime; | |||
| 2753 | ||||
| 2754 | /* per CPU and total */ | |||
| 2755 | for(i=0; i<xfrd->nsd->child_count; i++) { | |||
| 2756 | if(!ssl_printf(ssl, "server%d.queries=%lu\n", (int)i, | |||
| 2757 | (unsigned long)xfrd->nsd->children[i].query_count)) | |||
| 2758 | return; | |||
| 2759 | total += xfrd->nsd->children[i].query_count; | |||
| 2760 | } | |||
| 2761 | if(!ssl_printf(ssl, "num.queries=%lu\n", (unsigned long)total)) | |||
| 2762 | return; | |||
| 2763 | ||||
| 2764 | /* time elapsed and uptime (in seconds) */ | |||
| 2765 | timeval_subtract(&uptime, now, &xfrd->nsd->rc->boot_time); | |||
| 2766 | timeval_subtract(&elapsed, now, &xfrd->nsd->rc->stats_time); | |||
| 2767 | if(!ssl_printf(ssl, "time.boot=%lu.%6.6lu\n", | |||
| 2768 | (unsigned long)uptime.tv_sec, (unsigned long)uptime.tv_usec)) | |||
| 2769 | return; | |||
| 2770 | if(!ssl_printf(ssl, "time.elapsed=%lu.%6.6lu\n", | |||
| 2771 | (unsigned long)elapsed.tv_sec, (unsigned long)elapsed.tv_usec)) | |||
| 2772 | return; | |||
| 2773 | ||||
| 2774 | /* mem info, database on disksize */ | |||
| 2775 | if(!print_longnum(ssl, "size.db.disk=", xfrd->nsd->st.db_disk)) | |||
| 2776 | return; | |||
| 2777 | if(!print_longnum(ssl, "size.db.mem=", xfrd->nsd->st.db_mem)) | |||
| 2778 | return; | |||
| 2779 | if(!print_longnum(ssl, "size.xfrd.mem=", region_get_mem(xfrd->region))) | |||
| 2780 | return; | |||
| 2781 | if(!print_longnum(ssl, "size.config.disk=", | |||
| 2782 | xfrd->nsd->options->zonelist_off)) | |||
| 2783 | return; | |||
| 2784 | if(!print_longnum(ssl, "size.config.mem=", region_get_mem( | |||
| 2785 | xfrd->nsd->options->region))) | |||
| 2786 | return; | |||
| 2787 | print_stat_block(ssl, "", "", &xfrd->nsd->st); | |||
| 2788 | ||||
| 2789 | /* zone statistics */ | |||
| 2790 | if(!ssl_printf(ssl, "zone.master=%lu\n", | |||
| 2791 | (unsigned long)(xfrd->notify_zones->count - xfrd->zones->count))) | |||
| 2792 | return; | |||
| 2793 | if(!ssl_printf(ssl, "zone.slave=%lu\n", (unsigned long)xfrd->zones->count)) | |||
| 2794 | return; | |||
| 2795 | #ifdef USE_ZONE_STATS | |||
| 2796 | zonestat_print(ssl, xfrd, clear); /* per-zone statistics */ | |||
| 2797 | #else | |||
| 2798 | (void)clear; | |||
| 2799 | #endif | |||
| 2800 | } | |||
| 2801 | ||||
| 2802 | static void | |||
| 2803 | clear_stats(xfrd_state_type* xfrd) | |||
| 2804 | { | |||
| 2805 | size_t i; | |||
| 2806 | uint64_t dbd = xfrd->nsd->st.db_disk; | |||
| 2807 | uint64_t dbm = xfrd->nsd->st.db_mem; | |||
| 2808 | for(i=0; i<xfrd->nsd->child_count; i++) { | |||
| 2809 | xfrd->nsd->children[i].query_count = 0; | |||
| 2810 | } | |||
| 2811 | memset(&xfrd->nsd->st, 0, sizeof(struct nsdst)); | |||
| 2812 | /* zonestats are cleared by storing the cumulative value that | |||
| 2813 | * was last printed in the zonestat_clear array, and subtracting | |||
| 2814 | * that before the next stats printout */ | |||
| 2815 | xfrd->nsd->st.db_disk = dbd; | |||
| 2816 | xfrd->nsd->st.db_mem = dbm; | |||
| 2817 | } | |||
| 2818 | ||||
| 2819 | void | |||
| 2820 | daemon_remote_process_stats(struct daemon_remote* rc) | |||
| 2821 | { | |||
| 2822 | RES res; | |||
| 2823 | struct rc_state* s; | |||
| 2824 | struct timeval now; | |||
| 2825 | if(!rc) return; | |||
| 2826 | if(gettimeofday(&now, NULL((void*)0)) == -1) | |||
| 2827 | log_msg(LOG_ERR3, "gettimeofday: %s", strerror(errno(*__errno()))); | |||
| 2828 | /* pop one and give it stats */ | |||
| 2829 | while((s = rc->stats_list)) { | |||
| 2830 | assert(s->in_stats_list)((void)0); | |||
| 2831 | res.ssl = s->ssl; | |||
| 2832 | res.fd = s->fd; | |||
| 2833 | print_stats(&res, rc->xfrd, &now, (s->in_stats_list == 1)); | |||
| 2834 | if(s->in_stats_list == 1) { | |||
| 2835 | clear_stats(rc->xfrd); | |||
| 2836 | rc->stats_time = now; | |||
| 2837 | } | |||
| 2838 | VERBOSITY(3, (LOG_INFO, "remote control stats printed"))do { if ((3) <= verbosity) { log_msg (6, "remote control stats printed" ) ; } } while (0); | |||
| 2839 | rc->stats_list = s->next; | |||
| 2840 | s->in_stats_list = 0; | |||
| 2841 | clean_point(rc, s); | |||
| 2842 | } | |||
| 2843 | } | |||
| 2844 | #endif /* BIND8_STATS */ | |||
| 2845 | ||||
| 2846 | int | |||
| 2847 | create_local_accept_sock(const char *path, int* noproto) | |||
| 2848 | { | |||
| 2849 | #ifdef HAVE_SYS_UN_H1 | |||
| 2850 | int s; | |||
| 2851 | struct sockaddr_un usock; | |||
| 2852 | ||||
| 2853 | VERBOSITY(3, (LOG_INFO, "creating unix socket %s", path))do { if ((3) <= verbosity) { log_msg (6, "creating unix socket %s" , path) ; } } while (0); | |||
| 2854 | #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN1 | |||
| 2855 | /* this member exists on BSDs, not Linux */ | |||
| 2856 | usock.sun_len = (unsigned)sizeof(usock); | |||
| 2857 | #endif | |||
| 2858 | usock.sun_family = AF_LOCAL1; | |||
| 2859 | /* length is 92-108, 104 on FreeBSD */ | |||
| 2860 | (void)strlcpy(usock.sun_path, path, sizeof(usock.sun_path)); | |||
| 2861 | ||||
| 2862 | if ((s = socket(AF_LOCAL1, SOCK_STREAM1, 0)) == -1) { | |||
| 2863 | log_msg(LOG_ERR3, "Cannot create local socket %s (%s)", | |||
| 2864 | path, strerror(errno(*__errno()))); | |||
| 2865 | return -1; | |||
| 2866 | } | |||
| 2867 | ||||
| 2868 | if (unlink(path) && errno(*__errno()) != ENOENT2) { | |||
| 2869 | /* The socket already exists and cannot be removed */ | |||
| 2870 | log_msg(LOG_ERR3, "Cannot remove old local socket %s (%s)", | |||
| 2871 | path, strerror(errno(*__errno()))); | |||
| 2872 | goto err; | |||
| 2873 | } | |||
| 2874 | ||||
| 2875 | if (bind(s, (struct sockaddr *)&usock, | |||
| 2876 | (socklen_t)sizeof(struct sockaddr_un)) == -1) { | |||
| 2877 | log_msg(LOG_ERR3, "Cannot bind local socket %s (%s)", | |||
| 2878 | path, strerror(errno(*__errno()))); | |||
| 2879 | goto err; | |||
| 2880 | } | |||
| 2881 | ||||
| 2882 | if (fcntl(s, F_SETFL4, O_NONBLOCK0x0004) == -1) { | |||
| 2883 | log_msg(LOG_ERR3, "Cannot set non-blocking mode"); | |||
| 2884 | goto err; | |||
| 2885 | } | |||
| 2886 | ||||
| 2887 | if (listen(s, TCP_BACKLOG256) == -1) { | |||
| 2888 | log_msg(LOG_ERR3, "can't listen: %s", strerror(errno(*__errno()))); | |||
| 2889 | goto err; | |||
| 2890 | } | |||
| 2891 | ||||
| 2892 | (void)noproto; /*unused*/ | |||
| 2893 | return s; | |||
| 2894 | ||||
| 2895 | err: | |||
| 2896 | close(s); | |||
| 2897 | return -1; | |||
| 2898 | ||||
| 2899 | #else | |||
| 2900 | (void)path; | |||
| 2901 | log_msg(LOG_ERR3, "Local sockets are not supported"); | |||
| 2902 | *noproto = 1; | |||
| 2903 | return -1; | |||
| 2904 | #endif | |||
| 2905 | } | |||
| 2906 | ||||
| 2907 | #endif /* HAVE_SSL */ |