identd.c revision 1.2
1/*	$OpenBSD: identd.c,v 1.2 2013/03/18 01:20:46 dlg Exp $ */
2
3/*
4 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/ioctl.h>
20#include <sys/param.h>
21#include <sys/types.h>
22#include <sys/queue.h>
23#include <sys/socket.h>
24#include <sys/socketvar.h>
25#include <sys/sysctl.h>
26#include <sys/uio.h>
27
28#include <netinet/in.h>
29#include <netinet/in_systm.h>
30#include <netinet/ip_var.h>
31#include <netinet/tcp.h>
32#include <netinet/tcp_timer.h>
33#include <netinet/tcp_var.h>
34
35#include <arpa/inet.h>
36#include <netdb.h>
37
38#include <err.h>
39#include <ctype.h>
40#include <errno.h>
41#include <event.h>
42#include <fcntl.h>
43#include <pwd.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <stdarg.h>
48#include <syslog.h>
49#include <unistd.h>
50
51#define IDENTD_USER "_identd"
52
53#define TIMEOUT_MIN 4
54#define TIMEOUT_MAX 240
55#define TIMEOUT_DEFAULT 120
56
57enum ident_client_state {
58	S_BEGINNING = 0,
59	S_SERVER_PORT,
60	S_PRE_COMMA,
61	S_POST_COMMA,
62	S_CLIENT_PORT,
63	S_PRE_EOL,
64	S_EOL,
65
66	S_DEAD,
67	S_DYING
68};
69
70#define E_NONE		0
71#define E_NOUSER	1
72#define E_UNKNOWN	2
73#define E_HIDDEN	3
74
75struct ident_client {
76	struct {
77		/* from the socket */
78		struct sockaddr_storage ss;
79		socklen_t len;
80
81		/* from the request */
82		u_int port;
83	} client, server;
84	SIMPLEQ_ENTRY(ident_client) entry;
85	enum ident_client_state state;
86	struct event ev;
87
88	char *buf;
89	size_t buflen;
90	size_t bufoff;
91	uid_t uid;
92};
93
94struct ident_resolver {
95	SIMPLEQ_ENTRY(ident_resolver) entry;
96	char *buf;
97	size_t buflen;
98	u_int error;
99};
100
101struct identd_listener {
102	struct event ev, pause;
103};
104
105void	parent_rd(int, short, void *);
106void	parent_wr(int, short, void *);
107
108void	child_rd(int, short, void *);
109void	child_wr(int, short, void *);
110
111void	identd_listen(const char *, const char *, int);
112void	identd_paused(int, short, void *);
113void	identd_accept(int, short, void *);
114void	identd_request(int, short, void *);
115enum ident_client_state
116	identd_parse(struct ident_client *, int);
117void	identd_resolving(int, short, void *);
118void	identd_response(int, short, void *);
119int	fetchuid(struct ident_client *);
120
121const char *gethost(struct sockaddr_storage *);
122const char *getport(struct sockaddr_storage *);
123
124struct loggers {
125	void (*err)(int, const char *, ...);
126	void (*errx)(int, const char *, ...);
127	void (*warn)(const char *, ...);
128	void (*warnx)(const char *, ...);
129	void (*info)(const char *, ...);
130	void (*debug)(const char *, ...);
131};
132
133const struct loggers conslogger = {
134	err,
135	errx,
136	warn,
137	warnx,
138	warnx, /* info */
139	warnx /* debug */
140};
141
142void	syslog_err(int, const char *, ...);
143void	syslog_errx(int, const char *, ...);
144void	syslog_warn(const char *, ...);
145void	syslog_warnx(const char *, ...);
146void	syslog_info(const char *, ...);
147void	syslog_debug(const char *, ...);
148void	syslog_vstrerror(int, int, const char *, va_list);
149
150const struct loggers syslogger = {
151	syslog_err,
152	syslog_errx,
153	syslog_warn,
154	syslog_warnx,
155	syslog_info,
156	syslog_debug
157};
158
159const struct loggers *logger = &conslogger;
160
161#define lerr(_e, _f...) logger->err((_e), _f)
162#define lerrx(_e, _f...) logger->errx((_e), _f)
163#define lwarn(_f...) logger->warn(_f)
164#define lwarnx(_f...) logger->warnx(_f)
165#define linfo(_f...) logger->info(_f)
166#define ldebug(_f...) logger->debug(_f)
167
168const char *gethost(struct sockaddr_storage *);
169#define sa(_ss) ((struct sockaddr *)(_ss))
170
171__dead void
172usage(void)
173{
174	extern char *__progname;
175	fprintf(stderr, "usage: %s [-46d] [-l address] [-p port] "
176	    "[-t timeout]\n", __progname);
177	exit(1);
178}
179
180struct timeval timeout = { TIMEOUT_DEFAULT, 0 };
181int debug = 0;
182int on = 1;
183
184struct event proc_rd, proc_wr;
185union {
186	struct {
187		SIMPLEQ_HEAD(, ident_resolver) replies;
188	} parent;
189	struct {
190		SIMPLEQ_HEAD(, ident_client) pushing, popping;
191	} child;
192} sc;
193
194int
195main(int argc, char *argv[])
196{
197	extern char *__progname;
198	const char *errstr = NULL;
199
200	int		 c;
201	struct passwd	*pw;
202
203	char *addr = NULL;
204	char *port = "auth";
205	int family = AF_UNSPEC;
206
207	int pair[2];
208	pid_t parent;
209	int sibling;
210
211	while ((c = getopt(argc, argv, "46dl:p:t:")) != -1) {
212		switch (c) {
213		case '4':
214			family = AF_INET;
215			break;
216		case '6':
217			family = AF_INET6;
218			break;
219		case 'd':
220			debug = 1;
221			break;
222		case 'l':
223			addr = optarg;
224			break;
225		case 'p':
226			port = optarg;
227			break;
228		case 't':
229			timeout.tv_sec = strtonum(optarg,
230			    TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
231			if (errstr != NULL)
232				errx(1, "timeout %s is %s", optarg, errstr);
233			break;
234		default:
235			usage();
236			/* NOTREACHED */
237		}
238	}
239
240	argc -= optind;
241	argv += optind;
242
243	if (argc != 0)
244		usage();
245
246	if (geteuid() != 0)
247		errx(1, "need root privileges");
248
249	if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) == -1)
250		err(1, "socketpair");
251
252	pw = getpwnam(IDENTD_USER);
253	if (pw == NULL)
254		err(1, "no %s user", IDENTD_USER);
255
256	if (!debug && daemon(1, 0) == -1)
257		err(1, "daemon");
258
259	parent = fork();
260	switch (parent) {
261	case -1:
262		lerr(1, "fork");
263
264	case 0:
265		/* child */
266		setproctitle("listener");
267		close(pair[1]);
268		sibling = pair[0];
269		break;
270
271	default:
272		/* parent */
273		setproctitle("resolver");
274		close(pair[0]);
275		sibling = pair[1];
276		break;
277	}
278
279	if (!debug) {
280		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
281		tzset();
282		logger = &syslogger;
283	}
284
285	event_init();
286
287	if (ioctl(sibling, FIONBIO, &on) == -1)
288		lerr(1, "sibling ioctl(FIONBIO)");
289
290	if (parent) {
291		SIMPLEQ_INIT(&sc.parent.replies);
292
293		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
294		    parent_rd, NULL);
295		event_set(&proc_wr, sibling, EV_WRITE,
296		    parent_wr, NULL);
297	} else {
298		SIMPLEQ_INIT(&sc.child.pushing);
299		SIMPLEQ_INIT(&sc.child.popping);
300
301		identd_listen(addr, port, family);
302
303		if (chroot(pw->pw_dir) == -1)
304			lerr(1, "chroot(%s)", pw->pw_dir);
305
306		if (chdir("/") == -1)
307			lerr(1, "chdir(%s)", pw->pw_dir);
308
309		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
310		    child_rd, NULL);
311		event_set(&proc_wr, sibling, EV_WRITE,
312		    child_wr, NULL);
313	}
314
315	if (setgroups(1, &pw->pw_gid) ||
316	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
317	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
318		lerr(1, "unable to revoke privs");
319
320	event_add(&proc_rd, NULL);
321
322	event_dispatch();
323
324	return (0);
325}
326
327void
328parent_rd(int fd, short events, void *arg)
329{
330	struct ident_resolver *r;
331	struct passwd *pw;
332	ssize_t n;
333	uid_t uid;
334
335	n = read(fd, &uid, sizeof(uid));
336	switch (n) {
337	case -1:
338		switch (errno) {
339		case EAGAIN:
340		case EINTR:
341			return;
342		default:
343			lerr(1, "parent read");
344		}
345		break;
346	case 0:
347		lerrx(1, "child has gone");
348	case sizeof(uid):
349		break;
350	default:
351		lerrx(1, "unexpected %zd data from child", n);
352	}
353
354	event_add(&proc_wr, NULL);
355
356	r = calloc(1, sizeof(*r));
357	if (r == NULL)
358		lerr(1, "resolver alloc");
359
360	pw = getpwuid(uid);
361	if (pw == NULL) {
362		r->error = E_NOUSER;
363	} else {
364		n = asprintf(&r->buf, "%s", pw->pw_name);
365		if (n == -1)
366			r->error = E_UNKNOWN;
367		else {
368			r->error = E_NONE;
369			r->buflen = n;
370		}
371	}
372
373	SIMPLEQ_INSERT_TAIL(&sc.parent.replies, r, entry);
374	event_add(&proc_wr, NULL);
375}
376
377void
378parent_wr(int fd, short events, void *arg)
379{
380	struct ident_resolver *r = SIMPLEQ_FIRST(&sc.parent.replies);
381	struct iovec iov[2];
382	int iovcnt = 0;
383	ssize_t n;
384
385	iov[iovcnt].iov_base = &r->error;
386	iov[iovcnt].iov_len = sizeof(r->error);
387	iovcnt++;
388
389	if (r->buflen > 0) {
390		iov[iovcnt].iov_base = r->buf;
391		iov[iovcnt].iov_len = r->buflen;
392		iovcnt++;
393	}
394
395	n = writev(fd, iov, iovcnt);
396	if (n == -1) {
397		if (errno == EAGAIN) {
398			event_add(&proc_wr, NULL);
399			return;
400		}
401		lerr(1, "parent write");
402	}
403
404	if (n != sizeof(r->error) + r->buflen)
405		lerrx(1, "unexpected parent write length %zd", n);
406
407	SIMPLEQ_REMOVE_HEAD(&sc.parent.replies, entry);
408
409	if (r->buflen > 0)
410		free(r->buf);
411
412	free(r);
413}
414
415void
416child_rd(int fd, short events, void *arg)
417{
418	struct ident_client *c;
419	struct {
420		u_int error;
421		char buf[512];
422	} reply;
423	ssize_t n;
424
425	n = read(fd, &reply, sizeof(reply));
426	switch (n) {
427	case -1:
428		switch (errno) {
429		case EAGAIN:
430		case EINTR:
431			return;
432		default:
433			lerr(1, "child read");
434		}
435		break;
436	case 0:
437		lerrx(1, "parent has gone");
438	default:
439		break;
440	}
441
442	c = SIMPLEQ_FIRST(&sc.child.popping);
443	if (c == NULL)
444		lerrx(1, "unsolicited data from parent");
445
446	SIMPLEQ_REMOVE_HEAD(&sc.child.popping, entry);
447
448	if (n < sizeof(reply.error))
449		lerrx(1, "short data from parent");
450
451	/* check if something went wrong while the parent was working */
452	switch (c->state) {
453	case S_DYING: /* we're sending an error to the client */
454		c->state = S_DEAD;
455		return;
456	case S_DEAD: /* we finished sending an error to the client */
457		free(c);
458		return;
459	default:
460		break;
461	}
462
463	switch (reply.error) {
464	case E_NONE:
465		n = asprintf(&c->buf, "%u , %u : USERID : UNIX : %s\r\n",
466		    c->server.port, c->client.port, reply.buf);
467		break;
468	case E_NOUSER:
469		n = asprintf(&c->buf, "%u , %u : ERROR : NO-USER\r\n",
470		    c->server.port, c->client.port);
471		break;
472	case E_UNKNOWN:
473		n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
474		    c->server.port, c->client.port);
475		break;
476	case E_HIDDEN:
477		n = asprintf(&c->buf, "%u , %u : ERROR : HIDDEN-USER\r\n",
478		    c->server.port, c->client.port);
479		break;
480	default:
481		lerrx(1, "unexpected error from parent %u", reply.error);
482	}
483	if (n == -1)
484		goto fail;
485
486	c->buflen = n;
487
488	fd = EVENT_FD(&c->ev);
489	event_del(&c->ev);
490	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
491	    identd_response, c);
492	event_add(&c->ev, &timeout);
493	return;
494
495fail:
496	event_del(&c->ev);
497	close(fd);
498	free(c);
499}
500
501void
502child_wr(int fd, short events, void *arg)
503{
504	struct ident_client *c = SIMPLEQ_FIRST(&sc.child.pushing);
505	ssize_t n;
506
507	n = write(fd, &c->uid, sizeof(c->uid));
508	switch (n) {
509	case -1:
510		if (errno == EAGAIN) {
511			event_add(&proc_wr, NULL);
512			return;
513		}
514		lerr(1, "child write");
515	case sizeof(c->uid):
516		break;
517	default:
518		lerrx(1, "unexpected child write length %zd", n);
519	}
520
521	SIMPLEQ_REMOVE_HEAD(&sc.child.pushing, entry);
522	SIMPLEQ_INSERT_TAIL(&sc.child.popping, c, entry);
523
524	if (!SIMPLEQ_EMPTY(&sc.child.pushing))
525		event_add(&proc_wr, NULL);
526}
527
528void
529identd_listen(const char *addr, const char *port, int family)
530{
531	struct identd_listener *l = NULL;
532
533	struct addrinfo hints, *res, *res0;
534	int error;
535	int s;
536	int serrno;
537	const char *cause = NULL;
538
539	memset(&hints, 0, sizeof(hints));
540	hints.ai_family = family;
541	hints.ai_socktype = SOCK_STREAM;
542	hints.ai_flags = AI_PASSIVE;
543
544	error = getaddrinfo(addr, port, &hints, &res0);
545	if (error)
546		lerrx(1, "%s/%s: %s", addr, port, gai_strerror(error));
547
548	for (res = res0; res != NULL; res = res->ai_next) {
549		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
550		if (s == -1) {
551			cause = "socket";
552			continue;
553		}
554
555		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
556		    &on, sizeof(on)) == -1)
557			err(1, "listener setsockopt(SO_REUSEADDR)");
558
559		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
560			cause = "bind";
561			serrno = errno;
562			close(s);
563			errno = serrno;
564			continue;
565		}
566
567		if (ioctl(s, FIONBIO, &on) == -1)
568			err(1, "listener ioctl(FIONBIO)");
569
570		if (listen(s, 5) == -1)
571			err(1, "listen");
572
573		l = calloc(1, sizeof(*l));
574		if (l == NULL)
575			err(1, "listener ev alloc");
576
577		event_set(&l->ev, s, EV_READ | EV_PERSIST, identd_accept, l);
578		event_add(&l->ev, NULL);
579	        evtimer_set(&l->pause, identd_paused, l);
580	}
581	if (l == NULL)
582		err(1, "%s", cause);
583
584	freeaddrinfo(res0);
585}
586
587void
588identd_paused(int fd, short events, void *arg)
589{
590	struct identd_listener *l = arg;
591	event_add(&l->ev, NULL);
592}
593
594void
595identd_accept(int fd, short events, void *arg)
596{
597	struct identd_listener *l = arg;
598	struct sockaddr_storage ss;
599	struct timeval pause = { 1, 0 };
600	struct ident_client *c = NULL;
601	socklen_t len;
602	int s;
603
604	len = sizeof(ss);
605	s = accept(fd, sa(&ss), &len);
606	if (s == -1) {
607		switch (errno) {
608		case EINTR:
609		case EWOULDBLOCK:
610		case ECONNABORTED:
611			return;
612		case EMFILE:
613		case ENFILE:
614			event_del(&l->ev);
615			evtimer_add(&l->pause, &pause);
616			return;
617
618		default:
619			lerr(1, "accept");
620		}
621	}
622
623	if (ioctl(s, FIONBIO, &on) == -1)
624		lerr(1, "client ioctl(FIONBIO)");
625
626	c = calloc(1, sizeof(*c));
627	if (c == NULL) {
628		lwarn("client alloc");
629		close(fd);
630		return;
631	}
632
633	memcpy(&c->client.ss, &ss, len);
634	c->client.len = len;
635	ldebug("client: %s", gethost(&ss));
636
637	/* lookup the local ip it connected to */
638	c->server.len = sizeof(c->server.ss);
639	if (getsockname(s, sa(&c->server.ss), &c->server.len) == -1)
640		lerr(1, "getsockname");
641
642	event_set(&c->ev, s, EV_READ | EV_PERSIST, identd_request, c);
643	event_add(&c->ev, &timeout);
644}
645
646void
647identd_request(int fd, short events, void *arg)
648{
649	struct ident_client *c = arg;
650	char buf[64];
651	ssize_t n, i;
652	char *errstr = "INVALID-PORT";
653
654	if (events & EV_TIMEOUT) {
655		ldebug("%s request timeout", gethost(&c->client.ss));
656		goto fail;
657	}
658
659	n = read(fd, buf, sizeof(buf));
660	switch (n) {
661	case -1:
662		switch (errno) {
663		case EINTR:
664		case EAGAIN:
665			return;
666		default:
667			lwarn("%s read", gethost(&c->client.ss));
668			goto fail;
669		}
670		break;
671
672	case 0:
673		ldebug("%s closed connection", gethost(&c->client.ss));
674		goto fail;
675	default:
676		break;
677	}
678
679	for (i = 0; c->state < S_EOL && i < n; i++)
680		c->state = identd_parse(c, buf[i]);
681
682	if (c->state == S_DEAD)
683		goto error;
684	if (c->state != S_EOL)
685		return;
686
687	if (c->server.port < 1 || c->client.port < 1)
688		goto error;
689
690	event_del(&c->ev);
691
692	if (fetchuid(c) == -1) {
693		errstr = "NO-USER";
694		goto error;
695	}
696
697	SIMPLEQ_INSERT_TAIL(&sc.child.pushing, c, entry);
698
699	event_set(&c->ev, fd, EV_READ | EV_PERSIST, identd_resolving, c);
700	event_add(&c->ev, &timeout);
701
702	event_add(&proc_wr, NULL);
703	return;
704
705error:
706	n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
707	    c->server.port, c->client.port, errstr);
708	if (n == -1)
709		goto fail;
710
711	c->buflen = n;
712
713	event_del(&c->ev);
714
715	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
716	    identd_response, c);
717	event_add(&c->ev, &timeout);
718	return;
719
720fail:
721	event_del(&c->ev);
722	close(fd);
723	free(c);
724}
725
726void
727identd_resolving(int fd, short events, void *arg)
728{
729	struct ident_client *c = arg;
730	char buf[64];
731	ssize_t n;
732
733	/*
734	 * something happened while we're waiting for the parent to lookup
735	 * the user.
736	 */
737
738	if (events & EV_TIMEOUT) {
739		ldebug("%s timeout during resolving",
740		    gethost(&c->client.ss));
741		goto error;
742	}
743
744	n = read(fd, buf, sizeof(buf));
745	switch (n) {
746	case -1:
747		switch (errno) {
748		case EINTR:
749		case EAGAIN:
750			return;
751		default:
752			lerrx(1, "resolving read");
753		}
754		break;
755	case 0:
756		ldebug("%s closed connection during resolving",
757		    gethost(&c->client.ss));
758		goto fail;
759	default:
760		break;
761	}
762
763	/* throw it away */
764	return;
765
766error:
767	n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
768	    c->server.port, c->client.port);
769	if (n == -1)
770		goto fail;
771
772	c->buflen = n;
773
774	event_del(&c->ev);
775	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
776	    identd_response, c);
777	event_add(&c->ev, &timeout);
778	c->state = S_DYING;
779	return;
780
781fail:
782	event_del(&c->ev);
783	close(fd);
784	c->state = S_DEAD;
785}
786
787enum ident_client_state
788identd_parse(struct ident_client *c, int ch)
789{
790	enum ident_client_state s = c->state;
791
792	switch (s) {
793	case S_BEGINNING:
794		/* ignore leading space */
795		if (ch == '\t' || ch == ' ')
796			return (s);
797
798		if (ch == '0' || !isdigit(ch))
799			return (S_DEAD);
800
801		c->server.port = ch - '0';
802		return (S_SERVER_PORT);
803
804	case S_SERVER_PORT:
805		if (ch == '\t' || ch == ' ')
806			return (S_PRE_COMMA);
807		if (ch == ',')
808			return (S_POST_COMMA);
809
810		if (!isdigit(ch))
811			return (S_DEAD);
812
813		c->server.port *= 10;
814		c->server.port += ch - '0';
815		if (c->server.port > 65535)
816			return (S_DEAD);
817
818		return (s);
819
820	case S_PRE_COMMA:
821		if (ch == '\t' || ch == ' ')
822			return (s);
823		if (ch == ',')
824			return (S_POST_COMMA);
825
826		return (S_DEAD);
827
828	case S_POST_COMMA:
829		if (ch == '\t' || ch == ' ')
830			return (s);
831
832		if (ch == '0' || !isdigit(ch))
833			return (S_DEAD);
834
835		c->client.port = ch - '0';
836		return (S_CLIENT_PORT);
837
838	case S_CLIENT_PORT:
839		if (ch == '\t' || ch == ' ')
840			return (S_PRE_EOL);
841		if (ch == '\r' || ch == '\n')
842			return (S_EOL);
843
844		if (!isdigit(ch))
845			return (S_DEAD);
846
847		c->client.port *= 10;
848		c->client.port += ch - '0';
849		if (c->client.port > 65535)
850			return (S_DEAD);
851
852		return (s);
853
854	case S_PRE_EOL:
855		if (ch == '\t' || ch == ' ')
856			return (s);
857		if (ch == '\r' || ch == '\n')
858			return (S_EOL);
859
860		return (S_DEAD);
861
862	case S_EOL:
863		/* ignore trailing garbage */
864		return (s);
865
866	default:
867		return (S_DEAD);
868	}
869}
870
871void
872identd_response(int fd, short events, void *arg)
873{
874	struct ident_client *c = arg;
875	char buf[64];
876	ssize_t n;
877
878	if (events & EV_TIMEOUT) {
879		ldebug("%s timeout during response", gethost(&c->client.ss));
880		goto done;
881	}
882
883	if (events & EV_READ) {
884		n = read(fd, buf, sizeof(buf));
885		switch (n) {
886		case -1:
887			switch (errno) {
888			case EINTR:
889			case EAGAIN:
890				/* meh, try a write */
891				break;
892			default:
893				lerrx(1, "response read");
894			}
895			break;
896		case 0:
897			ldebug("%s closed connection during response",
898			    gethost(&c->client.ss));
899			goto done;
900		default:
901			/* flushed socket */
902			break;
903		}
904	}
905
906	if (!(events & EV_WRITE))
907		return; /* try again later */
908
909	n = write(fd, c->buf + c->bufoff, c->buflen - c->bufoff);
910	if (n == -1) {
911		switch (errno) {
912		case EAGAIN:
913			return; /* try again later */
914		default:
915			goto done;
916		}
917	}
918
919	c->bufoff += n;
920	if (c->bufoff != c->buflen)
921		return; /* try again later */
922
923done:
924	event_del(&c->ev);
925	close(fd);
926	free(c->buf);
927	if (c->state == S_DYING) /* it was queued for resolving */
928		c->state = S_DEAD;
929	else
930		free(c);
931}
932
933void
934syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
935{
936	char *s;
937
938	if (vasprintf(&s, fmt, ap) == -1) {
939		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
940		exit(1);
941	}
942
943	syslog(priority, "%s: %s", s, strerror(e));
944
945	free(s);
946}
947
948void
949syslog_err(int ecode, const char *fmt, ...)
950{
951	va_list ap;
952
953	va_start(ap, fmt);
954	syslog_vstrerror(errno, LOG_EMERG, fmt, ap);
955	va_end(ap);
956
957	exit(ecode);
958}
959
960void
961syslog_errx(int ecode, const char *fmt, ...)
962{
963	va_list ap;
964
965	va_start(ap, fmt);
966	vsyslog(LOG_WARNING, fmt, ap);
967	va_end(ap);
968
969	exit(ecode);
970}
971
972void
973syslog_warn(const char *fmt, ...)
974{
975	va_list ap;
976
977	va_start(ap, fmt);
978	syslog_vstrerror(errno, LOG_WARNING, fmt, ap);
979	va_end(ap);
980}
981
982void
983syslog_warnx(const char *fmt, ...)
984{
985	va_list ap;
986
987	va_start(ap, fmt);
988	vsyslog(LOG_WARNING, fmt, ap);
989	va_end(ap);
990}
991
992void
993syslog_info(const char *fmt, ...)
994{
995	va_list ap;
996
997	va_start(ap, fmt);
998	vsyslog(LOG_INFO, fmt, ap);
999	va_end(ap);
1000}
1001
1002void
1003syslog_debug(const char *fmt, ...)
1004{
1005	va_list ap;
1006
1007	if (!debug)
1008		return;
1009
1010	va_start(ap, fmt);
1011	vsyslog(LOG_DEBUG, fmt, ap);
1012	va_end(ap);
1013}
1014
1015const char *
1016gethost(struct sockaddr_storage *ss)
1017{
1018	struct sockaddr *sa = (struct sockaddr *)ss;
1019	static char buf[NI_MAXHOST];
1020
1021	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
1022	    NULL, 0, NI_NUMERICHOST) != 0)
1023		return ("(unknown)");
1024
1025	return (buf);
1026}
1027
1028const char *
1029getport(struct sockaddr_storage *ss)
1030{
1031	struct sockaddr *sa = (struct sockaddr *)ss;
1032	static char buf[NI_MAXSERV];
1033
1034	if (getnameinfo(sa, sa->sa_len, NULL, 0, buf, sizeof(buf),
1035	    NI_NUMERICSERV) != 0)
1036		return ("(unknown)");
1037
1038	return (buf);
1039}
1040
1041int
1042fetchuid(struct ident_client *c)
1043{
1044        struct tcp_ident_mapping tir;
1045	int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
1046	struct sockaddr_in *s4;
1047        struct sockaddr_in6 *s6;
1048        int err = 0;
1049        size_t len;
1050
1051        memset(&tir, 0, sizeof(tir));
1052	memcpy(&tir.faddr, &c->client.ss, sizeof(&tir.faddr));
1053	memcpy(&tir.laddr, &c->server.ss, sizeof(&tir.laddr));
1054
1055	switch (c->server.ss.ss_family) {
1056	case AF_INET:
1057		s4 = (struct sockaddr_in *)&tir.faddr;
1058		s4->sin_port = htons(c->client.port);
1059
1060		s4 = (struct sockaddr_in *)&tir.laddr;
1061		s4->sin_port = htons(c->server.port);
1062		break;
1063	case AF_INET6:
1064		s6 = (struct sockaddr_in6 *)&tir.faddr;
1065		s6->sin6_port = htons(c->client.port);
1066
1067		s6 = (struct sockaddr_in6 *)&tir.laddr;
1068		s6->sin6_port = htons(c->server.port);
1069		break;
1070	default:
1071		lerrx(1, "unexpected family %d", c->server.ss.ss_family);
1072	}
1073
1074	len = sizeof(tir);
1075        err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tir, &len, NULL, 0);
1076	if (err == -1)
1077		lerr(1, "sysctl");
1078
1079	if (tir.ruid == -1)
1080		return (-1);
1081
1082	c->uid = tir.ruid;
1083	return (0);
1084}
1085