listener.c revision 132943
164562Sgshapiro/* 2111823Sgshapiro * Copyright (c) 1999-2003 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> 12132943SgshapiroSM_RCSID("@(#)$Id: listener.c,v 8.109 2004/02/04 22:55:59 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 *)); 3598121Sgshapiro 3690792Sgshapiro/* 3798121Sgshapiro** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet 3898121Sgshapiro** 39125820Sgshapiro** Parameters: 4098121Sgshapiro** conn -- connection description 4198121Sgshapiro** backlog -- listen backlog 42125820Sgshapiro** dbg -- debug level 43125820Sgshapiro** rmsocket -- if true, try to unlink() the socket first 44132943Sgshapiro** (UNIX domain sockets only) 4598121Sgshapiro** smfi -- filter structure to use 4698121Sgshapiro** 47125820Sgshapiro** Return value: 48125820Sgshapiro** MI_SUCCESS/MI_FAILURE 4998121Sgshapiro*/ 5098121Sgshapiro 5198121Sgshapiroint 52125820Sgshapiromi_opensocket(conn, backlog, dbg, rmsocket, smfi) 5398121Sgshapiro char *conn; 5498121Sgshapiro int backlog; 5598121Sgshapiro int dbg; 56125820Sgshapiro bool rmsocket; 5798121Sgshapiro smfiDesc_ptr smfi; 5898121Sgshapiro{ 5998121Sgshapiro if (smfi == NULL || conn == NULL) 6098121Sgshapiro return MI_FAILURE; 6198121Sgshapiro 6298121Sgshapiro if (ValidSocket(listenfd)) 6398121Sgshapiro return MI_SUCCESS; 6498121Sgshapiro 6598121Sgshapiro if (dbg > 0) 6698121Sgshapiro { 6798121Sgshapiro smi_log(SMI_LOG_DEBUG, 6898121Sgshapiro "%s: Opening listen socket on conn %s", 6998121Sgshapiro smfi->xxfi_name, conn); 7098121Sgshapiro } 7198121Sgshapiro (void) smutex_init(&L_Mutex); 7298121Sgshapiro (void) smutex_lock(&L_Mutex); 73125820Sgshapiro listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name); 7498121Sgshapiro if (!ValidSocket(listenfd)) 7598121Sgshapiro { 7698121Sgshapiro smi_log(SMI_LOG_FATAL, 7798121Sgshapiro "%s: Unable to create listening socket on conn %s", 7898121Sgshapiro smfi->xxfi_name, conn); 7998121Sgshapiro (void) smutex_unlock(&L_Mutex); 8098121Sgshapiro return MI_FAILURE; 8198121Sgshapiro } 82132943Sgshapiro#if !SM_CONF_POLL 83110560Sgshapiro if (!SM_FD_OK_SELECT(listenfd)) 84110560Sgshapiro { 85110560Sgshapiro smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 86110560Sgshapiro smfi->xxfi_name, listenfd, FD_SETSIZE); 87110560Sgshapiro (void) smutex_unlock(&L_Mutex); 88110560Sgshapiro return MI_FAILURE; 89110560Sgshapiro } 90132943Sgshapiro#endif /* !SM_CONF_POLL */ 9198121Sgshapiro return MI_SUCCESS; 9298121Sgshapiro} 9398121Sgshapiro 9498121Sgshapiro/* 9564562Sgshapiro** MI_MILTEROPEN -- setup socket to listen on 9664562Sgshapiro** 9764562Sgshapiro** Parameters: 9864562Sgshapiro** conn -- connection description 9964562Sgshapiro** backlog -- listen backlog 100125820Sgshapiro** rmsocket -- if true, try to unlink() the socket first 101125820Sgshapiro** (UNIX domain sockets only) 10273188Sgshapiro** name -- name for logging 10364562Sgshapiro** 10464562Sgshapiro** Returns: 10564562Sgshapiro** socket upon success, error code otherwise. 10690792Sgshapiro** 10790792Sgshapiro** Side effect: 10890792Sgshapiro** sets sockpath if UNIX socket. 10964562Sgshapiro*/ 11064562Sgshapiro 11190792Sgshapiro#if NETUNIX 11290792Sgshapirostatic char *sockpath = NULL; 11390792Sgshapiro#endif /* NETUNIX */ 11490792Sgshapiro 11566494Sgshapirostatic socket_t 116125820Sgshapiromi_milteropen(conn, backlog, rmsocket, name) 11764562Sgshapiro char *conn; 11864562Sgshapiro int backlog; 119125820Sgshapiro bool rmsocket; 12064562Sgshapiro char *name; 12164562Sgshapiro{ 12266494Sgshapiro socket_t sock; 12364562Sgshapiro int sockopt = 1; 12498121Sgshapiro int fdflags; 12590792Sgshapiro size_t len = 0; 12664562Sgshapiro char *p; 12764562Sgshapiro char *colon; 12864562Sgshapiro char *at; 12964562Sgshapiro SOCKADDR addr; 13064562Sgshapiro 13164562Sgshapiro if (conn == NULL || conn[0] == '\0') 13264562Sgshapiro { 13364562Sgshapiro smi_log(SMI_LOG_ERR, "%s: empty or missing socket information", 13464562Sgshapiro name); 13566494Sgshapiro return INVALID_SOCKET; 13664562Sgshapiro } 13764562Sgshapiro (void) memset(&addr, '\0', sizeof addr); 13864562Sgshapiro 13964562Sgshapiro /* protocol:filename or protocol:port@host */ 14064562Sgshapiro p = conn; 14164562Sgshapiro colon = strchr(p, ':'); 14264562Sgshapiro if (colon != NULL) 14364562Sgshapiro { 14464562Sgshapiro *colon = '\0'; 14564562Sgshapiro 14694334Sgshapiro if (*p == '\0') 14764562Sgshapiro { 14864562Sgshapiro#if NETUNIX 14964562Sgshapiro /* default to AF_UNIX */ 15094334Sgshapiro addr.sa.sa_family = AF_UNIX; 15198121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 15264562Sgshapiro#else /* NETUNIX */ 15364562Sgshapiro# if NETINET 15464562Sgshapiro /* default to AF_INET */ 15564562Sgshapiro addr.sa.sa_family = AF_INET; 15698121Sgshapiro L_socksize = sizeof addr.sin; 15764562Sgshapiro# else /* NETINET */ 15864562Sgshapiro# if NETINET6 15964562Sgshapiro /* default to AF_INET6 */ 16064562Sgshapiro addr.sa.sa_family = AF_INET6; 16198121Sgshapiro L_socksize = sizeof addr.sin6; 16264562Sgshapiro# else /* NETINET6 */ 16364562Sgshapiro /* no protocols available */ 16464562Sgshapiro smi_log(SMI_LOG_ERR, 16564562Sgshapiro "%s: no valid socket protocols available", 16664562Sgshapiro name); 16766494Sgshapiro return INVALID_SOCKET; 16864562Sgshapiro# endif /* NETINET6 */ 16964562Sgshapiro# endif /* NETINET */ 17064562Sgshapiro#endif /* NETUNIX */ 17164562Sgshapiro } 17264562Sgshapiro#if NETUNIX 17364562Sgshapiro else if (strcasecmp(p, "unix") == 0 || 17464562Sgshapiro strcasecmp(p, "local") == 0) 17564562Sgshapiro { 17664562Sgshapiro addr.sa.sa_family = AF_UNIX; 17798121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 17864562Sgshapiro } 17964562Sgshapiro#endif /* NETUNIX */ 18064562Sgshapiro#if NETINET 18164562Sgshapiro else if (strcasecmp(p, "inet") == 0) 18264562Sgshapiro { 18364562Sgshapiro addr.sa.sa_family = AF_INET; 18498121Sgshapiro L_socksize = sizeof addr.sin; 18564562Sgshapiro } 18664562Sgshapiro#endif /* NETINET */ 18764562Sgshapiro#if NETINET6 18864562Sgshapiro else if (strcasecmp(p, "inet6") == 0) 18964562Sgshapiro { 19064562Sgshapiro addr.sa.sa_family = AF_INET6; 19198121Sgshapiro L_socksize = sizeof addr.sin6; 19264562Sgshapiro } 19364562Sgshapiro#endif /* NETINET6 */ 19464562Sgshapiro else 19564562Sgshapiro { 19664562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 19764562Sgshapiro name, p); 19866494Sgshapiro return INVALID_SOCKET; 19964562Sgshapiro } 20064562Sgshapiro *colon++ = ':'; 20164562Sgshapiro } 20264562Sgshapiro else 20364562Sgshapiro { 20464562Sgshapiro colon = p; 20564562Sgshapiro#if NETUNIX 20664562Sgshapiro /* default to AF_UNIX */ 20794334Sgshapiro addr.sa.sa_family = AF_UNIX; 20898121Sgshapiro L_socksize = sizeof (struct sockaddr_un); 20964562Sgshapiro#else /* NETUNIX */ 21064562Sgshapiro# if NETINET 21164562Sgshapiro /* default to AF_INET */ 21264562Sgshapiro addr.sa.sa_family = AF_INET; 21398121Sgshapiro L_socksize = sizeof addr.sin; 21464562Sgshapiro# else /* NETINET */ 21564562Sgshapiro# if NETINET6 21664562Sgshapiro /* default to AF_INET6 */ 21764562Sgshapiro addr.sa.sa_family = AF_INET6; 21898121Sgshapiro L_socksize = sizeof addr.sin6; 21964562Sgshapiro# else /* NETINET6 */ 22064562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 22164562Sgshapiro name, p); 22266494Sgshapiro return INVALID_SOCKET; 22364562Sgshapiro# endif /* NETINET6 */ 22464562Sgshapiro# endif /* NETINET */ 22564562Sgshapiro#endif /* NETUNIX */ 22664562Sgshapiro } 22764562Sgshapiro 22864562Sgshapiro#if NETUNIX 22964562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 23064562Sgshapiro { 23164562Sgshapiro# if 0 23264562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 23364562Sgshapiro# endif /* 0 */ 23464562Sgshapiro 23564562Sgshapiro at = colon; 23690792Sgshapiro len = strlen(colon) + 1; 23790792Sgshapiro if (len >= sizeof addr.sunix.sun_path) 23864562Sgshapiro { 23964562Sgshapiro errno = EINVAL; 24064562Sgshapiro smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", 24164562Sgshapiro name, colon); 24266494Sgshapiro return INVALID_SOCKET; 24364562Sgshapiro } 24490792Sgshapiro (void) sm_strlcpy(addr.sunix.sun_path, colon, 24590792Sgshapiro sizeof addr.sunix.sun_path); 24664562Sgshapiro# if 0 24764562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 24864562Sgshapiro S_IRUSR|S_IWUSR, NULL); 24964562Sgshapiro 25064562Sgshapiro /* if not safe, don't create */ 25164562Sgshapiro if (errno != 0) 25264562Sgshapiro { 25364562Sgshapiro smi_log(SMI_LOG_ERR, 25464562Sgshapiro "%s: UNIX socket name %s unsafe", 25564562Sgshapiro name, colon); 25666494Sgshapiro return INVALID_SOCKET; 25764562Sgshapiro } 25864562Sgshapiro# endif /* 0 */ 25964562Sgshapiro } 26064562Sgshapiro#endif /* NETUNIX */ 26164562Sgshapiro 26264562Sgshapiro#if NETINET || NETINET6 26364562Sgshapiro if ( 26464562Sgshapiro# if NETINET 26564562Sgshapiro addr.sa.sa_family == AF_INET 26664562Sgshapiro# endif /* NETINET */ 26764562Sgshapiro# if NETINET && NETINET6 26864562Sgshapiro || 26964562Sgshapiro# endif /* NETINET && NETINET6 */ 27064562Sgshapiro# if NETINET6 27164562Sgshapiro addr.sa.sa_family == AF_INET6 27264562Sgshapiro# endif /* NETINET6 */ 27364562Sgshapiro ) 27464562Sgshapiro { 27590792Sgshapiro unsigned short port; 27664562Sgshapiro 27764562Sgshapiro /* Parse port@host */ 27864562Sgshapiro at = strchr(colon, '@'); 27964562Sgshapiro if (at == NULL) 28064562Sgshapiro { 28164562Sgshapiro switch (addr.sa.sa_family) 28264562Sgshapiro { 28364562Sgshapiro# if NETINET 28464562Sgshapiro case AF_INET: 28564562Sgshapiro addr.sin.sin_addr.s_addr = INADDR_ANY; 28664562Sgshapiro break; 28764562Sgshapiro# endif /* NETINET */ 28864562Sgshapiro 28964562Sgshapiro# if NETINET6 29064562Sgshapiro case AF_INET6: 29164562Sgshapiro addr.sin6.sin6_addr = in6addr_any; 29264562Sgshapiro break; 29364562Sgshapiro# endif /* NETINET6 */ 29464562Sgshapiro } 29564562Sgshapiro } 29664562Sgshapiro else 29764562Sgshapiro *at = '\0'; 29864562Sgshapiro 29964562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 30090792Sgshapiro port = htons((unsigned short) atoi(colon)); 30164562Sgshapiro else 30264562Sgshapiro { 30364562Sgshapiro# ifdef NO_GETSERVBYNAME 30464562Sgshapiro smi_log(SMI_LOG_ERR, "%s: invalid port number %s", 30564562Sgshapiro name, colon); 30666494Sgshapiro return INVALID_SOCKET; 30764562Sgshapiro# else /* NO_GETSERVBYNAME */ 30864562Sgshapiro register struct servent *sp; 30964562Sgshapiro 31064562Sgshapiro sp = getservbyname(colon, "tcp"); 31164562Sgshapiro if (sp == NULL) 31264562Sgshapiro { 31364562Sgshapiro smi_log(SMI_LOG_ERR, 31464562Sgshapiro "%s: unknown port name %s", 31564562Sgshapiro name, colon); 31666494Sgshapiro return INVALID_SOCKET; 31764562Sgshapiro } 31864562Sgshapiro port = sp->s_port; 31964562Sgshapiro# endif /* NO_GETSERVBYNAME */ 32064562Sgshapiro } 32164562Sgshapiro if (at != NULL) 32264562Sgshapiro { 32364562Sgshapiro *at++ = '@'; 32464562Sgshapiro if (*at == '[') 32564562Sgshapiro { 32664562Sgshapiro char *end; 32764562Sgshapiro 32864562Sgshapiro end = strchr(at, ']'); 32964562Sgshapiro if (end != NULL) 33064562Sgshapiro { 33190792Sgshapiro bool found = false; 33264562Sgshapiro# if NETINET 33364562Sgshapiro unsigned long hid = INADDR_NONE; 33464562Sgshapiro# endif /* NETINET */ 33564562Sgshapiro# if NETINET6 33664562Sgshapiro struct sockaddr_in6 hid6; 33764562Sgshapiro# endif /* NETINET6 */ 33864562Sgshapiro 33964562Sgshapiro *end = '\0'; 34064562Sgshapiro# if NETINET 34164562Sgshapiro if (addr.sa.sa_family == AF_INET && 34290792Sgshapiro (hid = inet_addr(&at[1])) != INADDR_NONE) 34364562Sgshapiro { 34464562Sgshapiro addr.sin.sin_addr.s_addr = hid; 34564562Sgshapiro addr.sin.sin_port = port; 34690792Sgshapiro found = true; 34764562Sgshapiro } 34864562Sgshapiro# endif /* NETINET */ 34964562Sgshapiro# if NETINET6 35064562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 35164562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 35290792Sgshapiro mi_inet_pton(AF_INET6, &at[1], 35390792Sgshapiro &hid6.sin6_addr) == 1) 35464562Sgshapiro { 35564562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 35664562Sgshapiro addr.sin6.sin6_port = port; 35790792Sgshapiro found = true; 35864562Sgshapiro } 35964562Sgshapiro# endif /* NETINET6 */ 36064562Sgshapiro *end = ']'; 36164562Sgshapiro if (!found) 36264562Sgshapiro { 36364562Sgshapiro smi_log(SMI_LOG_ERR, 36464562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 36564562Sgshapiro name, at); 36666494Sgshapiro return INVALID_SOCKET; 36764562Sgshapiro } 36864562Sgshapiro } 36964562Sgshapiro else 37064562Sgshapiro { 37164562Sgshapiro smi_log(SMI_LOG_ERR, 37264562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 37364562Sgshapiro name, at); 37466494Sgshapiro return INVALID_SOCKET; 37564562Sgshapiro } 37664562Sgshapiro } 37764562Sgshapiro else 37864562Sgshapiro { 37971345Sgshapiro struct hostent *hp = NULL; 38071345Sgshapiro 38164562Sgshapiro hp = mi_gethostbyname(at, addr.sa.sa_family); 38264562Sgshapiro if (hp == NULL) 38364562Sgshapiro { 38464562Sgshapiro smi_log(SMI_LOG_ERR, 38564562Sgshapiro "%s: Unknown host name %s", 38664562Sgshapiro name, at); 38766494Sgshapiro return INVALID_SOCKET; 38864562Sgshapiro } 38964562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 39064562Sgshapiro switch (hp->h_addrtype) 39164562Sgshapiro { 39264562Sgshapiro# if NETINET 39364562Sgshapiro case AF_INET: 394120256Sgshapiro (void) memmove(&addr.sin.sin_addr, 395120256Sgshapiro hp->h_addr, 396120256Sgshapiro INADDRSZ); 39764562Sgshapiro addr.sin.sin_port = port; 39864562Sgshapiro break; 39964562Sgshapiro# endif /* NETINET */ 40064562Sgshapiro 40164562Sgshapiro# if NETINET6 40264562Sgshapiro case AF_INET6: 403120256Sgshapiro (void) memmove(&addr.sin6.sin6_addr, 404120256Sgshapiro hp->h_addr, 405120256Sgshapiro IN6ADDRSZ); 40664562Sgshapiro addr.sin6.sin6_port = port; 40764562Sgshapiro break; 40864562Sgshapiro# endif /* NETINET6 */ 40964562Sgshapiro 41064562Sgshapiro default: 41164562Sgshapiro smi_log(SMI_LOG_ERR, 41264562Sgshapiro "%s: Unknown protocol for %s (%d)", 41364562Sgshapiro name, at, hp->h_addrtype); 41466494Sgshapiro return INVALID_SOCKET; 41564562Sgshapiro } 41690792Sgshapiro# if NETINET6 41771345Sgshapiro freehostent(hp); 41890792Sgshapiro# endif /* NETINET6 */ 41964562Sgshapiro } 42064562Sgshapiro } 42164562Sgshapiro else 42264562Sgshapiro { 42364562Sgshapiro switch (addr.sa.sa_family) 42464562Sgshapiro { 42564562Sgshapiro# if NETINET 42664562Sgshapiro case AF_INET: 42764562Sgshapiro addr.sin.sin_port = port; 42864562Sgshapiro break; 42964562Sgshapiro# endif /* NETINET */ 43064562Sgshapiro# if NETINET6 43164562Sgshapiro case AF_INET6: 43264562Sgshapiro addr.sin6.sin6_port = port; 43364562Sgshapiro break; 43464562Sgshapiro# endif /* NETINET6 */ 43564562Sgshapiro } 43664562Sgshapiro } 43764562Sgshapiro } 43864562Sgshapiro#endif /* NETINET || NETINET6 */ 43964562Sgshapiro 44064562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 44164562Sgshapiro if (!ValidSocket(sock)) 44264562Sgshapiro { 44364562Sgshapiro smi_log(SMI_LOG_ERR, 44464562Sgshapiro "%s: Unable to create new socket: %s", 44590792Sgshapiro name, sm_errstring(errno)); 44666494Sgshapiro return INVALID_SOCKET; 44764562Sgshapiro } 44864562Sgshapiro 44998121Sgshapiro if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 || 45098121Sgshapiro fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1) 45198121Sgshapiro { 45298121Sgshapiro smi_log(SMI_LOG_ERR, 45398121Sgshapiro "%s: Unable to set close-on-exec: %s", name, 45498121Sgshapiro sm_errstring(errno)); 45598121Sgshapiro (void) closesocket(sock); 45698121Sgshapiro return INVALID_SOCKET; 45798121Sgshapiro } 45898121Sgshapiro 45964562Sgshapiro if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 46064562Sgshapiro sizeof(sockopt)) == -1) 46164562Sgshapiro { 46264562Sgshapiro smi_log(SMI_LOG_ERR, 46390792Sgshapiro "%s: Unable to setsockopt: %s", name, 46490792Sgshapiro sm_errstring(errno)); 46590792Sgshapiro (void) closesocket(sock); 46666494Sgshapiro return INVALID_SOCKET; 46764562Sgshapiro } 46864562Sgshapiro 469125820Sgshapiro#if NETUNIX 470125820Sgshapiro if (addr.sa.sa_family == AF_UNIX && rmsocket) 471125820Sgshapiro { 472125820Sgshapiro struct stat s; 473125820Sgshapiro 474125820Sgshapiro if (stat(colon, &s) != 0) 475125820Sgshapiro { 476125820Sgshapiro if (errno != ENOENT) 477125820Sgshapiro { 478125820Sgshapiro smi_log(SMI_LOG_ERR, 479125820Sgshapiro "%s: Unable to stat() %s: %s", 480125820Sgshapiro name, colon, sm_errstring(errno)); 481125820Sgshapiro (void) closesocket(sock); 482125820Sgshapiro return INVALID_SOCKET; 483125820Sgshapiro } 484125820Sgshapiro } 485125820Sgshapiro else if (!S_ISSOCK(s.st_mode)) 486125820Sgshapiro { 487125820Sgshapiro smi_log(SMI_LOG_ERR, 488125820Sgshapiro "%s: %s is not a UNIX domain socket", 489125820Sgshapiro name, colon); 490125820Sgshapiro (void) closesocket(sock); 491125820Sgshapiro return INVALID_SOCKET; 492125820Sgshapiro } 493125820Sgshapiro else if (unlink(colon) != 0) 494125820Sgshapiro { 495125820Sgshapiro smi_log(SMI_LOG_ERR, 496125820Sgshapiro "%s: Unable to remove %s: %s", 497125820Sgshapiro name, colon, sm_errstring(errno)); 498125820Sgshapiro (void) closesocket(sock); 499125820Sgshapiro return INVALID_SOCKET; 500125820Sgshapiro } 501125820Sgshapiro } 502125820Sgshapiro#endif /* NETUNIX */ 503125820Sgshapiro 50498121Sgshapiro if (bind(sock, &addr.sa, L_socksize) < 0) 50564562Sgshapiro { 50664562Sgshapiro smi_log(SMI_LOG_ERR, 50764562Sgshapiro "%s: Unable to bind to port %s: %s", 50890792Sgshapiro name, conn, sm_errstring(errno)); 50990792Sgshapiro (void) closesocket(sock); 51066494Sgshapiro return INVALID_SOCKET; 51164562Sgshapiro } 51264562Sgshapiro 51364562Sgshapiro if (listen(sock, backlog) < 0) 51464562Sgshapiro { 51564562Sgshapiro smi_log(SMI_LOG_ERR, 51690792Sgshapiro "%s: listen call failed: %s", name, 51790792Sgshapiro sm_errstring(errno)); 51890792Sgshapiro (void) closesocket(sock); 51966494Sgshapiro return INVALID_SOCKET; 52064562Sgshapiro } 52190792Sgshapiro 52290792Sgshapiro#if NETUNIX 52390792Sgshapiro if (addr.sa.sa_family == AF_UNIX && len > 0) 52490792Sgshapiro { 52590792Sgshapiro /* 52690792Sgshapiro ** Set global variable sockpath so the UNIX socket can be 52790792Sgshapiro ** unlink()ed at exit. 52890792Sgshapiro */ 52990792Sgshapiro 53090792Sgshapiro sockpath = (char *) malloc(len); 53190792Sgshapiro if (sockpath != NULL) 53290792Sgshapiro (void) sm_strlcpy(sockpath, colon, len); 53390792Sgshapiro else 53490792Sgshapiro { 53590792Sgshapiro smi_log(SMI_LOG_ERR, 53690792Sgshapiro "%s: can't malloc(%d) for sockpath: %s", 537110560Sgshapiro name, (int) len, sm_errstring(errno)); 53890792Sgshapiro (void) closesocket(sock); 53990792Sgshapiro return INVALID_SOCKET; 54090792Sgshapiro } 54190792Sgshapiro } 54290792Sgshapiro#endif /* NETUNIX */ 54398121Sgshapiro L_family = addr.sa.sa_family; 54464562Sgshapiro return sock; 54564562Sgshapiro} 54690792Sgshapiro/* 54764562Sgshapiro** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 54864562Sgshapiro** 54964562Sgshapiro** Parameters: 55064562Sgshapiro** arg -- argument to pass to mi_handle_session() 55164562Sgshapiro** 55264562Sgshapiro** Returns: 55364562Sgshapiro** results from mi_handle_session() 55464562Sgshapiro*/ 55564562Sgshapiro 55664562Sgshapirovoid * 55764562Sgshapiromi_thread_handle_wrapper(arg) 55864562Sgshapiro void *arg; 55964562Sgshapiro{ 56064562Sgshapiro return (void *) mi_handle_session(arg); 56164562Sgshapiro} 56264562Sgshapiro 56390792Sgshapiro/* 56466494Sgshapiro** MI_CLOSENER -- close listen socket 56564562Sgshapiro** 56690792Sgshapiro** NOTE: It is assumed that this function is called from a 56790792Sgshapiro** function that has a mutex lock (currently mi_stop_milters()). 56890792Sgshapiro** 56966494Sgshapiro** Parameters: 57066494Sgshapiro** none. 57166494Sgshapiro** 57266494Sgshapiro** Returns: 57366494Sgshapiro** none. 57466494Sgshapiro*/ 57566494Sgshapiro 57666494Sgshapirovoid 57766494Sgshapiromi_closener() 57866494Sgshapiro{ 57971345Sgshapiro (void) smutex_lock(&L_Mutex); 58066494Sgshapiro if (ValidSocket(listenfd)) 58166494Sgshapiro { 58290792Sgshapiro#if NETUNIX 58390792Sgshapiro bool removable; 58490792Sgshapiro struct stat sockinfo; 58590792Sgshapiro struct stat fileinfo; 58690792Sgshapiro 58790792Sgshapiro removable = sockpath != NULL && 58890792Sgshapiro geteuid() != 0 && 58990792Sgshapiro fstat(listenfd, &sockinfo) == 0 && 59090792Sgshapiro (S_ISFIFO(sockinfo.st_mode) 59190792Sgshapiro# ifdef S_ISSOCK 59290792Sgshapiro || S_ISSOCK(sockinfo.st_mode) 59390792Sgshapiro# endif /* S_ISSOCK */ 59490792Sgshapiro ); 59590792Sgshapiro#endif /* NETUNIX */ 59690792Sgshapiro 59790792Sgshapiro (void) closesocket(listenfd); 59866494Sgshapiro listenfd = INVALID_SOCKET; 59990792Sgshapiro 60090792Sgshapiro#if NETUNIX 60190792Sgshapiro /* XXX sleep() some time before doing this? */ 60290792Sgshapiro if (sockpath != NULL) 60390792Sgshapiro { 60490792Sgshapiro if (removable && 60590792Sgshapiro stat(sockpath, &fileinfo) == 0 && 60690792Sgshapiro ((fileinfo.st_dev == sockinfo.st_dev && 60790792Sgshapiro fileinfo.st_ino == sockinfo.st_ino) 60890792Sgshapiro# ifdef S_ISSOCK 60990792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61090792Sgshapiro# endif /* S_ISSOCK */ 61190792Sgshapiro ) 61290792Sgshapiro && 61390792Sgshapiro (S_ISFIFO(fileinfo.st_mode) 61490792Sgshapiro# ifdef S_ISSOCK 61590792Sgshapiro || S_ISSOCK(fileinfo.st_mode) 61690792Sgshapiro# endif /* S_ISSOCK */ 61790792Sgshapiro )) 61890792Sgshapiro (void) unlink(sockpath); 61990792Sgshapiro free(sockpath); 62090792Sgshapiro sockpath = NULL; 62190792Sgshapiro } 62290792Sgshapiro#endif /* NETUNIX */ 62366494Sgshapiro } 62471345Sgshapiro (void) smutex_unlock(&L_Mutex); 62566494Sgshapiro} 62666494Sgshapiro 62790792Sgshapiro/* 62866494Sgshapiro** MI_LISTENER -- Generic listener harness 62966494Sgshapiro** 63064562Sgshapiro** Open up listen port 63164562Sgshapiro** Wait for connections 63264562Sgshapiro** 63364562Sgshapiro** Parameters: 63464562Sgshapiro** conn -- connection description 63564562Sgshapiro** dbg -- debug level 63664562Sgshapiro** smfi -- filter structure to use 63764562Sgshapiro** timeout -- timeout for reads/writes 638125820Sgshapiro** backlog -- listen queue backlog size 63964562Sgshapiro** 64064562Sgshapiro** Returns: 64164562Sgshapiro** MI_SUCCESS -- Exited normally 64264562Sgshapiro** (session finished or we were told to exit) 64364562Sgshapiro** MI_FAILURE -- Network initialization failed. 64464562Sgshapiro*/ 64564562Sgshapiro 64690792Sgshapiro#if BROKEN_PTHREAD_SLEEP 64773188Sgshapiro 64873188Sgshapiro/* 64973188Sgshapiro** Solaris 2.6, perhaps others, gets an internal threads library panic 65073188Sgshapiro** when sleep() is used: 65173188Sgshapiro** 65273188Sgshapiro** thread_create() failed, returned 11 (EINVAL) 65373188Sgshapiro** co_enable, thr_create() returned error = 24 65473188Sgshapiro** libthread panic: co_enable failed (PID: 17793 LWP 1) 65573188Sgshapiro** stacktrace: 65673188Sgshapiro** ef526b10 65773188Sgshapiro** ef52646c 65873188Sgshapiro** ef534cbc 65973188Sgshapiro** 156a4 66073188Sgshapiro** 14644 66173188Sgshapiro** 1413c 66273188Sgshapiro** 135e0 66373188Sgshapiro** 0 66473188Sgshapiro*/ 66573188Sgshapiro 66690792Sgshapiro# define MI_SLEEP(s) \ 66773188Sgshapiro{ \ 66873188Sgshapiro int rs = 0; \ 66973188Sgshapiro struct timeval st; \ 67073188Sgshapiro \ 67173188Sgshapiro st.tv_sec = (s); \ 67273188Sgshapiro st.tv_usec = 0; \ 67373188Sgshapiro if (st.tv_sec > 0) \ 67473188Sgshapiro { \ 675102528Sgshapiro for (;;) \ 676102528Sgshapiro { \ 677102528Sgshapiro rs = select(0, NULL, NULL, NULL, &st); \ 678102528Sgshapiro if (rs < 0 && errno == EINTR) \ 679102528Sgshapiro continue; \ 680102528Sgshapiro if (rs != 0) \ 681102528Sgshapiro { \ 682102528Sgshapiro smi_log(SMI_LOG_ERR, \ 683110560Sgshapiro "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \ 684102528Sgshapiro rs, errno); \ 685102528Sgshapiro } \ 686110560Sgshapiro break; \ 687102528Sgshapiro } \ 68873188Sgshapiro } \ 68973188Sgshapiro} 69090792Sgshapiro#else /* BROKEN_PTHREAD_SLEEP */ 69190792Sgshapiro# define MI_SLEEP(s) sleep((s)) 69290792Sgshapiro#endif /* BROKEN_PTHREAD_SLEEP */ 69373188Sgshapiro 69464562Sgshapiroint 69566494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog) 69664562Sgshapiro char *conn; 69764562Sgshapiro int dbg; 69864562Sgshapiro smfiDesc_ptr smfi; 69964562Sgshapiro time_t timeout; 70066494Sgshapiro int backlog; 70164562Sgshapiro{ 70266494Sgshapiro socket_t connfd = INVALID_SOCKET; 703132943Sgshapiro#if _FFR_DUP_FD 704132943Sgshapiro socket_t dupfd = INVALID_SOCKET; 705132943Sgshapiro#endif /* _FFR_DUP_FD */ 70664562Sgshapiro int sockopt = 1; 707120256Sgshapiro int r, mistop; 70864562Sgshapiro int ret = MI_SUCCESS; 70990792Sgshapiro int mcnt = 0; /* error count for malloc() failures */ 71090792Sgshapiro int tcnt = 0; /* error count for thread_create() failures */ 71190792Sgshapiro int acnt = 0; /* error count for accept() failures */ 71290792Sgshapiro int scnt = 0; /* error count for select() failures */ 71377349Sgshapiro int save_errno = 0; 71464562Sgshapiro sthread_t thread_id; 71564562Sgshapiro _SOCK_ADDR cliaddr; 71664562Sgshapiro SOCKADDR_LEN_T clilen; 71764562Sgshapiro SMFICTX_PTR ctx; 718111823Sgshapiro FD_RD_VAR(rds, excs); 71964562Sgshapiro struct timeval chktime; 72064562Sgshapiro 721125820Sgshapiro if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE) 72264562Sgshapiro return MI_FAILURE; 72371345Sgshapiro 72498121Sgshapiro clilen = L_socksize; 72571345Sgshapiro (void) smutex_unlock(&L_Mutex); 726120256Sgshapiro while ((mistop = mi_stop()) == MILTER_CONT) 72764562Sgshapiro { 72871345Sgshapiro (void) smutex_lock(&L_Mutex); 72971345Sgshapiro if (!ValidSocket(listenfd)) 73071345Sgshapiro { 731120256Sgshapiro ret = MI_FAILURE; 732120256Sgshapiro smi_log(SMI_LOG_ERR, 733120256Sgshapiro "%s: listenfd=%d corrupted, terminating, errno=%d", 734120256Sgshapiro smfi->xxfi_name, listenfd, errno); 73571345Sgshapiro (void) smutex_unlock(&L_Mutex); 73671345Sgshapiro break; 73771345Sgshapiro } 73871345Sgshapiro 73964562Sgshapiro /* select on interface ports */ 740111823Sgshapiro FD_RD_INIT(listenfd, rds, excs); 74164562Sgshapiro chktime.tv_sec = MI_CHK_TIME; 74264562Sgshapiro chktime.tv_usec = 0; 743111823Sgshapiro r = FD_RD_READY(listenfd, rds, excs, &chktime); 74464562Sgshapiro if (r == 0) /* timeout */ 74571345Sgshapiro { 74671345Sgshapiro (void) smutex_unlock(&L_Mutex); 74764562Sgshapiro continue; /* just check mi_stop() */ 74871345Sgshapiro } 74964562Sgshapiro if (r < 0) 75064562Sgshapiro { 75177349Sgshapiro save_errno = errno; 75271345Sgshapiro (void) smutex_unlock(&L_Mutex); 75377349Sgshapiro if (save_errno == EINTR) 75464562Sgshapiro continue; 75590792Sgshapiro scnt++; 75690792Sgshapiro smi_log(SMI_LOG_ERR, 75790792Sgshapiro "%s: select() failed (%s), %s", 75890792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 75990792Sgshapiro scnt >= MAX_FAILS_S ? "abort" : "try again"); 76090792Sgshapiro MI_SLEEP(scnt); 76190792Sgshapiro if (scnt >= MAX_FAILS_S) 76290792Sgshapiro { 76390792Sgshapiro ret = MI_FAILURE; 76490792Sgshapiro break; 76590792Sgshapiro } 76690792Sgshapiro continue; 76764562Sgshapiro } 768111823Sgshapiro if (!FD_IS_RD_RDY(listenfd, rds, excs)) 76964562Sgshapiro { 77064562Sgshapiro /* some error: just stop for now... */ 77164562Sgshapiro ret = MI_FAILURE; 77271345Sgshapiro (void) smutex_unlock(&L_Mutex); 77390792Sgshapiro smi_log(SMI_LOG_ERR, 774111823Sgshapiro "%s: %s() returned exception for socket, abort", 775111823Sgshapiro smfi->xxfi_name, MI_POLLSELECT); 77664562Sgshapiro break; 77764562Sgshapiro } 77890792Sgshapiro scnt = 0; /* reset error counter for select() */ 77964562Sgshapiro 780120256Sgshapiro (void) memset(&cliaddr, '\0', sizeof cliaddr); 78164562Sgshapiro connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 78264562Sgshapiro &clilen); 78377349Sgshapiro save_errno = errno; 78471345Sgshapiro (void) smutex_unlock(&L_Mutex); 78564562Sgshapiro 78673188Sgshapiro /* 78773188Sgshapiro ** If remote side closes before 78873188Sgshapiro ** accept() finishes, sockaddr 78973188Sgshapiro ** might not be fully filled in. 79073188Sgshapiro */ 79173188Sgshapiro 79273188Sgshapiro if (ValidSocket(connfd) && 79373188Sgshapiro (clilen == 0 || 79473188Sgshapiro# ifdef BSD4_4_SOCKADDR 79573188Sgshapiro cliaddr.sa.sa_len == 0 || 79673188Sgshapiro# endif /* BSD4_4_SOCKADDR */ 79798121Sgshapiro cliaddr.sa.sa_family != L_family)) 79873188Sgshapiro { 79990792Sgshapiro (void) closesocket(connfd); 80073188Sgshapiro connfd = INVALID_SOCKET; 80177349Sgshapiro save_errno = EINVAL; 80273188Sgshapiro } 80373188Sgshapiro 804132943Sgshapiro#if !SM_CONF_POLL 805110560Sgshapiro /* check if acceptable for select() */ 806110560Sgshapiro if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd)) 807110560Sgshapiro { 808110560Sgshapiro (void) closesocket(connfd); 809110560Sgshapiro connfd = INVALID_SOCKET; 810110560Sgshapiro save_errno = ERANGE; 811110560Sgshapiro } 812132943Sgshapiro#endif /* !SM_CONF_POLL */ 813110560Sgshapiro 81466494Sgshapiro if (!ValidSocket(connfd)) 81564562Sgshapiro { 816132943Sgshapiro if (save_errno == EINTR 817132943Sgshapiro#ifdef EAGAIN 818132943Sgshapiro || save_errno == EAGAIN 819132943Sgshapiro#endif /* EAGAIN */ 820132943Sgshapiro#ifdef ECONNABORTED 821132943Sgshapiro || save_errno == ECONNABORTED 822132943Sgshapiro#endif /* ECONNABORTED */ 823132943Sgshapiro#ifdef EMFILE 824132943Sgshapiro || save_errno == EMFILE 825132943Sgshapiro#endif /* EMFILE */ 826132943Sgshapiro#ifdef ENFILE 827132943Sgshapiro || save_errno == ENFILE 828132943Sgshapiro#endif /* ENFILE */ 829132943Sgshapiro#ifdef ENOBUFS 830132943Sgshapiro || save_errno == ENOBUFS 831132943Sgshapiro#endif /* ENOBUFS */ 832132943Sgshapiro#ifdef ENOMEM 833132943Sgshapiro || save_errno == ENOMEM 834132943Sgshapiro#endif /* ENOMEM */ 835132943Sgshapiro#ifdef ENOSR 836132943Sgshapiro || save_errno == ENOSR 837132943Sgshapiro#endif /* ENOSR */ 838132943Sgshapiro#ifdef EWOULDBLOCK 839132943Sgshapiro || save_errno == EWOULDBLOCK 840132943Sgshapiro#endif /* EWOULDBLOCK */ 841132943Sgshapiro ) 84277349Sgshapiro continue; 84377349Sgshapiro acnt++; 84490792Sgshapiro smi_log(SMI_LOG_ERR, 84590792Sgshapiro "%s: accept() returned invalid socket (%s), %s", 84690792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 84790792Sgshapiro acnt >= MAX_FAILS_A ? "abort" : "try again"); 84877349Sgshapiro MI_SLEEP(acnt); 84977349Sgshapiro if (acnt >= MAX_FAILS_A) 85077349Sgshapiro { 85177349Sgshapiro ret = MI_FAILURE; 85277349Sgshapiro break; 85377349Sgshapiro } 85464562Sgshapiro continue; 85564562Sgshapiro } 85690792Sgshapiro acnt = 0; /* reset error counter for accept() */ 857132943Sgshapiro#if _FFR_DUP_FD 858132943Sgshapiro dupfd = fcntl(connfd, F_DUPFD, 256); 859132943Sgshapiro if (ValidSocket(dupfd) 860132943Sgshapiro# if !SM_CONF_POLL 861132943Sgshapiro && SM_FD_OK_SELECT(dupfd) 862132943Sgshapiro# endif /* !SM_CONF_POLL */ 863132943Sgshapiro ) 864132943Sgshapiro { 865132943Sgshapiro close(connfd); 866132943Sgshapiro connfd = dupfd; 867132943Sgshapiro dupfd = INVALID_SOCKET; 868132943Sgshapiro } 869132943Sgshapiro#endif /* _FFR_DUP_FD */ 87064562Sgshapiro 87164562Sgshapiro if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 87264562Sgshapiro (void *) &sockopt, sizeof sockopt) < 0) 87364562Sgshapiro { 87490792Sgshapiro smi_log(SMI_LOG_WARN, "%s: setsockopt() failed (%s)", 87590792Sgshapiro smfi->xxfi_name, sm_errstring(errno)); 87664562Sgshapiro /* XXX: continue? */ 87764562Sgshapiro } 87864562Sgshapiro if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 87964562Sgshapiro { 88090792Sgshapiro (void) closesocket(connfd); 88173188Sgshapiro mcnt++; 88290792Sgshapiro smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s", 88390792Sgshapiro smfi->xxfi_name, sm_errstring(save_errno), 88490792Sgshapiro mcnt >= MAX_FAILS_M ? "abort" : "try again"); 88573188Sgshapiro MI_SLEEP(mcnt); 88673188Sgshapiro if (mcnt >= MAX_FAILS_M) 88764562Sgshapiro { 88864562Sgshapiro ret = MI_FAILURE; 88964562Sgshapiro break; 89064562Sgshapiro } 89164562Sgshapiro continue; 89264562Sgshapiro } 89390792Sgshapiro mcnt = 0; /* reset error counter for malloc() */ 894120256Sgshapiro (void) memset(ctx, '\0', sizeof *ctx); 89564562Sgshapiro ctx->ctx_sd = connfd; 89664562Sgshapiro ctx->ctx_dbg = dbg; 89764562Sgshapiro ctx->ctx_timeout = timeout; 89864562Sgshapiro ctx->ctx_smfi = smfi; 89964562Sgshapiro#if 0 90064562Sgshapiro if (smfi->xxfi_eoh == NULL) 90164562Sgshapiro if (smfi->xxfi_eom == NULL) 90264562Sgshapiro if (smfi->xxfi_abort == NULL) 90364562Sgshapiro if (smfi->xxfi_close == NULL) 90464562Sgshapiro#endif /* 0 */ 90564562Sgshapiro if (smfi->xxfi_connect == NULL) 90664562Sgshapiro ctx->ctx_pflags |= SMFIP_NOCONNECT; 90764562Sgshapiro if (smfi->xxfi_helo == NULL) 90864562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHELO; 90964562Sgshapiro if (smfi->xxfi_envfrom == NULL) 91064562Sgshapiro ctx->ctx_pflags |= SMFIP_NOMAIL; 91164562Sgshapiro if (smfi->xxfi_envrcpt == NULL) 91264562Sgshapiro ctx->ctx_pflags |= SMFIP_NORCPT; 91364562Sgshapiro if (smfi->xxfi_header == NULL) 91464562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHDRS; 91564562Sgshapiro if (smfi->xxfi_eoh == NULL) 91664562Sgshapiro ctx->ctx_pflags |= SMFIP_NOEOH; 91764562Sgshapiro if (smfi->xxfi_body == NULL) 91864562Sgshapiro ctx->ctx_pflags |= SMFIP_NOBODY; 91964562Sgshapiro 92064562Sgshapiro if ((r = thread_create(&thread_id, 92164562Sgshapiro mi_thread_handle_wrapper, 92271345Sgshapiro (void *) ctx)) != 0) 92364562Sgshapiro { 92490792Sgshapiro tcnt++; 92564562Sgshapiro smi_log(SMI_LOG_ERR, 92690792Sgshapiro "%s: thread_create() failed: %d, %s", 92790792Sgshapiro smfi->xxfi_name, r, 92890792Sgshapiro tcnt >= MAX_FAILS_T ? "abort" : "try again"); 92973188Sgshapiro MI_SLEEP(tcnt); 93090792Sgshapiro (void) closesocket(connfd); 93164562Sgshapiro free(ctx); 93273188Sgshapiro if (tcnt >= MAX_FAILS_T) 93364562Sgshapiro { 93464562Sgshapiro ret = MI_FAILURE; 93564562Sgshapiro break; 93664562Sgshapiro } 93764562Sgshapiro continue; 93864562Sgshapiro } 93973188Sgshapiro tcnt = 0; 94064562Sgshapiro } 94164562Sgshapiro if (ret != MI_SUCCESS) 94264562Sgshapiro mi_stop_milters(MILTER_ABRT); 94371345Sgshapiro else 944120256Sgshapiro { 945120256Sgshapiro if (mistop != MILTER_CONT) 946120256Sgshapiro smi_log(SMI_LOG_INFO, "%s: mi_stop=%d", 947120256Sgshapiro smfi->xxfi_name, mistop); 94871345Sgshapiro mi_closener(); 949120256Sgshapiro } 95071345Sgshapiro (void) smutex_destroy(&L_Mutex); 95164562Sgshapiro return ret; 95264562Sgshapiro} 953