1 #if !defined(lint) && !defined(SABER)
2 static const char rcsid[] = "$Id: ctl_clnt.c,v 1.7.18.2 2007/05/18 06:24:39 marka Exp $";
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1998,1999 by Internet Software Consortium.
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 #include "port_before.h"
26 #include <sys/param.h>
28 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/nameser.h>
32 #include <arpa/inet.h>
42 #include <isc/assertions.h>
44 #include <isc/eventlib.h>
46 #include <isc/memcluster.h>
50 #include "port_after.h"
57 #define donefunc_p(ctx) ((ctx).donefunc != NULL)
58 #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
59 isdigit((unsigned char)(line[1])) && \
60 isdigit((unsigned char)(line[2])))
61 #define arpacont_p(line) (line[3] == '-')
62 #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
63 line[3] == '\r' || line[3] == '\0')
68 initializing = 0, connecting, connected, destroyed
72 LINK(struct ctl_tran) link;
73 LINK(struct ctl_tran) wlink;
74 struct ctl_cctx * ctx;
75 struct ctl_buf outbuf;
76 ctl_clntdone donefunc;
85 ctl_clntdone donefunc;
92 struct timespec timeout;
93 LIST(struct ctl_tran) tran;
94 LIST(struct ctl_tran) wtran;
99 static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int);
100 static void start_write(struct ctl_cctx *);
101 static void destroy(struct ctl_cctx *, int);
102 static void error(struct ctl_cctx *);
103 static void new_state(struct ctl_cctx *, enum state);
104 static void conn_done(evContext, void *, int,
107 static void write_done(evContext, void *, int, int);
108 static void start_read(struct ctl_cctx *);
109 static void stop_read(struct ctl_cctx *);
110 static void readable(evContext, void *, int, int);
111 static void start_timer(struct ctl_cctx *);
112 static void stop_timer(struct ctl_cctx *);
113 static void touch_timer(struct ctl_cctx *);
114 static void timer(evContext, void *,
115 struct timespec, struct timespec);
119 memchr(const void *b, int c, size_t len) {
120 const unsigned char *p = b;
123 for (i = 0; i < len; i++, p++)
124 if (*p == (unsigned char)c)
132 static const char * const state_names[] = {
133 "initializing", "connecting", "connected", "destroyed"
141 * create, condition, and connect to a listener on the control port.
144 ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len,
145 const struct sockaddr *sap, size_t sap_len,
146 ctl_clntdone donefunc, void *uap,
147 u_int timeout, ctl_logfunc logger)
149 static const char me[] = "ctl_client";
150 static const int on = 1;
151 struct ctl_cctx *ctx;
152 struct sockaddr *captmp;
156 ctx = memget(sizeof *ctx);
158 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
161 ctx->state = initializing;
163 ctx->logger = logger;
164 ctx->timeout = evConsTime(timeout, 0);
165 ctx->donefunc = donefunc;
167 ctx->coID.opaque = NULL;
168 ctx->tiID.opaque = NULL;
169 ctx->rdID.opaque = NULL;
170 ctx->wrID.opaque = NULL;
171 buffer_init(ctx->inbuf);
172 INIT_LIST(ctx->tran);
173 INIT_LIST(ctx->wtran);
174 ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
175 if (ctx->sock > evHighestFD(ctx->ev)) {
180 (*ctx->logger)(ctl_error, "%s: socket: %s",
181 me, strerror(errno));
185 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
186 (const char *)&on, sizeof on) != 0) {
187 (*ctx->logger)(ctl_warning,
188 "%s: setsockopt(REUSEADDR): %s",
189 me, strerror(errno));
191 DE_CONST(cap, captmp);
192 if (bind(ctx->sock, captmp, cap_len) < 0) {
193 (*ctx->logger)(ctl_error, "%s: bind: %s", me,
198 if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len,
199 conn_done, ctx, &ctx->coID) < 0) {
200 (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s",
201 me, ctx->sock, strerror(errno));
206 memput(ctx, sizeof *ctx);
210 new_state(ctx, connecting);
217 * close a client and release all of its resources.
220 ctl_endclient(struct ctl_cctx *ctx) {
221 if (ctx->state != destroyed)
223 memput(ctx, sizeof *ctx);
228 * ctl_command(ctx, cmd, len, donefunc, uap)
229 * Queue a transaction, which will begin with sending cmd
230 * and complete by calling donefunc with the answer.
233 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
234 ctl_clntdone donefunc, void *uap)
236 struct ctl_tran *tran;
240 switch (ctx->state) {
250 if (len >= (size_t)MAX_LINELEN) {
254 tran = new_tran(ctx, donefunc, uap, 1);
257 if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
259 memcpy(tran->outbuf.text, cmd, len);
260 tran->outbuf.used = len;
261 for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
262 if (!isascii((unsigned char)*pc) ||
263 !isprint((unsigned char)*pc))
271 static struct ctl_tran *
272 new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
273 struct ctl_tran *new = memget(sizeof *new);
278 buffer_init(new->outbuf);
279 new->donefunc = donefunc;
281 INIT_LINK(new, link);
282 INIT_LINK(new, wlink);
283 APPEND(ctx->tran, new, link);
285 APPEND(ctx->wtran, new, wlink);
290 start_write(struct ctl_cctx *ctx) {
291 static const char me[] = "isc/ctl_clnt::start_write";
292 struct ctl_tran *tran;
293 struct iovec iov[2], *iovp = iov;
296 REQUIRE(ctx->state == connecting || ctx->state == connected);
297 /* If there is a write in progress, don't try to write more yet. */
298 if (ctx->wrID.opaque != NULL)
300 /* If there are no trans, make sure timer is off, and we're done. */
301 if (EMPTY(ctx->wtran)) {
302 if (ctx->tiID.opaque != NULL)
306 /* Pull it off the head of the write queue. */
307 tran = HEAD(ctx->wtran);
308 UNLINK(ctx->wtran, tran, wlink);
309 /* Since there are some trans, make sure timer is successfully "on". */
310 if (ctx->tiID.opaque != NULL)
314 if (ctx->state == destroyed)
316 /* Marshall a newline-terminated message and clock it out. */
317 *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
318 DE_CONST("\r\n", tmp);
319 *iovp++ = evConsIovec(tmp, 2);
320 if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
321 write_done, tran, &ctx->wrID) < 0) {
322 (*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
327 if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
328 (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
336 destroy(struct ctl_cctx *ctx, int notify) {
337 struct ctl_tran *this, *next;
339 if (ctx->sock != -1) {
340 (void) close(ctx->sock);
343 switch (ctx->state) {
345 REQUIRE(ctx->wrID.opaque == NULL);
346 REQUIRE(EMPTY(ctx->tran));
348 * This test is nec'y since destroy() can be called from
349 * start_read() while the state is still "connecting".
351 if (ctx->coID.opaque != NULL) {
352 (void)evCancelConn(ctx->ev, ctx->coID);
353 ctx->coID.opaque = NULL;
357 REQUIRE(ctx->coID.opaque == NULL);
358 if (ctx->wrID.opaque != NULL) {
359 (void)evCancelRW(ctx->ev, ctx->wrID);
360 ctx->wrID.opaque = NULL;
362 if (ctx->rdID.opaque != NULL)
370 if (allocated_p(ctx->inbuf))
371 ctl_bufput(&ctx->inbuf);
372 for (this = HEAD(ctx->tran); this != NULL; this = next) {
373 next = NEXT(this, link);
374 if (allocated_p(this->outbuf))
375 ctl_bufput(&this->outbuf);
376 if (notify && this->donefunc != NULL)
377 (*this->donefunc)(ctx, this->uap, NULL, 0);
378 memput(this, sizeof *this);
380 if (ctx->tiID.opaque != NULL)
382 new_state(ctx, destroyed);
386 error(struct ctl_cctx *ctx) {
387 REQUIRE(ctx->state != destroyed);
392 new_state(struct ctl_cctx *ctx, enum state new_state) {
393 static const char me[] = "isc/ctl_clnt::new_state";
395 (*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
396 state_names[ctx->state], state_names[new_state]);
397 ctx->state = new_state;
401 conn_done(evContext ev, void *uap, int fd,
402 const void *la, int lalen,
403 const void *ra, int ralen)
405 static const char me[] = "isc/ctl_clnt::conn_done";
406 struct ctl_cctx *ctx = uap;
407 struct ctl_tran *tran;
415 ctx->coID.opaque = NULL;
417 (*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
422 new_state(ctx, connected);
423 tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
425 (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
431 if (ctx->state == destroyed) {
432 (*ctx->logger)(ctl_error, "%s: start_read failed: %s",
433 me, strerror(errno));
440 write_done(evContext lev, void *uap, int fd, int bytes) {
441 struct ctl_tran *tran = (struct ctl_tran *)uap;
442 struct ctl_cctx *ctx = tran->ctx;
447 ctx->wrID.opaque = NULL;
448 if (ctx->tiID.opaque != NULL)
450 ctl_bufput(&tran->outbuf);
459 start_read(struct ctl_cctx *ctx) {
460 static const char me[] = "isc/ctl_clnt::start_read";
462 REQUIRE(ctx->state == connecting || ctx->state == connected);
463 REQUIRE(ctx->rdID.opaque == NULL);
464 if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx,
467 (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
468 ctx->sock, strerror(errno));
475 stop_read(struct ctl_cctx *ctx) {
476 REQUIRE(ctx->coID.opaque == NULL);
477 REQUIRE(ctx->rdID.opaque != NULL);
478 (void)evDeselectFD(ctx->ev, ctx->rdID);
479 ctx->rdID.opaque = NULL;
483 readable(evContext ev, void *uap, int fd, int evmask) {
484 static const char me[] = "isc/ctl_clnt::readable";
485 struct ctl_cctx *ctx = uap;
486 struct ctl_tran *tran;
492 REQUIRE(ctx != NULL);
494 REQUIRE(evmask == EV_READ);
495 REQUIRE(ctx->state == connected);
496 REQUIRE(!EMPTY(ctx->tran));
497 tran = HEAD(ctx->tran);
498 if (!allocated_p(ctx->inbuf) &&
499 ctl_bufget(&ctx->inbuf, ctx->logger) < 0) {
500 (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me);
504 n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
505 MAX_LINELEN - ctx->inbuf.used);
507 (*ctx->logger)(ctl_warning, "%s: read: %s", me,
508 (n == 0) ? "Unexpected EOF" : strerror(errno));
512 if (ctx->tiID.opaque != NULL)
514 ctx->inbuf.used += n;
515 (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
518 eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
519 if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
523 if (!arpacode_p(ctx->inbuf.text)) {
524 /* XXX Doesn't FTP do this sometimes? Is it legal? */
525 (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me,
530 if (arpadone_p(ctx->inbuf.text))
532 else if (arpacont_p(ctx->inbuf.text))
535 /* XXX Doesn't FTP do this sometimes? Is it legal? */
536 (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
541 (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text,
542 (done ? 0 : CTL_MORE));
543 ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1);
544 if (ctx->inbuf.used == 0U)
545 ctl_bufput(&ctx->inbuf);
547 memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
549 UNLINK(ctx->tran, tran, link);
550 memput(tran, sizeof *tran);
555 if (allocated_p(ctx->inbuf))
559 if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
560 (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
566 /* Timer related stuff. */
569 start_timer(struct ctl_cctx *ctx) {
570 static const char me[] = "isc/ctl_clnt::start_timer";
572 REQUIRE(ctx->tiID.opaque == NULL);
573 if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){
574 (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me,
582 stop_timer(struct ctl_cctx *ctx) {
583 static const char me[] = "isc/ctl_clnt::stop_timer";
585 REQUIRE(ctx->tiID.opaque != NULL);
586 if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
587 (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
592 ctx->tiID.opaque = NULL;
596 touch_timer(struct ctl_cctx *ctx) {
597 REQUIRE(ctx->tiID.opaque != NULL);
599 evTouchIdleTimer(ctx->ev, ctx->tiID);
603 timer(evContext ev, void *uap, struct timespec due, struct timespec itv) {
604 static const char me[] = "isc/ctl_clnt::timer";
605 struct ctl_cctx *ctx = uap;
611 ctx->tiID.opaque = NULL;
612 (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me,
613 ctx->timeout.tv_sec, state_names[ctx->state]);