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