1 /* 2 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1998-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "port_before.h" 19 20 #include <sys/param.h> 21 #include <sys/file.h> 22 #include <sys/socket.h> 23 24 #include <netinet/in.h> 25 #include <arpa/nameser.h> 26 #include <arpa/inet.h> 27 28 #include <ctype.h> 29 #include <errno.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 #include <unistd.h> 35 #ifdef HAVE_MEMORY_H 36 #include <memory.h> 37 #endif 38 39 #include <isc/assertions.h> 40 #include <isc/ctl.h> 41 #include <isc/eventlib.h> 42 #include <isc/list.h> 43 #include <isc/memcluster.h> 44 45 #include "ctl_p.h" 46 47 #include "port_after.h" 48 49 /* Constants. */ 50 51 52 /* Macros. */ 53 54 #define donefunc_p(ctx) ((ctx).donefunc != NULL) 55 #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \ 56 isdigit((unsigned char)(line[1])) && \ 57 isdigit((unsigned char)(line[2]))) 58 #define arpacont_p(line) (line[3] == '-') 59 #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \ 60 line[3] == '\r' || line[3] == '\0') 61 62 /* Types. */ 63 64 enum state { 65 initializing = 0, connecting, connected, destroyed 66 }; 67 68 struct ctl_tran { 69 LINK(struct ctl_tran) link; 70 LINK(struct ctl_tran) wlink; 71 struct ctl_cctx * ctx; 72 struct ctl_buf outbuf; 73 ctl_clntdone donefunc; 74 void * uap; 75 }; 76 77 struct ctl_cctx { 78 enum state state; 79 evContext ev; 80 int sock; 81 ctl_logfunc logger; 82 ctl_clntdone donefunc; 83 void * uap; 84 evConnID coID; 85 evTimerID tiID; 86 evFileID rdID; 87 evStreamID wrID; 88 struct ctl_buf inbuf; 89 struct timespec timeout; 90 LIST(struct ctl_tran) tran; 91 LIST(struct ctl_tran) wtran; 92 }; 93 94 /* Forward. */ 95 96 static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int); 97 static void start_write(struct ctl_cctx *); 98 static void destroy(struct ctl_cctx *, int); 99 static void error(struct ctl_cctx *); 100 static void new_state(struct ctl_cctx *, enum state); 101 static void conn_done(evContext, void *, int, 102 const void *, int, 103 const void *, int); 104 static void write_done(evContext, void *, int, int); 105 static void start_read(struct ctl_cctx *); 106 static void stop_read(struct ctl_cctx *); 107 static void readable(evContext, void *, int, int); 108 static void start_timer(struct ctl_cctx *); 109 static void stop_timer(struct ctl_cctx *); 110 static void touch_timer(struct ctl_cctx *); 111 static void timer(evContext, void *, 112 struct timespec, struct timespec); 113 114 #ifndef HAVE_MEMCHR 115 static void * 116 memchr(const void *b, int c, size_t len) { 117 const unsigned char *p = b; 118 size_t i; 119 120 for (i = 0; i < len; i++, p++) 121 if (*p == (unsigned char)c) 122 return ((void *)p); 123 return (NULL); 124 } 125 #endif 126 127 /* Private data. */ 128 129 static const char * const state_names[] = { 130 "initializing", "connecting", "connected", "destroyed" 131 }; 132 133 /* Public. */ 134 135 /*% 136 * void 137 * ctl_client() 138 * create, condition, and connect to a listener on the control port. 139 */ 140 struct ctl_cctx * 141 ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len, 142 const struct sockaddr *sap, size_t sap_len, 143 ctl_clntdone donefunc, void *uap, 144 u_int timeout, ctl_logfunc logger) 145 { 146 static const char me[] = "ctl_client"; 147 static const int on = 1; 148 struct ctl_cctx *ctx; 149 struct sockaddr *captmp; 150 151 if (logger == NULL) 152 logger = ctl_logger; 153 ctx = memget(sizeof *ctx); 154 if (ctx == NULL) { 155 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno)); 156 goto fatal; 157 } 158 ctx->state = initializing; 159 ctx->ev = lev; 160 ctx->logger = logger; 161 ctx->timeout = evConsTime(timeout, 0); 162 ctx->donefunc = donefunc; 163 ctx->uap = uap; 164 ctx->coID.opaque = NULL; 165 ctx->tiID.opaque = NULL; 166 ctx->rdID.opaque = NULL; 167 ctx->wrID.opaque = NULL; 168 buffer_init(ctx->inbuf); 169 INIT_LIST(ctx->tran); 170 INIT_LIST(ctx->wtran); 171 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC); 172 if (ctx->sock > evHighestFD(ctx->ev)) { 173 ctx->sock = -1; 174 errno = ENOTSOCK; 175 } 176 if (ctx->sock < 0) { 177 (*ctx->logger)(ctl_error, "%s: socket: %s", 178 me, strerror(errno)); 179 goto fatal; 180 } 181 if (cap != NULL) { 182 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR, 183 (const char *)&on, sizeof on) != 0) { 184 (*ctx->logger)(ctl_warning, 185 "%s: setsockopt(REUSEADDR): %s", 186 me, strerror(errno)); 187 } 188 DE_CONST(cap, captmp); 189 if (bind(ctx->sock, captmp, cap_len) < 0) { 190 (*ctx->logger)(ctl_error, "%s: bind: %s", me, 191 strerror(errno)); 192 goto fatal; 193 } 194 } 195 if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len, 196 conn_done, ctx, &ctx->coID) < 0) { 197 (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s", 198 me, ctx->sock, strerror(errno)); 199 fatal: 200 if (ctx != NULL) { 201 if (ctx->sock >= 0) 202 close(ctx->sock); 203 memput(ctx, sizeof *ctx); 204 } 205 return (NULL); 206 } 207 new_state(ctx, connecting); 208 return (ctx); 209 } 210 211 /*% 212 * void 213 * ctl_endclient(ctx) 214 * close a client and release all of its resources. 215 */ 216 void 217 ctl_endclient(struct ctl_cctx *ctx) { 218 if (ctx->state != destroyed) 219 destroy(ctx, 0); 220 memput(ctx, sizeof *ctx); 221 } 222 223 /*% 224 * int 225 * ctl_command(ctx, cmd, len, donefunc, uap) 226 * Queue a transaction, which will begin with sending cmd 227 * and complete by calling donefunc with the answer. 228 */ 229 int 230 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len, 231 ctl_clntdone donefunc, void *uap) 232 { 233 struct ctl_tran *tran; 234 char *pc; 235 unsigned int n; 236 237 switch (ctx->state) { 238 case destroyed: 239 errno = ENOTCONN; 240 return (-1); 241 case connecting: 242 case connected: 243 break; 244 default: 245 abort(); 246 } 247 if (len >= (size_t)MAX_LINELEN) { 248 errno = EMSGSIZE; 249 return (-1); 250 } 251 tran = new_tran(ctx, donefunc, uap, 1); 252 if (tran == NULL) 253 return (-1); 254 if (ctl_bufget(&tran->outbuf, ctx->logger) < 0) 255 return (-1); 256 memcpy(tran->outbuf.text, cmd, len); 257 tran->outbuf.used = len; 258 for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++) 259 if (!isascii((unsigned char)*pc) || 260 !isprint((unsigned char)*pc)) 261 *pc = '\040'; 262 start_write(ctx); 263 return (0); 264 } 265 266 /* Private. */ 267 268 static struct ctl_tran * 269 new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) { 270 struct ctl_tran *new = memget(sizeof *new); 271 272 if (new == NULL) 273 return (NULL); 274 new->ctx = ctx; 275 buffer_init(new->outbuf); 276 new->donefunc = donefunc; 277 new->uap = uap; 278 INIT_LINK(new, link); 279 INIT_LINK(new, wlink); 280 APPEND(ctx->tran, new, link); 281 if (w) 282 APPEND(ctx->wtran, new, wlink); 283 return (new); 284 } 285 286 static void 287 start_write(struct ctl_cctx *ctx) { 288 static const char me[] = "isc/ctl_clnt::start_write"; 289 struct ctl_tran *tran; 290 struct iovec iov[2], *iovp = iov; 291 char * tmp; 292 293 REQUIRE(ctx->state == connecting || ctx->state == connected); 294 /* If there is a write in progress, don't try to write more yet. */ 295 if (ctx->wrID.opaque != NULL) 296 return; 297 /* If there are no trans, make sure timer is off, and we're done. */ 298 if (EMPTY(ctx->wtran)) { 299 if (ctx->tiID.opaque != NULL) 300 stop_timer(ctx); 301 return; 302 } 303 /* Pull it off the head of the write queue. */ 304 tran = HEAD(ctx->wtran); 305 UNLINK(ctx->wtran, tran, wlink); 306 /* Since there are some trans, make sure timer is successfully "on". */ 307 if (ctx->tiID.opaque != NULL) 308 touch_timer(ctx); 309 else 310 start_timer(ctx); 311 if (ctx->state == destroyed) 312 return; 313 /* Marshall a newline-terminated message and clock it out. */ 314 *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used); 315 DE_CONST("\r\n", tmp); 316 *iovp++ = evConsIovec(tmp, 2); 317 if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov, 318 write_done, tran, &ctx->wrID) < 0) { 319 (*ctx->logger)(ctl_error, "%s: evWrite: %s", me, 320 strerror(errno)); 321 error(ctx); 322 return; 323 } 324 if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) { 325 (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me, 326 strerror(errno)); 327 error(ctx); 328 return; 329 } 330 } 331 332 static void 333 destroy(struct ctl_cctx *ctx, int notify) { 334 struct ctl_tran *this, *next; 335 336 if (ctx->sock != -1) { 337 (void) close(ctx->sock); 338 ctx->sock = -1; 339 } 340 switch (ctx->state) { 341 case connecting: 342 REQUIRE(ctx->wrID.opaque == NULL); 343 REQUIRE(EMPTY(ctx->tran)); 344 /* 345 * This test is nec'y since destroy() can be called from 346 * start_read() while the state is still "connecting". 347 */ 348 if (ctx->coID.opaque != NULL) { 349 (void)evCancelConn(ctx->ev, ctx->coID); 350 ctx->coID.opaque = NULL; 351 } 352 break; 353 case connected: 354 REQUIRE(ctx->coID.opaque == NULL); 355 if (ctx->wrID.opaque != NULL) { 356 (void)evCancelRW(ctx->ev, ctx->wrID); 357 ctx->wrID.opaque = NULL; 358 } 359 if (ctx->rdID.opaque != NULL) 360 stop_read(ctx); 361 break; 362 case destroyed: 363 break; 364 default: 365 abort(); 366 } 367 if (allocated_p(ctx->inbuf)) 368 ctl_bufput(&ctx->inbuf); 369 for (this = HEAD(ctx->tran); this != NULL; this = next) { 370 next = NEXT(this, link); 371 if (allocated_p(this->outbuf)) 372 ctl_bufput(&this->outbuf); 373 if (notify && this->donefunc != NULL) 374 (*this->donefunc)(ctx, this->uap, NULL, 0); 375 memput(this, sizeof *this); 376 } 377 if (ctx->tiID.opaque != NULL) 378 stop_timer(ctx); 379 new_state(ctx, destroyed); 380 } 381 382 static void 383 error(struct ctl_cctx *ctx) { 384 REQUIRE(ctx->state != destroyed); 385 destroy(ctx, 1); 386 } 387 388 static void 389 new_state(struct ctl_cctx *ctx, enum state new_state) { 390 static const char me[] = "isc/ctl_clnt::new_state"; 391 392 (*ctx->logger)(ctl_debug, "%s: %s -> %s", me, 393 state_names[ctx->state], state_names[new_state]); 394 ctx->state = new_state; 395 } 396 397 static void 398 conn_done(evContext ev, void *uap, int fd, 399 const void *la, int lalen, 400 const void *ra, int ralen) 401 { 402 static const char me[] = "isc/ctl_clnt::conn_done"; 403 struct ctl_cctx *ctx = uap; 404 struct ctl_tran *tran; 405 406 UNUSED(ev); 407 UNUSED(la); 408 UNUSED(lalen); 409 UNUSED(ra); 410 UNUSED(ralen); 411 412 ctx->coID.opaque = NULL; 413 if (fd < 0) { 414 (*ctx->logger)(ctl_error, "%s: evConnect: %s", me, 415 strerror(errno)); 416 error(ctx); 417 return; 418 } 419 new_state(ctx, connected); 420 tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0); 421 if (tran == NULL) { 422 (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me, 423 strerror(errno)); 424 error(ctx); 425 return; 426 } 427 start_read(ctx); 428 if (ctx->state == destroyed) { 429 (*ctx->logger)(ctl_error, "%s: start_read failed: %s", 430 me, strerror(errno)); 431 error(ctx); 432 return; 433 } 434 } 435 436 static void 437 write_done(evContext lev, void *uap, int fd, int bytes) { 438 struct ctl_tran *tran = (struct ctl_tran *)uap; 439 struct ctl_cctx *ctx = tran->ctx; 440 441 UNUSED(lev); 442 UNUSED(fd); 443 444 ctx->wrID.opaque = NULL; 445 if (ctx->tiID.opaque != NULL) 446 touch_timer(ctx); 447 ctl_bufput(&tran->outbuf); 448 start_write(ctx); 449 if (bytes < 0) 450 destroy(ctx, 1); 451 else 452 start_read(ctx); 453 } 454 455 static void 456 start_read(struct ctl_cctx *ctx) { 457 static const char me[] = "isc/ctl_clnt::start_read"; 458 459 REQUIRE(ctx->state == connecting || ctx->state == connected); 460 REQUIRE(ctx->rdID.opaque == NULL); 461 if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx, 462 &ctx->rdID) < 0) 463 { 464 (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me, 465 ctx->sock, strerror(errno)); 466 error(ctx); 467 return; 468 } 469 } 470 471 static void 472 stop_read(struct ctl_cctx *ctx) { 473 REQUIRE(ctx->coID.opaque == NULL); 474 REQUIRE(ctx->rdID.opaque != NULL); 475 (void)evDeselectFD(ctx->ev, ctx->rdID); 476 ctx->rdID.opaque = NULL; 477 } 478 479 static void 480 readable(evContext ev, void *uap, int fd, int evmask) { 481 static const char me[] = "isc/ctl_clnt::readable"; 482 struct ctl_cctx *ctx = uap; 483 struct ctl_tran *tran; 484 ssize_t n; 485 char *eos; 486 487 UNUSED(ev); 488 489 REQUIRE(ctx != NULL); 490 REQUIRE(fd >= 0); 491 REQUIRE(evmask == EV_READ); 492 REQUIRE(ctx->state == connected); 493 REQUIRE(!EMPTY(ctx->tran)); 494 tran = HEAD(ctx->tran); 495 if (!allocated_p(ctx->inbuf) && 496 ctl_bufget(&ctx->inbuf, ctx->logger) < 0) { 497 (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me); 498 error(ctx); 499 return; 500 } 501 n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used, 502 MAX_LINELEN - ctx->inbuf.used); 503 if (n <= 0) { 504 (*ctx->logger)(ctl_warning, "%s: read: %s", me, 505 (n == 0) ? "Unexpected EOF" : strerror(errno)); 506 error(ctx); 507 return; 508 } 509 if (ctx->tiID.opaque != NULL) 510 touch_timer(ctx); 511 ctx->inbuf.used += n; 512 (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me, 513 n, ctx->inbuf.used); 514 again: 515 eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used); 516 if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') { 517 int done = 0; 518 519 eos[-1] = '\0'; 520 if (!arpacode_p(ctx->inbuf.text)) { 521 /* XXX Doesn't FTP do this sometimes? Is it legal? */ 522 (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me, 523 ctx->inbuf.text); 524 error(ctx); 525 return; 526 } 527 if (arpadone_p(ctx->inbuf.text)) 528 done = 1; 529 else if (arpacont_p(ctx->inbuf.text)) 530 done = 0; 531 else { 532 /* XXX Doesn't FTP do this sometimes? Is it legal? */ 533 (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me, 534 ctx->inbuf.text); 535 error(ctx); 536 return; 537 } 538 (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text, 539 (done ? 0 : CTL_MORE)); 540 ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1); 541 if (ctx->inbuf.used == 0U) 542 ctl_bufput(&ctx->inbuf); 543 else 544 memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used); 545 if (done) { 546 UNLINK(ctx->tran, tran, link); 547 memput(tran, sizeof *tran); 548 stop_read(ctx); 549 start_write(ctx); 550 return; 551 } 552 if (allocated_p(ctx->inbuf)) 553 goto again; 554 return; 555 } 556 if (ctx->inbuf.used == (size_t)MAX_LINELEN) { 557 (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me, 558 ctx->inbuf.text); 559 error(ctx); 560 } 561 } 562 563 /* Timer related stuff. */ 564 565 static void 566 start_timer(struct ctl_cctx *ctx) { 567 static const char me[] = "isc/ctl_clnt::start_timer"; 568 569 REQUIRE(ctx->tiID.opaque == NULL); 570 if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){ 571 (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me, 572 strerror(errno)); 573 error(ctx); 574 return; 575 } 576 } 577 578 static void 579 stop_timer(struct ctl_cctx *ctx) { 580 static const char me[] = "isc/ctl_clnt::stop_timer"; 581 582 REQUIRE(ctx->tiID.opaque != NULL); 583 if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) { 584 (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me, 585 strerror(errno)); 586 error(ctx); 587 return; 588 } 589 ctx->tiID.opaque = NULL; 590 } 591 592 static void 593 touch_timer(struct ctl_cctx *ctx) { 594 REQUIRE(ctx->tiID.opaque != NULL); 595 596 evTouchIdleTimer(ctx->ev, ctx->tiID); 597 } 598 599 static void 600 timer(evContext ev, void *uap, struct timespec due, struct timespec itv) { 601 static const char me[] = "isc/ctl_clnt::timer"; 602 struct ctl_cctx *ctx = uap; 603 604 UNUSED(ev); 605 UNUSED(due); 606 UNUSED(itv); 607 608 ctx->tiID.opaque = NULL; 609 (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me, 610 ctx->timeout.tv_sec, state_names[ctx->state]); 611 error(ctx); 612 } 613 614 /*! \file */ 615