listener.c revision 157001
164562Sgshapiro/* 2157001Sgshapiro * Copyright (c) 1999-2006 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> 12157001SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.115 2006/01/24 00:48:39 ca 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 461157001Sgshapiro if ( 462157001Sgshapiro#if NETUNIX 463157001Sgshapiro addr.sa.sa_family != AF_UNIX && 464157001Sgshapiro#endif /* NETUNIX */ 465157001Sgshapiro setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 46664562Sgshapiro sizeof(sockopt)) == -1) 46764562Sgshapiro { 46864562Sgshapiro smi_log(SMI_LOG_ERR, 469157001Sgshapiro "%s: set reuseaddr failed (%s)", name, 47090792Sgshapiro sm_errstring(errno)); 47190792Sgshapiro (void) closesocket(sock); 47266494Sgshapiro return INVALID_SOCKET; 47364562Sgshapiro } 47464562Sgshapiro 475125820Sgshapiro#if NETUNIX 476125820Sgshapiro if (addr.sa.sa_family == AF_UNIX && rmsocket) 477125820Sgshapiro { 478125820Sgshapiro struct stat s; 479125820Sgshapiro 480125820Sgshapiro if (stat(colon, &s) != 0) 481125820Sgshapiro { 482125820Sgshapiro if (errno != ENOENT) 483125820Sgshapiro { 484125820Sgshapiro smi_log(SMI_LOG_ERR, 485125820Sgshapiro "%s: Unable to stat() %s: %s", 486125820Sgshapiro name, colon, sm_errstring(errno)); 487125820Sgshapiro (void) closesocket(sock); 488125820Sgshapiro return INVALID_SOCKET; 489125820Sgshapiro } 490125820Sgshapiro } 491125820Sgshapiro else if (!S_ISSOCK(s.st_mode)) 492125820Sgshapiro { 493125820Sgshapiro smi_log(SMI_LOG_ERR, 494125820Sgshapiro "%s: %s is not a UNIX domain socket", 495125820Sgshapiro name, colon); 496125820Sgshapiro (void) closesocket(sock); 497125820Sgshapiro return INVALID_SOCKET; 498125820Sgshapiro } 499125820Sgshapiro else if (unlink(colon) != 0) 500125820Sgshapiro { 501125820Sgshapiro smi_log(SMI_LOG_ERR, 502125820Sgshapiro "%s: Unable to remove %s: %s", 503125820Sgshapiro name, colon, sm_errstring(errno)); 504125820Sgshapiro (void) closesocket(sock); 505125820Sgshapiro return INVALID_SOCKET; 506125820Sgshapiro } 507125820Sgshapiro } 508125820Sgshapiro#endif /* NETUNIX */ 509125820Sgshapiro 51098121Sgshapiro if (bind(sock, &addr.sa, L_socksize) < 0) 51164562Sgshapiro { 51264562Sgshapiro smi_log(SMI_LOG_ERR, 51364562Sgshapiro "%s: Unable to bind to port %s: %s", 51490792Sgshapiro name, conn, sm_errstring(errno)); 51590792Sgshapiro (void) closesocket(sock); 51666494Sgshapiro return INVALID_SOCKET; 51764562Sgshapiro } 51864562Sgshapiro 51964562Sgshapiro if (listen(sock, backlog) < 0) 52064562Sgshapiro { 52164562Sgshapiro smi_log(SMI_LOG_ERR, 52290792Sgshapiro "%s: listen call failed: %s", name, 52390792Sgshapiro sm_errstring(errno)); 52490792Sgshapiro (void) closesocket(sock); 52566494Sgshapiro return INVALID_SOCKET; 52664562Sgshapiro } 52790792Sgshapiro 52890792Sgshapiro#if NETUNIX 52990792Sgshapiro if (addr.sa.sa_family == AF_UNIX && len > 0) 53090792Sgshapiro { 53190792Sgshapiro /* 53290792Sgshapiro ** Set global variable sockpath so the UNIX socket can be 53390792Sgshapiro ** unlink()ed at exit. 53490792Sgshapiro */ 53590792Sgshapiro 53690792Sgshapiro sockpath = (char *) malloc(len); 53790792Sgshapiro if (sockpath != NULL) 53890792Sgshapiro (void) sm_strlcpy(sockpath, colon, len); 53990792Sgshapiro else 54090792Sgshapiro { 54190792Sgshapiro smi_log(SMI_LOG_ERR, 54290792Sgshapiro "%s: can't malloc(%d) for sockpath: %s", 543110560Sgshapiro name, (int) len, sm_errstring(errno)); 54490792Sgshapiro (void) closesocket(sock); 54590792Sgshapiro return INVALID_SOCKET; 54690792Sgshapiro } 54790792Sgshapiro } 54890792Sgshapiro#endif /* NETUNIX */ 54998121Sgshapiro L_family = addr.sa.sa_family; 55064562Sgshapiro return sock; 55164562Sgshapiro} 55290792Sgshapiro/* 55364562Sgshapiro** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 55464562Sgshapiro** 55564562Sgshapiro** Parameters: 55664562Sgshapiro** arg -- argument to pass to mi_handle_session() 55764562Sgshapiro** 55864562Sgshapiro** Returns: 55964562Sgshapiro** results from mi_handle_session() 56064562Sgshapiro*/ 56164562Sgshapiro 562141858Sgshapirostatic void * 56364562Sgshapiromi_thread_handle_wrapper(arg) 56464562Sgshapiro void *arg; 56564562Sgshapiro{ 56664562Sgshapiro return (void *) mi_handle_session(arg); 56764562Sgshapiro} 56864562Sgshapiro 56990792Sgshapiro/* 57066494Sgshapiro** MI_CLOSENER -- close listen socket 57164562Sgshapiro** 57266494Sgshapiro** Parameters: 57366494Sgshapiro** none. 57466494Sgshapiro** 57566494Sgshapiro** Returns: 57666494Sgshapiro** none. 57766494Sgshapiro*/ 57866494Sgshapiro 57966494Sgshapirovoid 58066494Sgshapiromi_closener() 58166494Sgshapiro{ 58271345Sgshapiro (void) smutex_lock(&L_Mutex); 58366494Sgshapiro if (ValidSocket(listenfd)) 58466494Sgshapiro { 58590792Sgshapiro#if NETUNIX 58690792Sgshapiro bool removable; 58790792Sgshapiro struct stat sockinfo; 58890792Sgshapiro struct stat fileinfo; 58990792Sgshapiro 59090792Sgshapiro removable = sockpath != NULL && 59190792Sgshapiro geteuid() != 0 && 59290792Sgshapiro fstat(listenfd, &sockinfo) == 0 && 59390792Sgshapiro (S_ISFIFO(sockinfo.st_mode) 59490792Sgshapiro# ifdef S_ISSOCK 59590792Sgshapiro || S_ISSOCK(sockinfo.st_mode) 59690792Sgshapiro# endif /* S_ISSOCK */ 59790792Sgshapiro ); 59890792Sgshapiro#endif /* NETUNIX */ 59990792Sgshapiro 60090792Sgshapiro (void) closesocket(listenfd); 60166494Sgshapiro listenfd = INVALID_SOCKET; 60290792Sgshapiro 60390792Sgshapiro#if NETUNIX 60490792Sgshapiro /* XXX sleep() some time before doing this? */ 60590792Sgshapiro if (sockpath != NULL) 60690792Sgshapiro { 60790792Sgshapiro if (removable && 60890792Sgshapiro stat(sockpath, &fileinfo) == 0 && 60990792Sgshapiro ((fileinfo.st_dev == sockinfo.st_dev && 61090792Sgshapiro fileinfo.st_ino == sockinfo.st_ino) 61190792Sgshapiro# ifdef S_ISSOCK 61290792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61390792Sgshapiro# endif /* S_ISSOCK */ 61490792Sgshapiro ) 61590792Sgshapiro && 61690792Sgshapiro (S_ISFIFO(fileinfo.st_mode) 61790792Sgshapiro# ifdef S_ISSOCK 61890792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61990792Sgshapiro# endif /* S_ISSOCK */ 62090792Sgshapiro )) 62190792Sgshapiro (void) unlink(sockpath); 62290792Sgshapiro free(sockpath); 62390792Sgshapiro sockpath = NULL; 62490792Sgshapiro } 62590792Sgshapiro#endif /* NETUNIX */ 62666494Sgshapiro } 62771345Sgshapiro (void) smutex_unlock(&L_Mutex); 62866494Sgshapiro} 62966494Sgshapiro 63090792Sgshapiro/* 63166494Sgshapiro** MI_LISTENER -- Generic listener harness 63266494Sgshapiro** 63364562Sgshapiro** Open up listen port 63464562Sgshapiro** Wait for connections 63564562Sgshapiro** 63664562Sgshapiro** Parameters: 63764562Sgshapiro** conn -- connection description 63864562Sgshapiro** dbg -- debug level 63964562Sgshapiro** smfi -- filter structure to use 64064562Sgshapiro** timeout -- timeout for reads/writes 641125820Sgshapiro** backlog -- listen queue backlog size 64264562Sgshapiro** 64364562Sgshapiro** Returns: 64464562Sgshapiro** MI_SUCCESS -- Exited normally 64564562Sgshapiro** (session finished or we were told to exit) 64664562Sgshapiro** MI_FAILURE -- Network initialization failed. 64764562Sgshapiro*/ 64864562Sgshapiro 64990792Sgshapiro#if BROKEN_PTHREAD_SLEEP 65073188Sgshapiro 65173188Sgshapiro/* 65273188Sgshapiro** Solaris 2.6, perhaps others, gets an internal threads library panic 65373188Sgshapiro** when sleep() is used: 65473188Sgshapiro** 65573188Sgshapiro** thread_create() failed, returned 11 (EINVAL) 65673188Sgshapiro** co_enable, thr_create() returned error = 24 65773188Sgshapiro** libthread panic: co_enable failed (PID: 17793 LWP 1) 65873188Sgshapiro** stacktrace: 65973188Sgshapiro** ef526b10 66073188Sgshapiro** ef52646c 66173188Sgshapiro** ef534cbc 66273188Sgshapiro** 156a4 66373188Sgshapiro** 14644 66473188Sgshapiro** 1413c 66573188Sgshapiro** 135e0 66673188Sgshapiro** 0 66773188Sgshapiro*/ 66873188Sgshapiro 66990792Sgshapiro# define MI_SLEEP(s) \ 67073188Sgshapiro{ \ 67173188Sgshapiro int rs = 0; \ 67273188Sgshapiro struct timeval st; \ 67373188Sgshapiro \ 67473188Sgshapiro st.tv_sec = (s); \ 67573188Sgshapiro st.tv_usec = 0; \ 67673188Sgshapiro if (st.tv_sec > 0) \ 67773188Sgshapiro { \ 678102528Sgshapiro for (;;) \ 679102528Sgshapiro { \ 680102528Sgshapiro rs = select(0, NULL, NULL, NULL, &st); \ 681102528Sgshapiro if (rs < 0 && errno == EINTR) \ 682102528Sgshapiro continue; \ 683102528Sgshapiro if (rs != 0) \ 684102528Sgshapiro { \ 685102528Sgshapiro smi_log(SMI_LOG_ERR, \ 686110560Sgshapiro "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \ 687102528Sgshapiro rs, errno); \ 688102528Sgshapiro } \ 689110560Sgshapiro break; \ 690102528Sgshapiro } \ 69173188Sgshapiro } \ 69273188Sgshapiro} 69390792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */ 69490792Sgshapiro# define MI_SLEEP(s) sleep((s)) 69590792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */ 69673188Sgshapiro 69764562Sgshapiroint 69866494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog) 69964562Sgshapiro char *conn; 70064562Sgshapiro int dbg; 70164562Sgshapiro smfiDesc_ptr smfi; 70264562Sgshapiro time_t timeout; 70366494Sgshapiro int backlog; 70464562Sgshapiro{ 70566494Sgshapiro socket_t connfd = INVALID_SOCKET; 706132943Sgshapiro#if _FFR_DUP_FD 707132943Sgshapiro socket_t dupfd = INVALID_SOCKET; 708132943Sgshapiro#endif /* _FFR_DUP_FD */ 70964562Sgshapiro int sockopt = 1; 710120256Sgshapiro int r, mistop; 71164562Sgshapiro int ret = MI_SUCCESS; 71290792Sgshapiro int mcnt = 0; /* error count for malloc() failures */ 71390792Sgshapiro int tcnt = 0; /* error count for thread_create() failures */ 71490792Sgshapiro int acnt = 0; /* error count for accept() failures */ 71590792Sgshapiro int scnt = 0; /* error count for select() failures */ 71677349Sgshapiro int save_errno = 0; 71764562Sgshapiro sthread_t thread_id; 71864562Sgshapiro _SOCK_ADDR cliaddr; 71964562Sgshapiro SOCKADDR_LEN_T clilen; 72064562Sgshapiro SMFICTX_PTR ctx; 721111823Sgshapiro FD_RD_VAR(rds, excs); 72264562Sgshapiro struct timeval chktime; 72364562Sgshapiro 724125820Sgshapiro if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE) 72564562Sgshapiro return MI_FAILURE; 72671345Sgshapiro 72798121Sgshapiro clilen = L_socksize; 728120256Sgshapiro while ((mistop = mi_stop()) == MILTER_CONT) 72964562Sgshapiro { 73071345Sgshapiro (void) smutex_lock(&L_Mutex); 73171345Sgshapiro if (!ValidSocket(listenfd)) 73271345Sgshapiro { 733120256Sgshapiro ret = MI_FAILURE; 734120256Sgshapiro smi_log(SMI_LOG_ERR, 735120256Sgshapiro "%s: listenfd=%d corrupted, terminating, errno=%d", 736120256Sgshapiro smfi->xxfi_name, listenfd, errno); 73771345Sgshapiro (void) smutex_unlock(&L_Mutex); 73871345Sgshapiro break; 73971345Sgshapiro } 74071345Sgshapiro 74164562Sgshapiro /* select on interface ports */ 742111823Sgshapiro FD_RD_INIT(listenfd, rds, excs); 74364562Sgshapiro chktime.tv_sec = MI_CHK_TIME; 74464562Sgshapiro chktime.tv_usec = 0; 745111823Sgshapiro r = FD_RD_READY(listenfd, rds, excs, &chktime); 74664562Sgshapiro if (r == 0) /* timeout */ 74771345Sgshapiro { 74871345Sgshapiro (void) smutex_unlock(&L_Mutex); 74964562Sgshapiro continue; /* just check mi_stop() */ 75071345Sgshapiro } 75164562Sgshapiro if (r < 0) 75264562Sgshapiro { 75377349Sgshapiro save_errno = errno; 75471345Sgshapiro (void) smutex_unlock(&L_Mutex); 75577349Sgshapiro if (save_errno == EINTR) 75664562Sgshapiro continue; 75790792Sgshapiro scnt++; 75890792Sgshapiro smi_log(SMI_LOG_ERR, 75990792Sgshapiro "%s: select() failed (%s), %s", 76090792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 76190792Sgshapiro scnt >= MAX_FAILS_S ? "abort" : "try again"); 76290792Sgshapiro MI_SLEEP(scnt); 76390792Sgshapiro if (scnt >= MAX_FAILS_S) 76490792Sgshapiro { 76590792Sgshapiro ret = MI_FAILURE; 76690792Sgshapiro break; 76790792Sgshapiro } 76890792Sgshapiro continue; 76964562Sgshapiro } 770111823Sgshapiro if (!FD_IS_RD_RDY(listenfd, rds, excs)) 77164562Sgshapiro { 77264562Sgshapiro /* some error: just stop for now... */ 77364562Sgshapiro ret = MI_FAILURE; 77471345Sgshapiro (void) smutex_unlock(&L_Mutex); 77590792Sgshapiro smi_log(SMI_LOG_ERR, 776111823Sgshapiro "%s: %s() returned exception for socket, abort", 777111823Sgshapiro smfi->xxfi_name, MI_POLLSELECT); 77864562Sgshapiro break; 77964562Sgshapiro } 78090792Sgshapiro scnt = 0; /* reset error counter for select() */ 78164562Sgshapiro 782120256Sgshapiro (void) memset(&cliaddr, '\0', sizeof cliaddr); 78364562Sgshapiro connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 78464562Sgshapiro &clilen); 78577349Sgshapiro save_errno = errno; 78671345Sgshapiro (void) smutex_unlock(&L_Mutex); 78764562Sgshapiro 78873188Sgshapiro /* 78973188Sgshapiro ** If remote side closes before 79073188Sgshapiro ** accept() finishes, sockaddr 79173188Sgshapiro ** might not be fully filled in. 79273188Sgshapiro */ 79373188Sgshapiro 79473188Sgshapiro if (ValidSocket(connfd) && 79573188Sgshapiro (clilen == 0 || 79673188Sgshapiro# ifdef BSD4_4_SOCKADDR 79773188Sgshapiro cliaddr.sa.sa_len == 0 || 79873188Sgshapiro# endif /* BSD4_4_SOCKADDR */ 79998121Sgshapiro cliaddr.sa.sa_family != L_family)) 80073188Sgshapiro { 80190792Sgshapiro (void) closesocket(connfd); 80273188Sgshapiro connfd = INVALID_SOCKET; 80377349Sgshapiro save_errno = EINVAL; 80473188Sgshapiro } 80573188Sgshapiro 806132943Sgshapiro#if !SM_CONF_POLL 807110560Sgshapiro /* check if acceptable for select() */ 808110560Sgshapiro if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd)) 809110560Sgshapiro { 810110560Sgshapiro (void) closesocket(connfd); 811110560Sgshapiro connfd = INVALID_SOCKET; 812110560Sgshapiro save_errno = ERANGE; 813110560Sgshapiro } 814132943Sgshapiro#endif /* !SM_CONF_POLL */ 815110560Sgshapiro 81666494Sgshapiro if (!ValidSocket(connfd)) 81764562Sgshapiro { 818132943Sgshapiro if (save_errno == EINTR 819132943Sgshapiro#ifdef EAGAIN 820132943Sgshapiro || save_errno == EAGAIN 821132943Sgshapiro#endif /* EAGAIN */ 822132943Sgshapiro#ifdef ECONNABORTED 823132943Sgshapiro || save_errno == ECONNABORTED 824132943Sgshapiro#endif /* ECONNABORTED */ 825132943Sgshapiro#ifdef EMFILE 826132943Sgshapiro || save_errno == EMFILE 827132943Sgshapiro#endif /* EMFILE */ 828132943Sgshapiro#ifdef ENFILE 829132943Sgshapiro || save_errno == ENFILE 830132943Sgshapiro#endif /* ENFILE */ 831132943Sgshapiro#ifdef ENOBUFS 832132943Sgshapiro || save_errno == ENOBUFS 833132943Sgshapiro#endif /* ENOBUFS */ 834132943Sgshapiro#ifdef ENOMEM 835132943Sgshapiro || save_errno == ENOMEM 836132943Sgshapiro#endif /* ENOMEM */ 837132943Sgshapiro#ifdef ENOSR 838132943Sgshapiro || save_errno == ENOSR 839132943Sgshapiro#endif /* ENOSR */ 840132943Sgshapiro#ifdef EWOULDBLOCK 841132943Sgshapiro || save_errno == EWOULDBLOCK 842132943Sgshapiro#endif /* EWOULDBLOCK */ 843132943Sgshapiro ) 84477349Sgshapiro continue; 84577349Sgshapiro acnt++; 84690792Sgshapiro smi_log(SMI_LOG_ERR, 84790792Sgshapiro "%s: accept() returned invalid socket (%s), %s", 84890792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 84990792Sgshapiro acnt >= MAX_FAILS_A ? "abort" : "try again"); 85077349Sgshapiro MI_SLEEP(acnt); 85177349Sgshapiro if (acnt >= MAX_FAILS_A) 85277349Sgshapiro { 85377349Sgshapiro ret = MI_FAILURE; 85477349Sgshapiro break; 85577349Sgshapiro } 85664562Sgshapiro continue; 85764562Sgshapiro } 85890792Sgshapiro acnt = 0; /* reset error counter for accept() */ 859132943Sgshapiro#if _FFR_DUP_FD 860132943Sgshapiro dupfd = fcntl(connfd, F_DUPFD, 256); 861132943Sgshapiro if (ValidSocket(dupfd) 862132943Sgshapiro# if !SM_CONF_POLL 863132943Sgshapiro && SM_FD_OK_SELECT(dupfd) 864132943Sgshapiro# endif /* !SM_CONF_POLL */ 865132943Sgshapiro ) 866132943Sgshapiro { 867132943Sgshapiro close(connfd); 868132943Sgshapiro connfd = dupfd; 869132943Sgshapiro dupfd = INVALID_SOCKET; 870132943Sgshapiro } 871132943Sgshapiro#endif /* _FFR_DUP_FD */ 87264562Sgshapiro 87364562Sgshapiro if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 87464562Sgshapiro (void *) &sockopt, sizeof sockopt) < 0) 87564562Sgshapiro { 876157001Sgshapiro smi_log(SMI_LOG_WARN, 877157001Sgshapiro "%s: set keepalive failed (%s)", 87890792Sgshapiro smfi->xxfi_name, sm_errstring(errno)); 87964562Sgshapiro /* XXX: continue? */ 88064562Sgshapiro } 88164562Sgshapiro if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 88264562Sgshapiro { 88390792Sgshapiro (void) closesocket(connfd); 88473188Sgshapiro mcnt++; 88590792Sgshapiro smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s", 88690792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 88790792Sgshapiro mcnt >= MAX_FAILS_M ? "abort" : "try again"); 88873188Sgshapiro MI_SLEEP(mcnt); 88973188Sgshapiro if (mcnt >= MAX_FAILS_M) 89064562Sgshapiro { 89164562Sgshapiro ret = MI_FAILURE; 89264562Sgshapiro break; 89364562Sgshapiro } 89464562Sgshapiro continue; 89564562Sgshapiro } 89690792Sgshapiro mcnt = 0; /* reset error counter for malloc() */ 897120256Sgshapiro (void) memset(ctx, '\0', sizeof *ctx); 89864562Sgshapiro ctx->ctx_sd = connfd; 89964562Sgshapiro ctx->ctx_dbg = dbg; 90064562Sgshapiro ctx->ctx_timeout = timeout; 90164562Sgshapiro ctx->ctx_smfi = smfi; 90264562Sgshapiro#if 0 90364562Sgshapiro if (smfi->xxfi_eoh == NULL) 90464562Sgshapiro if (smfi->xxfi_eom == NULL) 90564562Sgshapiro if (smfi->xxfi_abort == NULL) 90664562Sgshapiro if (smfi->xxfi_close == NULL) 90764562Sgshapiro#endif /* 0 */ 90864562Sgshapiro if (smfi->xxfi_connect == NULL) 90964562Sgshapiro ctx->ctx_pflags |= SMFIP_NOCONNECT; 91064562Sgshapiro if (smfi->xxfi_helo == NULL) 91164562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHELO; 91264562Sgshapiro if (smfi->xxfi_envfrom == NULL) 91364562Sgshapiro ctx->ctx_pflags |= SMFIP_NOMAIL; 91464562Sgshapiro if (smfi->xxfi_envrcpt == NULL) 91564562Sgshapiro ctx->ctx_pflags |= SMFIP_NORCPT; 91664562Sgshapiro if (smfi->xxfi_header == NULL) 91764562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHDRS; 91864562Sgshapiro if (smfi->xxfi_eoh == NULL) 91964562Sgshapiro ctx->ctx_pflags |= SMFIP_NOEOH; 92064562Sgshapiro if (smfi->xxfi_body == NULL) 92164562Sgshapiro ctx->ctx_pflags |= SMFIP_NOBODY; 92264562Sgshapiro 92364562Sgshapiro if ((r = thread_create(&thread_id, 92464562Sgshapiro mi_thread_handle_wrapper, 92571345Sgshapiro (void *) ctx)) != 0) 92664562Sgshapiro { 92790792Sgshapiro tcnt++; 92864562Sgshapiro smi_log(SMI_LOG_ERR, 92990792Sgshapiro "%s: thread_create() failed: %d, %s", 93090792Sgshapiro smfi->xxfi_name, r, 93190792Sgshapiro tcnt >= MAX_FAILS_T ? "abort" : "try again"); 93273188Sgshapiro MI_SLEEP(tcnt); 93390792Sgshapiro (void) closesocket(connfd); 93464562Sgshapiro free(ctx); 93573188Sgshapiro if (tcnt >= MAX_FAILS_T) 93664562Sgshapiro { 93764562Sgshapiro ret = MI_FAILURE; 93864562Sgshapiro break; 93964562Sgshapiro } 94064562Sgshapiro continue; 94164562Sgshapiro } 94273188Sgshapiro tcnt = 0; 94364562Sgshapiro } 94464562Sgshapiro if (ret != MI_SUCCESS) 94564562Sgshapiro mi_stop_milters(MILTER_ABRT); 94671345Sgshapiro else 947120256Sgshapiro { 948120256Sgshapiro if (mistop != MILTER_CONT) 949120256Sgshapiro smi_log(SMI_LOG_INFO, "%s: mi_stop=%d", 950120256Sgshapiro smfi->xxfi_name, mistop); 95171345Sgshapiro mi_closener(); 952120256Sgshapiro } 95371345Sgshapiro (void) smutex_destroy(&L_Mutex); 95464562Sgshapiro return ret; 95564562Sgshapiro} 956