listener.c revision 141858
164562Sgshapiro/* 2141858Sgshapiro * Copyright (c) 1999-2004 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 464562Sgshapiro * 564562Sgshapiro * By using this file, you agree to the terms and conditions set 664562Sgshapiro * forth in the LICENSE file which can be found at the top level of 764562Sgshapiro * the sendmail distribution. 864562Sgshapiro * 964562Sgshapiro */ 1064562Sgshapiro 1190792Sgshapiro#include <sm/gen.h> 12141858SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.111 2004/09/20 21:11:15 msk Exp $") 1364562Sgshapiro 1464562Sgshapiro/* 1564562Sgshapiro** listener.c -- threaded network listener 1664562Sgshapiro*/ 1764562Sgshapiro 1864562Sgshapiro#include "libmilter.h" 1990792Sgshapiro#include <sm/errstring.h> 2064562Sgshapiro 21125820Sgshapiro#include <sys/types.h> 22125820Sgshapiro#include <sys/stat.h> 2364562Sgshapiro 24125820Sgshapiro 2564562Sgshapiro# if NETINET || NETINET6 2664562Sgshapiro# include <arpa/inet.h> 2764562Sgshapiro# endif /* NETINET || NETINET6 */ 2890792Sgshapiro 2990792Sgshapirostatic smutex_t L_Mutex; 3098121Sgshapirostatic int L_family; 3198121Sgshapirostatic SOCKADDR_LEN_T L_socksize; 3298121Sgshapirostatic socket_t listenfd = INVALID_SOCKET; 3390792Sgshapiro 34125820Sgshapirostatic socket_t mi_milteropen __P((char *, int, bool, char *)); 35141858Sgshapirostatic void *mi_thread_handle_wrapper __P((void *)); 3698121Sgshapiro 3790792Sgshapiro/* 3898121Sgshapiro** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet 3998121Sgshapiro** 40125820Sgshapiro** Parameters: 4198121Sgshapiro** conn -- connection description 4298121Sgshapiro** backlog -- listen backlog 43125820Sgshapiro** dbg -- debug level 44125820Sgshapiro** rmsocket -- if true, try to unlink() the socket first 45132943Sgshapiro** (UNIX domain sockets only) 4698121Sgshapiro** smfi -- filter structure to use 4798121Sgshapiro** 48125820Sgshapiro** Return value: 49125820Sgshapiro** MI_SUCCESS/MI_FAILURE 5098121Sgshapiro*/ 5198121Sgshapiro 5298121Sgshapiroint 53125820Sgshapiromi_opensocket(conn, backlog, dbg, rmsocket, smfi) 5498121Sgshapiro char *conn; 5598121Sgshapiro int backlog; 5698121Sgshapiro int dbg; 57125820Sgshapiro bool rmsocket; 5898121Sgshapiro smfiDesc_ptr smfi; 5998121Sgshapiro{ 6098121Sgshapiro if (smfi == NULL || conn == NULL) 6198121Sgshapiro return MI_FAILURE; 6298121Sgshapiro 6398121Sgshapiro if (ValidSocket(listenfd)) 6498121Sgshapiro return MI_SUCCESS; 6598121Sgshapiro 6698121Sgshapiro if (dbg > 0) 6798121Sgshapiro { 6898121Sgshapiro smi_log(SMI_LOG_DEBUG, 6998121Sgshapiro "%s: Opening listen socket on conn %s", 7098121Sgshapiro smfi->xxfi_name, conn); 7198121Sgshapiro } 7298121Sgshapiro (void) smutex_init(&L_Mutex); 7398121Sgshapiro (void) smutex_lock(&L_Mutex); 74125820Sgshapiro listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name); 7598121Sgshapiro if (!ValidSocket(listenfd)) 7698121Sgshapiro { 7798121Sgshapiro smi_log(SMI_LOG_FATAL, 7898121Sgshapiro "%s: Unable to create listening socket on conn %s", 7998121Sgshapiro smfi->xxfi_name, conn); 8098121Sgshapiro (void) smutex_unlock(&L_Mutex); 8198121Sgshapiro return MI_FAILURE; 8298121Sgshapiro } 83132943Sgshapiro#if !SM_CONF_POLL 84110560Sgshapiro if (!SM_FD_OK_SELECT(listenfd)) 85110560Sgshapiro { 86110560Sgshapiro smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 87110560Sgshapiro smfi->xxfi_name, listenfd, FD_SETSIZE); 88110560Sgshapiro (void) smutex_unlock(&L_Mutex); 89110560Sgshapiro return MI_FAILURE; 90110560Sgshapiro } 91132943Sgshapiro#endif /* !SM_CONF_POLL */ 92141858Sgshapiro (void) smutex_unlock(&L_Mutex); 9398121Sgshapiro return MI_SUCCESS; 9498121Sgshapiro} 9598121Sgshapiro 9698121Sgshapiro/* 9764562Sgshapiro** MI_MILTEROPEN -- setup socket to listen on 9864562Sgshapiro** 9964562Sgshapiro** Parameters: 10064562Sgshapiro** conn -- connection description 10164562Sgshapiro** backlog -- listen backlog 102125820Sgshapiro** rmsocket -- if true, try to unlink() the socket first 103125820Sgshapiro** (UNIX domain sockets only) 10473188Sgshapiro** name -- name for logging 10564562Sgshapiro** 10664562Sgshapiro** Returns: 10764562Sgshapiro** socket upon success, error code otherwise. 10890792Sgshapiro** 10990792Sgshapiro** Side effect: 11090792Sgshapiro** sets sockpath if UNIX socket. 11164562Sgshapiro*/ 11264562Sgshapiro 11390792Sgshapiro#if NETUNIX 11490792Sgshapirostatic char *sockpath = NULL; 11590792Sgshapiro#endif /* NETUNIX */ 11690792Sgshapiro 11766494Sgshapirostatic socket_t 118125820Sgshapiromi_milteropen(conn, backlog, rmsocket, name) 11964562Sgshapiro char *conn; 12064562Sgshapiro int backlog; 121125820Sgshapiro bool rmsocket; 12264562Sgshapiro char *name; 12364562Sgshapiro{ 12466494Sgshapiro socket_t sock; 12564562Sgshapiro int sockopt = 1; 12698121Sgshapiro int fdflags; 12790792Sgshapiro size_t len = 0; 12864562Sgshapiro char *p; 12964562Sgshapiro char *colon; 13064562Sgshapiro char *at; 13164562Sgshapiro SOCKADDR addr; 13264562Sgshapiro 13364562Sgshapiro if (conn == NULL || conn[0] == '\0') 13464562Sgshapiro { 13564562Sgshapiro smi_log(SMI_LOG_ERR, "%s: empty or missing socket information", 13664562Sgshapiro name); 13766494Sgshapiro return INVALID_SOCKET; 13864562Sgshapiro } 13964562Sgshapiro (void) memset(&addr, '\0', sizeof addr); 14064562Sgshapiro 14164562Sgshapiro /* protocol:filename or protocol:port@host */ 14264562Sgshapiro p = conn; 14364562Sgshapiro colon = strchr(p, ':'); 14464562Sgshapiro if (colon != NULL) 14564562Sgshapiro { 14664562Sgshapiro *colon = '\0'; 14764562Sgshapiro 14894334Sgshapiro if (*p == '\0') 14964562Sgshapiro { 15064562Sgshapiro#if NETUNIX 15164562Sgshapiro /* default to AF_UNIX */ 15294334Sgshapiro addr.sa.sa_family = AF_UNIX; 15398121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 15464562Sgshapiro#else /* NETUNIX */ 15564562Sgshapiro# if NETINET 15664562Sgshapiro /* default to AF_INET */ 15764562Sgshapiro addr.sa.sa_family = AF_INET; 15898121Sgshapiro L_socksize = sizeof addr.sin; 15964562Sgshapiro# else /* NETINET */ 16064562Sgshapiro# if NETINET6 16164562Sgshapiro /* default to AF_INET6 */ 16264562Sgshapiro addr.sa.sa_family = AF_INET6; 16398121Sgshapiro L_socksize = sizeof addr.sin6; 16464562Sgshapiro# else /* NETINET6 */ 16564562Sgshapiro /* no protocols available */ 16664562Sgshapiro smi_log(SMI_LOG_ERR, 16764562Sgshapiro "%s: no valid socket protocols available", 16864562Sgshapiro name); 16966494Sgshapiro return INVALID_SOCKET; 17064562Sgshapiro# endif /* NETINET6 */ 17164562Sgshapiro# endif /* NETINET */ 17264562Sgshapiro#endif /* NETUNIX */ 17364562Sgshapiro } 17464562Sgshapiro#if NETUNIX 17564562Sgshapiro else if (strcasecmp(p, "unix") == 0 || 17664562Sgshapiro strcasecmp(p, "local") == 0) 17764562Sgshapiro { 17864562Sgshapiro addr.sa.sa_family = AF_UNIX; 17998121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 18064562Sgshapiro } 18164562Sgshapiro#endif /* NETUNIX */ 18264562Sgshapiro#if NETINET 18364562Sgshapiro else if (strcasecmp(p, "inet") == 0) 18464562Sgshapiro { 18564562Sgshapiro addr.sa.sa_family = AF_INET; 18698121Sgshapiro L_socksize = sizeof addr.sin; 18764562Sgshapiro } 18864562Sgshapiro#endif /* NETINET */ 18964562Sgshapiro#if NETINET6 19064562Sgshapiro else if (strcasecmp(p, "inet6") == 0) 19164562Sgshapiro { 19264562Sgshapiro addr.sa.sa_family = AF_INET6; 19398121Sgshapiro L_socksize = sizeof addr.sin6; 19464562Sgshapiro } 19564562Sgshapiro#endif /* NETINET6 */ 19664562Sgshapiro else 19764562Sgshapiro { 19864562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 19964562Sgshapiro name, p); 20066494Sgshapiro return INVALID_SOCKET; 20164562Sgshapiro } 20264562Sgshapiro *colon++ = ':'; 20364562Sgshapiro } 20464562Sgshapiro else 20564562Sgshapiro { 20664562Sgshapiro colon = p; 20764562Sgshapiro#if NETUNIX 20864562Sgshapiro /* default to AF_UNIX */ 20994334Sgshapiro addr.sa.sa_family = AF_UNIX; 21098121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 21164562Sgshapiro#else /* NETUNIX */ 21264562Sgshapiro# if NETINET 21364562Sgshapiro /* default to AF_INET */ 21464562Sgshapiro addr.sa.sa_family = AF_INET; 21598121Sgshapiro L_socksize = sizeof addr.sin; 21664562Sgshapiro# else /* NETINET */ 21764562Sgshapiro# if NETINET6 21864562Sgshapiro /* default to AF_INET6 */ 21964562Sgshapiro addr.sa.sa_family = AF_INET6; 22098121Sgshapiro L_socksize = sizeof addr.sin6; 22164562Sgshapiro# else /* NETINET6 */ 22264562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 22364562Sgshapiro name, p); 22466494Sgshapiro return INVALID_SOCKET; 22564562Sgshapiro# endif /* NETINET6 */ 22664562Sgshapiro# endif /* NETINET */ 22764562Sgshapiro#endif /* NETUNIX */ 22864562Sgshapiro } 22964562Sgshapiro 23064562Sgshapiro#if NETUNIX 23164562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 23264562Sgshapiro { 23364562Sgshapiro# if 0 23464562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 23564562Sgshapiro# endif /* 0 */ 23664562Sgshapiro 23764562Sgshapiro at = colon; 23890792Sgshapiro len = strlen(colon) + 1; 23990792Sgshapiro if (len >= sizeof addr.sunix.sun_path) 24064562Sgshapiro { 24164562Sgshapiro errno = EINVAL; 24264562Sgshapiro smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", 24364562Sgshapiro name, colon); 24466494Sgshapiro return INVALID_SOCKET; 24564562Sgshapiro } 24690792Sgshapiro (void) sm_strlcpy(addr.sunix.sun_path, colon, 24790792Sgshapiro sizeof addr.sunix.sun_path); 24864562Sgshapiro# if 0 24964562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 25064562Sgshapiro S_IRUSR|S_IWUSR, NULL); 25164562Sgshapiro 25264562Sgshapiro /* if not safe, don't create */ 25364562Sgshapiro if (errno != 0) 25464562Sgshapiro { 25564562Sgshapiro smi_log(SMI_LOG_ERR, 25664562Sgshapiro "%s: UNIX socket name %s unsafe", 25764562Sgshapiro name, colon); 25866494Sgshapiro return INVALID_SOCKET; 25964562Sgshapiro } 26064562Sgshapiro# endif /* 0 */ 26164562Sgshapiro } 26264562Sgshapiro#endif /* NETUNIX */ 26364562Sgshapiro 26464562Sgshapiro#if NETINET || NETINET6 26564562Sgshapiro if ( 26664562Sgshapiro# if NETINET 26764562Sgshapiro addr.sa.sa_family == AF_INET 26864562Sgshapiro# endif /* NETINET */ 26964562Sgshapiro# if NETINET && NETINET6 27064562Sgshapiro || 27164562Sgshapiro# endif /* NETINET && NETINET6 */ 27264562Sgshapiro# if NETINET6 27364562Sgshapiro addr.sa.sa_family == AF_INET6 27464562Sgshapiro# endif /* NETINET6 */ 27564562Sgshapiro ) 27664562Sgshapiro { 27790792Sgshapiro unsigned short port; 27864562Sgshapiro 27964562Sgshapiro /* Parse port@host */ 28064562Sgshapiro at = strchr(colon, '@'); 28164562Sgshapiro if (at == NULL) 28264562Sgshapiro { 28364562Sgshapiro switch (addr.sa.sa_family) 28464562Sgshapiro { 28564562Sgshapiro# if NETINET 28664562Sgshapiro case AF_INET: 28764562Sgshapiro addr.sin.sin_addr.s_addr = INADDR_ANY; 28864562Sgshapiro break; 28964562Sgshapiro# endif /* NETINET */ 29064562Sgshapiro 29164562Sgshapiro# if NETINET6 29264562Sgshapiro case AF_INET6: 29364562Sgshapiro addr.sin6.sin6_addr = in6addr_any; 29464562Sgshapiro break; 29564562Sgshapiro# endif /* NETINET6 */ 29664562Sgshapiro } 29764562Sgshapiro } 29864562Sgshapiro else 29964562Sgshapiro *at = '\0'; 30064562Sgshapiro 30164562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 30290792Sgshapiro port = htons((unsigned short) atoi(colon)); 30364562Sgshapiro else 30464562Sgshapiro { 30564562Sgshapiro# ifdef NO_GETSERVBYNAME 30664562Sgshapiro smi_log(SMI_LOG_ERR, "%s: invalid port number %s", 30764562Sgshapiro name, colon); 30866494Sgshapiro return INVALID_SOCKET; 30964562Sgshapiro# else /* NO_GETSERVBYNAME */ 31064562Sgshapiro register struct servent *sp; 31164562Sgshapiro 31264562Sgshapiro sp = getservbyname(colon, "tcp"); 31364562Sgshapiro if (sp == NULL) 31464562Sgshapiro { 31564562Sgshapiro smi_log(SMI_LOG_ERR, 31664562Sgshapiro "%s: unknown port name %s", 31764562Sgshapiro name, colon); 31866494Sgshapiro return INVALID_SOCKET; 31964562Sgshapiro } 32064562Sgshapiro port = sp->s_port; 32164562Sgshapiro# endif /* NO_GETSERVBYNAME */ 32264562Sgshapiro } 32364562Sgshapiro if (at != NULL) 32464562Sgshapiro { 32564562Sgshapiro *at++ = '@'; 32664562Sgshapiro if (*at == '[') 32764562Sgshapiro { 32864562Sgshapiro char *end; 32964562Sgshapiro 33064562Sgshapiro end = strchr(at, ']'); 33164562Sgshapiro if (end != NULL) 33264562Sgshapiro { 33390792Sgshapiro bool found = false; 33464562Sgshapiro# if NETINET 33564562Sgshapiro unsigned long hid = INADDR_NONE; 33664562Sgshapiro# endif /* NETINET */ 33764562Sgshapiro# if NETINET6 33864562Sgshapiro struct sockaddr_in6 hid6; 33964562Sgshapiro# endif /* NETINET6 */ 34064562Sgshapiro 34164562Sgshapiro *end = '\0'; 34264562Sgshapiro# if NETINET 34364562Sgshapiro if (addr.sa.sa_family == AF_INET && 34490792Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 34564562Sgshapiro { 34664562Sgshapiro addr.sin.sin_addr.s_addr = hid; 34764562Sgshapiro addr.sin.sin_port = port; 34890792Sgshapiro found = true; 34964562Sgshapiro } 35064562Sgshapiro# endif /* NETINET */ 35164562Sgshapiro# if NETINET6 35264562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 35364562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 35490792Sgshapiro mi_inet_pton(AF_INET6, &at[1], 35590792Sgshapiro &hid6.sin6_addr) == 1) 35664562Sgshapiro { 35764562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 35864562Sgshapiro addr.sin6.sin6_port = port; 35990792Sgshapiro found = true; 36064562Sgshapiro } 36164562Sgshapiro# endif /* NETINET6 */ 36264562Sgshapiro *end = ']'; 36364562Sgshapiro if (!found) 36464562Sgshapiro { 36564562Sgshapiro smi_log(SMI_LOG_ERR, 36664562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 36764562Sgshapiro name, at); 36866494Sgshapiro return INVALID_SOCKET; 36964562Sgshapiro } 37064562Sgshapiro } 37164562Sgshapiro else 37264562Sgshapiro { 37364562Sgshapiro smi_log(SMI_LOG_ERR, 37464562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 37564562Sgshapiro name, at); 37666494Sgshapiro return INVALID_SOCKET; 37764562Sgshapiro } 37864562Sgshapiro } 37964562Sgshapiro else 38064562Sgshapiro { 38171345Sgshapiro struct hostent *hp = NULL; 38271345Sgshapiro 38364562Sgshapiro hp = mi_gethostbyname(at, addr.sa.sa_family); 38464562Sgshapiro if (hp == NULL) 38564562Sgshapiro { 38664562Sgshapiro smi_log(SMI_LOG_ERR, 38764562Sgshapiro "%s: Unknown host name %s", 38864562Sgshapiro name, at); 38966494Sgshapiro return INVALID_SOCKET; 39064562Sgshapiro } 39164562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 39264562Sgshapiro switch (hp->h_addrtype) 39364562Sgshapiro { 39464562Sgshapiro# if NETINET 39564562Sgshapiro case AF_INET: 396120256Sgshapiro (void) memmove(&addr.sin.sin_addr, 397120256Sgshapiro hp->h_addr, 398120256Sgshapiro INADDRSZ); 39964562Sgshapiro addr.sin.sin_port = port; 40064562Sgshapiro break; 40164562Sgshapiro# endif /* NETINET */ 40264562Sgshapiro 40364562Sgshapiro# if NETINET6 40464562Sgshapiro case AF_INET6: 405120256Sgshapiro (void) memmove(&addr.sin6.sin6_addr, 406120256Sgshapiro hp->h_addr, 407120256Sgshapiro IN6ADDRSZ); 40864562Sgshapiro addr.sin6.sin6_port = port; 40964562Sgshapiro break; 41064562Sgshapiro# endif /* NETINET6 */ 41164562Sgshapiro 41264562Sgshapiro default: 41364562Sgshapiro smi_log(SMI_LOG_ERR, 41464562Sgshapiro "%s: Unknown protocol for %s (%d)", 41564562Sgshapiro name, at, hp->h_addrtype); 41666494Sgshapiro return INVALID_SOCKET; 41764562Sgshapiro } 41890792Sgshapiro# if NETINET6 41971345Sgshapiro freehostent(hp); 42090792Sgshapiro# endif /* NETINET6 */ 42164562Sgshapiro } 42264562Sgshapiro } 42364562Sgshapiro else 42464562Sgshapiro { 42564562Sgshapiro switch (addr.sa.sa_family) 42664562Sgshapiro { 42764562Sgshapiro# if NETINET 42864562Sgshapiro case AF_INET: 42964562Sgshapiro addr.sin.sin_port = port; 43064562Sgshapiro break; 43164562Sgshapiro# endif /* NETINET */ 43264562Sgshapiro# if NETINET6 43364562Sgshapiro case AF_INET6: 43464562Sgshapiro addr.sin6.sin6_port = port; 43564562Sgshapiro break; 43664562Sgshapiro# endif /* NETINET6 */ 43764562Sgshapiro } 43864562Sgshapiro } 43964562Sgshapiro } 44064562Sgshapiro#endif /* NETINET || NETINET6 */ 44164562Sgshapiro 44264562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 44364562Sgshapiro if (!ValidSocket(sock)) 44464562Sgshapiro { 44564562Sgshapiro smi_log(SMI_LOG_ERR, 44664562Sgshapiro "%s: Unable to create new socket: %s", 44790792Sgshapiro name, sm_errstring(errno)); 44866494Sgshapiro return INVALID_SOCKET; 44964562Sgshapiro } 45064562Sgshapiro 45198121Sgshapiro if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 || 45298121Sgshapiro fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1) 45398121Sgshapiro { 45498121Sgshapiro smi_log(SMI_LOG_ERR, 45598121Sgshapiro "%s: Unable to set close-on-exec: %s", name, 45698121Sgshapiro sm_errstring(errno)); 45798121Sgshapiro (void) closesocket(sock); 45898121Sgshapiro return INVALID_SOCKET; 45998121Sgshapiro } 46098121Sgshapiro 46164562Sgshapiro if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 46264562Sgshapiro sizeof(sockopt)) == -1) 46364562Sgshapiro { 46464562Sgshapiro smi_log(SMI_LOG_ERR, 46590792Sgshapiro "%s: Unable to setsockopt: %s", name, 46690792Sgshapiro sm_errstring(errno)); 46790792Sgshapiro (void) closesocket(sock); 46866494Sgshapiro return INVALID_SOCKET; 46964562Sgshapiro } 47064562Sgshapiro 471125820Sgshapiro#if NETUNIX 472125820Sgshapiro if (addr.sa.sa_family == AF_UNIX && rmsocket) 473125820Sgshapiro { 474125820Sgshapiro struct stat s; 475125820Sgshapiro 476125820Sgshapiro if (stat(colon, &s) != 0) 477125820Sgshapiro { 478125820Sgshapiro if (errno != ENOENT) 479125820Sgshapiro { 480125820Sgshapiro smi_log(SMI_LOG_ERR, 481125820Sgshapiro "%s: Unable to stat() %s: %s", 482125820Sgshapiro name, colon, sm_errstring(errno)); 483125820Sgshapiro (void) closesocket(sock); 484125820Sgshapiro return INVALID_SOCKET; 485125820Sgshapiro } 486125820Sgshapiro } 487125820Sgshapiro else if (!S_ISSOCK(s.st_mode)) 488125820Sgshapiro { 489125820Sgshapiro smi_log(SMI_LOG_ERR, 490125820Sgshapiro "%s: %s is not a UNIX domain socket", 491125820Sgshapiro name, colon); 492125820Sgshapiro (void) closesocket(sock); 493125820Sgshapiro return INVALID_SOCKET; 494125820Sgshapiro } 495125820Sgshapiro else if (unlink(colon) != 0) 496125820Sgshapiro { 497125820Sgshapiro smi_log(SMI_LOG_ERR, 498125820Sgshapiro "%s: Unable to remove %s: %s", 499125820Sgshapiro name, colon, sm_errstring(errno)); 500125820Sgshapiro (void) closesocket(sock); 501125820Sgshapiro return INVALID_SOCKET; 502125820Sgshapiro } 503125820Sgshapiro } 504125820Sgshapiro#endif /* NETUNIX */ 505125820Sgshapiro 50698121Sgshapiro if (bind(sock, &addr.sa, L_socksize) < 0) 50764562Sgshapiro { 50864562Sgshapiro smi_log(SMI_LOG_ERR, 50964562Sgshapiro "%s: Unable to bind to port %s: %s", 51090792Sgshapiro name, conn, sm_errstring(errno)); 51190792Sgshapiro (void) closesocket(sock); 51266494Sgshapiro return INVALID_SOCKET; 51364562Sgshapiro } 51464562Sgshapiro 51564562Sgshapiro if (listen(sock, backlog) < 0) 51664562Sgshapiro { 51764562Sgshapiro smi_log(SMI_LOG_ERR, 51890792Sgshapiro "%s: listen call failed: %s", name, 51990792Sgshapiro sm_errstring(errno)); 52090792Sgshapiro (void) closesocket(sock); 52166494Sgshapiro return INVALID_SOCKET; 52264562Sgshapiro } 52390792Sgshapiro 52490792Sgshapiro#if NETUNIX 52590792Sgshapiro if (addr.sa.sa_family == AF_UNIX && len > 0) 52690792Sgshapiro { 52790792Sgshapiro /* 52890792Sgshapiro ** Set global variable sockpath so the UNIX socket can be 52990792Sgshapiro ** unlink()ed at exit. 53090792Sgshapiro */ 53190792Sgshapiro 53290792Sgshapiro sockpath = (char *) malloc(len); 53390792Sgshapiro if (sockpath != NULL) 53490792Sgshapiro (void) sm_strlcpy(sockpath, colon, len); 53590792Sgshapiro else 53690792Sgshapiro { 53790792Sgshapiro smi_log(SMI_LOG_ERR, 53890792Sgshapiro "%s: can't malloc(%d) for sockpath: %s", 539110560Sgshapiro name, (int) len, sm_errstring(errno)); 54090792Sgshapiro (void) closesocket(sock); 54190792Sgshapiro return INVALID_SOCKET; 54290792Sgshapiro } 54390792Sgshapiro } 54490792Sgshapiro#endif /* NETUNIX */ 54598121Sgshapiro L_family = addr.sa.sa_family; 54664562Sgshapiro return sock; 54764562Sgshapiro} 54890792Sgshapiro/* 54964562Sgshapiro** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 55064562Sgshapiro** 55164562Sgshapiro** Parameters: 55264562Sgshapiro** arg -- argument to pass to mi_handle_session() 55364562Sgshapiro** 55464562Sgshapiro** Returns: 55564562Sgshapiro** results from mi_handle_session() 55664562Sgshapiro*/ 55764562Sgshapiro 558141858Sgshapirostatic void * 55964562Sgshapiromi_thread_handle_wrapper(arg) 56064562Sgshapiro void *arg; 56164562Sgshapiro{ 56264562Sgshapiro return (void *) mi_handle_session(arg); 56364562Sgshapiro} 56464562Sgshapiro 56590792Sgshapiro/* 56666494Sgshapiro** MI_CLOSENER -- close listen socket 56764562Sgshapiro** 56890792Sgshapiro** NOTE: It is assumed that this function is called from a 56990792Sgshapiro** function that has a mutex lock (currently mi_stop_milters()). 57090792Sgshapiro** 57166494Sgshapiro** Parameters: 57266494Sgshapiro** none. 57366494Sgshapiro** 57466494Sgshapiro** Returns: 57566494Sgshapiro** none. 57666494Sgshapiro*/ 57766494Sgshapiro 57866494Sgshapirovoid 57966494Sgshapiromi_closener() 58066494Sgshapiro{ 58171345Sgshapiro (void) smutex_lock(&L_Mutex); 58266494Sgshapiro if (ValidSocket(listenfd)) 58366494Sgshapiro { 58490792Sgshapiro#if NETUNIX 58590792Sgshapiro bool removable; 58690792Sgshapiro struct stat sockinfo; 58790792Sgshapiro struct stat fileinfo; 58890792Sgshapiro 58990792Sgshapiro removable = sockpath != NULL && 59090792Sgshapiro geteuid() != 0 && 59190792Sgshapiro fstat(listenfd, &sockinfo) == 0 && 59290792Sgshapiro (S_ISFIFO(sockinfo.st_mode) 59390792Sgshapiro# ifdef S_ISSOCK 59490792Sgshapiro || S_ISSOCK(sockinfo.st_mode) 59590792Sgshapiro# endif /* S_ISSOCK */ 59690792Sgshapiro ); 59790792Sgshapiro#endif /* NETUNIX */ 59890792Sgshapiro 59990792Sgshapiro (void) closesocket(listenfd); 60066494Sgshapiro listenfd = INVALID_SOCKET; 60190792Sgshapiro 60290792Sgshapiro#if NETUNIX 60390792Sgshapiro /* XXX sleep() some time before doing this? */ 60490792Sgshapiro if (sockpath != NULL) 60590792Sgshapiro { 60690792Sgshapiro if (removable && 60790792Sgshapiro stat(sockpath, &fileinfo) == 0 && 60890792Sgshapiro ((fileinfo.st_dev == sockinfo.st_dev && 60990792Sgshapiro fileinfo.st_ino == sockinfo.st_ino) 61090792Sgshapiro# ifdef S_ISSOCK 61190792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61290792Sgshapiro# endif /* S_ISSOCK */ 61390792Sgshapiro ) 61490792Sgshapiro && 61590792Sgshapiro (S_ISFIFO(fileinfo.st_mode) 61690792Sgshapiro# ifdef S_ISSOCK 61790792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61890792Sgshapiro# endif /* S_ISSOCK */ 61990792Sgshapiro )) 62090792Sgshapiro (void) unlink(sockpath); 62190792Sgshapiro free(sockpath); 62290792Sgshapiro sockpath = NULL; 62390792Sgshapiro } 62490792Sgshapiro#endif /* NETUNIX */ 62566494Sgshapiro } 62671345Sgshapiro (void) smutex_unlock(&L_Mutex); 62766494Sgshapiro} 62866494Sgshapiro 62990792Sgshapiro/* 63066494Sgshapiro** MI_LISTENER -- Generic listener harness 63166494Sgshapiro** 63264562Sgshapiro** Open up listen port 63364562Sgshapiro** Wait for connections 63464562Sgshapiro** 63564562Sgshapiro** Parameters: 63664562Sgshapiro** conn -- connection description 63764562Sgshapiro** dbg -- debug level 63864562Sgshapiro** smfi -- filter structure to use 63964562Sgshapiro** timeout -- timeout for reads/writes 640125820Sgshapiro** backlog -- listen queue backlog size 64164562Sgshapiro** 64264562Sgshapiro** Returns: 64364562Sgshapiro** MI_SUCCESS -- Exited normally 64464562Sgshapiro** (session finished or we were told to exit) 64564562Sgshapiro** MI_FAILURE -- Network initialization failed. 64664562Sgshapiro*/ 64764562Sgshapiro 64890792Sgshapiro#if BROKEN_PTHREAD_SLEEP 64973188Sgshapiro 65073188Sgshapiro/* 65173188Sgshapiro** Solaris 2.6, perhaps others, gets an internal threads library panic 65273188Sgshapiro** when sleep() is used: 65373188Sgshapiro** 65473188Sgshapiro** thread_create() failed, returned 11 (EINVAL) 65573188Sgshapiro** co_enable, thr_create() returned error = 24 65673188Sgshapiro** libthread panic: co_enable failed (PID: 17793 LWP 1) 65773188Sgshapiro** stacktrace: 65873188Sgshapiro** ef526b10 65973188Sgshapiro** ef52646c 66073188Sgshapiro** ef534cbc 66173188Sgshapiro** 156a4 66273188Sgshapiro** 14644 66373188Sgshapiro** 1413c 66473188Sgshapiro** 135e0 66573188Sgshapiro** 0 66673188Sgshapiro*/ 66773188Sgshapiro 66890792Sgshapiro# define MI_SLEEP(s) \ 66973188Sgshapiro{ \ 67073188Sgshapiro int rs = 0; \ 67173188Sgshapiro struct timeval st; \ 67273188Sgshapiro \ 67373188Sgshapiro st.tv_sec = (s); \ 67473188Sgshapiro st.tv_usec = 0; \ 67573188Sgshapiro if (st.tv_sec > 0) \ 67673188Sgshapiro { \ 677102528Sgshapiro for (;;) \ 678102528Sgshapiro { \ 679102528Sgshapiro rs = select(0, NULL, NULL, NULL, &st); \ 680102528Sgshapiro if (rs < 0 && errno == EINTR) \ 681102528Sgshapiro continue; \ 682102528Sgshapiro if (rs != 0) \ 683102528Sgshapiro { \ 684102528Sgshapiro smi_log(SMI_LOG_ERR, \ 685110560Sgshapiro "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \ 686102528Sgshapiro rs, errno); \ 687102528Sgshapiro } \ 688110560Sgshapiro break; \ 689102528Sgshapiro } \ 69073188Sgshapiro } \ 69173188Sgshapiro} 69290792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */ 69390792Sgshapiro# define MI_SLEEP(s) sleep((s)) 69490792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */ 69573188Sgshapiro 69664562Sgshapiroint 69766494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog) 69864562Sgshapiro char *conn; 69964562Sgshapiro int dbg; 70064562Sgshapiro smfiDesc_ptr smfi; 70164562Sgshapiro time_t timeout; 70266494Sgshapiro int backlog; 70364562Sgshapiro{ 70466494Sgshapiro socket_t connfd = INVALID_SOCKET; 705132943Sgshapiro#if _FFR_DUP_FD 706132943Sgshapiro socket_t dupfd = INVALID_SOCKET; 707132943Sgshapiro#endif /* _FFR_DUP_FD */ 70864562Sgshapiro int sockopt = 1; 709120256Sgshapiro int r, mistop; 71064562Sgshapiro int ret = MI_SUCCESS; 71190792Sgshapiro int mcnt = 0; /* error count for malloc() failures */ 71290792Sgshapiro int tcnt = 0; /* error count for thread_create() failures */ 71390792Sgshapiro int acnt = 0; /* error count for accept() failures */ 71490792Sgshapiro int scnt = 0; /* error count for select() failures */ 71577349Sgshapiro int save_errno = 0; 71664562Sgshapiro sthread_t thread_id; 71764562Sgshapiro _SOCK_ADDR cliaddr; 71864562Sgshapiro SOCKADDR_LEN_T clilen; 71964562Sgshapiro SMFICTX_PTR ctx; 720111823Sgshapiro FD_RD_VAR(rds, excs); 72164562Sgshapiro struct timeval chktime; 72264562Sgshapiro 723125820Sgshapiro if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE) 72464562Sgshapiro return MI_FAILURE; 72571345Sgshapiro 72698121Sgshapiro clilen = L_socksize; 727120256Sgshapiro while ((mistop = mi_stop()) == MILTER_CONT) 72864562Sgshapiro { 72971345Sgshapiro (void) smutex_lock(&L_Mutex); 73071345Sgshapiro if (!ValidSocket(listenfd)) 73171345Sgshapiro { 732120256Sgshapiro ret = MI_FAILURE; 733120256Sgshapiro smi_log(SMI_LOG_ERR, 734120256Sgshapiro "%s: listenfd=%d corrupted, terminating, errno=%d", 735120256Sgshapiro smfi->xxfi_name, listenfd, errno); 73671345Sgshapiro (void) smutex_unlock(&L_Mutex); 73771345Sgshapiro break; 73871345Sgshapiro } 73971345Sgshapiro 74064562Sgshapiro /* select on interface ports */ 741111823Sgshapiro FD_RD_INIT(listenfd, rds, excs); 74264562Sgshapiro chktime.tv_sec = MI_CHK_TIME; 74364562Sgshapiro chktime.tv_usec = 0; 744111823Sgshapiro r = FD_RD_READY(listenfd, rds, excs, &chktime); 74564562Sgshapiro if (r == 0) /* timeout */ 74671345Sgshapiro { 74771345Sgshapiro (void) smutex_unlock(&L_Mutex); 74864562Sgshapiro continue; /* just check mi_stop() */ 74971345Sgshapiro } 75064562Sgshapiro if (r < 0) 75164562Sgshapiro { 75277349Sgshapiro save_errno = errno; 75371345Sgshapiro (void) smutex_unlock(&L_Mutex); 75477349Sgshapiro if (save_errno == EINTR) 75564562Sgshapiro continue; 75690792Sgshapiro scnt++; 75790792Sgshapiro smi_log(SMI_LOG_ERR, 75890792Sgshapiro "%s: select() failed (%s), %s", 75990792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 76090792Sgshapiro scnt >= MAX_FAILS_S ? "abort" : "try again"); 76190792Sgshapiro MI_SLEEP(scnt); 76290792Sgshapiro if (scnt >= MAX_FAILS_S) 76390792Sgshapiro { 76490792Sgshapiro ret = MI_FAILURE; 76590792Sgshapiro break; 76690792Sgshapiro } 76790792Sgshapiro continue; 76864562Sgshapiro } 769111823Sgshapiro if (!FD_IS_RD_RDY(listenfd, rds, excs)) 77064562Sgshapiro { 77164562Sgshapiro /* some error: just stop for now... */ 77264562Sgshapiro ret = MI_FAILURE; 77371345Sgshapiro (void) smutex_unlock(&L_Mutex); 77490792Sgshapiro smi_log(SMI_LOG_ERR, 775111823Sgshapiro "%s: %s() returned exception for socket, abort", 776111823Sgshapiro smfi->xxfi_name, MI_POLLSELECT); 77764562Sgshapiro break; 77864562Sgshapiro } 77990792Sgshapiro scnt = 0; /* reset error counter for select() */ 78064562Sgshapiro 781120256Sgshapiro (void) memset(&cliaddr, '\0', sizeof cliaddr); 78264562Sgshapiro connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 78364562Sgshapiro &clilen); 78477349Sgshapiro save_errno = errno; 78571345Sgshapiro (void) smutex_unlock(&L_Mutex); 78664562Sgshapiro 78773188Sgshapiro /* 78873188Sgshapiro ** If remote side closes before 78973188Sgshapiro ** accept() finishes, sockaddr 79073188Sgshapiro ** might not be fully filled in. 79173188Sgshapiro */ 79273188Sgshapiro 79373188Sgshapiro if (ValidSocket(connfd) && 79473188Sgshapiro (clilen == 0 || 79573188Sgshapiro# ifdef BSD4_4_SOCKADDR 79673188Sgshapiro cliaddr.sa.sa_len == 0 || 79773188Sgshapiro# endif /* BSD4_4_SOCKADDR */ 79898121Sgshapiro cliaddr.sa.sa_family != L_family)) 79973188Sgshapiro { 80090792Sgshapiro (void) closesocket(connfd); 80173188Sgshapiro connfd = INVALID_SOCKET; 80277349Sgshapiro save_errno = EINVAL; 80373188Sgshapiro } 80473188Sgshapiro 805132943Sgshapiro#if !SM_CONF_POLL 806110560Sgshapiro /* check if acceptable for select() */ 807110560Sgshapiro if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd)) 808110560Sgshapiro { 809110560Sgshapiro (void) closesocket(connfd); 810110560Sgshapiro connfd = INVALID_SOCKET; 811110560Sgshapiro save_errno = ERANGE; 812110560Sgshapiro } 813132943Sgshapiro#endif /* !SM_CONF_POLL */ 814110560Sgshapiro 81566494Sgshapiro if (!ValidSocket(connfd)) 81664562Sgshapiro { 817132943Sgshapiro if (save_errno == EINTR 818132943Sgshapiro#ifdef EAGAIN 819132943Sgshapiro || save_errno == EAGAIN 820132943Sgshapiro#endif /* EAGAIN */ 821132943Sgshapiro#ifdef ECONNABORTED 822132943Sgshapiro || save_errno == ECONNABORTED 823132943Sgshapiro#endif /* ECONNABORTED */ 824132943Sgshapiro#ifdef EMFILE 825132943Sgshapiro || save_errno == EMFILE 826132943Sgshapiro#endif /* EMFILE */ 827132943Sgshapiro#ifdef ENFILE 828132943Sgshapiro || save_errno == ENFILE 829132943Sgshapiro#endif /* ENFILE */ 830132943Sgshapiro#ifdef ENOBUFS 831132943Sgshapiro || save_errno == ENOBUFS 832132943Sgshapiro#endif /* ENOBUFS */ 833132943Sgshapiro#ifdef ENOMEM 834132943Sgshapiro || save_errno == ENOMEM 835132943Sgshapiro#endif /* ENOMEM */ 836132943Sgshapiro#ifdef ENOSR 837132943Sgshapiro || save_errno == ENOSR 838132943Sgshapiro#endif /* ENOSR */ 839132943Sgshapiro#ifdef EWOULDBLOCK 840132943Sgshapiro || save_errno == EWOULDBLOCK 841132943Sgshapiro#endif /* EWOULDBLOCK */ 842132943Sgshapiro ) 84377349Sgshapiro continue; 84477349Sgshapiro acnt++; 84590792Sgshapiro smi_log(SMI_LOG_ERR, 84690792Sgshapiro "%s: accept() returned invalid socket (%s), %s", 84790792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 84890792Sgshapiro acnt >= MAX_FAILS_A ? "abort" : "try again"); 84977349Sgshapiro MI_SLEEP(acnt); 85077349Sgshapiro if (acnt >= MAX_FAILS_A) 85177349Sgshapiro { 85277349Sgshapiro ret = MI_FAILURE; 85377349Sgshapiro break; 85477349Sgshapiro } 85564562Sgshapiro continue; 85664562Sgshapiro } 85790792Sgshapiro acnt = 0; /* reset error counter for accept() */ 858132943Sgshapiro#if _FFR_DUP_FD 859132943Sgshapiro dupfd = fcntl(connfd, F_DUPFD, 256); 860132943Sgshapiro if (ValidSocket(dupfd) 861132943Sgshapiro# if !SM_CONF_POLL 862132943Sgshapiro && SM_FD_OK_SELECT(dupfd) 863132943Sgshapiro# endif /* !SM_CONF_POLL */ 864132943Sgshapiro ) 865132943Sgshapiro { 866132943Sgshapiro close(connfd); 867132943Sgshapiro connfd = dupfd; 868132943Sgshapiro dupfd = INVALID_SOCKET; 869132943Sgshapiro } 870132943Sgshapiro#endif /* _FFR_DUP_FD */ 87164562Sgshapiro 87264562Sgshapiro if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 87364562Sgshapiro (void *) &sockopt, sizeof sockopt) < 0) 87464562Sgshapiro { 87590792Sgshapiro smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)", 87690792Sgshapiro smfi->xxfi_name, sm_errstring(errno)); 87764562Sgshapiro /* XXX: continue? */ 87864562Sgshapiro } 87964562Sgshapiro if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 88064562Sgshapiro { 88190792Sgshapiro (void) closesocket(connfd); 88273188Sgshapiro mcnt++; 88390792Sgshapiro smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s", 88490792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 88590792Sgshapiro mcnt >= MAX_FAILS_M ? "abort" : "try again"); 88673188Sgshapiro MI_SLEEP(mcnt); 88773188Sgshapiro if (mcnt >= MAX_FAILS_M) 88864562Sgshapiro { 88964562Sgshapiro ret = MI_FAILURE; 89064562Sgshapiro break; 89164562Sgshapiro } 89264562Sgshapiro continue; 89364562Sgshapiro } 89490792Sgshapiro mcnt = 0; /* reset error counter for malloc() */ 895120256Sgshapiro (void) memset(ctx, '\0', sizeof *ctx); 89664562Sgshapiro ctx->ctx_sd = connfd; 89764562Sgshapiro ctx->ctx_dbg = dbg; 89864562Sgshapiro ctx->ctx_timeout = timeout; 89964562Sgshapiro ctx->ctx_smfi = smfi; 90064562Sgshapiro#if 0 90164562Sgshapiro if (smfi->xxfi_eoh == NULL) 90264562Sgshapiro if (smfi->xxfi_eom == NULL) 90364562Sgshapiro if (smfi->xxfi_abort == NULL) 90464562Sgshapiro if (smfi->xxfi_close == NULL) 90564562Sgshapiro#endif /* 0 */ 90664562Sgshapiro if (smfi->xxfi_connect == NULL) 90764562Sgshapiro ctx->ctx_pflags |= SMFIP_NOCONNECT; 90864562Sgshapiro if (smfi->xxfi_helo == NULL) 90964562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHELO; 91064562Sgshapiro if (smfi->xxfi_envfrom == NULL) 91164562Sgshapiro ctx->ctx_pflags |= SMFIP_NOMAIL; 91264562Sgshapiro if (smfi->xxfi_envrcpt == NULL) 91364562Sgshapiro ctx->ctx_pflags |= SMFIP_NORCPT; 91464562Sgshapiro if (smfi->xxfi_header == NULL) 91564562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHDRS; 91664562Sgshapiro if (smfi->xxfi_eoh == NULL) 91764562Sgshapiro ctx->ctx_pflags |= SMFIP_NOEOH; 91864562Sgshapiro if (smfi->xxfi_body == NULL) 91964562Sgshapiro ctx->ctx_pflags |= SMFIP_NOBODY; 92064562Sgshapiro 92164562Sgshapiro if ((r = thread_create(&thread_id, 92264562Sgshapiro mi_thread_handle_wrapper, 92371345Sgshapiro (void *) ctx)) != 0) 92464562Sgshapiro { 92590792Sgshapiro tcnt++; 92664562Sgshapiro smi_log(SMI_LOG_ERR, 92790792Sgshapiro "%s: thread_create() failed: %d, %s", 92890792Sgshapiro smfi->xxfi_name, r, 92990792Sgshapiro tcnt >= MAX_FAILS_T ? "abort" : "try again"); 93073188Sgshapiro MI_SLEEP(tcnt); 93190792Sgshapiro (void) closesocket(connfd); 93264562Sgshapiro free(ctx); 93373188Sgshapiro if (tcnt >= MAX_FAILS_T) 93464562Sgshapiro { 93564562Sgshapiro ret = MI_FAILURE; 93664562Sgshapiro break; 93764562Sgshapiro } 93864562Sgshapiro continue; 93964562Sgshapiro } 94073188Sgshapiro tcnt = 0; 94164562Sgshapiro } 94264562Sgshapiro if (ret != MI_SUCCESS) 94364562Sgshapiro mi_stop_milters(MILTER_ABRT); 94471345Sgshapiro else 945120256Sgshapiro { 946120256Sgshapiro if (mistop != MILTER_CONT) 947120256Sgshapiro smi_log(SMI_LOG_INFO, "%s: mi_stop=%d", 948120256Sgshapiro smfi->xxfi_name, mistop); 94971345Sgshapiro mi_closener(); 950120256Sgshapiro } 95171345Sgshapiro (void) smutex_destroy(&L_Mutex); 95264562Sgshapiro return ret; 95364562Sgshapiro} 954