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