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