]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/lib/bind/isc/ctl_clnt.c
This commit was generated by cvs2svn to compensate for changes in r179404,
[FreeBSD/FreeBSD.git] / contrib / bind9 / lib / bind / isc / ctl_clnt.c
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 $";
3 #endif /* not lint */
4
5 /*
6  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7  * Copyright (c) 1998,1999 by Internet Software Consortium.
8  *
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.
12  *
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.
20  */
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
42 #include <isc/assertions.h>
43 #include <isc/ctl.h>
44 #include <isc/eventlib.h>
45 #include <isc/list.h>
46 #include <isc/memcluster.h>
47
48 #include "ctl_p.h"
49
50 #include "port_after.h"
51
52 /* Constants. */
53
54
55 /* Macros. */
56
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')
64
65 /* Types. */
66
67 enum state {
68         initializing = 0, connecting, connected, destroyed
69 };
70
71 struct ctl_tran {
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;
77         void *                  uap;
78 };
79
80 struct ctl_cctx {
81         enum state              state;
82         evContext               ev;
83         int                     sock;
84         ctl_logfunc             logger;
85         ctl_clntdone            donefunc;
86         void *                  uap;
87         evConnID                coID;
88         evTimerID               tiID;
89         evFileID                rdID;
90         evStreamID              wrID;
91         struct ctl_buf          inbuf;
92         struct timespec         timeout;
93         LIST(struct ctl_tran)   tran;
94         LIST(struct ctl_tran)   wtran;
95 };
96
97 /* Forward. */
98
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,
105                                   const void *, int,
106                                   const 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);
116
117 #ifndef HAVE_MEMCHR
118 static void *
119 memchr(const void *b, int c, size_t len) {
120         const unsigned char *p = b;
121         size_t i;
122
123         for (i = 0; i < len; i++, p++)
124                 if (*p == (unsigned char)c)
125                         return ((void *)p); 
126         return (NULL);
127 }
128 #endif
129
130 /* Private data. */
131
132 static const char * const state_names[] = {
133         "initializing", "connecting", "connected", "destroyed"
134 };
135
136 /* Public. */
137
138 /*%
139  * void
140  * ctl_client()
141  *      create, condition, and connect to a listener on the control port.
142  */
143 struct ctl_cctx *
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)
148 {
149         static const char me[] = "ctl_client";
150         static const int on = 1;
151         struct ctl_cctx *ctx;
152         struct sockaddr *captmp;
153
154         if (logger == NULL)
155                 logger = ctl_logger;
156         ctx = memget(sizeof *ctx);
157         if (ctx == NULL) {
158                 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
159                 goto fatal;
160         }
161         ctx->state = initializing;
162         ctx->ev = lev;
163         ctx->logger = logger;
164         ctx->timeout = evConsTime(timeout, 0);
165         ctx->donefunc = donefunc;
166         ctx->uap = uap;
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)) {
176                 ctx->sock = -1;
177                 errno = ENOTSOCK;
178         }
179         if (ctx->sock < 0) {
180                 (*ctx->logger)(ctl_error, "%s: socket: %s",
181                                me, strerror(errno));
182                 goto fatal;
183         }
184         if (cap != NULL) {
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));
190                 }
191                 DE_CONST(cap, captmp);
192                 if (bind(ctx->sock, captmp, cap_len) < 0) {
193                         (*ctx->logger)(ctl_error, "%s: bind: %s", me,
194                                        strerror(errno));
195                         goto fatal;
196                 }
197         }
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));
202  fatal:
203                 if (ctx != NULL) {
204                         if (ctx->sock >= 0)
205                                 close(ctx->sock);
206                         memput(ctx, sizeof *ctx);
207                 }
208                 return (NULL);
209         }
210         new_state(ctx, connecting);
211         return (ctx);
212 }
213
214 /*%
215  * void
216  * ctl_endclient(ctx)
217  *      close a client and release all of its resources.
218  */
219 void
220 ctl_endclient(struct ctl_cctx *ctx) {
221         if (ctx->state != destroyed)
222                 destroy(ctx, 0);
223         memput(ctx, sizeof *ctx);
224 }
225
226 /*%
227  * int
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.
231  */
232 int
233 ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
234             ctl_clntdone donefunc, void *uap)
235 {
236         struct ctl_tran *tran;
237         char *pc;
238         unsigned int n;
239
240         switch (ctx->state) {
241         case destroyed:
242                 errno = ENOTCONN;
243                 return (-1);
244         case connecting:
245         case connected:
246                 break;
247         default:
248                 abort();
249         }
250         if (len >= (size_t)MAX_LINELEN) {
251                 errno = EMSGSIZE;
252                 return (-1);
253         }
254         tran = new_tran(ctx, donefunc, uap, 1);
255         if (tran == NULL)
256                 return (-1);
257         if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
258                 return (-1);
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))
264                         *pc = '\040';
265         start_write(ctx);
266         return (0);
267 }
268
269 /* Private. */
270
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);
274
275         if (new == NULL)
276                 return (NULL);
277         new->ctx = ctx;
278         buffer_init(new->outbuf);
279         new->donefunc = donefunc;
280         new->uap = uap;
281         INIT_LINK(new, link);
282         INIT_LINK(new, wlink);
283         APPEND(ctx->tran, new, link);
284         if (w)
285                 APPEND(ctx->wtran, new, wlink);
286         return (new);
287 }
288
289 static void
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;
294         char * tmp;
295
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)
299                 return;
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)
303                         stop_timer(ctx);
304                 return;
305         }
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)
311                 touch_timer(ctx);
312         else
313                 start_timer(ctx);
314         if (ctx->state == destroyed)
315                 return;
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,
323                                strerror(errno));
324                 error(ctx);
325                 return;
326         }
327         if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
328                 (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
329                                strerror(errno));
330                 error(ctx);
331                 return;
332         }
333 }
334
335 static void
336 destroy(struct ctl_cctx *ctx, int notify) {
337         struct ctl_tran *this, *next;
338
339         if (ctx->sock != -1) {
340                 (void) close(ctx->sock);
341                 ctx->sock = -1;
342         }
343         switch (ctx->state) {
344         case connecting:
345                 REQUIRE(ctx->wrID.opaque == NULL);
346                 REQUIRE(EMPTY(ctx->tran));
347                 /*
348                  * This test is nec'y since destroy() can be called from
349                  * start_read() while the state is still "connecting".
350                  */
351                 if (ctx->coID.opaque != NULL) {
352                         (void)evCancelConn(ctx->ev, ctx->coID);
353                         ctx->coID.opaque = NULL;
354                 }
355                 break;
356         case connected:
357                 REQUIRE(ctx->coID.opaque == NULL);
358                 if (ctx->wrID.opaque != NULL) {
359                         (void)evCancelRW(ctx->ev, ctx->wrID);
360                         ctx->wrID.opaque = NULL;
361                 }
362                 if (ctx->rdID.opaque != NULL)
363                         stop_read(ctx);
364                 break;
365         case destroyed:
366                 break;
367         default:
368                 abort();
369         }
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);
379         }
380         if (ctx->tiID.opaque != NULL)
381                 stop_timer(ctx);
382         new_state(ctx, destroyed);
383 }
384
385 static void
386 error(struct ctl_cctx *ctx) {
387         REQUIRE(ctx->state != destroyed);
388         destroy(ctx, 1);
389 }
390
391 static void
392 new_state(struct ctl_cctx *ctx, enum state new_state) {
393         static const char me[] = "isc/ctl_clnt::new_state";
394
395         (*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
396                        state_names[ctx->state], state_names[new_state]);
397         ctx->state = new_state;
398 }
399
400 static void
401 conn_done(evContext ev, void *uap, int fd,
402           const void *la, int lalen,
403           const void *ra, int ralen)
404 {
405         static const char me[] = "isc/ctl_clnt::conn_done";
406         struct ctl_cctx *ctx = uap;
407         struct ctl_tran *tran;
408
409         UNUSED(ev);
410         UNUSED(la);
411         UNUSED(lalen);
412         UNUSED(ra);
413         UNUSED(ralen);
414
415         ctx->coID.opaque = NULL;
416         if (fd < 0) {
417                 (*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
418                                strerror(errno));
419                 error(ctx);
420                 return;
421         }
422         new_state(ctx, connected);
423         tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
424         if (tran == NULL) {
425                 (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
426                                strerror(errno));
427                 error(ctx);
428                 return;
429         }
430         start_read(ctx);
431         if (ctx->state == destroyed) {
432                 (*ctx->logger)(ctl_error, "%s: start_read failed: %s",
433                                me, strerror(errno));
434                 error(ctx);
435                 return;
436         }
437 }
438
439 static void
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;
443
444         UNUSED(lev);
445         UNUSED(fd);
446
447         ctx->wrID.opaque = NULL;
448         if (ctx->tiID.opaque != NULL)
449                 touch_timer(ctx);
450         ctl_bufput(&tran->outbuf);
451         start_write(ctx);
452         if (bytes < 0)
453                 destroy(ctx, 1);
454         else
455                 start_read(ctx);
456 }
457
458 static void
459 start_read(struct ctl_cctx *ctx) {
460         static const char me[] = "isc/ctl_clnt::start_read";
461
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,
465                        &ctx->rdID) < 0)
466         {
467                 (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
468                                ctx->sock, strerror(errno));
469                 error(ctx);
470                 return;
471         }
472 }
473
474 static void
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;
480 }
481
482 static void
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;
487         ssize_t n;
488         char *eos;
489
490         UNUSED(ev);
491
492         REQUIRE(ctx != NULL);
493         REQUIRE(fd >= 0);
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);
501                 error(ctx);
502                 return;
503         }
504         n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
505                  MAX_LINELEN - ctx->inbuf.used);
506         if (n <= 0) {
507                 (*ctx->logger)(ctl_warning, "%s: read: %s", me,
508                                (n == 0) ? "Unexpected EOF" : strerror(errno));
509                 error(ctx);
510                 return;
511         }
512         if (ctx->tiID.opaque != NULL)
513                 touch_timer(ctx);
514         ctx->inbuf.used += n;
515         (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
516                        n, ctx->inbuf.used);
517  again:
518         eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
519         if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
520                 int done = 0;
521
522                 eos[-1] = '\0';
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,
526                                        ctx->inbuf.text);
527                         error(ctx);
528                         return;
529                 }
530                 if (arpadone_p(ctx->inbuf.text))
531                         done = 1;
532                 else if (arpacont_p(ctx->inbuf.text))
533                         done = 0;
534                 else {
535                         /* XXX Doesn't FTP do this sometimes? Is it legal? */
536                         (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
537                                        ctx->inbuf.text);
538                         error(ctx);
539                         return;
540                 }
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);
546                 else
547                         memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
548                 if (done) {
549                         UNLINK(ctx->tran, tran, link);
550                         memput(tran, sizeof *tran);
551                         stop_read(ctx);
552                         start_write(ctx);
553                         return;
554                 }
555                 if (allocated_p(ctx->inbuf))
556                         goto again;
557                 return;
558         }
559         if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
560                 (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
561                                ctx->inbuf.text);
562                 error(ctx);
563         }
564 }
565
566 /* Timer related stuff. */
567
568 static void
569 start_timer(struct ctl_cctx *ctx) {
570         static const char me[] = "isc/ctl_clnt::start_timer";
571
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,
575                                strerror(errno));
576                 error(ctx);
577                 return;
578         }
579 }
580
581 static void
582 stop_timer(struct ctl_cctx *ctx) {
583         static const char me[] = "isc/ctl_clnt::stop_timer";
584
585         REQUIRE(ctx->tiID.opaque != NULL);
586         if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
587                 (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
588                                strerror(errno));
589                 error(ctx);
590                 return;
591         }
592         ctx->tiID.opaque = NULL;
593 }
594
595 static void
596 touch_timer(struct ctl_cctx *ctx) {
597         REQUIRE(ctx->tiID.opaque != NULL);
598
599         evTouchIdleTimer(ctx->ev, ctx->tiID);
600 }
601
602 static void
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;
606
607         UNUSED(ev);
608         UNUSED(due);
609         UNUSED(itv);
610
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]);
614         error(ctx);
615 }
616
617 /*! \file */