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