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 #if !defined(lint) && !defined(SABER)
19 static const char rcsid[] = "$Id: ctl_srvr.c,v 1.10 2008/11/14 02:36:51 marka Exp $";
20 #endif /* not lint */
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 *
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)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
ctl_endserver(struct ctl_sctx * ctx)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
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)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
ctl_sendhelp(struct ctl_sess * sess,u_int code)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 *
ctl_getcsctx(struct ctl_sess * sess)387 ctl_getcsctx(struct ctl_sess *sess) {
388 return (sess->csctx);
389 }
390
391 void *
ctl_setcsctx(struct ctl_sess * sess,void * csctx)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
ctl_accept(evContext lev,void * uap,int fd,const void * lav,int lalen,const void * rav,int ralen)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
ctl_new_state(struct ctl_sess * sess,enum state new_state,const char * reason)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
ctl_close(struct ctl_sess * sess)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
ctl_start_read(struct ctl_sess * sess)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
ctl_stop_read(struct ctl_sess * sess)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
ctl_readable(evContext lev,void * uap,int fd,int evmask)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
ctl_wrtimeout(evContext lev,void * uap,struct timespec due,struct timespec itv)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
ctl_rdtimeout(evContext lev,void * uap,struct timespec due,struct timespec itv)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
ctl_docommand(struct ctl_sess * sess)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
ctl_writedone(evContext lev,void * uap,int fd,int bytes)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
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)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
ctl_signal_done(struct ctl_sctx * ctx,struct ctl_sess * sess)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 */
788