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 static const char rcsid
[] = "$Id: ctl_clnt.c,v 1.11 2008/11/14 02:36:51 marka Exp $";
22 #include "port_before.h"
24 #include <sys/param.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/nameser.h>
30 #include <arpa/inet.h>
43 #include <isc/assertions.h>
45 #include <isc/eventlib.h>
47 #include <isc/memcluster.h>
51 #include "port_after.h"
58 #define donefunc_p(ctx) ((ctx).donefunc != NULL)
59 #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
60 isdigit((unsigned char)(line[1])) && \
61 isdigit((unsigned char)(line[2])))
62 #define arpacont_p(line) (line[3] == '-')
63 #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
64 line[3] == '\r' || line[3] == '\0')
69 initializing
= 0, connecting
, connected
, destroyed
73 LINK(struct ctl_tran
) link
;
74 LINK(struct ctl_tran
) wlink
;
75 struct ctl_cctx
* ctx
;
76 struct ctl_buf outbuf
;
77 ctl_clntdone donefunc
;
86 ctl_clntdone donefunc
;
93 struct timespec timeout
;
94 LIST(struct ctl_tran
) tran
;
95 LIST(struct ctl_tran
) wtran
;
100 static struct ctl_tran
*new_tran(struct ctl_cctx
*, ctl_clntdone
, void *, int);
101 static void start_write(struct ctl_cctx
*);
102 static void destroy(struct ctl_cctx
*, int);
103 static void error(struct ctl_cctx
*);
104 static void new_state(struct ctl_cctx
*, enum state
);
105 static void conn_done(evContext
, void *, int,
108 static void write_done(evContext
, void *, int, int);
109 static void start_read(struct ctl_cctx
*);
110 static void stop_read(struct ctl_cctx
*);
111 static void readable(evContext
, void *, int, int);
112 static void start_timer(struct ctl_cctx
*);
113 static void stop_timer(struct ctl_cctx
*);
114 static void touch_timer(struct ctl_cctx
*);
115 static void timer(evContext
, void *,
116 struct timespec
, struct timespec
);
120 memchr(const void *b
, int c
, size_t len
) {
121 const unsigned char *p
= b
;
124 for (i
= 0; i
< len
; i
++, p
++)
125 if (*p
== (unsigned char)c
)
133 static const char * const state_names
[] = {
134 "initializing", "connecting", "connected", "destroyed"
142 * create, condition, and connect to a listener on the control port.
145 ctl_client(evContext lev
, const struct sockaddr
*cap
, size_t cap_len
,
146 const struct sockaddr
*sap
, size_t sap_len
,
147 ctl_clntdone donefunc
, void *uap
,
148 u_int timeout
, ctl_logfunc logger
)
150 static const char me
[] = "ctl_client";
151 static const int on
= 1;
152 struct ctl_cctx
*ctx
;
153 struct sockaddr
*captmp
;
157 ctx
= memget(sizeof *ctx
);
159 (*logger
)(ctl_error
, "%s: getmem: %s", me
, strerror(errno
));
162 ctx
->state
= initializing
;
164 ctx
->logger
= logger
;
165 ctx
->timeout
= evConsTime(timeout
, 0);
166 ctx
->donefunc
= donefunc
;
168 ctx
->coID
.opaque
= NULL
;
169 ctx
->tiID
.opaque
= NULL
;
170 ctx
->rdID
.opaque
= NULL
;
171 ctx
->wrID
.opaque
= NULL
;
172 buffer_init(ctx
->inbuf
);
173 INIT_LIST(ctx
->tran
);
174 INIT_LIST(ctx
->wtran
);
175 ctx
->sock
= socket(sap
->sa_family
, SOCK_STREAM
, PF_UNSPEC
);
176 if (ctx
->sock
> evHighestFD(ctx
->ev
)) {
181 (*ctx
->logger
)(ctl_error
, "%s: socket: %s",
182 me
, strerror(errno
));
186 if (setsockopt(ctx
->sock
, SOL_SOCKET
, SO_REUSEADDR
,
187 (const char *)&on
, sizeof on
) != 0) {
188 (*ctx
->logger
)(ctl_warning
,
189 "%s: setsockopt(REUSEADDR): %s",
190 me
, strerror(errno
));
192 DE_CONST(cap
, captmp
);
193 if (bind(ctx
->sock
, captmp
, cap_len
) < 0) {
194 (*ctx
->logger
)(ctl_error
, "%s: bind: %s", me
,
199 if (evConnect(lev
, ctx
->sock
, (const struct sockaddr
*)sap
, sap_len
,
200 conn_done
, ctx
, &ctx
->coID
) < 0) {
201 (*ctx
->logger
)(ctl_error
, "%s: evConnect(fd %d): %s",
202 me
, ctx
->sock
, strerror(errno
));
207 memput(ctx
, sizeof *ctx
);
211 new_state(ctx
, connecting
);
218 * close a client and release all of its resources.
221 ctl_endclient(struct ctl_cctx
*ctx
) {
222 if (ctx
->state
!= destroyed
)
224 memput(ctx
, sizeof *ctx
);
229 * ctl_command(ctx, cmd, len, donefunc, uap)
230 * Queue a transaction, which will begin with sending cmd
231 * and complete by calling donefunc with the answer.
234 ctl_command(struct ctl_cctx
*ctx
, const char *cmd
, size_t len
,
235 ctl_clntdone donefunc
, void *uap
)
237 struct ctl_tran
*tran
;
241 switch (ctx
->state
) {
251 if (len
>= (size_t)MAX_LINELEN
) {
255 tran
= new_tran(ctx
, donefunc
, uap
, 1);
258 if (ctl_bufget(&tran
->outbuf
, ctx
->logger
) < 0)
260 memcpy(tran
->outbuf
.text
, cmd
, len
);
261 tran
->outbuf
.used
= len
;
262 for (pc
= tran
->outbuf
.text
, n
= 0; n
< tran
->outbuf
.used
; pc
++, n
++)
263 if (!isascii((unsigned char)*pc
) ||
264 !isprint((unsigned char)*pc
))
272 static struct ctl_tran
*
273 new_tran(struct ctl_cctx
*ctx
, ctl_clntdone donefunc
, void *uap
, int w
) {
274 struct ctl_tran
*new = memget(sizeof *new);
279 buffer_init(new->outbuf
);
280 new->donefunc
= donefunc
;
282 INIT_LINK(new, link
);
283 INIT_LINK(new, wlink
);
284 APPEND(ctx
->tran
, new, link
);
286 APPEND(ctx
->wtran
, new, wlink
);
291 start_write(struct ctl_cctx
*ctx
) {
292 static const char me
[] = "isc/ctl_clnt::start_write";
293 struct ctl_tran
*tran
;
294 struct iovec iov
[2], *iovp
= iov
;
297 REQUIRE(ctx
->state
== connecting
|| ctx
->state
== connected
);
298 /* If there is a write in progress, don't try to write more yet. */
299 if (ctx
->wrID
.opaque
!= NULL
)
301 /* If there are no trans, make sure timer is off, and we're done. */
302 if (EMPTY(ctx
->wtran
)) {
303 if (ctx
->tiID
.opaque
!= NULL
)
307 /* Pull it off the head of the write queue. */
308 tran
= HEAD(ctx
->wtran
);
309 UNLINK(ctx
->wtran
, tran
, wlink
);
310 /* Since there are some trans, make sure timer is successfully "on". */
311 if (ctx
->tiID
.opaque
!= NULL
)
315 if (ctx
->state
== destroyed
)
317 /* Marshall a newline-terminated message and clock it out. */
318 *iovp
++ = evConsIovec(tran
->outbuf
.text
, tran
->outbuf
.used
);
319 DE_CONST("\r\n", tmp
);
320 *iovp
++ = evConsIovec(tmp
, 2);
321 if (evWrite(ctx
->ev
, ctx
->sock
, iov
, iovp
- iov
,
322 write_done
, tran
, &ctx
->wrID
) < 0) {
323 (*ctx
->logger
)(ctl_error
, "%s: evWrite: %s", me
,
328 if (evTimeRW(ctx
->ev
, ctx
->wrID
, ctx
->tiID
) < 0) {
329 (*ctx
->logger
)(ctl_error
, "%s: evTimeRW: %s", me
,
337 destroy(struct ctl_cctx
*ctx
, int notify
) {
338 struct ctl_tran
*this, *next
;
340 if (ctx
->sock
!= -1) {
341 (void) close(ctx
->sock
);
344 switch (ctx
->state
) {
346 REQUIRE(ctx
->wrID
.opaque
== NULL
);
347 REQUIRE(EMPTY(ctx
->tran
));
349 * This test is nec'y since destroy() can be called from
350 * start_read() while the state is still "connecting".
352 if (ctx
->coID
.opaque
!= NULL
) {
353 (void)evCancelConn(ctx
->ev
, ctx
->coID
);
354 ctx
->coID
.opaque
= NULL
;
358 REQUIRE(ctx
->coID
.opaque
== NULL
);
359 if (ctx
->wrID
.opaque
!= NULL
) {
360 (void)evCancelRW(ctx
->ev
, ctx
->wrID
);
361 ctx
->wrID
.opaque
= NULL
;
363 if (ctx
->rdID
.opaque
!= NULL
)
371 if (allocated_p(ctx
->inbuf
))
372 ctl_bufput(&ctx
->inbuf
);
373 for (this = HEAD(ctx
->tran
); this != NULL
; this = next
) {
374 next
= NEXT(this, link
);
375 if (allocated_p(this->outbuf
))
376 ctl_bufput(&this->outbuf
);
377 if (notify
&& this->donefunc
!= NULL
)
378 (*this->donefunc
)(ctx
, this->uap
, NULL
, 0);
379 memput(this, sizeof *this);
381 if (ctx
->tiID
.opaque
!= NULL
)
383 new_state(ctx
, destroyed
);
387 error(struct ctl_cctx
*ctx
) {
388 REQUIRE(ctx
->state
!= destroyed
);
393 new_state(struct ctl_cctx
*ctx
, enum state new_state
) {
394 static const char me
[] = "isc/ctl_clnt::new_state";
396 (*ctx
->logger
)(ctl_debug
, "%s: %s -> %s", me
,
397 state_names
[ctx
->state
], state_names
[new_state
]);
398 ctx
->state
= new_state
;
402 conn_done(evContext ev
, void *uap
, int fd
,
403 const void *la
, int lalen
,
404 const void *ra
, int ralen
)
406 static const char me
[] = "isc/ctl_clnt::conn_done";
407 struct ctl_cctx
*ctx
= uap
;
408 struct ctl_tran
*tran
;
416 ctx
->coID
.opaque
= NULL
;
418 (*ctx
->logger
)(ctl_error
, "%s: evConnect: %s", me
,
423 new_state(ctx
, connected
);
424 tran
= new_tran(ctx
, ctx
->donefunc
, ctx
->uap
, 0);
426 (*ctx
->logger
)(ctl_error
, "%s: new_tran failed: %s", me
,
432 if (ctx
->state
== destroyed
) {
433 (*ctx
->logger
)(ctl_error
, "%s: start_read failed: %s",
434 me
, strerror(errno
));
441 write_done(evContext lev
, void *uap
, int fd
, int bytes
) {
442 struct ctl_tran
*tran
= (struct ctl_tran
*)uap
;
443 struct ctl_cctx
*ctx
= tran
->ctx
;
448 ctx
->wrID
.opaque
= NULL
;
449 if (ctx
->tiID
.opaque
!= NULL
)
451 ctl_bufput(&tran
->outbuf
);
460 start_read(struct ctl_cctx
*ctx
) {
461 static const char me
[] = "isc/ctl_clnt::start_read";
463 REQUIRE(ctx
->state
== connecting
|| ctx
->state
== connected
);
464 REQUIRE(ctx
->rdID
.opaque
== NULL
);
465 if (evSelectFD(ctx
->ev
, ctx
->sock
, EV_READ
, readable
, ctx
,
468 (*ctx
->logger
)(ctl_error
, "%s: evSelect(fd %d): %s", me
,
469 ctx
->sock
, strerror(errno
));
476 stop_read(struct ctl_cctx
*ctx
) {
477 REQUIRE(ctx
->coID
.opaque
== NULL
);
478 REQUIRE(ctx
->rdID
.opaque
!= NULL
);
479 (void)evDeselectFD(ctx
->ev
, ctx
->rdID
);
480 ctx
->rdID
.opaque
= NULL
;
484 readable(evContext ev
, void *uap
, int fd
, int evmask
) {
485 static const char me
[] = "isc/ctl_clnt::readable";
486 struct ctl_cctx
*ctx
= uap
;
487 struct ctl_tran
*tran
;
493 REQUIRE(ctx
!= NULL
);
495 REQUIRE(evmask
== EV_READ
);
496 REQUIRE(ctx
->state
== connected
);
497 REQUIRE(!EMPTY(ctx
->tran
));
498 tran
= HEAD(ctx
->tran
);
499 if (!allocated_p(ctx
->inbuf
) &&
500 ctl_bufget(&ctx
->inbuf
, ctx
->logger
) < 0) {
501 (*ctx
->logger
)(ctl_error
, "%s: can't get an input buffer", me
);
505 n
= read(ctx
->sock
, ctx
->inbuf
.text
+ ctx
->inbuf
.used
,
506 MAX_LINELEN
- ctx
->inbuf
.used
);
508 (*ctx
->logger
)(ctl_warning
, "%s: read: %s", me
,
509 (n
== 0) ? "Unexpected EOF" : strerror(errno
));
513 if (ctx
->tiID
.opaque
!= NULL
)
515 ctx
->inbuf
.used
+= n
;
516 (*ctx
->logger
)(ctl_debug
, "%s: read %d, used %d", me
,
519 eos
= memchr(ctx
->inbuf
.text
, '\n', ctx
->inbuf
.used
);
520 if (eos
!= NULL
&& eos
!= ctx
->inbuf
.text
&& eos
[-1] == '\r') {
524 if (!arpacode_p(ctx
->inbuf
.text
)) {
525 /* XXX Doesn't FTP do this sometimes? Is it legal? */
526 (*ctx
->logger
)(ctl_error
, "%s: no arpa code (%s)", me
,
531 if (arpadone_p(ctx
->inbuf
.text
))
533 else if (arpacont_p(ctx
->inbuf
.text
))
536 /* XXX Doesn't FTP do this sometimes? Is it legal? */
537 (*ctx
->logger
)(ctl_error
, "%s: no arpa flag (%s)", me
,
542 (*tran
->donefunc
)(ctx
, tran
->uap
, ctx
->inbuf
.text
,
543 (done
? 0 : CTL_MORE
));
544 ctx
->inbuf
.used
-= ((eos
- ctx
->inbuf
.text
) + 1);
545 if (ctx
->inbuf
.used
== 0U)
546 ctl_bufput(&ctx
->inbuf
);
548 memmove(ctx
->inbuf
.text
, eos
+ 1, ctx
->inbuf
.used
);
550 UNLINK(ctx
->tran
, tran
, link
);
551 memput(tran
, sizeof *tran
);
556 if (allocated_p(ctx
->inbuf
))
560 if (ctx
->inbuf
.used
== (size_t)MAX_LINELEN
) {
561 (*ctx
->logger
)(ctl_error
, "%s: line too long (%-10s...)", me
,
567 /* Timer related stuff. */
570 start_timer(struct ctl_cctx
*ctx
) {
571 static const char me
[] = "isc/ctl_clnt::start_timer";
573 REQUIRE(ctx
->tiID
.opaque
== NULL
);
574 if (evSetIdleTimer(ctx
->ev
, timer
, ctx
, ctx
->timeout
, &ctx
->tiID
) < 0){
575 (*ctx
->logger
)(ctl_error
, "%s: evSetIdleTimer: %s", me
,
583 stop_timer(struct ctl_cctx
*ctx
) {
584 static const char me
[] = "isc/ctl_clnt::stop_timer";
586 REQUIRE(ctx
->tiID
.opaque
!= NULL
);
587 if (evClearIdleTimer(ctx
->ev
, ctx
->tiID
) < 0) {
588 (*ctx
->logger
)(ctl_error
, "%s: evClearIdleTimer: %s", me
,
593 ctx
->tiID
.opaque
= NULL
;
597 touch_timer(struct ctl_cctx
*ctx
) {
598 REQUIRE(ctx
->tiID
.opaque
!= NULL
);
600 evTouchIdleTimer(ctx
->ev
, ctx
->tiID
);
604 timer(evContext ev
, void *uap
, struct timespec due
, struct timespec itv
) {
605 static const char me
[] = "isc/ctl_clnt::timer";
606 struct ctl_cctx
*ctx
= uap
;
612 ctx
->tiID
.opaque
= NULL
;
613 (*ctx
->logger
)(ctl_error
, "%s: timeout after %u seconds while %s", me
,
614 ctx
->timeout
.tv_sec
, state_names
[ctx
->state
]);