1 /* $OpenBSD: smtp.c,v 1.32 2009/03/29 14:18:20 jacekm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@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/tree.h> 23 #include <sys/param.h> 24 #include <sys/socket.h> 25 26 #include <ctype.h> 27 #include <event.h> 28 #include <pwd.h> 29 #include <signal.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <time.h> 34 #include <unistd.h> 35 36 #include "smtpd.h" 37 38 __dead void smtp_shutdown(void); 39 void smtp_sig_handler(int, short, void *); 40 void smtp_dispatch_parent(int, short, void *); 41 void smtp_dispatch_mfa(int, short, void *); 42 void smtp_dispatch_lka(int, short, void *); 43 void smtp_dispatch_queue(int, short, void *); 44 void smtp_dispatch_control(int, short, void *); 45 void smtp_setup_events(struct smtpd *); 46 void smtp_disable_events(struct smtpd *); 47 void smtp_pause(struct smtpd *); 48 void smtp_resume(struct smtpd *); 49 void smtp_accept(int, short, void *); 50 void session_timeout(int, short, void *); 51 void session_auth_pickup(struct session *, char *, size_t); 52 53 struct s_session s_smtp; 54 55 void 56 smtp_sig_handler(int sig, short event, void *p) 57 { 58 switch (sig) { 59 case SIGINT: 60 case SIGTERM: 61 smtp_shutdown(); 62 break; 63 default: 64 fatalx("smtp_sig_handler: unexpected signal"); 65 } 66 } 67 68 void 69 smtp_dispatch_parent(int sig, short event, void *p) 70 { 71 struct smtpd *env = p; 72 struct imsgbuf *ibuf; 73 struct imsg imsg; 74 ssize_t n; 75 76 ibuf = env->sc_ibufs[PROC_PARENT]; 77 switch (event) { 78 case EV_READ: 79 if ((n = imsg_read(ibuf)) == -1) 80 fatal("imsg_read_error"); 81 if (n == 0) { 82 /* this pipe is dead, so remove the event handler */ 83 event_del(&ibuf->ev); 84 event_loopexit(NULL); 85 return; 86 } 87 break; 88 case EV_WRITE: 89 if (msgbuf_write(&ibuf->w) == -1) 90 fatal("msgbuf_write"); 91 imsg_event_add(ibuf); 92 return; 93 default: 94 fatalx("unknown event"); 95 } 96 97 for (;;) { 98 if ((n = imsg_get(ibuf, &imsg)) == -1) 99 fatal("parent_dispatch_smtp: imsg_read error"); 100 if (n == 0) 101 break; 102 103 switch (imsg.hdr.type) { 104 case IMSG_CONF_START: 105 if (env->sc_flags & SMTPD_CONFIGURING) 106 break; 107 env->sc_flags |= SMTPD_CONFIGURING; 108 smtp_disable_events(env); 109 break; 110 case IMSG_CONF_SSL: { 111 struct ssl *s; 112 struct ssl *x_ssl; 113 114 if (!(env->sc_flags & SMTPD_CONFIGURING)) 115 break; 116 117 if ((s = calloc(1, sizeof(*s))) == NULL) 118 fatal(NULL); 119 x_ssl = imsg.data; 120 (void)strlcpy(s->ssl_name, x_ssl->ssl_name, 121 sizeof(s->ssl_name)); 122 s->ssl_cert_len = x_ssl->ssl_cert_len; 123 if ((s->ssl_cert = 124 strdup((char *)imsg.data + sizeof(*s))) == NULL) 125 fatal(NULL); 126 s->ssl_key_len = x_ssl->ssl_key_len; 127 if ((s->ssl_key = strdup((char *)imsg.data + 128 (sizeof(*s) + s->ssl_cert_len))) == NULL) 129 fatal(NULL); 130 131 SPLAY_INSERT(ssltree, &env->sc_ssl, s); 132 break; 133 } 134 case IMSG_CONF_LISTENER: { 135 struct listener *l; 136 struct ssl key; 137 138 if (!(env->sc_flags & SMTPD_CONFIGURING)) 139 break; 140 141 if ((l = calloc(1, sizeof(*l))) == NULL) 142 fatal(NULL); 143 memcpy(l, imsg.data, sizeof(*l)); 144 if ((l->fd = imsg_get_fd(ibuf, &imsg)) == -1) 145 fatal("cannot get fd"); 146 147 log_debug("smtp_dispatch_parent: " 148 "got fd %d for listener: %p", l->fd, l); 149 150 (void)strlcpy(key.ssl_name, l->ssl_cert_name, 151 sizeof(key.ssl_name)); 152 153 if (l->flags & F_SSL) 154 if ((l->ssl = SPLAY_FIND(ssltree, 155 &env->sc_ssl, &key)) == NULL) 156 fatal("parent and smtp desynchronized"); 157 158 TAILQ_INSERT_TAIL(&env->sc_listeners, l, entry); 159 break; 160 } 161 case IMSG_CONF_END: 162 if (!(env->sc_flags & SMTPD_CONFIGURING)) 163 break; 164 smtp_setup_events(env); 165 env->sc_flags &= ~SMTPD_CONFIGURING; 166 break; 167 case IMSG_PARENT_AUTHENTICATE: { 168 struct session *s; 169 struct session key; 170 struct session_auth_reply *reply; 171 172 log_debug("smtp_dispatch_parent: parent handled authentication"); 173 reply = imsg.data; 174 key.s_id = reply->session_id; 175 key.s_msg.id = reply->session_id; 176 177 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 178 if (s == NULL) 179 fatal("smtp_dispatch_parent: session is gone"); 180 181 if (s->s_flags & F_QUIT) { 182 session_destroy(s); 183 break; 184 } 185 s->s_flags &= ~F_EVLOCKED; 186 187 if (reply->value) 188 s->s_flags |= F_AUTHENTICATED; 189 190 session_auth_pickup(s, NULL, 0); 191 192 break; 193 } 194 default: 195 log_warnx("smtp_dispatch_parent: got imsg %d", 196 imsg.hdr.type); 197 fatalx("smtp_dispatch_parent: unexpected imsg"); 198 } 199 imsg_free(&imsg); 200 } 201 imsg_event_add(ibuf); 202 } 203 204 void 205 smtp_dispatch_mfa(int sig, short event, void *p) 206 { 207 struct smtpd *env = p; 208 struct imsgbuf *ibuf; 209 struct imsg imsg; 210 ssize_t n; 211 212 ibuf = env->sc_ibufs[PROC_MFA]; 213 switch (event) { 214 case EV_READ: 215 if ((n = imsg_read(ibuf)) == -1) 216 fatal("imsg_read_error"); 217 if (n == 0) { 218 /* this pipe is dead, so remove the event handler */ 219 event_del(&ibuf->ev); 220 event_loopexit(NULL); 221 return; 222 } 223 break; 224 case EV_WRITE: 225 if (msgbuf_write(&ibuf->w) == -1) 226 fatal("msgbuf_write"); 227 imsg_event_add(ibuf); 228 return; 229 default: 230 fatalx("unknown event"); 231 } 232 233 for (;;) { 234 if ((n = imsg_get(ibuf, &imsg)) == -1) 235 fatal("smtp_dispatch_mfa: imsg_read error"); 236 if (n == 0) 237 break; 238 239 switch (imsg.hdr.type) { 240 case IMSG_MFA_MAIL: 241 case IMSG_MFA_RCPT: { 242 struct submit_status *ss; 243 struct session *s; 244 struct session key; 245 246 log_debug("smtp_dispatch_mfa: mfa handled return path"); 247 ss = imsg.data; 248 key.s_id = ss->id; 249 key.s_msg.id = ss->id; 250 251 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 252 if (s == NULL) 253 fatal("smtp_dispatch_mfa: session is gone"); 254 255 if (s->s_flags & F_QUIT) { 256 session_destroy(s); 257 break; 258 } 259 s->s_flags &= ~F_EVLOCKED; 260 261 session_pickup(s, ss); 262 break; 263 } 264 default: 265 log_warnx("smtp_dispatch_mfa: got imsg %d", 266 imsg.hdr.type); 267 fatalx("smtp_dispatch_mfa: unexpected imsg"); 268 } 269 imsg_free(&imsg); 270 } 271 imsg_event_add(ibuf); 272 } 273 274 void 275 smtp_dispatch_lka(int sig, short event, void *p) 276 { 277 struct smtpd *env = p; 278 struct imsgbuf *ibuf; 279 struct imsg imsg; 280 ssize_t n; 281 282 ibuf = env->sc_ibufs[PROC_LKA]; 283 switch (event) { 284 case EV_READ: 285 if ((n = imsg_read(ibuf)) == -1) 286 fatal("imsg_read_error"); 287 if (n == 0) { 288 /* this pipe is dead, so remove the event handler */ 289 event_del(&ibuf->ev); 290 event_loopexit(NULL); 291 return; 292 } 293 break; 294 case EV_WRITE: 295 if (msgbuf_write(&ibuf->w) == -1) 296 fatal("msgbuf_write"); 297 imsg_event_add(ibuf); 298 return; 299 default: 300 fatalx("unknown event"); 301 } 302 303 for (;;) { 304 if ((n = imsg_get(ibuf, &imsg)) == -1) 305 fatal("smtp_dispatch_lka: imsg_read error"); 306 if (n == 0) 307 break; 308 309 switch (imsg.hdr.type) { 310 case IMSG_LKA_HOST: { 311 struct session key; 312 struct session *s; 313 struct session *ss; 314 315 ss = imsg.data; 316 key.s_id = ss->s_id; 317 318 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 319 if (s == NULL) 320 fatal("smtp_dispatch_lka: session is gone"); 321 322 strlcpy(s->s_hostname, ss->s_hostname, 323 sizeof(s->s_hostname)); 324 strlcpy(s->s_msg.session_hostname, ss->s_hostname, 325 sizeof(s->s_msg.session_hostname)); 326 327 session_init(s->s_l, s); 328 329 break; 330 } 331 default: 332 log_warnx("smtp_dispatch_lka: got imsg %d", 333 imsg.hdr.type); 334 fatalx("smtp_dispatch_lka: unexpected imsg"); 335 } 336 imsg_free(&imsg); 337 } 338 imsg_event_add(ibuf); 339 } 340 341 void 342 smtp_dispatch_queue(int sig, short event, void *p) 343 { 344 struct smtpd *env = p; 345 struct imsgbuf *ibuf; 346 struct imsg imsg; 347 ssize_t n; 348 349 ibuf = env->sc_ibufs[PROC_QUEUE]; 350 switch (event) { 351 case EV_READ: 352 if ((n = imsg_read(ibuf)) == -1) 353 fatal("imsg_read_error"); 354 if (n == 0) { 355 /* this pipe is dead, so remove the event handler */ 356 event_del(&ibuf->ev); 357 event_loopexit(NULL); 358 return; 359 } 360 break; 361 case EV_WRITE: 362 if (msgbuf_write(&ibuf->w) == -1) 363 fatal("msgbuf_write"); 364 imsg_event_add(ibuf); 365 return; 366 default: 367 fatalx("unknown event"); 368 } 369 370 for (;;) { 371 if ((n = imsg_get(ibuf, &imsg)) == -1) 372 fatal("smtp_dispatch_queue: imsg_read error"); 373 if (n == 0) 374 break; 375 376 switch (imsg.hdr.type) { 377 case IMSG_QUEUE_CREATE_MESSAGE: { 378 struct submit_status *ss; 379 struct session *s; 380 struct session key; 381 382 log_debug("smtp_dispatch_queue: queue handled message creation"); 383 ss = imsg.data; 384 385 key.s_id = ss->id; 386 387 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 388 if (s == NULL) 389 fatal("smtp_dispatch_queue: session is gone"); 390 391 if (s->s_flags & F_QUIT) { 392 session_destroy(s); 393 break; 394 } 395 s->s_flags &= ~F_EVLOCKED; 396 397 (void)strlcpy(s->s_msg.message_id, ss->u.msgid, 398 sizeof(s->s_msg.message_id)); 399 session_pickup(s, ss); 400 break; 401 } 402 case IMSG_QUEUE_MESSAGE_FILE: { 403 struct submit_status *ss; 404 struct session *s; 405 struct session key; 406 int fd; 407 408 log_debug("smtp_dispatch_queue: queue handled message creation"); 409 ss = imsg.data; 410 411 key.s_id = ss->id; 412 413 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 414 if (s == NULL) 415 fatal("smtp_dispatch_queue: session is gone"); 416 417 fd = imsg_get_fd(ibuf, &imsg); 418 419 if (s->s_flags & F_QUIT) { 420 session_destroy(s); 421 break; 422 } 423 s->s_flags &= ~F_EVLOCKED; 424 425 if (fd != -1) { 426 s->datafp = fdopen(fd, "w"); 427 if (s->datafp == NULL) { 428 /* no need to handle error, it will be 429 * caught in session_pickup() 430 */ 431 close(fd); 432 } 433 } 434 session_pickup(s, ss); 435 436 break; 437 } 438 case IMSG_QUEUE_TEMPFAIL: { 439 struct submit_status *ss; 440 struct session *s; 441 struct session key; 442 443 log_debug("smtp_dispatch_queue: queue acknownedged a temporary failure"); 444 ss = imsg.data; 445 key.s_id = ss->id; 446 key.s_msg.id = ss->id; 447 448 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 449 if (s == NULL) 450 fatal("smtp_dispatch_queue: session is gone"); 451 452 if (s->s_flags & F_QUIT) { 453 session_destroy(s); 454 break; 455 } 456 s->s_flags &= ~F_EVLOCKED; 457 s->s_msg.status |= S_MESSAGE_TEMPFAILURE; 458 break; 459 } 460 461 case IMSG_QUEUE_COMMIT_ENVELOPES: 462 case IMSG_QUEUE_COMMIT_MESSAGE: { 463 struct submit_status *ss; 464 struct session *s; 465 struct session key; 466 467 log_debug("smtp_dispatch_queue: queue acknowledged message submission"); 468 ss = imsg.data; 469 key.s_id = ss->id; 470 key.s_msg.id = ss->id; 471 472 s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key); 473 if (s == NULL) 474 fatal("smtp_dispatch_queue: session is gone"); 475 476 if (s->s_flags & F_QUIT) { 477 if (imsg.hdr.type == IMSG_QUEUE_COMMIT_MESSAGE) { 478 s->s_msg.message_id[0] = '\0'; 479 s->s_msg.message_uid[0] = '\0'; 480 } 481 session_destroy(s); 482 break; 483 } 484 s->s_flags &= ~F_EVLOCKED; 485 486 session_pickup(s, ss); 487 break; 488 } 489 default: 490 log_warnx("smtp_dispatch_queue: got imsg %d", 491 imsg.hdr.type); 492 fatalx("smtp_dispatch_queue: unexpected imsg"); 493 } 494 imsg_free(&imsg); 495 } 496 imsg_event_add(ibuf); 497 } 498 499 void 500 smtp_dispatch_control(int sig, short event, void *p) 501 { 502 struct smtpd *env = p; 503 struct imsgbuf *ibuf; 504 struct imsg imsg; 505 ssize_t n; 506 507 ibuf = env->sc_ibufs[PROC_CONTROL]; 508 switch (event) { 509 case EV_READ: 510 if ((n = imsg_read(ibuf)) == -1) 511 fatal("imsg_read_error"); 512 if (n == 0) { 513 /* this pipe is dead, so remove the event handler */ 514 event_del(&ibuf->ev); 515 event_loopexit(NULL); 516 return; 517 } 518 break; 519 case EV_WRITE: 520 if (msgbuf_write(&ibuf->w) == -1) 521 fatal("msgbuf_write"); 522 imsg_event_add(ibuf); 523 return; 524 default: 525 fatalx("unknown event"); 526 } 527 528 for (;;) { 529 if ((n = imsg_get(ibuf, &imsg)) == -1) 530 fatal("smtp_dispatch_control: imsg_read error"); 531 if (n == 0) 532 break; 533 534 switch (imsg.hdr.type) { 535 case IMSG_SMTP_PAUSE: 536 smtp_pause(env); 537 break; 538 case IMSG_SMTP_RESUME: 539 smtp_resume(env); 540 break; 541 case IMSG_STATS: { 542 struct stats *s; 543 544 s = imsg.data; 545 s->u.smtp = s_smtp; 546 imsg_compose(ibuf, IMSG_STATS, 0, 0, -1, s, sizeof(*s)); 547 break; 548 } 549 default: 550 log_warnx("smtp_dispatch_control: got imsg %d", 551 imsg.hdr.type); 552 fatalx("smtp_dispatch_control: unexpected imsg"); 553 } 554 imsg_free(&imsg); 555 } 556 imsg_event_add(ibuf); 557 } 558 559 void 560 smtp_shutdown(void) 561 { 562 log_info("smtp server exiting"); 563 _exit(0); 564 } 565 566 pid_t 567 smtp(struct smtpd *env) 568 { 569 pid_t pid; 570 struct passwd *pw; 571 572 struct event ev_sigint; 573 struct event ev_sigterm; 574 575 struct peer peers[] = { 576 { PROC_PARENT, smtp_dispatch_parent }, 577 { PROC_MFA, smtp_dispatch_mfa }, 578 { PROC_QUEUE, smtp_dispatch_queue }, 579 { PROC_LKA, smtp_dispatch_lka }, 580 { PROC_CONTROL, smtp_dispatch_control } 581 }; 582 583 switch (pid = fork()) { 584 case -1: 585 fatal("smtp: cannot fork"); 586 case 0: 587 break; 588 default: 589 return (pid); 590 } 591 592 ssl_init(); 593 purge_config(env, PURGE_EVERYTHING); 594 595 pw = env->sc_pw; 596 597 #ifndef DEBUG 598 if (chroot(pw->pw_dir) == -1) 599 fatal("smtp: chroot"); 600 if (chdir("/") == -1) 601 fatal("smtp: chdir(\"/\")"); 602 #else 603 #warning disabling privilege revocation and chroot in DEBUG MODE 604 #endif 605 606 setproctitle("smtp server"); 607 smtpd_process = PROC_SMTP; 608 609 #ifndef DEBUG 610 if (setgroups(1, &pw->pw_gid) || 611 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 612 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 613 fatal("smtp: cannot drop privileges"); 614 #endif 615 616 event_init(); 617 618 signal_set(&ev_sigint, SIGINT, smtp_sig_handler, env); 619 signal_set(&ev_sigterm, SIGTERM, smtp_sig_handler, env); 620 signal_add(&ev_sigint, NULL); 621 signal_add(&ev_sigterm, NULL); 622 signal(SIGPIPE, SIG_IGN); 623 signal(SIGHUP, SIG_IGN); 624 625 config_pipes(env, peers, 5); 626 config_peers(env, peers, 5); 627 628 smtp_setup_events(env); 629 event_dispatch(); 630 smtp_shutdown(); 631 632 return (0); 633 } 634 635 void 636 smtp_setup_events(struct smtpd *env) 637 { 638 struct listener *l; 639 struct timeval tv; 640 641 TAILQ_FOREACH(l, &env->sc_listeners, entry) { 642 log_debug("smtp_setup_events: configuring listener: %p%s.", 643 l, (l->flags & F_SSL)?" (with ssl)":""); 644 645 session_socket_blockmode(l->fd, BM_NONBLOCK); 646 if (listen(l->fd, SMTPD_BACKLOG) == -1) 647 fatal("listen"); 648 l->env = env; 649 event_set(&l->ev, l->fd, EV_READ, smtp_accept, l); 650 event_add(&l->ev, NULL); 651 ssl_setup(env, l); 652 } 653 654 evtimer_set(&env->sc_ev, session_timeout, env); 655 tv.tv_sec = 1; 656 tv.tv_usec = 0; 657 evtimer_add(&env->sc_ev, &tv); 658 } 659 660 void 661 smtp_disable_events(struct smtpd *env) 662 { 663 struct listener *l; 664 665 log_debug("smtp_disable_events: closing listening sockets"); 666 while ((l = TAILQ_FIRST(&env->sc_listeners)) != NULL) { 667 TAILQ_REMOVE(&env->sc_listeners, l, entry); 668 event_del(&l->ev); 669 close(l->fd); 670 free(l); 671 } 672 TAILQ_INIT(&env->sc_listeners); 673 } 674 675 void 676 smtp_pause(struct smtpd *env) 677 { 678 log_debug("smtp_pause_listeners: pausing listening sockets"); 679 smtp_disable_events(env); 680 env->sc_opts |= SMTPD_SMTP_PAUSED; 681 } 682 683 void 684 smtp_resume(struct smtpd *env) 685 { 686 log_debug("smtp_pause_listeners: resuming listening sockets"); 687 imsg_compose(env->sc_ibufs[PROC_PARENT], IMSG_PARENT_SEND_CONFIG, 688 0, 0, -1, NULL, 0); 689 env->sc_opts &= ~SMTPD_SMTP_PAUSED; 690 } 691 692 void 693 smtp_accept(int fd, short event, void *p) 694 { 695 int s_fd; 696 struct sockaddr_storage ss; 697 struct listener *l = p; 698 struct session *s; 699 socklen_t len; 700 701 log_debug("smtp_accept: incoming client on listener: %p", l); 702 len = sizeof(struct sockaddr_storage); 703 if ((s_fd = accept(l->fd, (struct sockaddr *)&ss, &len)) == -1) { 704 event_del(&l->ev); 705 return; 706 } 707 708 log_debug("smtp_accept: accepted client on listener: %p", l); 709 if ((s = calloc(1, sizeof(*s))) == NULL) 710 fatal(NULL); 711 len = sizeof(s->s_ss); 712 713 s->s_id = queue_generate_id(); 714 s->s_fd = s_fd; 715 s->s_tm = time(NULL); 716 s->s_env = l->env; 717 s->s_l = l; 718 719 (void)memcpy(&s->s_ss, &ss, sizeof(s->s_ss)); 720 721 event_add(&l->ev, NULL); 722 723 s_smtp.sessions++; 724 s_smtp.sessions_active++; 725 726 if (s_smtp.sessions_active == s->s_env->sc_maxconn) 727 event_del(&l->ev); 728 729 strlcpy(s->s_hostname, "<unknown>", sizeof(s->s_hostname)); 730 strlcpy(s->s_msg.session_hostname, s->s_hostname, 731 sizeof(s->s_msg.session_hostname)); 732 imsg_compose(s->s_env->sc_ibufs[PROC_LKA], IMSG_LKA_HOST, 0, 0, -1, s, 733 sizeof(struct session)); 734 735 SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s); 736 } 737 738 void 739 smtp_listener_setup(struct smtpd *env, struct listener *l) 740 { 741 int opt; 742 743 if ((l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0)) == -1) 744 fatal("socket"); 745 746 opt = 1; 747 setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 748 749 if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1) 750 fatal("bind"); 751 } 752