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