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