2 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2003 Internet Software Consortium.
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.
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.
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 $";
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>
45 #include <isc/assertions.h>
47 #include <isc/eventlib.h>
49 #include <isc/memcluster.h>
53 #include "port_after.h"
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')
71 initializing
= 0, connecting
, connected
, destroyed
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
;
88 ctl_clntdone donefunc
;
95 struct timespec timeout
;
96 LIST(struct ctl_tran
) tran
;
97 LIST(struct ctl_tran
) wtran
;
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,
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
);
122 memchr(const void *b
, int c
, size_t len
) {
123 const unsigned char *p
= b
;
126 for (i
= 0; i
< len
; i
++, p
++)
127 if (*p
== (unsigned char)c
)
135 static const char * const state_names
[] = {
136 "initializing", "connecting", "connected", "destroyed"
144 * create, condition, and connect to a listener on the control port.
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
)
152 static const char me
[] = "ctl_client";
153 static const int on
= 1;
154 struct ctl_cctx
*ctx
;
155 struct sockaddr
*captmp
;
159 ctx
= memget(sizeof *ctx
);
161 (*logger
)(ctl_error
, "%s: getmem: %s", me
, strerror(errno
));
164 ctx
->state
= initializing
;
166 ctx
->logger
= logger
;
167 ctx
->timeout
= evConsTime(timeout
, 0);
168 ctx
->donefunc
= donefunc
;
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
)) {
183 (*ctx
->logger
)(ctl_error
, "%s: socket: %s",
184 me
, strerror(errno
));
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
));
194 DE_CONST(cap
, captmp
);
195 if (bind(ctx
->sock
, captmp
, cap_len
) < 0) {
196 (*ctx
->logger
)(ctl_error
, "%s: bind: %s", me
,
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
));
209 memput(ctx
, sizeof *ctx
);
213 new_state(ctx
, connecting
);
220 * close a client and release all of its resources.
223 ctl_endclient(struct ctl_cctx
*ctx
) {
224 if (ctx
->state
!= destroyed
)
226 memput(ctx
, sizeof *ctx
);
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.
236 ctl_command(struct ctl_cctx
*ctx
, const char *cmd
, size_t len
,
237 ctl_clntdone donefunc
, void *uap
)
239 struct ctl_tran
*tran
;
243 switch (ctx
->state
) {
253 if (len
>= (size_t)MAX_LINELEN
) {
257 tran
= new_tran(ctx
, donefunc
, uap
, 1);
260 if (ctl_bufget(&tran
->outbuf
, ctx
->logger
) < 0)
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
))
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);
281 buffer_init(new->outbuf
);
282 new->donefunc
= donefunc
;
284 INIT_LINK(new, link
);
285 INIT_LINK(new, wlink
);
286 APPEND(ctx
->tran
, new, link
);
288 APPEND(ctx
->wtran
, new, wlink
);
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
;
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
)
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
)
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
)
317 if (ctx
->state
== destroyed
)
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
,
330 if (evTimeRW(ctx
->ev
, ctx
->wrID
, ctx
->tiID
) < 0) {
331 (*ctx
->logger
)(ctl_error
, "%s: evTimeRW: %s", me
,
339 destroy(struct ctl_cctx
*ctx
, int notify
) {
340 struct ctl_tran
*this, *next
;
342 if (ctx
->sock
!= -1) {
343 (void) close(ctx
->sock
);
346 switch (ctx
->state
) {
348 REQUIRE(ctx
->wrID
.opaque
== NULL
);
349 REQUIRE(EMPTY(ctx
->tran
));
351 * This test is nec'y since destroy() can be called from
352 * start_read() while the state is still "connecting".
354 if (ctx
->coID
.opaque
!= NULL
) {
355 (void)evCancelConn(ctx
->ev
, ctx
->coID
);
356 ctx
->coID
.opaque
= NULL
;
360 REQUIRE(ctx
->coID
.opaque
== NULL
);
361 if (ctx
->wrID
.opaque
!= NULL
) {
362 (void)evCancelRW(ctx
->ev
, ctx
->wrID
);
363 ctx
->wrID
.opaque
= NULL
;
365 if (ctx
->rdID
.opaque
!= NULL
)
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);
383 if (ctx
->tiID
.opaque
!= NULL
)
385 new_state(ctx
, destroyed
);
389 error(struct ctl_cctx
*ctx
) {
390 REQUIRE(ctx
->state
!= destroyed
);
395 new_state(struct ctl_cctx
*ctx
, enum state new_state
) {
396 static const char me
[] = "isc/ctl_clnt::new_state";
398 (*ctx
->logger
)(ctl_debug
, "%s: %s -> %s", me
,
399 state_names
[ctx
->state
], state_names
[new_state
]);
400 ctx
->state
= new_state
;
404 conn_done(evContext ev
, void *uap
, int fd
,
405 const void *la
, int lalen
,
406 const void *ra
, int ralen
)
408 static const char me
[] = "isc/ctl_clnt::conn_done";
409 struct ctl_cctx
*ctx
= uap
;
410 struct ctl_tran
*tran
;
418 ctx
->coID
.opaque
= NULL
;
420 (*ctx
->logger
)(ctl_error
, "%s: evConnect: %s", me
,
425 new_state(ctx
, connected
);
426 tran
= new_tran(ctx
, ctx
->donefunc
, ctx
->uap
, 0);
428 (*ctx
->logger
)(ctl_error
, "%s: new_tran failed: %s", me
,
434 if (ctx
->state
== destroyed
) {
435 (*ctx
->logger
)(ctl_error
, "%s: start_read failed: %s",
436 me
, strerror(errno
));
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
;
450 ctx
->wrID
.opaque
= NULL
;
451 if (ctx
->tiID
.opaque
!= NULL
)
453 ctl_bufput(&tran
->outbuf
);
462 start_read(struct ctl_cctx
*ctx
) {
463 static const char me
[] = "isc/ctl_clnt::start_read";
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
,
470 (*ctx
->logger
)(ctl_error
, "%s: evSelect(fd %d): %s", me
,
471 ctx
->sock
, strerror(errno
));
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
;
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
;
495 REQUIRE(ctx
!= NULL
);
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
);
507 n
= read(ctx
->sock
, ctx
->inbuf
.text
+ ctx
->inbuf
.used
,
508 MAX_LINELEN
- ctx
->inbuf
.used
);
510 (*ctx
->logger
)(ctl_warning
, "%s: read: %s", me
,
511 (n
== 0) ? "Unexpected EOF" : strerror(errno
));
515 if (ctx
->tiID
.opaque
!= NULL
)
517 ctx
->inbuf
.used
+= n
;
518 (*ctx
->logger
)(ctl_debug
, "%s: read %d, used %d", me
,
521 eos
= memchr(ctx
->inbuf
.text
, '\n', ctx
->inbuf
.used
);
522 if (eos
!= NULL
&& eos
!= ctx
->inbuf
.text
&& eos
[-1] == '\r') {
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
,
533 if (arpadone_p(ctx
->inbuf
.text
))
535 else if (arpacont_p(ctx
->inbuf
.text
))
538 /* XXX Doesn't FTP do this sometimes? Is it legal? */
539 (*ctx
->logger
)(ctl_error
, "%s: no arpa flag (%s)", me
,
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
);
550 memmove(ctx
->inbuf
.text
, eos
+ 1, ctx
->inbuf
.used
);
552 UNLINK(ctx
->tran
, tran
, link
);
553 memput(tran
, sizeof *tran
);
558 if (allocated_p(ctx
->inbuf
))
562 if (ctx
->inbuf
.used
== (size_t)MAX_LINELEN
) {
563 (*ctx
->logger
)(ctl_error
, "%s: line too long (%-10s...)", me
,
569 /* Timer related stuff. */
572 start_timer(struct ctl_cctx
*ctx
) {
573 static const char me
[] = "isc/ctl_clnt::start_timer";
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
,
585 stop_timer(struct ctl_cctx
*ctx
) {
586 static const char me
[] = "isc/ctl_clnt::stop_timer";
588 REQUIRE(ctx
->tiID
.opaque
!= NULL
);
589 if (evClearIdleTimer(ctx
->ev
, ctx
->tiID
) < 0) {
590 (*ctx
->logger
)(ctl_error
, "%s: evClearIdleTimer: %s", me
,
595 ctx
->tiID
.opaque
= NULL
;
599 touch_timer(struct ctl_cctx
*ctx
) {
600 REQUIRE(ctx
->tiID
.opaque
!= NULL
);
602 evTouchIdleTimer(ctx
->ev
, ctx
->tiID
);
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
;
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
]);