Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / lib / bind / isc / ctl_srvr.c
1 #if !defined(lint) && !defined(SABER)
2 static const char rcsid[] = "$Id: ctl_srvr.c,v 1.3.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 #include <sys/un.h>
30
31 #include <netinet/in.h>
32 #include <arpa/nameser.h>
33 #include <arpa/inet.h>
34
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43
44 #include <isc/assertions.h>
45 #include <isc/ctl.h>
46 #include <isc/eventlib.h>
47 #include <isc/list.h>
48 #include <isc/logging.h>
49 #include <isc/memcluster.h>
50
51 #include "ctl_p.h"
52
53 #include "port_after.h"
54
55 #ifdef SPRINTF_CHAR
56 # define SPRINTF(x) strlen(sprintf/**/x)
57 #else
58 # define SPRINTF(x) ((size_t)sprintf x)
59 #endif
60
61 /* Macros. */
62
63 #define lastverb_p(verb)        (verb->name == NULL || verb->func == NULL)
64 #define address_expr            ctl_sa_ntop((struct sockaddr *)&sess->sa, \
65                                             tmp, sizeof tmp, ctx->logger)
66
67 /* Types. */
68
69 enum state {
70         available = 0, initializing, writing, reading, reading_data,
71         processing, idling, quitting, closing
72 };
73
74 union sa_un {
75         struct sockaddr_in in;
76 #ifndef NO_SOCKADDR_UN
77         struct sockaddr_un un;
78 #endif
79 };
80
81 struct ctl_sess {
82         LINK(struct ctl_sess)   link;
83         struct ctl_sctx *       ctx;
84         enum state              state;
85         int                     sock;
86         union sa_un             sa;
87         evFileID                rdID;
88         evStreamID              wrID;
89         evTimerID               rdtiID;
90         evTimerID               wrtiID;
91         struct ctl_buf          inbuf;
92         struct ctl_buf          outbuf;
93         const struct ctl_verb * verb;
94         u_int                   helpcode;
95         const void *            respctx;
96         u_int                   respflags;
97         ctl_srvrdone            donefunc;
98         void *                  uap;
99         void *                  csctx;
100 };
101
102 struct ctl_sctx {
103         evContext               ev;
104         void *                  uctx;
105         u_int                   unkncode;
106         u_int                   timeoutcode;
107         const struct ctl_verb * verbs;
108         const struct ctl_verb * connverb;
109         int                     sock;
110         int                     max_sess;
111         int                     cur_sess;
112         struct timespec         timeout;
113         ctl_logfunc             logger;
114         evConnID                acID;
115         LIST(struct ctl_sess)   sess;
116 };
117
118 /* Forward. */
119
120 static void                     ctl_accept(evContext, void *, int,
121                                            const void *, int,
122                                            const void *, int);
123 static void                     ctl_close(struct ctl_sess *);
124 static void                     ctl_new_state(struct ctl_sess *,
125                                               enum state,
126                                               const char *);
127 static void                     ctl_start_read(struct ctl_sess *);
128 static void                     ctl_stop_read(struct ctl_sess *);
129 static void                     ctl_readable(evContext, void *, int, int);
130 static void                     ctl_rdtimeout(evContext, void *,
131                                               struct timespec,
132                                               struct timespec);
133 static void                     ctl_wrtimeout(evContext, void *,
134                                               struct timespec,
135                                               struct timespec);
136 static void                     ctl_docommand(struct ctl_sess *);
137 static void                     ctl_writedone(evContext, void *, int, int);
138 static void                     ctl_morehelp(struct ctl_sctx *,
139                                              struct ctl_sess *,
140                                              const struct ctl_verb *,
141                                              const char *,
142                                              u_int, const void *, void *);
143 static void                     ctl_signal_done(struct ctl_sctx *,
144                                                 struct ctl_sess *);
145
146 /* Private data. */
147
148 static const char *             state_names[] = {
149         "available", "initializing", "writing", "reading",
150         "reading_data", "processing", "idling", "quitting", "closing"
151 };
152
153 static const char               space[] = " ";
154
155 static const struct ctl_verb    fakehelpverb = {
156         "fakehelp", ctl_morehelp , NULL
157 };
158
159 /* Public. */
160
161 /*
162  * void
163  * ctl_server()
164  *      create, condition, and start a listener on the control port.
165  */
166 struct ctl_sctx *
167 ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len,
168            const struct ctl_verb *verbs,
169            u_int unkncode, u_int timeoutcode,
170            u_int timeout, int backlog, int max_sess,
171            ctl_logfunc logger, void *uctx)
172 {
173         static const char me[] = "ctl_server";
174         static const int on = 1;
175         const struct ctl_verb *connverb;
176         struct ctl_sctx *ctx;
177         int save_errno;
178
179         if (logger == NULL)
180                 logger = ctl_logger;
181         for (connverb = verbs;
182              connverb->name != NULL && connverb->func != NULL;
183              connverb++)
184                 if (connverb->name[0] == '\0')
185                         break;
186         if (connverb->func == NULL) {
187                 (*logger)(ctl_error, "%s: no connection verb found", me);
188                 return (NULL);
189         }
190         ctx = memget(sizeof *ctx);
191         if (ctx == NULL) {
192                 (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
193                 return (NULL);
194         }
195         ctx->ev = lev;
196         ctx->uctx = uctx;
197         ctx->unkncode = unkncode;
198         ctx->timeoutcode = timeoutcode;
199         ctx->verbs = verbs;
200         ctx->timeout = evConsTime(timeout, 0);
201         ctx->logger = logger;
202         ctx->connverb = connverb;
203         ctx->max_sess = max_sess;
204         ctx->cur_sess = 0;
205         INIT_LIST(ctx->sess);
206         ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
207         if (ctx->sock > evHighestFD(ctx->ev)) {
208                 ctx->sock = -1;
209                 errno = ENOTSOCK;
210         }
211         if (ctx->sock < 0) {
212                 save_errno = errno;
213                 (*ctx->logger)(ctl_error, "%s: socket: %s",
214                                me, strerror(errno));
215                 memput(ctx, sizeof *ctx);
216                 errno = save_errno;
217                 return (NULL);
218         }
219         if (ctx->sock > evHighestFD(lev)) {
220                 close(ctx->sock);
221                 (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD");
222                 errno = ENFILE;
223                 memput(ctx, sizeof *ctx);
224                 return (NULL);
225         }
226 #ifdef NO_UNIX_REUSEADDR
227         if (sap->sa_family != AF_UNIX)
228 #endif
229                 if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
230                                (const char *)&on, sizeof on) != 0) {
231                         (*ctx->logger)(ctl_warning,
232                                        "%s: setsockopt(REUSEADDR): %s",
233                                        me, strerror(errno));
234                 }
235         if (bind(ctx->sock, sap, sap_len) < 0) {
236                 char tmp[MAX_NTOP];
237                 save_errno = errno;
238                 (*ctx->logger)(ctl_error, "%s: bind: %s: %s",
239                                me, ctl_sa_ntop((const struct sockaddr *)sap,
240                                tmp, sizeof tmp, ctx->logger),
241                                strerror(save_errno));
242                 close(ctx->sock);
243                 memput(ctx, sizeof *ctx);
244                 errno = save_errno;
245                 return (NULL);
246         }
247         if (fcntl(ctx->sock, F_SETFD, 1) < 0) {
248                 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
249                                strerror(errno));
250         }
251         if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx,
252                      &ctx->acID) < 0) {
253                 save_errno = errno;
254                 (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s",
255                                me, ctx->sock, strerror(errno));
256                 close(ctx->sock);
257                 memput(ctx, sizeof *ctx);
258                 errno = save_errno;
259                 return (NULL);
260         }
261         (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d",
262                        me, ctx, ctx->sock);
263         return (ctx);
264 }
265
266 /*
267  * void
268  * ctl_endserver(ctx)
269  *      if the control listener is open, close it.  clean out all eventlib
270  *      stuff.  close all active sessions.
271  */
272 void
273 ctl_endserver(struct ctl_sctx *ctx) {
274         static const char me[] = "ctl_endserver";
275         struct ctl_sess *this, *next;
276
277         (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p",
278                        me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess);
279         if (ctx->acID.opaque != NULL) {
280                 (void)evCancelConn(ctx->ev, ctx->acID);
281                 ctx->acID.opaque = NULL;
282         }
283         if (ctx->sock != -1) {
284                 (void) close(ctx->sock);
285                 ctx->sock = -1;
286         }
287         for (this = HEAD(ctx->sess); this != NULL; this = next) {
288                 next = NEXT(this, link);
289                 ctl_close(this);
290         }
291         memput(ctx, sizeof *ctx);
292 }
293
294 /*
295  * If body is non-NULL then it we add a "." line after it.
296  * Caller must have  escaped lines with leading ".".
297  */
298 void
299 ctl_response(struct ctl_sess *sess, u_int code, const char *text,
300              u_int flags, const void *respctx, ctl_srvrdone donefunc,
301              void *uap, const char *body, size_t bodylen)
302 {
303         static const char me[] = "ctl_response";
304         struct iovec iov[3], *iovp = iov;
305         struct ctl_sctx *ctx = sess->ctx;
306         char tmp[MAX_NTOP], *pc;
307         int n;
308
309         REQUIRE(sess->state == initializing ||
310                 sess->state == processing ||
311                 sess->state == reading_data ||
312                 sess->state == writing);
313         REQUIRE(sess->wrtiID.opaque == NULL);
314         REQUIRE(sess->wrID.opaque == NULL);
315         ctl_new_state(sess, writing, me);
316         sess->donefunc = donefunc;
317         sess->uap = uap;
318         if (!allocated_p(sess->outbuf) &&
319             ctl_bufget(&sess->outbuf, ctx->logger) < 0) {
320                 (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer",
321                                me, address_expr);
322                 goto untimely;
323         }
324         if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) {
325                 (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing",
326                                me, address_expr);
327                 goto untimely;
328         }
329         sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n",
330                                      code, (flags & CTL_MORE) != 0 ? '-' : ' ',
331                                      text));
332         for (pc = sess->outbuf.text, n = 0;
333              n < (int)sess->outbuf.used-2; pc++, n++)
334                 if (!isascii((unsigned char)*pc) ||
335                     !isprint((unsigned char)*pc))
336                         *pc = '\040';
337         *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used);
338         if (body != NULL) {
339                 char *tmp;
340                 DE_CONST(body, tmp);
341                 *iovp++ = evConsIovec(tmp, bodylen);
342                 DE_CONST(".\r\n", tmp);
343                 *iovp++ = evConsIovec(tmp, 3);
344         }
345         (*ctx->logger)(ctl_debug, "%s: [%d] %s", me,
346                        sess->outbuf.used, sess->outbuf.text);
347         if (evWrite(ctx->ev, sess->sock, iov, iovp - iov,
348                     ctl_writedone, sess, &sess->wrID) < 0) {
349                 (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me,
350                                address_expr, strerror(errno));
351                 goto untimely;
352         }
353         if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout,
354                            &sess->wrtiID) < 0)
355         {
356                 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
357                                address_expr, strerror(errno));
358                 goto untimely;
359         }
360         if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) {
361                 (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me,
362                                address_expr, strerror(errno));
363  untimely:
364                 ctl_signal_done(ctx, sess);
365                 ctl_close(sess);
366                 return;
367         }
368         sess->respctx = respctx;
369         sess->respflags = flags;
370 }
371
372 void
373 ctl_sendhelp(struct ctl_sess *sess, u_int code) {
374         static const char me[] = "ctl_sendhelp";
375         struct ctl_sctx *ctx = sess->ctx;
376
377         sess->helpcode = code;
378         sess->verb = &fakehelpverb;
379         ctl_morehelp(ctx, sess, NULL, me, CTL_MORE,
380                      (const void *)ctx->verbs, NULL);
381 }
382
383 void *
384 ctl_getcsctx(struct ctl_sess *sess) {
385         return (sess->csctx);
386 }
387
388 void *
389 ctl_setcsctx(struct ctl_sess *sess, void *csctx) {
390         void *old = sess->csctx;
391
392         sess->csctx = csctx;
393         return (old);
394 }
395
396 /* Private functions. */
397
398 static void
399 ctl_accept(evContext lev, void *uap, int fd,
400            const void *lav, int lalen,
401            const void *rav, int ralen)
402 {
403         static const char me[] = "ctl_accept";
404         struct ctl_sctx *ctx = uap;
405         struct ctl_sess *sess = NULL;
406         char tmp[MAX_NTOP];
407
408         UNUSED(lev);
409         UNUSED(lalen);
410         UNUSED(ralen);
411
412         if (fd < 0) {
413                 (*ctx->logger)(ctl_error, "%s: accept: %s",
414                                me, strerror(errno));
415                 return;
416         }
417         if (ctx->cur_sess == ctx->max_sess) {
418                 (*ctx->logger)(ctl_error, "%s: %s: too many control sessions",
419                                me, ctl_sa_ntop((const struct sockaddr *)rav,
420                                                tmp, sizeof tmp,
421                                                ctx->logger));
422                 (void) close(fd);
423                 return;
424         }
425         sess = memget(sizeof *sess);
426         if (sess == NULL) {
427                 (*ctx->logger)(ctl_error, "%s: memget: %s", me,
428                                strerror(errno));
429                 (void) close(fd);
430                 return;
431         }
432         if (fcntl(fd, F_SETFD, 1) < 0) {
433                 (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
434                                strerror(errno));
435         }
436         ctx->cur_sess++;
437         INIT_LINK(sess, link);
438         APPEND(ctx->sess, sess, link);
439         sess->ctx = ctx;
440         sess->sock = fd;
441         sess->wrID.opaque = NULL;
442         sess->rdID.opaque = NULL;
443         sess->wrtiID.opaque = NULL;
444         sess->rdtiID.opaque = NULL;
445         sess->respctx = NULL;
446         sess->csctx = NULL;
447         if (((const struct sockaddr *)rav)->sa_family == AF_UNIX)
448                 ctl_sa_copy((const struct sockaddr *)lav,
449                             (struct sockaddr *)&sess->sa);
450         else
451                 ctl_sa_copy((const struct sockaddr *)rav,
452                             (struct sockaddr *)&sess->sa);
453         sess->donefunc = NULL;
454         buffer_init(sess->inbuf);
455         buffer_init(sess->outbuf);
456         sess->state = available;
457         ctl_new_state(sess, initializing, me);
458         sess->verb = ctx->connverb;
459         (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)",
460                        me, address_expr, sess->sock);
461         (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0,
462                                (const struct sockaddr *)rav, ctx->uctx);
463 }
464
465 static void
466 ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason)
467 {
468         static const char me[] = "ctl_new_state";
469         struct ctl_sctx *ctx = sess->ctx;
470         char tmp[MAX_NTOP];
471
472         (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)",
473                        me, address_expr,
474                        state_names[sess->state],
475                        state_names[new_state], reason);
476         sess->state = new_state;
477 }
478
479 static void
480 ctl_close(struct ctl_sess *sess) {
481         static const char me[] = "ctl_close";
482         struct ctl_sctx *ctx = sess->ctx;
483         char tmp[MAX_NTOP];
484
485         REQUIRE(sess->state == initializing ||
486                 sess->state == writing ||
487                 sess->state == reading ||
488                 sess->state == processing ||
489                 sess->state == reading_data ||
490                 sess->state == idling);
491         REQUIRE(sess->sock != -1);
492         if (sess->state == reading || sess->state == reading_data)
493                 ctl_stop_read(sess);
494         else if (sess->state == writing) {
495                 if (sess->wrID.opaque != NULL) {
496                         (void) evCancelRW(ctx->ev, sess->wrID);
497                         sess->wrID.opaque = NULL;
498                 }
499                 if (sess->wrtiID.opaque != NULL) {
500                         (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
501                         sess->wrtiID.opaque = NULL;
502                 }
503         }
504         ctl_new_state(sess, closing, me);
505         (void) close(sess->sock);
506         if (allocated_p(sess->inbuf))
507                 ctl_bufput(&sess->inbuf);
508         if (allocated_p(sess->outbuf))
509                 ctl_bufput(&sess->outbuf);
510         (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)",
511                        me, address_expr, sess->sock);
512         UNLINK(ctx->sess, sess, link);
513         memput(sess, sizeof *sess);
514         ctx->cur_sess--;
515 }
516
517 static void
518 ctl_start_read(struct ctl_sess *sess) {
519         static const char me[] = "ctl_start_read";
520         struct ctl_sctx *ctx = sess->ctx;
521         char tmp[MAX_NTOP];
522
523         REQUIRE(sess->state == initializing ||
524                 sess->state == writing ||
525                 sess->state == processing ||
526                 sess->state == idling);
527         REQUIRE(sess->rdtiID.opaque == NULL);
528         REQUIRE(sess->rdID.opaque == NULL);
529         sess->inbuf.used = 0;
530         if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout,
531                            &sess->rdtiID) < 0)
532         {
533                 (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
534                                address_expr, strerror(errno));
535                 ctl_close(sess);
536                 return;
537         }
538         if (evSelectFD(ctx->ev, sess->sock, EV_READ,
539                        ctl_readable, sess, &sess->rdID) < 0) {
540                 (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me,
541                                address_expr, strerror(errno));
542                 return;
543         }
544         ctl_new_state(sess, reading, me);
545 }
546
547 static void
548 ctl_stop_read(struct ctl_sess *sess) {
549         static const char me[] = "ctl_stop_read";
550         struct ctl_sctx *ctx = sess->ctx;
551
552         REQUIRE(sess->state == reading || sess->state == reading_data);
553         REQUIRE(sess->rdID.opaque != NULL);
554         (void) evDeselectFD(ctx->ev, sess->rdID);
555         sess->rdID.opaque = NULL;
556         if (sess->rdtiID.opaque != NULL) {
557                 (void) evClearIdleTimer(ctx->ev, sess->rdtiID);
558                 sess->rdtiID.opaque = NULL;
559         }
560         ctl_new_state(sess, idling, me);
561 }
562
563 static void
564 ctl_readable(evContext lev, void *uap, int fd, int evmask) {
565         static const char me[] = "ctl_readable";
566         struct ctl_sess *sess = uap;
567         struct ctl_sctx *ctx = sess->ctx;
568         char *eos, tmp[MAX_NTOP];
569         ssize_t n;
570
571         REQUIRE(sess != NULL);
572         REQUIRE(fd >= 0);
573         REQUIRE(evmask == EV_READ);
574         REQUIRE(sess->state == reading || sess->state == reading_data);
575         evTouchIdleTimer(lev, sess->rdtiID);
576         if (!allocated_p(sess->inbuf) &&
577             ctl_bufget(&sess->inbuf, ctx->logger) < 0) {
578                 (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer",
579                                me, address_expr);
580                 ctl_close(sess);
581                 return;
582         }
583         n = read(sess->sock, sess->inbuf.text + sess->inbuf.used,
584                  MAX_LINELEN - sess->inbuf.used);
585         if (n <= 0) {
586                 (*ctx->logger)(ctl_debug, "%s: %s: read: %s",
587                                me, address_expr,
588                                (n == 0) ? "Unexpected EOF" : strerror(errno));
589                 ctl_close(sess);
590                 return;
591         }
592         sess->inbuf.used += n;
593         eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used);
594         if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') {
595                 eos[-1] = '\0';
596                 if ((sess->respflags & CTL_DATA) != 0) {
597                         INSIST(sess->verb != NULL);
598                         (*sess->verb->func)(sess->ctx, sess, sess->verb,
599                                             sess->inbuf.text,
600                                             CTL_DATA, sess->respctx,
601                                             sess->ctx->uctx);
602                 } else {
603                         ctl_stop_read(sess);
604                         ctl_docommand(sess);
605                 }
606                 sess->inbuf.used -= ((eos - sess->inbuf.text) + 1);
607                 if (sess->inbuf.used == 0U)
608                         ctl_bufput(&sess->inbuf);
609                 else
610                         memmove(sess->inbuf.text, eos + 1, sess->inbuf.used);
611                 return;
612         }
613         if (sess->inbuf.used == (size_t)MAX_LINELEN) {
614                 (*ctx->logger)(ctl_error, "%s: %s: line too long, closing",
615                                me, address_expr);
616                 ctl_close(sess);
617         }
618 }
619
620 static void
621 ctl_wrtimeout(evContext lev, void *uap,
622               struct timespec due,
623               struct timespec itv)
624 {
625         static const char me[] = "ctl_wrtimeout";
626         struct ctl_sess *sess = uap;
627         struct ctl_sctx *ctx = sess->ctx;
628         char tmp[MAX_NTOP];
629         
630         UNUSED(lev);
631         UNUSED(due);
632         UNUSED(itv);
633
634         REQUIRE(sess->state == writing);
635         sess->wrtiID.opaque = NULL;
636         (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing",
637                        me, address_expr);
638         if (sess->wrID.opaque != NULL) {
639                 (void) evCancelRW(ctx->ev, sess->wrID);
640                 sess->wrID.opaque = NULL;
641         }
642         ctl_signal_done(ctx, sess);
643         ctl_new_state(sess, processing, me);
644         ctl_close(sess);
645 }
646
647 static void
648 ctl_rdtimeout(evContext lev, void *uap,
649               struct timespec due,
650               struct timespec itv)
651 {
652         static const char me[] = "ctl_rdtimeout";
653         struct ctl_sess *sess = uap;
654         struct ctl_sctx *ctx = sess->ctx;
655         char tmp[MAX_NTOP];
656
657         UNUSED(lev);
658         UNUSED(due);
659         UNUSED(itv);
660
661         REQUIRE(sess->state == reading);
662         sess->rdtiID.opaque = NULL;
663         (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing",
664                        me, address_expr);
665         if (sess->state == reading || sess->state == reading_data)
666                 ctl_stop_read(sess);
667         ctl_signal_done(ctx, sess);
668         ctl_new_state(sess, processing, me);
669         ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL,
670                      NULL, NULL, NULL, 0);
671 }
672
673 static void
674 ctl_docommand(struct ctl_sess *sess) {
675         static const char me[] = "ctl_docommand";
676         char *name, *rest, tmp[MAX_NTOP];
677         struct ctl_sctx *ctx = sess->ctx;
678         const struct ctl_verb *verb;
679
680         REQUIRE(allocated_p(sess->inbuf));
681         (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]",
682                        me, address_expr,
683                        sess->inbuf.text, (u_int)sess->inbuf.used);
684         ctl_new_state(sess, processing, me);
685         name = sess->inbuf.text + strspn(sess->inbuf.text, space);
686         rest = name + strcspn(name, space);
687         if (*rest != '\0') {
688                 *rest++ = '\0';
689                 rest += strspn(rest, space);
690         }
691         for (verb = ctx->verbs;
692              verb != NULL && verb->name != NULL && verb->func != NULL;
693              verb++)
694                 if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0)
695                         break;
696         if (verb != NULL && verb->name != NULL && verb->func != NULL) {
697                 sess->verb = verb;
698                 (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx);
699         } else {
700                 char buf[1100];
701
702                 if (sizeof "Unrecognized command \"\" (args \"\")" +
703                     strlen(name) + strlen(rest) > sizeof buf)
704                         strcpy(buf, "Unrecognized command (buf ovf)");
705                 else
706                         sprintf(buf,
707                                 "Unrecognized command \"%s\" (args \"%s\")",
708                                 name, rest);
709                 ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL,
710                              NULL, 0);
711         }
712 }
713
714 static void
715 ctl_writedone(evContext lev, void *uap, int fd, int bytes) {
716         static const char me[] = "ctl_writedone";
717         struct ctl_sess *sess = uap;
718         struct ctl_sctx *ctx = sess->ctx;
719         char tmp[MAX_NTOP];
720         int save_errno = errno;
721
722         UNUSED(lev);
723         UNUSED(uap);
724
725         REQUIRE(sess->state == writing);
726         REQUIRE(fd == sess->sock);
727         REQUIRE(sess->wrtiID.opaque != NULL);
728         sess->wrID.opaque = NULL;
729         (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
730         sess->wrtiID.opaque = NULL;
731         if (bytes < 0) {
732                 (*ctx->logger)(ctl_error, "%s: %s: %s",
733                                me, address_expr, strerror(save_errno));
734                 ctl_close(sess);
735                 return;
736         }
737
738         INSIST(allocated_p(sess->outbuf));
739         ctl_bufput(&sess->outbuf);
740         if ((sess->respflags & CTL_EXIT) != 0) {
741                 ctl_signal_done(ctx, sess);
742                 ctl_close(sess);
743                 return;
744         } else if ((sess->respflags & CTL_MORE) != 0) {
745                 INSIST(sess->verb != NULL);
746                 (*sess->verb->func)(sess->ctx, sess, sess->verb, "",
747                                     CTL_MORE, sess->respctx, sess->ctx->uctx);
748         } else {
749                 ctl_signal_done(ctx, sess);
750                 ctl_start_read(sess);
751         }
752 }
753
754 static void
755 ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess,
756              const struct ctl_verb *verb, const char *text,
757              u_int respflags, const void *respctx, void *uctx)
758 {
759         const struct ctl_verb *this = respctx, *next = this + 1;
760
761         UNUSED(ctx);
762         UNUSED(verb);
763         UNUSED(text);
764         UNUSED(uctx);
765
766         REQUIRE(!lastverb_p(this));
767         REQUIRE((respflags & CTL_MORE) != 0);
768         if (lastverb_p(next))
769                 respflags &= ~CTL_MORE;
770         ctl_response(sess, sess->helpcode, this->help, respflags, next,
771                      NULL, NULL, NULL, 0);
772 }
773
774 static void
775 ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) {
776         if (sess->donefunc != NULL) {
777                 (*sess->donefunc)(ctx, sess, sess->uap);
778                 sess->donefunc = NULL;
779         }
780 }