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