listener.c revision 102528
10SN/A/* 2157SN/A * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers. 30SN/A * All rights reserved. 40SN/A * 50SN/A * By using this file, you agree to the terms and conditions set 60SN/A * forth in the LICENSE file which can be found at the top level of 7157SN/A * the sendmail distribution. 80SN/A * 9157SN/A */ 100SN/A 110SN/A#include <sm/gen.h> 120SN/ASM_RCSID("@(#)$Id: listener.c,v 8.85.2.1 2002/08/09 22:13:36 gshapiro Exp $") 130SN/A 140SN/A/* 150SN/A** listener.c -- threaded network listener 160SN/A*/ 170SN/A 180SN/A#include "libmilter.h" 190SN/A#include <sm/errstring.h> 200SN/A 21157SN/A 22157SN/A# if NETINET || NETINET6 23157SN/A# include <arpa/inet.h> 240SN/A# endif /* NETINET || NETINET6 */ 250SN/A 260SN/Astatic smutex_t L_Mutex; 270SN/Astatic int L_family; 280SN/Astatic SOCKADDR_LEN_T L_socksize; 290SN/Astatic socket_t listenfd = INVALID_SOCKET; 300SN/A 310SN/Astatic socket_t mi_milteropen __P((char *, int, char *)); 320SN/A 330SN/A/* 340SN/A** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet 350SN/A** 360SN/A** Parameters: 370SN/A** conn -- connection description 380SN/A** backlog -- listen backlog 390SN/A** dbg -- debug level 400SN/A** smfi -- filter structure to use 410SN/A** 420SN/A** Return value: 430SN/A** MI_SUCCESS/MI_FAILURE 44*/ 45 46int 47mi_opensocket(conn, backlog, dbg, smfi) 48 char *conn; 49 int backlog; 50 int dbg; 51 smfiDesc_ptr smfi; 52{ 53 if (smfi == NULL || conn == NULL) 54 return MI_FAILURE; 55 56 if (ValidSocket(listenfd)) 57 return MI_SUCCESS; 58 59 if (dbg > 0) 60 { 61 smi_log(SMI_LOG_DEBUG, 62 "%s: Opening listen socket on conn %s", 63 smfi->xxfi_name, conn); 64 } 65 (void) smutex_init(&L_Mutex); 66 (void) smutex_lock(&L_Mutex); 67 listenfd = mi_milteropen(conn, backlog, smfi->xxfi_name); 68 if (!ValidSocket(listenfd)) 69 { 70 smi_log(SMI_LOG_FATAL, 71 "%s: Unable to create listening socket on conn %s", 72 smfi->xxfi_name, conn); 73 (void) smutex_unlock(&L_Mutex); 74 return MI_FAILURE; 75 } 76 77 return MI_SUCCESS; 78} 79 80/* 81** MI_MILTEROPEN -- setup socket to listen on 82** 83** Parameters: 84** conn -- connection description 85** backlog -- listen backlog 86** name -- name for logging 87** 88** Returns: 89** socket upon success, error code otherwise. 90** 91** Side effect: 92** sets sockpath if UNIX socket. 93*/ 94 95#if NETUNIX 96static char *sockpath = NULL; 97#endif /* NETUNIX */ 98 99static socket_t 100mi_milteropen(conn, backlog, name) 101 char *conn; 102 int backlog; 103 char *name; 104{ 105 socket_t sock; 106 int sockopt = 1; 107 int fdflags; 108 size_t len = 0; 109 char *p; 110 char *colon; 111 char *at; 112 SOCKADDR addr; 113 114 if (conn == NULL || conn[0] == '\0') 115 { 116 smi_log(SMI_LOG_ERR, "%s: empty or missing socket information", 117 name); 118 return INVALID_SOCKET; 119 } 120 (void) memset(&addr, '\0', sizeof addr); 121 122 /* protocol:filename or protocol:port@host */ 123 p = conn; 124 colon = strchr(p, ':'); 125 if (colon != NULL) 126 { 127 *colon = '\0'; 128 129 if (*p == '\0') 130 { 131#if NETUNIX 132 /* default to AF_UNIX */ 133 addr.sa.sa_family = AF_UNIX; 134 L_socksize = sizeof (struct sockaddr_un); 135#else /* NETUNIX */ 136# if NETINET 137 /* default to AF_INET */ 138 addr.sa.sa_family = AF_INET; 139 L_socksize = sizeof addr.sin; 140# else /* NETINET */ 141# if NETINET6 142 /* default to AF_INET6 */ 143 addr.sa.sa_family = AF_INET6; 144 L_socksize = sizeof addr.sin6; 145# else /* NETINET6 */ 146 /* no protocols available */ 147 smi_log(SMI_LOG_ERR, 148 "%s: no valid socket protocols available", 149 name); 150 return INVALID_SOCKET; 151# endif /* NETINET6 */ 152# endif /* NETINET */ 153#endif /* NETUNIX */ 154 } 155#if NETUNIX 156 else if (strcasecmp(p, "unix") == 0 || 157 strcasecmp(p, "local") == 0) 158 { 159 addr.sa.sa_family = AF_UNIX; 160 L_socksize = sizeof (struct sockaddr_un); 161 } 162#endif /* NETUNIX */ 163#if NETINET 164 else if (strcasecmp(p, "inet") == 0) 165 { 166 addr.sa.sa_family = AF_INET; 167 L_socksize = sizeof addr.sin; 168 } 169#endif /* NETINET */ 170#if NETINET6 171 else if (strcasecmp(p, "inet6") == 0) 172 { 173 addr.sa.sa_family = AF_INET6; 174 L_socksize = sizeof addr.sin6; 175 } 176#endif /* NETINET6 */ 177 else 178 { 179 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 180 name, p); 181 return INVALID_SOCKET; 182 } 183 *colon++ = ':'; 184 } 185 else 186 { 187 colon = p; 188#if NETUNIX 189 /* default to AF_UNIX */ 190 addr.sa.sa_family = AF_UNIX; 191 L_socksize = sizeof (struct sockaddr_un); 192#else /* NETUNIX */ 193# if NETINET 194 /* default to AF_INET */ 195 addr.sa.sa_family = AF_INET; 196 L_socksize = sizeof addr.sin; 197# else /* NETINET */ 198# if NETINET6 199 /* default to AF_INET6 */ 200 addr.sa.sa_family = AF_INET6; 201 L_socksize = sizeof addr.sin6; 202# else /* NETINET6 */ 203 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 204 name, p); 205 return INVALID_SOCKET; 206# endif /* NETINET6 */ 207# endif /* NETINET */ 208#endif /* NETUNIX */ 209 } 210 211#if NETUNIX 212 if (addr.sa.sa_family == AF_UNIX) 213 { 214# if 0 215 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 216# endif /* 0 */ 217 218 at = colon; 219 len = strlen(colon) + 1; 220 if (len >= sizeof addr.sunix.sun_path) 221 { 222 errno = EINVAL; 223 smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", 224 name, colon); 225 return INVALID_SOCKET; 226 } 227 (void) sm_strlcpy(addr.sunix.sun_path, colon, 228 sizeof addr.sunix.sun_path); 229# if 0 230 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 231 S_IRUSR|S_IWUSR, NULL); 232 233 /* if not safe, don't create */ 234 if (errno != 0) 235 { 236 smi_log(SMI_LOG_ERR, 237 "%s: UNIX socket name %s unsafe", 238 name, colon); 239 return INVALID_SOCKET; 240 } 241# endif /* 0 */ 242 } 243#endif /* NETUNIX */ 244 245#if NETINET || NETINET6 246 if ( 247# if NETINET 248 addr.sa.sa_family == AF_INET 249# endif /* NETINET */ 250# if NETINET && NETINET6 251 || 252# endif /* NETINET && NETINET6 */ 253# if NETINET6 254 addr.sa.sa_family == AF_INET6 255# endif /* NETINET6 */ 256 ) 257 { 258 unsigned short port; 259 260 /* Parse port@host */ 261 at = strchr(colon, '@'); 262 if (at == NULL) 263 { 264 switch (addr.sa.sa_family) 265 { 266# if NETINET 267 case AF_INET: 268 addr.sin.sin_addr.s_addr = INADDR_ANY; 269 break; 270# endif /* NETINET */ 271 272# if NETINET6 273 case AF_INET6: 274 addr.sin6.sin6_addr = in6addr_any; 275 break; 276# endif /* NETINET6 */ 277 } 278 } 279 else 280 *at = '\0'; 281 282 if (isascii(*colon) && isdigit(*colon)) 283 port = htons((unsigned short) atoi(colon)); 284 else 285 { 286# ifdef NO_GETSERVBYNAME 287 smi_log(SMI_LOG_ERR, "%s: invalid port number %s", 288 name, colon); 289 return INVALID_SOCKET; 290# else /* NO_GETSERVBYNAME */ 291 register struct servent *sp; 292 293 sp = getservbyname(colon, "tcp"); 294 if (sp == NULL) 295 { 296 smi_log(SMI_LOG_ERR, 297 "%s: unknown port name %s", 298 name, colon); 299 return INVALID_SOCKET; 300 } 301 port = sp->s_port; 302# endif /* NO_GETSERVBYNAME */ 303 } 304 if (at != NULL) 305 { 306 *at++ = '@'; 307 if (*at == '[') 308 { 309 char *end; 310 311 end = strchr(at, ']'); 312 if (end != NULL) 313 { 314 bool found = false; 315# if NETINET 316 unsigned long hid = INADDR_NONE; 317# endif /* NETINET */ 318# if NETINET6 319 struct sockaddr_in6 hid6; 320# endif /* NETINET6 */ 321 322 *end = '\0'; 323# if NETINET 324 if (addr.sa.sa_family == AF_INET && 325 (hid = inet_addr(&at[1])) != INADDR_NONE) 326 { 327 addr.sin.sin_addr.s_addr = hid; 328 addr.sin.sin_port = port; 329 found = true; 330 } 331# endif /* NETINET */ 332# if NETINET6 333 (void) memset(&hid6, '\0', sizeof hid6); 334 if (addr.sa.sa_family == AF_INET6 && 335 mi_inet_pton(AF_INET6, &at[1], 336 &hid6.sin6_addr) == 1) 337 { 338 addr.sin6.sin6_addr = hid6.sin6_addr; 339 addr.sin6.sin6_port = port; 340 found = true; 341 } 342# endif /* NETINET6 */ 343 *end = ']'; 344 if (!found) 345 { 346 smi_log(SMI_LOG_ERR, 347 "%s: Invalid numeric domain spec \"%s\"", 348 name, at); 349 return INVALID_SOCKET; 350 } 351 } 352 else 353 { 354 smi_log(SMI_LOG_ERR, 355 "%s: Invalid numeric domain spec \"%s\"", 356 name, at); 357 return INVALID_SOCKET; 358 } 359 } 360 else 361 { 362 struct hostent *hp = NULL; 363 364 hp = mi_gethostbyname(at, addr.sa.sa_family); 365 if (hp == NULL) 366 { 367 smi_log(SMI_LOG_ERR, 368 "%s: Unknown host name %s", 369 name, at); 370 return INVALID_SOCKET; 371 } 372 addr.sa.sa_family = hp->h_addrtype; 373 switch (hp->h_addrtype) 374 { 375# if NETINET 376 case AF_INET: 377 memmove(&addr.sin.sin_addr, 378 hp->h_addr, 379 INADDRSZ); 380 addr.sin.sin_port = port; 381 break; 382# endif /* NETINET */ 383 384# if NETINET6 385 case AF_INET6: 386 memmove(&addr.sin6.sin6_addr, 387 hp->h_addr, 388 IN6ADDRSZ); 389 addr.sin6.sin6_port = port; 390 break; 391# endif /* NETINET6 */ 392 393 default: 394 smi_log(SMI_LOG_ERR, 395 "%s: Unknown protocol for %s (%d)", 396 name, at, hp->h_addrtype); 397 return INVALID_SOCKET; 398 } 399# if NETINET6 400 freehostent(hp); 401# endif /* NETINET6 */ 402 } 403 } 404 else 405 { 406 switch (addr.sa.sa_family) 407 { 408# if NETINET 409 case AF_INET: 410 addr.sin.sin_port = port; 411 break; 412# endif /* NETINET */ 413# if NETINET6 414 case AF_INET6: 415 addr.sin6.sin6_port = port; 416 break; 417# endif /* NETINET6 */ 418 } 419 } 420 } 421#endif /* NETINET || NETINET6 */ 422 423 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 424 if (!ValidSocket(sock)) 425 { 426 smi_log(SMI_LOG_ERR, 427 "%s: Unable to create new socket: %s", 428 name, sm_errstring(errno)); 429 return INVALID_SOCKET; 430 } 431 432 if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 || 433 fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1) 434 { 435 smi_log(SMI_LOG_ERR, 436 "%s: Unable to set close-on-exec: %s", name, 437 sm_errstring(errno)); 438 (void) closesocket(sock); 439 return INVALID_SOCKET; 440 } 441 442 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 443 sizeof(sockopt)) == -1) 444 { 445 smi_log(SMI_LOG_ERR, 446 "%s: Unable to setsockopt: %s", name, 447 sm_errstring(errno)); 448 (void) closesocket(sock); 449 return INVALID_SOCKET; 450 } 451 452 if (bind(sock, &addr.sa, L_socksize) < 0) 453 { 454 smi_log(SMI_LOG_ERR, 455 "%s: Unable to bind to port %s: %s", 456 name, conn, sm_errstring(errno)); 457 (void) closesocket(sock); 458 return INVALID_SOCKET; 459 } 460 461 if (listen(sock, backlog) < 0) 462 { 463 smi_log(SMI_LOG_ERR, 464 "%s: listen call failed: %s", name, 465 sm_errstring(errno)); 466 (void) closesocket(sock); 467 return INVALID_SOCKET; 468 } 469 470#if NETUNIX 471 if (addr.sa.sa_family == AF_UNIX && len > 0) 472 { 473 /* 474 ** Set global variable sockpath so the UNIX socket can be 475 ** unlink()ed at exit. 476 */ 477 478 sockpath = (char *) malloc(len); 479 if (sockpath != NULL) 480 (void) sm_strlcpy(sockpath, colon, len); 481 else 482 { 483 smi_log(SMI_LOG_ERR, 484 "%s: can't malloc(%d) for sockpath: %s", 485 name, len, sm_errstring(errno)); 486 (void) closesocket(sock); 487 return INVALID_SOCKET; 488 } 489 } 490#endif /* NETUNIX */ 491 L_family = addr.sa.sa_family; 492 return sock; 493} 494/* 495** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 496** 497** Parameters: 498** arg -- argument to pass to mi_handle_session() 499** 500** Returns: 501** results from mi_handle_session() 502*/ 503 504void * 505mi_thread_handle_wrapper(arg) 506 void *arg; 507{ 508 return (void *) mi_handle_session(arg); 509} 510 511/* 512** MI_CLOSENER -- close listen socket 513** 514** NOTE: It is assumed that this function is called from a 515** function that has a mutex lock (currently mi_stop_milters()). 516** 517** Parameters: 518** none. 519** 520** Returns: 521** none. 522*/ 523 524void 525mi_closener() 526{ 527 (void) smutex_lock(&L_Mutex); 528 if (ValidSocket(listenfd)) 529 { 530#if NETUNIX 531 bool removable; 532 struct stat sockinfo; 533 struct stat fileinfo; 534 535 removable = sockpath != NULL && 536#if _FFR_MILTER_ROOT_UNSAFE 537 geteuid() != 0 && 538#endif /* _FFR_MILTER_ROOT_UNSAFE */ 539 fstat(listenfd, &sockinfo) == 0 && 540 (S_ISFIFO(sockinfo.st_mode) 541# ifdef S_ISSOCK 542 || S_ISSOCK(sockinfo.st_mode) 543# endif /* S_ISSOCK */ 544 ); 545#endif /* NETUNIX */ 546 547 (void) closesocket(listenfd); 548 listenfd = INVALID_SOCKET; 549 550#if NETUNIX 551 /* XXX sleep() some time before doing this? */ 552 if (sockpath != NULL) 553 { 554 if (removable && 555 stat(sockpath, &fileinfo) == 0 && 556 ((fileinfo.st_dev == sockinfo.st_dev && 557 fileinfo.st_ino == sockinfo.st_ino) 558# ifdef S_ISSOCK 559 || S_ISSOCK(fileinfo.st_mode) 560# endif /* S_ISSOCK */ 561 ) 562 && 563 (S_ISFIFO(fileinfo.st_mode) 564# ifdef S_ISSOCK 565 || S_ISSOCK(fileinfo.st_mode) 566# endif /* S_ISSOCK */ 567 )) 568 (void) unlink(sockpath); 569 free(sockpath); 570 sockpath = NULL; 571 } 572#endif /* NETUNIX */ 573 } 574 (void) smutex_unlock(&L_Mutex); 575} 576 577/* 578** MI_LISTENER -- Generic listener harness 579** 580** Open up listen port 581** Wait for connections 582** 583** Parameters: 584** conn -- connection description 585** dbg -- debug level 586** smfi -- filter structure to use 587** timeout -- timeout for reads/writes 588** backlog -- listen queue backlog size 589** 590** Returns: 591** MI_SUCCESS -- Exited normally 592** (session finished or we were told to exit) 593** MI_FAILURE -- Network initialization failed. 594*/ 595 596#if BROKEN_PTHREAD_SLEEP 597 598/* 599** Solaris 2.6, perhaps others, gets an internal threads library panic 600** when sleep() is used: 601** 602** thread_create() failed, returned 11 (EINVAL) 603** co_enable, thr_create() returned error = 24 604** libthread panic: co_enable failed (PID: 17793 LWP 1) 605** stacktrace: 606** ef526b10 607** ef52646c 608** ef534cbc 609** 156a4 610** 14644 611** 1413c 612** 135e0 613** 0 614*/ 615 616# define MI_SLEEP(s) \ 617{ \ 618 int rs = 0; \ 619 struct timeval st; \ 620 \ 621 st.tv_sec = (s); \ 622 st.tv_usec = 0; \ 623 if (st.tv_sec > 0) \ 624 { \ 625 for (;;) \ 626 { \ 627 rs = select(0, NULL, NULL, NULL, &st); \ 628 if (rs < 0 && errno == EINTR) \ 629 continue; \ 630 if (rs != 0) \ 631 { \ 632 smi_log(SMI_LOG_ERR, \ 633 "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \ 634 rs, errno); \ 635 } \ 636 } \ 637 } \ 638} 639#else /* BROKEN_PTHREAD_SLEEP */ 640# define MI_SLEEP(s) sleep((s)) 641#endif /* BROKEN_PTHREAD_SLEEP */ 642 643int 644mi_listener(conn, dbg, smfi, timeout, backlog) 645 char *conn; 646 int dbg; 647 smfiDesc_ptr smfi; 648 time_t timeout; 649 int backlog; 650{ 651 socket_t connfd = INVALID_SOCKET; 652 int sockopt = 1; 653 int r; 654 int ret = MI_SUCCESS; 655 int mcnt = 0; /* error count for malloc() failures */ 656 int tcnt = 0; /* error count for thread_create() failures */ 657 int acnt = 0; /* error count for accept() failures */ 658 int scnt = 0; /* error count for select() failures */ 659 int save_errno = 0; 660 sthread_t thread_id; 661 _SOCK_ADDR cliaddr; 662 SOCKADDR_LEN_T clilen; 663 SMFICTX_PTR ctx; 664 fd_set readset, excset; 665 struct timeval chktime; 666 667 if (mi_opensocket(conn, backlog, dbg, smfi) == MI_FAILURE) 668 return MI_FAILURE; 669 670 clilen = L_socksize; 671 672 if (listenfd >= FD_SETSIZE) 673 { 674 smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 675 smfi->xxfi_name, listenfd, FD_SETSIZE); 676 (void) smutex_unlock(&L_Mutex); 677 return MI_FAILURE; 678 } 679 (void) smutex_unlock(&L_Mutex); 680 681 while (mi_stop() == MILTER_CONT) 682 { 683 (void) smutex_lock(&L_Mutex); 684 if (!ValidSocket(listenfd)) 685 { 686 (void) smutex_unlock(&L_Mutex); 687 break; 688 } 689 690 /* select on interface ports */ 691 FD_ZERO(&readset); 692 FD_ZERO(&excset); 693 FD_SET((unsigned int) listenfd, &readset); 694 FD_SET((unsigned int) listenfd, &excset); 695 chktime.tv_sec = MI_CHK_TIME; 696 chktime.tv_usec = 0; 697 r = select(listenfd + 1, &readset, NULL, &excset, &chktime); 698 if (r == 0) /* timeout */ 699 { 700 (void) smutex_unlock(&L_Mutex); 701 continue; /* just check mi_stop() */ 702 } 703 if (r < 0) 704 { 705 save_errno = errno; 706 (void) smutex_unlock(&L_Mutex); 707 if (save_errno == EINTR) 708 continue; 709 scnt++; 710 smi_log(SMI_LOG_ERR, 711 "%s: select() failed (%s), %s", 712 smfi->xxfi_name, sm_errstring(save_errno), 713 scnt >= MAX_FAILS_S ? "abort" : "try again"); 714 MI_SLEEP(scnt); 715 if (scnt >= MAX_FAILS_S) 716 { 717 ret = MI_FAILURE; 718 break; 719 } 720 continue; 721 } 722 if (!FD_ISSET(listenfd, &readset)) 723 { 724 /* some error: just stop for now... */ 725 ret = MI_FAILURE; 726 (void) smutex_unlock(&L_Mutex); 727 smi_log(SMI_LOG_ERR, 728 "%s: select() returned exception for socket, abort", 729 smfi->xxfi_name); 730 break; 731 } 732 scnt = 0; /* reset error counter for select() */ 733 734 memset(&cliaddr, '\0', sizeof cliaddr); 735 connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 736 &clilen); 737 save_errno = errno; 738 (void) smutex_unlock(&L_Mutex); 739 740 /* 741 ** If remote side closes before 742 ** accept() finishes, sockaddr 743 ** might not be fully filled in. 744 */ 745 746 if (ValidSocket(connfd) && 747 (clilen == 0 || 748# ifdef BSD4_4_SOCKADDR 749 cliaddr.sa.sa_len == 0 || 750# endif /* BSD4_4_SOCKADDR */ 751 cliaddr.sa.sa_family != L_family)) 752 { 753 (void) closesocket(connfd); 754 connfd = INVALID_SOCKET; 755 save_errno = EINVAL; 756 } 757 758 if (!ValidSocket(connfd)) 759 { 760 if (save_errno == EINTR) 761 continue; 762 acnt++; 763 smi_log(SMI_LOG_ERR, 764 "%s: accept() returned invalid socket (%s), %s", 765 smfi->xxfi_name, sm_errstring(save_errno), 766 acnt >= MAX_FAILS_A ? "abort" : "try again"); 767 MI_SLEEP(acnt); 768 if (acnt >= MAX_FAILS_A) 769 { 770 ret = MI_FAILURE; 771 break; 772 } 773 continue; 774 } 775 acnt = 0; /* reset error counter for accept() */ 776 777 if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 778 (void *) &sockopt, sizeof sockopt) < 0) 779 { 780 smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)", 781 smfi->xxfi_name, sm_errstring(errno)); 782 /* XXX: continue? */ 783 } 784 if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 785 { 786 (void) closesocket(connfd); 787 mcnt++; 788 smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s", 789 smfi->xxfi_name, sm_errstring(save_errno), 790 mcnt >= MAX_FAILS_M ? "abort" : "try again"); 791 MI_SLEEP(mcnt); 792 if (mcnt >= MAX_FAILS_M) 793 { 794 ret = MI_FAILURE; 795 break; 796 } 797 continue; 798 } 799 mcnt = 0; /* reset error counter for malloc() */ 800 memset(ctx, '\0', sizeof *ctx); 801 ctx->ctx_sd = connfd; 802 ctx->ctx_dbg = dbg; 803 ctx->ctx_timeout = timeout; 804 ctx->ctx_smfi = smfi; 805#if 0 806 if (smfi->xxfi_eoh == NULL) 807 if (smfi->xxfi_eom == NULL) 808 if (smfi->xxfi_abort == NULL) 809 if (smfi->xxfi_close == NULL) 810#endif /* 0 */ 811 if (smfi->xxfi_connect == NULL) 812 ctx->ctx_pflags |= SMFIP_NOCONNECT; 813 if (smfi->xxfi_helo == NULL) 814 ctx->ctx_pflags |= SMFIP_NOHELO; 815 if (smfi->xxfi_envfrom == NULL) 816 ctx->ctx_pflags |= SMFIP_NOMAIL; 817 if (smfi->xxfi_envrcpt == NULL) 818 ctx->ctx_pflags |= SMFIP_NORCPT; 819 if (smfi->xxfi_header == NULL) 820 ctx->ctx_pflags |= SMFIP_NOHDRS; 821 if (smfi->xxfi_eoh == NULL) 822 ctx->ctx_pflags |= SMFIP_NOEOH; 823 if (smfi->xxfi_body == NULL) 824 ctx->ctx_pflags |= SMFIP_NOBODY; 825 826 if ((r = thread_create(&thread_id, 827 mi_thread_handle_wrapper, 828 (void *) ctx)) != 0) 829 { 830 tcnt++; 831 smi_log(SMI_LOG_ERR, 832 "%s: thread_create() failed: %d, %s", 833 smfi->xxfi_name, r, 834 tcnt >= MAX_FAILS_T ? "abort" : "try again"); 835 MI_SLEEP(tcnt); 836 (void) closesocket(connfd); 837 free(ctx); 838 if (tcnt >= MAX_FAILS_T) 839 { 840 ret = MI_FAILURE; 841 break; 842 } 843 continue; 844 } 845 tcnt = 0; 846 } 847 if (ret != MI_SUCCESS) 848 mi_stop_milters(MILTER_ABRT); 849 else 850 mi_closener(); 851 (void) smutex_destroy(&L_Mutex); 852 return ret; 853} 854