1 /*
2 * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2003 Internet Software Consortium.
4 *
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.
8 *
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.
16 */
17
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 $";
20 #endif /* not lint */
21
22 /* Extern. */
23
24 #include "port_before.h"
25
26 #include <sys/param.h>
27 #include <sys/file.h>
28 #include <sys/socket.h>
29
30 #include <netinet/in.h>
31 #include <arpa/nameser.h>
32 #include <arpa/inet.h>
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41 #ifdef HAVE_MEMORY_H
42 #include <memory.h>
43 #endif
44
45 #include <isc/assertions.h>
46 #include <isc/ctl.h>
47 #include <isc/eventlib.h>
48 #include <isc/list.h>
49 #include <isc/memcluster.h>
50
51 #include "ctl_p.h"
52
53 #include "port_after.h"
54
55 /* Constants. */
56
57
58 /* Macros. */
59
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')
67
68 /* Types. */
69
70 enum state {
71 initializing = 0, connecting, connected, destroyed
72 };
73
74 struct ctl_tran {
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;
80 void * uap;
81 };
82
83 struct ctl_cctx {
84 enum state state;
85 evContext ev;
86 int sock;
87 ctl_logfunc logger;
88 ctl_clntdone donefunc;
89 void * uap;
90 evConnID coID;
91 evTimerID tiID;
92 evFileID rdID;
93 evStreamID wrID;
94 struct ctl_buf inbuf;
95 struct timespec timeout;
96 LIST(struct ctl_tran) tran;
97 LIST(struct ctl_tran) wtran;
98 };
99
100 /* Forward. */
101
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,
108 const void *, int,
109 const 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);
119
120 #ifndef HAVE_MEMCHR
121 static void *
memchr(const void * b,int c,size_t len)122 memchr(const void *b, int c, size_t len) {
123 const unsigned char *p = b;
124 size_t i;
125
126 for (i = 0; i < len; i++, p++)
127 if (*p == (unsigned char)c)
128 return ((void *)p);
129 return (NULL);
130 }
131 #endif
132
133 /* Private data. */
134
135 static const char * const state_names[] = {
136 "initializing", "connecting", "connected", "destroyed"
137 };
138
139 /* Public. */
140
141 /*%
142 * void
143 * ctl_client()
144 * create, condition, and connect to a listener on the control port.
145 */
146 struct ctl_cctx *
ctl_client(evContext lev,const struct sockaddr * cap,size_t cap_len,const struct sockaddr * sap,size_t sap_len,ctl_clntdone donefunc,void * uap,u_int timeout,ctl_logfunc logger)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)
151 {
152 static const char me[] = "ctl_client";
153 static const int on = 1;
154 struct ctl_cctx *ctx;
155 struct sockaddr *captmp;
156
157 if (logger == NULL)
158 logger = ctl_logger;
159 ctx = memget(sizeof *ctx);
160 if (ctx == NULL) {
161 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
162 goto fatal;
163 }
164 ctx->state = initializing;
165 ctx->ev = lev;
166 ctx->logger = logger;
167 ctx->timeout = evConsTime(timeout, 0);
168 ctx->donefunc = donefunc;
169 ctx->uap = uap;
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)) {
179 ctx->sock = -1;
180 errno = ENOTSOCK;
181 }
182 if (ctx->sock < 0) {
183 (*ctx->logger)(ctl_error, "%s: socket: %s",
184 me, strerror(errno));
185 goto fatal;
186 }
187 if (cap != NULL) {
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));
193 }
194 DE_CONST(cap, captmp);
195 if (bind(ctx->sock, captmp, cap_len) < 0) {
196 (*ctx->logger)(ctl_error, "%s: bind: %s", me,
197 strerror(errno));
198 goto fatal;
199 }
200 }
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));
205 fatal:
206 if (ctx != NULL) {
207 if (ctx->sock >= 0)
208 close(ctx->sock);
209 memput(ctx, sizeof *ctx);
210 }
211 return (NULL);
212 }
213 new_state(ctx, connecting);
214 return (ctx);
215 }
216
217 /*%
218 * void
219 * ctl_endclient(ctx)
220 * close a client and release all of its resources.
221 */
222 void
ctl_endclient(struct ctl_cctx * ctx)223 ctl_endclient(struct ctl_cctx *ctx) {
224 if (ctx->state != destroyed)
225 destroy(ctx, 0);
226 memput(ctx, sizeof *ctx);
227 }
228
229 /*%
230 * int
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.
234 */
235 int
ctl_command(struct ctl_cctx * ctx,const char * cmd,size_t len,ctl_clntdone donefunc,void * uap)236 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
237 ctl_clntdone donefunc, void *uap)
238 {
239 struct ctl_tran *tran;
240 char *pc;
241 unsigned int n;
242
243 switch (ctx->state) {
244 case destroyed:
245 errno = ENOTCONN;
246 return (-1);
247 case connecting:
248 case connected:
249 break;
250 default:
251 abort();
252 }
253 if (len >= (size_t)MAX_LINELEN) {
254 errno = EMSGSIZE;
255 return (-1);
256 }
257 tran = new_tran(ctx, donefunc, uap, 1);
258 if (tran == NULL)
259 return (-1);
260 if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
261 return (-1);
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))
267 *pc = '\040';
268 start_write(ctx);
269 return (0);
270 }
271
272 /* Private. */
273
274 static struct ctl_tran *
new_tran(struct ctl_cctx * ctx,ctl_clntdone donefunc,void * uap,int w)275 new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
276 struct ctl_tran *new = memget(sizeof *new);
277
278 if (new == NULL)
279 return (NULL);
280 new->ctx = ctx;
281 buffer_init(new->outbuf);
282 new->donefunc = donefunc;
283 new->uap = uap;
284 INIT_LINK(new, link);
285 INIT_LINK(new, wlink);
286 APPEND(ctx->tran, new, link);
287 if (w)
288 APPEND(ctx->wtran, new, wlink);
289 return (new);
290 }
291
292 static void
start_write(struct ctl_cctx * ctx)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;
297 char * tmp;
298
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)
302 return;
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)
306 stop_timer(ctx);
307 return;
308 }
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)
314 touch_timer(ctx);
315 else
316 start_timer(ctx);
317 if (ctx->state == destroyed)
318 return;
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,
326 strerror(errno));
327 error(ctx);
328 return;
329 }
330 if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
331 (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
332 strerror(errno));
333 error(ctx);
334 return;
335 }
336 }
337
338 static void
destroy(struct ctl_cctx * ctx,int notify)339 destroy(struct ctl_cctx *ctx, int notify) {
340 struct ctl_tran *this, *next;
341
342 if (ctx->sock != -1) {
343 (void) close(ctx->sock);
344 ctx->sock = -1;
345 }
346 switch (ctx->state) {
347 case connecting:
348 REQUIRE(ctx->wrID.opaque == NULL);
349 REQUIRE(EMPTY(ctx->tran));
350 /*
351 * This test is nec'y since destroy() can be called from
352 * start_read() while the state is still "connecting".
353 */
354 if (ctx->coID.opaque != NULL) {
355 (void)evCancelConn(ctx->ev, ctx->coID);
356 ctx->coID.opaque = NULL;
357 }
358 break;
359 case connected:
360 REQUIRE(ctx->coID.opaque == NULL);
361 if (ctx->wrID.opaque != NULL) {
362 (void)evCancelRW(ctx->ev, ctx->wrID);
363 ctx->wrID.opaque = NULL;
364 }
365 if (ctx->rdID.opaque != NULL)
366 stop_read(ctx);
367 break;
368 case destroyed:
369 break;
370 default:
371 abort();
372 }
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);
382 }
383 if (ctx->tiID.opaque != NULL)
384 stop_timer(ctx);
385 new_state(ctx, destroyed);
386 }
387
388 static void
error(struct ctl_cctx * ctx)389 error(struct ctl_cctx *ctx) {
390 REQUIRE(ctx->state != destroyed);
391 destroy(ctx, 1);
392 }
393
394 static void
new_state(struct ctl_cctx * ctx,enum state new_state)395 new_state(struct ctl_cctx *ctx, enum state new_state) {
396 static const char me[] = "isc/ctl_clnt::new_state";
397
398 (*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
399 state_names[ctx->state], state_names[new_state]);
400 ctx->state = new_state;
401 }
402
403 static void
conn_done(evContext ev,void * uap,int fd,const void * la,int lalen,const void * ra,int ralen)404 conn_done(evContext ev, void *uap, int fd,
405 const void *la, int lalen,
406 const void *ra, int ralen)
407 {
408 static const char me[] = "isc/ctl_clnt::conn_done";
409 struct ctl_cctx *ctx = uap;
410 struct ctl_tran *tran;
411
412 UNUSED(ev);
413 UNUSED(la);
414 UNUSED(lalen);
415 UNUSED(ra);
416 UNUSED(ralen);
417
418 ctx->coID.opaque = NULL;
419 if (fd < 0) {
420 (*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
421 strerror(errno));
422 error(ctx);
423 return;
424 }
425 new_state(ctx, connected);
426 tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
427 if (tran == NULL) {
428 (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
429 strerror(errno));
430 error(ctx);
431 return;
432 }
433 start_read(ctx);
434 if (ctx->state == destroyed) {
435 (*ctx->logger)(ctl_error, "%s: start_read failed: %s",
436 me, strerror(errno));
437 error(ctx);
438 return;
439 }
440 }
441
442 static void
write_done(evContext lev,void * uap,int fd,int bytes)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;
446
447 UNUSED(lev);
448 UNUSED(fd);
449
450 ctx->wrID.opaque = NULL;
451 if (ctx->tiID.opaque != NULL)
452 touch_timer(ctx);
453 ctl_bufput(&tran->outbuf);
454 start_write(ctx);
455 if (bytes < 0)
456 destroy(ctx, 1);
457 else
458 start_read(ctx);
459 }
460
461 static void
start_read(struct ctl_cctx * ctx)462 start_read(struct ctl_cctx *ctx) {
463 static const char me[] = "isc/ctl_clnt::start_read";
464
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,
468 &ctx->rdID) < 0)
469 {
470 (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
471 ctx->sock, strerror(errno));
472 error(ctx);
473 return;
474 }
475 }
476
477 static void
stop_read(struct ctl_cctx * ctx)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;
483 }
484
485 static void
readable(evContext ev,void * uap,int fd,int evmask)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;
490 ssize_t n;
491 char *eos;
492
493 UNUSED(ev);
494
495 REQUIRE(ctx != NULL);
496 REQUIRE(fd >= 0);
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);
504 error(ctx);
505 return;
506 }
507 n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
508 MAX_LINELEN - ctx->inbuf.used);
509 if (n <= 0) {
510 (*ctx->logger)(ctl_warning, "%s: read: %s", me,
511 (n == 0) ? "Unexpected EOF" : strerror(errno));
512 error(ctx);
513 return;
514 }
515 if (ctx->tiID.opaque != NULL)
516 touch_timer(ctx);
517 ctx->inbuf.used += n;
518 (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
519 n, ctx->inbuf.used);
520 again:
521 eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
522 if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
523 int done = 0;
524
525 eos[-1] = '\0';
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,
529 ctx->inbuf.text);
530 error(ctx);
531 return;
532 }
533 if (arpadone_p(ctx->inbuf.text))
534 done = 1;
535 else if (arpacont_p(ctx->inbuf.text))
536 done = 0;
537 else {
538 /* XXX Doesn't FTP do this sometimes? Is it legal? */
539 (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
540 ctx->inbuf.text);
541 error(ctx);
542 return;
543 }
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);
549 else
550 memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
551 if (done) {
552 UNLINK(ctx->tran, tran, link);
553 memput(tran, sizeof *tran);
554 stop_read(ctx);
555 start_write(ctx);
556 return;
557 }
558 if (allocated_p(ctx->inbuf))
559 goto again;
560 return;
561 }
562 if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
563 (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
564 ctx->inbuf.text);
565 error(ctx);
566 }
567 }
568
569 /* Timer related stuff. */
570
571 static void
start_timer(struct ctl_cctx * ctx)572 start_timer(struct ctl_cctx *ctx) {
573 static const char me[] = "isc/ctl_clnt::start_timer";
574
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,
578 strerror(errno));
579 error(ctx);
580 return;
581 }
582 }
583
584 static void
stop_timer(struct ctl_cctx * ctx)585 stop_timer(struct ctl_cctx *ctx) {
586 static const char me[] = "isc/ctl_clnt::stop_timer";
587
588 REQUIRE(ctx->tiID.opaque != NULL);
589 if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
590 (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
591 strerror(errno));
592 error(ctx);
593 return;
594 }
595 ctx->tiID.opaque = NULL;
596 }
597
598 static void
touch_timer(struct ctl_cctx * ctx)599 touch_timer(struct ctl_cctx *ctx) {
600 REQUIRE(ctx->tiID.opaque != NULL);
601
602 evTouchIdleTimer(ctx->ev, ctx->tiID);
603 }
604
605 static void
timer(evContext ev,void * uap,struct timespec due,struct timespec itv)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;
609
610 UNUSED(ev);
611 UNUSED(due);
612 UNUSED(itv);
613
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]);
617 error(ctx);
618 }
619
620 /*! \file */
621