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
66enum state {
67	available = 0, initializing, writing, reading, reading_data,
68	processing, idling, quitting, closing
69};
70
71union sa_un {
72	struct sockaddr_in in;
73#ifndef NO_SOCKADDR_UN
74	struct sockaddr_un un;
75#endif
76};
77
78struct 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
99struct 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
117static void			ctl_accept(evContext, void *, int,
118					   const void *, int,
119					   const void *, int);
120static void			ctl_close(struct ctl_sess *);
121static void			ctl_new_state(struct ctl_sess *,
122					      enum state,
123					      const char *);
124static void			ctl_start_read(struct ctl_sess *);
125static void			ctl_stop_read(struct ctl_sess *);
126static void			ctl_readable(evContext, void *, int, int);
127static void			ctl_rdtimeout(evContext, void *,
128					      struct timespec,
129					      struct timespec);
130static void			ctl_wrtimeout(evContext, void *,
131					      struct timespec,
132					      struct timespec);
133static void			ctl_docommand(struct ctl_sess *);
134static void			ctl_writedone(evContext, void *, int, int);
135static void			ctl_morehelp(struct ctl_sctx *,
136					     struct ctl_sess *,
137					     const struct ctl_verb *,
138					     const char *,
139					     u_int, const void *, void *);
140static void			ctl_signal_done(struct ctl_sctx *,
141						struct ctl_sess *);
142
143/* Private data. */
144
145static const char *		state_names[] = {
146	"available", "initializing", "writing", "reading",
147	"reading_data", "processing", "idling", "quitting", "closing"
148};
149
150static const char		space[] = " ";
151
152static 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 */
163struct ctl_sctx *
164ctl_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 */
269void
270ctl_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 */
295void
296ctl_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
369void
370ctl_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
380void *
381ctl_getcsctx(struct ctl_sess *sess) {
382	return (sess->csctx);
383}
384
385void *
386ctl_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
395static void
396ctl_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
462static void
463ctl_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
476static void
477ctl_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
514static void
515ctl_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
544static void
545ctl_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
560static void
561ctl_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
619static void
620ctl_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
646static void
647ctl_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
672static void
673ctl_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
713static void
714ctl_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
753static void
754ctl_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
773static void
774ctl_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