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