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