1/* $OpenBSD: ldapclient.c,v 1.50 2024/05/21 05:00:48 jsg Exp $ */
2
3/*
4 * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6 *
7 * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/types.h>
21#include <sys/queue.h>
22#include <sys/socket.h>
23#include <sys/tree.h>
24
25#include <netinet/in.h>
26#include <arpa/inet.h>
27
28#include <netdb.h>
29#include <errno.h>
30#include <err.h>
31#include <signal.h>
32#include <event.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include <pwd.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <limits.h>
40
41#include "aldap.h"
42#include "log.h"
43#include "ypldap.h"
44
45void    client_sig_handler(int, short, void *);
46void	client_dispatch_dns(int, short, void *);
47void    client_dispatch_parent(int, short, void *);
48void    client_shutdown(void);
49void    client_configure(struct env *);
50void    client_periodic_update(int, short, void *);
51int	client_build_req(struct idm *, struct idm_req *, struct aldap_message *,
52	    int, int);
53int	client_search_idm(struct env *, struct idm *, struct aldap *,
54	    char **, char *, int, int, enum imsg_type);
55int	client_try_idm(struct env *, struct idm *, struct ypldap_addr *);
56void	client_addr_init(struct idm *);
57int	client_addr_free(struct idm *);
58
59void
60client_addr_init(struct idm *idm)
61{
62        struct sockaddr_in      *sa_in;
63        struct sockaddr_in6     *sa_in6;
64        struct ypldap_addr      *h;
65	int                     defport;
66
67	if (idm->idm_port != 0)
68		defport = idm->idm_port;
69	else if (idm->idm_flags & F_SSL)
70		defport = LDAPS_PORT;
71	else
72		defport = LDAP_PORT;
73
74	TAILQ_FOREACH(h, &idm->idm_addr, next) {
75                switch (h->ss.ss_family) {
76                case AF_INET:
77                        sa_in = (struct sockaddr_in *)&h->ss;
78                        if (ntohs(sa_in->sin_port) == 0)
79                                sa_in->sin_port = htons(defport);
80                        idm->idm_state = STATE_DNS_DONE;
81                        break;
82                case AF_INET6:
83                        sa_in6 = (struct sockaddr_in6 *)&h->ss;
84                        if (ntohs(sa_in6->sin6_port) == 0)
85                                sa_in6->sin6_port = htons(defport);
86                        idm->idm_state = STATE_DNS_DONE;
87                        break;
88                default:
89                        fatalx("king bula sez: wrong AF in client_addr_init");
90                        /* not reached */
91                }
92        }
93}
94
95int
96client_addr_free(struct idm *idm)
97{
98        struct ypldap_addr	*h;
99
100	while (!TAILQ_EMPTY(&idm->idm_addr)) {
101		h = TAILQ_FIRST(&idm->idm_addr);
102		TAILQ_REMOVE(&idm->idm_addr, h, next);
103		free(h);
104	}
105
106	return (0);
107}
108
109void
110client_sig_handler(int sig, short event, void *p)
111{
112	switch (sig) {
113	case SIGINT:
114	case SIGTERM:
115		client_shutdown();
116		break;
117	case SIGHUP:
118		/* ingore */
119		break;
120	default:
121		fatalx("unexpected signal");
122	}
123}
124
125void
126client_dispatch_dns(int fd, short events, void *p)
127{
128	struct imsg		 imsg;
129	u_int16_t		 dlen;
130	u_char			*data;
131	struct ypldap_addr	*h;
132	int			 n, wait_cnt = 0;
133	struct idm		*idm;
134	int			 shut = 0;
135
136	struct env		*env = p;
137	struct imsgev		*iev = env->sc_iev_dns;
138	struct imsgbuf		*ibuf = &iev->ibuf;
139
140	if ((events & (EV_READ | EV_WRITE)) == 0)
141		fatalx("unknown event");
142
143	if (events & EV_READ) {
144		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
145			fatal("imsg_read error");
146		if (n == 0)
147			shut = 1;
148	}
149	if (events & EV_WRITE) {
150		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
151			fatal("msgbuf_write");
152		if (n == 0)
153			shut = 1;
154		goto done;
155	}
156
157	for (;;) {
158		if ((n = imsg_get(ibuf, &imsg)) == -1)
159			fatal("client_dispatch_dns: imsg_get error");
160		if (n == 0)
161			break;
162
163		switch (imsg.hdr.type) {
164		case IMSG_HOST_DNS:
165			TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
166				if (idm->idm_id == imsg.hdr.peerid)
167					break;
168			if (idm == NULL) {
169				log_warnx("IMSG_HOST_DNS with invalid peerID");
170				break;
171			}
172			if (!TAILQ_EMPTY(&idm->idm_addr)) {
173				log_warnx("IMSG_HOST_DNS but addrs set!");
174				break;
175			}
176
177			dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
178			if (dlen == 0) {	/* no data -> temp error */
179				idm->idm_state = STATE_DNS_TEMPFAIL;
180				break;
181			}
182
183			data = (u_char *)imsg.data;
184			while (dlen >= sizeof(struct sockaddr_storage)) {
185				if ((h = calloc(1, sizeof(*h))) == NULL)
186					fatal(NULL);
187				memcpy(&h->ss, data, sizeof(h->ss));
188				TAILQ_INSERT_HEAD(&idm->idm_addr, h, next);
189
190				data += sizeof(h->ss);
191				dlen -= sizeof(h->ss);
192			}
193			if (dlen != 0)
194				fatalx("IMSG_HOST_DNS: dlen != 0");
195
196			client_addr_init(idm);
197
198			break;
199		default:
200			break;
201		}
202		imsg_free(&imsg);
203	}
204
205	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
206		TAILQ_FOREACH(h, &idm->idm_addr, next) {
207			if (client_try_idm(env, idm, h) == -1)
208				idm->idm_state = STATE_LDAP_FAIL;
209			else
210				break;
211		}
212
213		if (idm->idm_state < STATE_LDAP_DONE)
214			wait_cnt++;
215	}
216	if (wait_cnt == 0)
217		imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
218		    NULL, 0);
219
220done:
221	if (!shut)
222		imsg_event_add(iev);
223	else {
224		/* this pipe is dead, so remove the event handler */
225		event_del(&iev->ev);
226		event_loopexit(NULL);
227	}
228}
229
230void
231client_dispatch_parent(int fd, short events, void *p)
232{
233	int			 n;
234	int			 shut = 0;
235	struct imsg		 imsg;
236	struct env		*env = p;
237	struct imsgev		*iev = env->sc_iev;
238	struct imsgbuf		*ibuf = &iev->ibuf;
239
240	if ((events & (EV_READ | EV_WRITE)) == 0)
241		fatalx("unknown event");
242
243	if (events & EV_READ) {
244		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
245			fatal("imsg_read error");
246		if (n == 0)
247			shut = 1;
248	}
249	if (events & EV_WRITE) {
250		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
251			fatal("msgbuf_write");
252		if (n == 0)
253			shut = 1;
254		goto done;
255	}
256
257	for (;;) {
258		if ((n = imsg_get(ibuf, &imsg)) == -1)
259			fatal("client_dispatch_parent: imsg_get error");
260		if (n == 0)
261			break;
262
263		switch (imsg.hdr.type) {
264		case IMSG_CONF_START: {
265			struct env	params;
266
267			if (env->sc_flags & F_CONFIGURING) {
268				log_warnx("configuration already in progress");
269				break;
270			}
271			memcpy(&params, imsg.data, sizeof(params));
272			log_debug("configuration starting");
273			env->sc_flags |= F_CONFIGURING;
274			purge_config(env);
275			memcpy(&env->sc_conf_tv, &params.sc_conf_tv,
276			    sizeof(env->sc_conf_tv));
277			env->sc_flags |= params.sc_flags;
278			break;
279		}
280		case IMSG_CONF_IDM: {
281			struct idm	*idm;
282
283			if (!(env->sc_flags & F_CONFIGURING))
284				break;
285			if ((idm = calloc(1, sizeof(*idm))) == NULL)
286				fatal(NULL);
287			memcpy(idm, imsg.data, sizeof(*idm));
288			idm->idm_env = env;
289			TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry);
290			break;
291		}
292		case IMSG_CONF_END:
293			env->sc_flags &= ~F_CONFIGURING;
294			log_debug("applying configuration");
295			client_configure(env);
296			break;
297		default:
298			log_debug("client_dispatch_parent: unexpect imsg %d",
299			    imsg.hdr.type);
300
301			break;
302		}
303		imsg_free(&imsg);
304	}
305
306done:
307	if (!shut)
308		imsg_event_add(iev);
309	else {
310		/* this pipe is dead, so remove the event handler */
311		event_del(&iev->ev);
312		event_loopexit(NULL);
313	}
314}
315
316void
317client_shutdown(void)
318{
319	log_info("ldap client exiting");
320	_exit(0);
321}
322
323pid_t
324ldapclient(int pipe_main2client[2])
325{
326	pid_t            pid;
327	int              pipe_dns[2];
328	struct passwd	*pw;
329	struct event	 ev_sigint;
330	struct event	 ev_sigterm;
331	struct event	 ev_sighup;
332	struct env	 env;
333
334	switch (pid = fork()) {
335	case -1:
336		fatal("cannot fork");
337		break;
338	case 0:
339		break;
340	default:
341		return (pid);
342	}
343
344	memset(&env, 0, sizeof(env));
345	TAILQ_INIT(&env.sc_idms);
346
347	if ((pw = getpwnam(YPLDAP_USER)) == NULL)
348		fatal("getpwnam");
349
350	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1)
351		fatal("socketpair");
352	ypldap_dns(pipe_dns, pw);
353	close(pipe_dns[1]);
354
355#ifndef DEBUG
356	if (chroot(pw->pw_dir) == -1)
357		fatal("chroot");
358	if (chdir("/") == -1)
359		fatal("chdir");
360#else
361#warning disabling chrooting in DEBUG mode
362#endif
363	setproctitle("ldap client");
364	ypldap_process = PROC_CLIENT;
365	log_procname = log_procnames[ypldap_process];
366
367#ifndef DEBUG
368	if (setgroups(1, &pw->pw_gid) ||
369	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
370	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
371		fatal("cannot drop privileges");
372#else
373#warning disabling privilege revocation in DEBUG mode
374#endif
375
376	if (pledge("stdio inet", NULL) == -1)
377		fatal("pledge");
378
379	event_init();
380	signal(SIGPIPE, SIG_IGN);
381	signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
382	signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
383	signal_set(&ev_sighup, SIGHUP, client_sig_handler, NULL);
384	signal_add(&ev_sigint, NULL);
385	signal_add(&ev_sigterm, NULL);
386	signal_add(&ev_sighup, NULL);
387
388	close(pipe_main2client[0]);
389	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
390		fatal(NULL);
391	if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL)
392		fatal(NULL);
393
394	env.sc_iev->events = EV_READ;
395	env.sc_iev->data = &env;
396	imsg_init(&env.sc_iev->ibuf, pipe_main2client[1]);
397	env.sc_iev->handler = client_dispatch_parent;
398	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
399	    env.sc_iev->handler, &env);
400	event_add(&env.sc_iev->ev, NULL);
401
402	env.sc_iev_dns->events = EV_READ;
403	env.sc_iev_dns->data = &env;
404	imsg_init(&env.sc_iev_dns->ibuf, pipe_dns[0]);
405	env.sc_iev_dns->handler = client_dispatch_dns;
406	event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd,
407	    env.sc_iev_dns->events, env.sc_iev_dns->handler, &env);
408	event_add(&env.sc_iev_dns->ev, NULL);
409
410	event_dispatch();
411	client_shutdown();
412
413	return (0);
414
415}
416
417int
418client_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m,
419    int min_attr, int max_attr)
420{
421	struct aldap_stringset	*ldap_attrs;
422	int	 i;
423	size_t	 k;
424
425	memset(ir, 0, sizeof(*ir));
426	for (i = min_attr; i < max_attr; i++) {
427		if (idm->idm_flags & F_FIXED_ATTR(i)) {
428			if (strlcat(ir->ir_line, idm->idm_attrs[i],
429			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
430				/*
431				 * entry yields a line > 1024, trash it.
432				 */
433				return (-1);
434
435			if (i == ATTR_UID) {
436				ir->ir_key.ik_uid = strtonum(
437				    idm->idm_attrs[i], 0,
438				    UID_MAX, NULL);
439			} else if (i == ATTR_GR_GID) {
440				ir->ir_key.ik_gid = strtonum(
441				    idm->idm_attrs[i], 0,
442				    GID_MAX, NULL);
443			}
444		} else if (idm->idm_list & F_LIST(i)) {
445			aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs);
446			for (k = 0; k >= 0 && ldap_attrs && k < ldap_attrs->len; k++) {
447				/* XXX: Fail when attributes have illegal characters e.g. ',' */
448				if (strlcat(ir->ir_line,
449				    ldap_attrs->str[k].ostr_val,
450				    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
451					continue;
452				if (k + 1 < ldap_attrs->len)
453					if (strlcat(ir->ir_line, ",",
454						    sizeof(ir->ir_line))
455					    >= sizeof(ir->ir_line)) {
456						aldap_free_attr(ldap_attrs);
457						return (-1);
458					}
459			}
460			aldap_free_attr(ldap_attrs);
461		} else {
462			if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1)
463				return (-1);
464			if (strlcat(ir->ir_line, ldap_attrs->str[0].ostr_val,
465			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) {
466				aldap_free_attr(ldap_attrs);
467				return (-1);
468			}
469			if (i == ATTR_UID) {
470				ir->ir_key.ik_uid = strtonum(
471				    ldap_attrs->str[0].ostr_val, 0, UID_MAX,
472				    NULL);
473			} else if (i == ATTR_GR_GID) {
474				ir->ir_key.ik_uid = strtonum(
475				    ldap_attrs->str[0].ostr_val, 0, GID_MAX,
476				    NULL);
477			}
478			aldap_free_attr(ldap_attrs);
479		}
480
481		if (i + 1 != max_attr)
482			if (strlcat(ir->ir_line, ":",
483			    sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
484				return (-1);
485	}
486
487	return (0);
488}
489
490int
491client_search_idm(struct env *env, struct idm *idm, struct aldap *al,
492    char **attrs, char *filter, int min_attr, int max_attr,
493    enum imsg_type type)
494{
495	struct idm_req		 ir;
496	struct aldap_message	*m;
497	struct aldap_page_control *pg = NULL;
498	const char		*errstr;
499	char			*dn;
500
501	dn = idm->idm_basedn;
502	if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0')
503		dn = idm->idm_groupdn;
504
505	do {
506		if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE,
507		    filter, attrs, 0, 0, 0, pg) == -1) {
508			aldap_get_errno(al, &errstr);
509			log_debug("%s", errstr);
510			return (-1);
511		}
512
513		if (pg != NULL) {
514			aldap_freepage(pg);
515			pg = NULL;
516		}
517
518		while ((m = aldap_parse(al)) != NULL) {
519			if (al->msgid != m->msgid) {
520				goto fail;
521			}
522
523			if (m->message_type == LDAP_RES_SEARCH_RESULT) {
524				if (m->page != NULL && m->page->cookie_len != 0)
525					pg = m->page;
526				else
527					pg = NULL;
528
529				aldap_freemsg(m);
530				break;
531			}
532
533			if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
534				goto fail;
535			}
536
537			if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0)
538				imsg_compose_event(env->sc_iev, type, 0, 0, -1,
539				    &ir, sizeof(ir.ir_key) +
540				    strlen(ir.ir_line) + 1);
541
542			aldap_freemsg(m);
543		}
544	} while (pg != NULL);
545
546	return (0);
547
548fail:
549	aldap_freemsg(m);
550	if (pg != NULL) {
551		aldap_freepage(pg);
552	}
553
554	return (-1);
555}
556
557int
558client_try_idm(struct env *env, struct idm *idm, struct ypldap_addr *addr)
559{
560	const char		*where;
561	char			 hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
562	char			*attrs[ATTR_MAX+1];
563	int			 fd = -1;
564	int			 i, j;
565	struct sockaddr		*sa = (struct sockaddr *)&addr->ss;
566	struct aldap_message	*m;
567	struct aldap		*al;
568
569	if (getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), sbuf,
570	    sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV))
571		errx(1, "could not get numeric hostname");
572
573	where = "connect";
574	if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1)
575		return (-1);
576
577	if (connect(fd, sa, SA_LEN(sa)) != 0) {
578		log_warn("connect to %s port %s failed", hbuf, sbuf);
579		close(fd);
580		return (-1);
581	}
582
583	al = aldap_init(fd);
584	if (al == NULL) {
585		close(fd);
586		return (-1);
587	}
588
589	if (idm->idm_flags & F_STARTTLS) {
590		log_debug("requesting starttls");
591		where = "starttls";
592		if (aldap_req_starttls(al) == -1)
593			goto bad;
594
595		where = "parsing";
596		if ((m = aldap_parse(al)) == NULL)
597			goto bad;
598		where = "verifying msgid";
599		if (al->msgid != m->msgid) {
600			aldap_freemsg(m);
601			goto bad;
602		}
603		where = "starttls result";
604		if (aldap_get_resultcode(m) != LDAP_SUCCESS) {
605			aldap_freemsg(m);
606			goto bad;
607		}
608		aldap_freemsg(m);
609	}
610
611	if (idm->idm_flags & (F_STARTTLS | F_SSL)) {
612		log_debug("starting tls");
613		where = "enabling tls";
614		if (aldap_tls(al, idm->idm_tls_config, idm->idm_name) < 0) {
615			const char *err;
616			aldap_get_errno(al, &err);
617			log_warnx("TLS handshake with %s(%s) failed: %s",
618			    idm->idm_name, hbuf, err);
619			goto bad;
620		}
621	}
622
623	if (idm->idm_flags & F_NEEDAUTH) {
624		int rc;
625
626		where = "binding";
627		if (idm->idm_bindext != 0)
628			rc = aldap_bind_sasl_external(al, idm->idm_bindextid);
629		else
630			rc = aldap_bind(al, idm->idm_binddn, idm->idm_bindcred);
631		if (rc == -1)
632			goto bad;
633
634		where = "parsing";
635		if ((m = aldap_parse(al)) == NULL)
636			goto bad;
637		where = "verifying msgid";
638		if (al->msgid != m->msgid) {
639			aldap_freemsg(m);
640			goto bad;
641		}
642		where = "bind response";
643		rc = aldap_get_resultcode(m);
644		if (rc != LDAP_SUCCESS) {
645			log_warnx("LDAP bind with %s(%s) failed: result code"
646			    " %d", idm->idm_name, hbuf, rc);
647			aldap_freemsg(m);
648			goto bad;
649		}
650		aldap_freemsg(m);
651	}
652
653	memset(attrs, 0, sizeof(attrs));
654	for (i = 0, j = 0; i < ATTR_MAX; i++) {
655		if (idm->idm_flags & F_FIXED_ATTR(i))
656			continue;
657		attrs[j++] = idm->idm_attrs[i];
658	}
659	attrs[j] = NULL;
660
661	/*
662	 * build password line.
663	 */
664	where = "search";
665	log_debug("searching password entries");
666	if (client_search_idm(env, idm, al, attrs,
667	    idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1)
668		goto bad;
669
670	memset(attrs, 0, sizeof(attrs));
671	for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) {
672		if (idm->idm_flags & F_FIXED_ATTR(i))
673			continue;
674		attrs[j++] = idm->idm_attrs[i];
675	}
676	attrs[j] = NULL;
677
678	/*
679	 * build group line.
680	 */
681	where = "search";
682	log_debug("searching group entries");
683	if (client_search_idm(env, idm, al, attrs,
684	    idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX,
685	    IMSG_GRP_ENTRY) == -1)
686		goto bad;
687
688	aldap_close(al);
689
690	idm->idm_state = STATE_LDAP_DONE;
691
692	return (0);
693bad:
694	aldap_close(al);
695	log_debug("directory %s(%s) errored out in %s", idm->idm_name, hbuf,
696	    where);
697	return (-1);
698}
699
700void
701client_periodic_update(int fd, short event, void *p)
702{
703	struct env	*env = p;
704
705	struct idm	*idm;
706	int		 fail_cnt = 0;
707
708	/* If LDAP isn't finished, notify the master process to trash the
709	 * update. */
710	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
711		if (idm->idm_state < STATE_LDAP_DONE)
712			fail_cnt++;
713
714		idm->idm_state = STATE_NONE;
715
716		client_addr_free(idm);
717	}
718	if (fail_cnt > 0) {
719		log_debug("trash the update");
720		imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1,
721		    NULL, 0);
722	}
723
724	client_configure(env);
725}
726
727void
728client_configure(struct env *env)
729{
730	struct timeval	 tv;
731	struct idm	*idm;
732        u_int16_t        dlen;
733
734	log_debug("connecting to directories");
735
736	imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0);
737
738	/* Start the DNS lookups */
739	TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
740		dlen = strlen(idm->idm_name) + 1;
741		imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
742		    0, -1, idm->idm_name, dlen);
743	}
744
745	tv.tv_sec = env->sc_conf_tv.tv_sec;
746	tv.tv_usec = env->sc_conf_tv.tv_usec;
747	evtimer_set(&env->sc_conf_ev, client_periodic_update, env);
748	evtimer_add(&env->sc_conf_ev, &tv);
749}
750