1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate #if !defined(lint) && !defined(SABER)
7*0Sstevel@tonic-gate static const char rcsid[] = "$Id: ctl_clnt.c,v 8.18 2002/07/08 05:10:23 marka Exp $";
8*0Sstevel@tonic-gate #endif /* not lint */
9*0Sstevel@tonic-gate 
10*0Sstevel@tonic-gate /*
11*0Sstevel@tonic-gate  * Copyright (c) 1998,1999 by Internet Software Consortium.
12*0Sstevel@tonic-gate  *
13*0Sstevel@tonic-gate  * Permission to use, copy, modify, and distribute this software for any
14*0Sstevel@tonic-gate  * purpose with or without fee is hereby granted, provided that the above
15*0Sstevel@tonic-gate  * copyright notice and this permission notice appear in all copies.
16*0Sstevel@tonic-gate  *
17*0Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
18*0Sstevel@tonic-gate  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
19*0Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
20*0Sstevel@tonic-gate  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
21*0Sstevel@tonic-gate  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
22*0Sstevel@tonic-gate  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
23*0Sstevel@tonic-gate  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24*0Sstevel@tonic-gate  * SOFTWARE.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /* Extern. */
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include "port_before.h"
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <sys/param.h>
34*0Sstevel@tonic-gate #include <sys/file.h>
35*0Sstevel@tonic-gate #include <sys/socket.h>
36*0Sstevel@tonic-gate 
37*0Sstevel@tonic-gate #include <netinet/in.h>
38*0Sstevel@tonic-gate #include <arpa/nameser.h>
39*0Sstevel@tonic-gate #include <arpa/inet.h>
40*0Sstevel@tonic-gate 
41*0Sstevel@tonic-gate #include <ctype.h>
42*0Sstevel@tonic-gate #include <errno.h>
43*0Sstevel@tonic-gate #include <stdio.h>
44*0Sstevel@tonic-gate #include <stdlib.h>
45*0Sstevel@tonic-gate #include <string.h>
46*0Sstevel@tonic-gate #include <time.h>
47*0Sstevel@tonic-gate #include <unistd.h>
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #include <isc/assertions.h>
50*0Sstevel@tonic-gate #include <isc/ctl.h>
51*0Sstevel@tonic-gate #include <isc/eventlib.h>
52*0Sstevel@tonic-gate #include <isc/list.h>
53*0Sstevel@tonic-gate #include <isc/memcluster.h>
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate #include "ctl_p.h"
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate #include "port_after.h"
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate /* Constants. */
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate /* Macros. */
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate #define donefunc_p(ctx) ((ctx).donefunc != NULL)
65*0Sstevel@tonic-gate #define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
66*0Sstevel@tonic-gate 			  isdigit((unsigned char)(line[1])) && \
67*0Sstevel@tonic-gate 			  isdigit((unsigned char)(line[2])))
68*0Sstevel@tonic-gate #define arpacont_p(line) (line[3] == '-')
69*0Sstevel@tonic-gate #define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
70*0Sstevel@tonic-gate 			  line[3] == '\r' || line[3] == '\0')
71*0Sstevel@tonic-gate 
72*0Sstevel@tonic-gate /* Types. */
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate enum state {
75*0Sstevel@tonic-gate 	initializing = 0, connecting, connected, destroyed
76*0Sstevel@tonic-gate };
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate struct ctl_tran {
79*0Sstevel@tonic-gate 	LINK(struct ctl_tran)	link;
80*0Sstevel@tonic-gate 	LINK(struct ctl_tran)	wlink;
81*0Sstevel@tonic-gate 	struct ctl_cctx *	ctx;
82*0Sstevel@tonic-gate 	struct ctl_buf		outbuf;
83*0Sstevel@tonic-gate 	ctl_clntdone		donefunc;
84*0Sstevel@tonic-gate 	void *			uap;
85*0Sstevel@tonic-gate };
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate struct ctl_cctx {
88*0Sstevel@tonic-gate 	enum state		state;
89*0Sstevel@tonic-gate 	evContext		ev;
90*0Sstevel@tonic-gate 	int			sock;
91*0Sstevel@tonic-gate 	ctl_logfunc		logger;
92*0Sstevel@tonic-gate 	ctl_clntdone		donefunc;
93*0Sstevel@tonic-gate 	void *			uap;
94*0Sstevel@tonic-gate 	evConnID		coID;
95*0Sstevel@tonic-gate 	evTimerID		tiID;
96*0Sstevel@tonic-gate 	evFileID		rdID;
97*0Sstevel@tonic-gate 	evStreamID		wrID;
98*0Sstevel@tonic-gate 	struct ctl_buf		inbuf;
99*0Sstevel@tonic-gate 	struct timespec		timeout;
100*0Sstevel@tonic-gate 	LIST(struct ctl_tran)	tran;
101*0Sstevel@tonic-gate 	LIST(struct ctl_tran)	wtran;
102*0Sstevel@tonic-gate };
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate /* Forward. */
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int);
107*0Sstevel@tonic-gate static void		start_write(struct ctl_cctx *);
108*0Sstevel@tonic-gate static void		destroy(struct ctl_cctx *, int);
109*0Sstevel@tonic-gate static void		error(struct ctl_cctx *);
110*0Sstevel@tonic-gate static void		new_state(struct ctl_cctx *, enum state);
111*0Sstevel@tonic-gate static void		conn_done(evContext, void *, int,
112*0Sstevel@tonic-gate 				  const void *, int,
113*0Sstevel@tonic-gate 				  const void *, int);
114*0Sstevel@tonic-gate static void		write_done(evContext, void *, int, int);
115*0Sstevel@tonic-gate static void		start_read(struct ctl_cctx *);
116*0Sstevel@tonic-gate static void		stop_read(struct ctl_cctx *);
117*0Sstevel@tonic-gate static void		readable(evContext, void *, int, int);
118*0Sstevel@tonic-gate static void		start_timer(struct ctl_cctx *);
119*0Sstevel@tonic-gate static void		stop_timer(struct ctl_cctx *);
120*0Sstevel@tonic-gate static void		touch_timer(struct ctl_cctx *);
121*0Sstevel@tonic-gate static void		timer(evContext, void *,
122*0Sstevel@tonic-gate 			      struct timespec, struct timespec);
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate /* Private data. */
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate static const char * const state_names[] = {
127*0Sstevel@tonic-gate 	"initializing", "connecting", "connected", "destroyed"
128*0Sstevel@tonic-gate };
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate /* Public. */
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate /*
133*0Sstevel@tonic-gate  * void
134*0Sstevel@tonic-gate  * ctl_client()
135*0Sstevel@tonic-gate  *	create, condition, and connect to a listener on the control port.
136*0Sstevel@tonic-gate  */
137*0Sstevel@tonic-gate struct ctl_cctx *
138*0Sstevel@tonic-gate ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len,
139*0Sstevel@tonic-gate 	   const struct sockaddr *sap, size_t sap_len,
140*0Sstevel@tonic-gate 	   ctl_clntdone donefunc, void *uap,
141*0Sstevel@tonic-gate 	   u_int timeout, ctl_logfunc logger)
142*0Sstevel@tonic-gate {
143*0Sstevel@tonic-gate 	static const char me[] = "ctl_client";
144*0Sstevel@tonic-gate 	static const int on = 1;
145*0Sstevel@tonic-gate 	struct ctl_cctx *ctx;
146*0Sstevel@tonic-gate 	struct sockaddr *captmp;
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	if (logger == NULL)
149*0Sstevel@tonic-gate 		logger = ctl_logger;
150*0Sstevel@tonic-gate 	ctx = memget(sizeof *ctx);
151*0Sstevel@tonic-gate 	if (ctx == NULL) {
152*0Sstevel@tonic-gate 		(*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
153*0Sstevel@tonic-gate 		goto fatal;
154*0Sstevel@tonic-gate 	}
155*0Sstevel@tonic-gate 	ctx->state = initializing;
156*0Sstevel@tonic-gate 	ctx->ev = lev;
157*0Sstevel@tonic-gate 	ctx->logger = logger;
158*0Sstevel@tonic-gate 	ctx->timeout = evConsTime(timeout, 0);
159*0Sstevel@tonic-gate 	ctx->donefunc = donefunc;
160*0Sstevel@tonic-gate 	ctx->uap = uap;
161*0Sstevel@tonic-gate 	ctx->coID.opaque = NULL;
162*0Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
163*0Sstevel@tonic-gate 	ctx->rdID.opaque = NULL;
164*0Sstevel@tonic-gate 	ctx->wrID.opaque = NULL;
165*0Sstevel@tonic-gate 	buffer_init(ctx->inbuf);
166*0Sstevel@tonic-gate 	INIT_LIST(ctx->tran);
167*0Sstevel@tonic-gate 	INIT_LIST(ctx->wtran);
168*0Sstevel@tonic-gate 	ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
169*0Sstevel@tonic-gate 	if (ctx->sock > evHighestFD(ctx->ev)) {
170*0Sstevel@tonic-gate 		ctx->sock = -1;
171*0Sstevel@tonic-gate 		errno = ENOTSOCK;
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 	if (ctx->sock < 0) {
174*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: socket: %s",
175*0Sstevel@tonic-gate 			       me, strerror(errno));
176*0Sstevel@tonic-gate 		goto fatal;
177*0Sstevel@tonic-gate 	}
178*0Sstevel@tonic-gate 	if (cap != NULL) {
179*0Sstevel@tonic-gate 		if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
180*0Sstevel@tonic-gate 			       (const char *)&on, sizeof on) != 0) {
181*0Sstevel@tonic-gate 			(*ctx->logger)(ctl_warning,
182*0Sstevel@tonic-gate 				       "%s: setsockopt(REUSEADDR): %s",
183*0Sstevel@tonic-gate 				       me, strerror(errno));
184*0Sstevel@tonic-gate 		}
185*0Sstevel@tonic-gate 		DE_CONST(cap, captmp);
186*0Sstevel@tonic-gate 		if (bind(ctx->sock, captmp, cap_len) < 0) {
187*0Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: bind: %s", me,
188*0Sstevel@tonic-gate 				       strerror(errno));
189*0Sstevel@tonic-gate 			goto fatal;
190*0Sstevel@tonic-gate 		}
191*0Sstevel@tonic-gate 	}
192*0Sstevel@tonic-gate 	if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len,
193*0Sstevel@tonic-gate 		      conn_done, ctx, &ctx->coID) < 0) {
194*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s",
195*0Sstevel@tonic-gate 			       me, ctx->sock, strerror(errno));
196*0Sstevel@tonic-gate  fatal:
197*0Sstevel@tonic-gate 		if (ctx != NULL) {
198*0Sstevel@tonic-gate 			if (ctx->sock >= 0)
199*0Sstevel@tonic-gate 				close(ctx->sock);
200*0Sstevel@tonic-gate 			memput(ctx, sizeof *ctx);
201*0Sstevel@tonic-gate 		}
202*0Sstevel@tonic-gate 		return (NULL);
203*0Sstevel@tonic-gate 	}
204*0Sstevel@tonic-gate 	new_state(ctx, connecting);
205*0Sstevel@tonic-gate 	return (ctx);
206*0Sstevel@tonic-gate }
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate /*
209*0Sstevel@tonic-gate  * void
210*0Sstevel@tonic-gate  * ctl_endclient(ctx)
211*0Sstevel@tonic-gate  *	close a client and release all of its resources.
212*0Sstevel@tonic-gate  */
213*0Sstevel@tonic-gate void
214*0Sstevel@tonic-gate ctl_endclient(struct ctl_cctx *ctx) {
215*0Sstevel@tonic-gate 	if (ctx->state != destroyed)
216*0Sstevel@tonic-gate 		destroy(ctx, 0);
217*0Sstevel@tonic-gate 	memput(ctx, sizeof *ctx);
218*0Sstevel@tonic-gate }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate /*
221*0Sstevel@tonic-gate  * int
222*0Sstevel@tonic-gate  * ctl_command(ctx, cmd, len, donefunc, uap)
223*0Sstevel@tonic-gate  *	Queue a transaction, which will begin with sending cmd
224*0Sstevel@tonic-gate  *	and complete by calling donefunc with the answer.
225*0Sstevel@tonic-gate  */
226*0Sstevel@tonic-gate int
227*0Sstevel@tonic-gate ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
228*0Sstevel@tonic-gate 	    ctl_clntdone donefunc, void *uap)
229*0Sstevel@tonic-gate {
230*0Sstevel@tonic-gate 	struct ctl_tran *tran;
231*0Sstevel@tonic-gate 	char *pc;
232*0Sstevel@tonic-gate 	unsigned int n;
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	switch (ctx->state) {
235*0Sstevel@tonic-gate 	case destroyed:
236*0Sstevel@tonic-gate 		errno = ENOTCONN;
237*0Sstevel@tonic-gate 		return (-1);
238*0Sstevel@tonic-gate 	case connecting:
239*0Sstevel@tonic-gate 	case connected:
240*0Sstevel@tonic-gate 		break;
241*0Sstevel@tonic-gate 	default:
242*0Sstevel@tonic-gate 		abort();
243*0Sstevel@tonic-gate 	}
244*0Sstevel@tonic-gate 	if (len >= MAX_LINELEN) {
245*0Sstevel@tonic-gate 		errno = EMSGSIZE;
246*0Sstevel@tonic-gate 		return (-1);
247*0Sstevel@tonic-gate 	}
248*0Sstevel@tonic-gate 	tran = new_tran(ctx, donefunc, uap, 1);
249*0Sstevel@tonic-gate 	if (tran == NULL)
250*0Sstevel@tonic-gate 		return (-1);
251*0Sstevel@tonic-gate 	if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
252*0Sstevel@tonic-gate 		return (-1);
253*0Sstevel@tonic-gate 	memcpy(tran->outbuf.text, cmd, len);
254*0Sstevel@tonic-gate 	tran->outbuf.used = len;
255*0Sstevel@tonic-gate 	for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
256*0Sstevel@tonic-gate 		if (!isascii((unsigned char)*pc) ||
257*0Sstevel@tonic-gate 		    !isprint((unsigned char)*pc))
258*0Sstevel@tonic-gate 			*pc = '\040';
259*0Sstevel@tonic-gate 	start_write(ctx);
260*0Sstevel@tonic-gate 	return (0);
261*0Sstevel@tonic-gate }
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate /* Private. */
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate static struct ctl_tran *
266*0Sstevel@tonic-gate new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
267*0Sstevel@tonic-gate 	struct ctl_tran *new = memget(sizeof *new);
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	if (new == NULL)
270*0Sstevel@tonic-gate 		return (NULL);
271*0Sstevel@tonic-gate 	new->ctx = ctx;
272*0Sstevel@tonic-gate 	buffer_init(new->outbuf);
273*0Sstevel@tonic-gate 	new->donefunc = donefunc;
274*0Sstevel@tonic-gate 	new->uap = uap;
275*0Sstevel@tonic-gate 	INIT_LINK(new, link);
276*0Sstevel@tonic-gate 	INIT_LINK(new, wlink);
277*0Sstevel@tonic-gate 	APPEND(ctx->tran, new, link);
278*0Sstevel@tonic-gate 	if (w)
279*0Sstevel@tonic-gate 		APPEND(ctx->wtran, new, wlink);
280*0Sstevel@tonic-gate 	return (new);
281*0Sstevel@tonic-gate }
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate static void
284*0Sstevel@tonic-gate start_write(struct ctl_cctx *ctx) {
285*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_write";
286*0Sstevel@tonic-gate 	struct ctl_tran *tran;
287*0Sstevel@tonic-gate 	struct iovec iov[2], *iovp = iov;
288*0Sstevel@tonic-gate 	char * tmp;
289*0Sstevel@tonic-gate 
290*0Sstevel@tonic-gate 	REQUIRE(ctx->state == connecting || ctx->state == connected);
291*0Sstevel@tonic-gate 	/* If there is a write in progress, don't try to write more yet. */
292*0Sstevel@tonic-gate 	if (ctx->wrID.opaque != NULL)
293*0Sstevel@tonic-gate 		return;
294*0Sstevel@tonic-gate 	/* If there are no trans, make sure timer is off, and we're done. */
295*0Sstevel@tonic-gate 	if (EMPTY(ctx->wtran)) {
296*0Sstevel@tonic-gate 		if (ctx->tiID.opaque != NULL)
297*0Sstevel@tonic-gate 			stop_timer(ctx);
298*0Sstevel@tonic-gate 		return;
299*0Sstevel@tonic-gate 	}
300*0Sstevel@tonic-gate 	/* Pull it off the head of the write queue. */
301*0Sstevel@tonic-gate 	tran = HEAD(ctx->wtran);
302*0Sstevel@tonic-gate 	UNLINK(ctx->wtran, tran, wlink);
303*0Sstevel@tonic-gate 	/* Since there are some trans, make sure timer is successfully "on". */
304*0Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
305*0Sstevel@tonic-gate 		touch_timer(ctx);
306*0Sstevel@tonic-gate 	else
307*0Sstevel@tonic-gate 		start_timer(ctx);
308*0Sstevel@tonic-gate 	if (ctx->state == destroyed)
309*0Sstevel@tonic-gate 		return;
310*0Sstevel@tonic-gate 	/* Marshall a newline-terminated message and clock it out. */
311*0Sstevel@tonic-gate 	*iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
312*0Sstevel@tonic-gate 	DE_CONST("\r\n", tmp);
313*0Sstevel@tonic-gate 	*iovp++ = evConsIovec(tmp, 2);
314*0Sstevel@tonic-gate 	if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
315*0Sstevel@tonic-gate 		    write_done, tran, &ctx->wrID) < 0) {
316*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
317*0Sstevel@tonic-gate 			       strerror(errno));
318*0Sstevel@tonic-gate 		error(ctx);
319*0Sstevel@tonic-gate 		return;
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 	if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
322*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
323*0Sstevel@tonic-gate 			       strerror(errno));
324*0Sstevel@tonic-gate 		error(ctx);
325*0Sstevel@tonic-gate 		return;
326*0Sstevel@tonic-gate 	}
327*0Sstevel@tonic-gate }
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate static void
330*0Sstevel@tonic-gate destroy(struct ctl_cctx *ctx, int notify) {
331*0Sstevel@tonic-gate 	struct ctl_tran *this, *next;
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	if (ctx->sock != -1) {
334*0Sstevel@tonic-gate 		(void) close(ctx->sock);
335*0Sstevel@tonic-gate 		ctx->sock = -1;
336*0Sstevel@tonic-gate 	}
337*0Sstevel@tonic-gate 	switch (ctx->state) {
338*0Sstevel@tonic-gate 	case connecting:
339*0Sstevel@tonic-gate 		REQUIRE(ctx->wrID.opaque == NULL);
340*0Sstevel@tonic-gate 		REQUIRE(EMPTY(ctx->tran));
341*0Sstevel@tonic-gate 		/*
342*0Sstevel@tonic-gate 		 * This test is nec'y since destroy() can be called from
343*0Sstevel@tonic-gate 		 * start_read() while the state is still "connecting".
344*0Sstevel@tonic-gate 		 */
345*0Sstevel@tonic-gate 		if (ctx->coID.opaque != NULL) {
346*0Sstevel@tonic-gate 			(void)evCancelConn(ctx->ev, ctx->coID);
347*0Sstevel@tonic-gate 			ctx->coID.opaque = NULL;
348*0Sstevel@tonic-gate 		}
349*0Sstevel@tonic-gate 		break;
350*0Sstevel@tonic-gate 	case connected:
351*0Sstevel@tonic-gate 		REQUIRE(ctx->coID.opaque == NULL);
352*0Sstevel@tonic-gate 		if (ctx->wrID.opaque != NULL) {
353*0Sstevel@tonic-gate 			(void)evCancelRW(ctx->ev, ctx->wrID);
354*0Sstevel@tonic-gate 			ctx->wrID.opaque = NULL;
355*0Sstevel@tonic-gate 		}
356*0Sstevel@tonic-gate 		if (ctx->rdID.opaque != NULL)
357*0Sstevel@tonic-gate 			stop_read(ctx);
358*0Sstevel@tonic-gate 		break;
359*0Sstevel@tonic-gate 	case destroyed:
360*0Sstevel@tonic-gate 		break;
361*0Sstevel@tonic-gate 	default:
362*0Sstevel@tonic-gate 		abort();
363*0Sstevel@tonic-gate 	}
364*0Sstevel@tonic-gate 	if (allocated_p(ctx->inbuf))
365*0Sstevel@tonic-gate 		ctl_bufput(&ctx->inbuf);
366*0Sstevel@tonic-gate 	for (this = HEAD(ctx->tran); this != NULL; this = next) {
367*0Sstevel@tonic-gate 		next = NEXT(this, link);
368*0Sstevel@tonic-gate 		if (allocated_p(this->outbuf))
369*0Sstevel@tonic-gate 			ctl_bufput(&this->outbuf);
370*0Sstevel@tonic-gate 		if (notify && this->donefunc != NULL)
371*0Sstevel@tonic-gate 			(*this->donefunc)(ctx, this->uap, NULL, 0);
372*0Sstevel@tonic-gate 		memput(this, sizeof *this);
373*0Sstevel@tonic-gate 	}
374*0Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
375*0Sstevel@tonic-gate 		stop_timer(ctx);
376*0Sstevel@tonic-gate 	new_state(ctx, destroyed);
377*0Sstevel@tonic-gate }
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate static void
380*0Sstevel@tonic-gate error(struct ctl_cctx *ctx) {
381*0Sstevel@tonic-gate 	REQUIRE(ctx->state != destroyed);
382*0Sstevel@tonic-gate 	destroy(ctx, 1);
383*0Sstevel@tonic-gate }
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate static void
386*0Sstevel@tonic-gate new_state(struct ctl_cctx *ctx, enum state new_state) {
387*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::new_state";
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	(*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
390*0Sstevel@tonic-gate 		       state_names[ctx->state], state_names[new_state]);
391*0Sstevel@tonic-gate 	ctx->state = new_state;
392*0Sstevel@tonic-gate }
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate static void
395*0Sstevel@tonic-gate conn_done(evContext ev, void *uap, int fd,
396*0Sstevel@tonic-gate 	  const void *la, int lalen,
397*0Sstevel@tonic-gate 	  const void *ra, int ralen)
398*0Sstevel@tonic-gate {
399*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::conn_done";
400*0Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
401*0Sstevel@tonic-gate 	struct ctl_tran *tran;
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	UNUSED(ev);
404*0Sstevel@tonic-gate 	UNUSED(la);
405*0Sstevel@tonic-gate 	UNUSED(lalen);
406*0Sstevel@tonic-gate 	UNUSED(ra);
407*0Sstevel@tonic-gate 	UNUSED(ralen);
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	ctx->coID.opaque = NULL;
410*0Sstevel@tonic-gate 	if (fd < 0) {
411*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
412*0Sstevel@tonic-gate 			       strerror(errno));
413*0Sstevel@tonic-gate 		error(ctx);
414*0Sstevel@tonic-gate 		return;
415*0Sstevel@tonic-gate 	}
416*0Sstevel@tonic-gate 	new_state(ctx, connected);
417*0Sstevel@tonic-gate 	tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
418*0Sstevel@tonic-gate 	if (tran == NULL) {
419*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
420*0Sstevel@tonic-gate 			       strerror(errno));
421*0Sstevel@tonic-gate 		error(ctx);
422*0Sstevel@tonic-gate 		return;
423*0Sstevel@tonic-gate 	}
424*0Sstevel@tonic-gate 	start_read(ctx);
425*0Sstevel@tonic-gate 	if (ctx->state == destroyed) {
426*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: start_read failed: %s",
427*0Sstevel@tonic-gate 			       me, strerror(errno));
428*0Sstevel@tonic-gate 		error(ctx);
429*0Sstevel@tonic-gate 		return;
430*0Sstevel@tonic-gate 	}
431*0Sstevel@tonic-gate }
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate static void
434*0Sstevel@tonic-gate write_done(evContext lev, void *uap, int fd, int bytes) {
435*0Sstevel@tonic-gate 	struct ctl_tran *tran = (struct ctl_tran *)uap;
436*0Sstevel@tonic-gate 	struct ctl_cctx *ctx = tran->ctx;
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	UNUSED(lev);
439*0Sstevel@tonic-gate 	UNUSED(fd);
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate 	ctx->wrID.opaque = NULL;
442*0Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
443*0Sstevel@tonic-gate 		touch_timer(ctx);
444*0Sstevel@tonic-gate 	ctl_bufput(&tran->outbuf);
445*0Sstevel@tonic-gate 	start_write(ctx);
446*0Sstevel@tonic-gate 	if (bytes < 0)
447*0Sstevel@tonic-gate 		destroy(ctx, 1);
448*0Sstevel@tonic-gate 	else
449*0Sstevel@tonic-gate 		start_read(ctx);
450*0Sstevel@tonic-gate }
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate static void
453*0Sstevel@tonic-gate start_read(struct ctl_cctx *ctx) {
454*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_read";
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	REQUIRE(ctx->state == connecting || ctx->state == connected);
457*0Sstevel@tonic-gate 	REQUIRE(ctx->rdID.opaque == NULL);
458*0Sstevel@tonic-gate 	if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx,
459*0Sstevel@tonic-gate 		       &ctx->rdID) < 0)
460*0Sstevel@tonic-gate 	{
461*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
462*0Sstevel@tonic-gate 			       ctx->sock, strerror(errno));
463*0Sstevel@tonic-gate 		error(ctx);
464*0Sstevel@tonic-gate 		return;
465*0Sstevel@tonic-gate 	}
466*0Sstevel@tonic-gate }
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate static void
469*0Sstevel@tonic-gate stop_read(struct ctl_cctx *ctx) {
470*0Sstevel@tonic-gate 	REQUIRE(ctx->coID.opaque == NULL);
471*0Sstevel@tonic-gate 	REQUIRE(ctx->rdID.opaque != NULL);
472*0Sstevel@tonic-gate 	(void)evDeselectFD(ctx->ev, ctx->rdID);
473*0Sstevel@tonic-gate 	ctx->rdID.opaque = NULL;
474*0Sstevel@tonic-gate }
475*0Sstevel@tonic-gate 
476*0Sstevel@tonic-gate static void
477*0Sstevel@tonic-gate readable(evContext ev, void *uap, int fd, int evmask) {
478*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::readable";
479*0Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
480*0Sstevel@tonic-gate 	struct ctl_tran *tran;
481*0Sstevel@tonic-gate 	ssize_t n;
482*0Sstevel@tonic-gate 	char *eos;
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 	UNUSED(ev);
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	REQUIRE(ctx != NULL);
487*0Sstevel@tonic-gate 	REQUIRE(fd >= 0);
488*0Sstevel@tonic-gate 	REQUIRE(evmask == EV_READ);
489*0Sstevel@tonic-gate 	REQUIRE(ctx->state == connected);
490*0Sstevel@tonic-gate 	REQUIRE(!EMPTY(ctx->tran));
491*0Sstevel@tonic-gate 	tran = HEAD(ctx->tran);
492*0Sstevel@tonic-gate 	if (!allocated_p(ctx->inbuf) &&
493*0Sstevel@tonic-gate 	    ctl_bufget(&ctx->inbuf, ctx->logger) < 0) {
494*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: can't get an input buffer", me);
495*0Sstevel@tonic-gate 		error(ctx);
496*0Sstevel@tonic-gate 		return;
497*0Sstevel@tonic-gate 	}
498*0Sstevel@tonic-gate 	n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
499*0Sstevel@tonic-gate 		 MAX_LINELEN - ctx->inbuf.used);
500*0Sstevel@tonic-gate 	if (n <= 0) {
501*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_warning, "%s: read: %s", me,
502*0Sstevel@tonic-gate 			       (n == 0) ? "Unexpected EOF" : strerror(errno));
503*0Sstevel@tonic-gate 		error(ctx);
504*0Sstevel@tonic-gate 		return;
505*0Sstevel@tonic-gate 	}
506*0Sstevel@tonic-gate 	if (ctx->tiID.opaque != NULL)
507*0Sstevel@tonic-gate 		touch_timer(ctx);
508*0Sstevel@tonic-gate 	ctx->inbuf.used += n;
509*0Sstevel@tonic-gate 	(*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
510*0Sstevel@tonic-gate 		       n, ctx->inbuf.used);
511*0Sstevel@tonic-gate  again:
512*0Sstevel@tonic-gate 	eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
513*0Sstevel@tonic-gate 	if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
514*0Sstevel@tonic-gate 		int done = 0;
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate 		eos[-1] = '\0';
517*0Sstevel@tonic-gate 		if (!arpacode_p(ctx->inbuf.text)) {
518*0Sstevel@tonic-gate 			/* XXX Doesn't FTP do this sometimes? Is it legal? */
519*0Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me,
520*0Sstevel@tonic-gate 				       ctx->inbuf.text);
521*0Sstevel@tonic-gate 			error(ctx);
522*0Sstevel@tonic-gate 			return;
523*0Sstevel@tonic-gate 		}
524*0Sstevel@tonic-gate 		if (arpadone_p(ctx->inbuf.text))
525*0Sstevel@tonic-gate 			done = 1;
526*0Sstevel@tonic-gate 		else if (arpacont_p(ctx->inbuf.text))
527*0Sstevel@tonic-gate 			done = 0;
528*0Sstevel@tonic-gate 		else {
529*0Sstevel@tonic-gate 			/* XXX Doesn't FTP do this sometimes? Is it legal? */
530*0Sstevel@tonic-gate 			(*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
531*0Sstevel@tonic-gate 				       ctx->inbuf.text);
532*0Sstevel@tonic-gate 			error(ctx);
533*0Sstevel@tonic-gate 			return;
534*0Sstevel@tonic-gate 		}
535*0Sstevel@tonic-gate 		(*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text,
536*0Sstevel@tonic-gate 				  (done ? 0 : CTL_MORE));
537*0Sstevel@tonic-gate 		ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1);
538*0Sstevel@tonic-gate 		if (ctx->inbuf.used == 0)
539*0Sstevel@tonic-gate 			ctl_bufput(&ctx->inbuf);
540*0Sstevel@tonic-gate 		else
541*0Sstevel@tonic-gate 			memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
542*0Sstevel@tonic-gate 		if (done) {
543*0Sstevel@tonic-gate 			UNLINK(ctx->tran, tran, link);
544*0Sstevel@tonic-gate 			memput(tran, sizeof *tran);
545*0Sstevel@tonic-gate 			stop_read(ctx);
546*0Sstevel@tonic-gate 			start_write(ctx);
547*0Sstevel@tonic-gate 			return;
548*0Sstevel@tonic-gate 		}
549*0Sstevel@tonic-gate 		if (allocated_p(ctx->inbuf))
550*0Sstevel@tonic-gate 			goto again;
551*0Sstevel@tonic-gate 		return;
552*0Sstevel@tonic-gate 	}
553*0Sstevel@tonic-gate 	if (ctx->inbuf.used == MAX_LINELEN) {
554*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
555*0Sstevel@tonic-gate 			       ctx->inbuf.text);
556*0Sstevel@tonic-gate 		error(ctx);
557*0Sstevel@tonic-gate 	}
558*0Sstevel@tonic-gate }
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate /* Timer related stuff. */
561*0Sstevel@tonic-gate 
562*0Sstevel@tonic-gate static void
563*0Sstevel@tonic-gate start_timer(struct ctl_cctx *ctx) {
564*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::start_timer";
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque == NULL);
567*0Sstevel@tonic-gate 	if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){
568*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me,
569*0Sstevel@tonic-gate 			       strerror(errno));
570*0Sstevel@tonic-gate 		error(ctx);
571*0Sstevel@tonic-gate 		return;
572*0Sstevel@tonic-gate 	}
573*0Sstevel@tonic-gate }
574*0Sstevel@tonic-gate 
575*0Sstevel@tonic-gate static void
576*0Sstevel@tonic-gate stop_timer(struct ctl_cctx *ctx) {
577*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::stop_timer";
578*0Sstevel@tonic-gate 
579*0Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque != NULL);
580*0Sstevel@tonic-gate 	if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
581*0Sstevel@tonic-gate 		(*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
582*0Sstevel@tonic-gate 			       strerror(errno));
583*0Sstevel@tonic-gate 		error(ctx);
584*0Sstevel@tonic-gate 		return;
585*0Sstevel@tonic-gate 	}
586*0Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
587*0Sstevel@tonic-gate }
588*0Sstevel@tonic-gate 
589*0Sstevel@tonic-gate static void
590*0Sstevel@tonic-gate touch_timer(struct ctl_cctx *ctx) {
591*0Sstevel@tonic-gate 	REQUIRE(ctx->tiID.opaque != NULL);
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	evTouchIdleTimer(ctx->ev, ctx->tiID);
594*0Sstevel@tonic-gate }
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate static void
597*0Sstevel@tonic-gate timer(evContext ev, void *uap, struct timespec due, struct timespec itv) {
598*0Sstevel@tonic-gate 	static const char me[] = "isc/ctl_clnt::timer";
599*0Sstevel@tonic-gate 	struct ctl_cctx *ctx = uap;
600*0Sstevel@tonic-gate 
601*0Sstevel@tonic-gate 	UNUSED(ev);
602*0Sstevel@tonic-gate 	UNUSED(due);
603*0Sstevel@tonic-gate 	UNUSED(itv);
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate 	ctx->tiID.opaque = NULL;
606*0Sstevel@tonic-gate 	(*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me,
607*0Sstevel@tonic-gate 		       ctx->timeout.tv_sec, state_names[ctx->state]);
608*0Sstevel@tonic-gate 	error(ctx);
609*0Sstevel@tonic-gate }
610