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