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