listener.c revision 77349
164562Sgshapiro/* 273188Sgshapiro * Copyright (c) 1999-2001 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 1164562Sgshapiro#ifndef lint 1277349Sgshapirostatic char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.22 2001/05/16 17:15:58 ca Exp $"; 1364562Sgshapiro#endif /* ! lint */ 1464562Sgshapiro 1564562Sgshapiro#if _FFR_MILTER 1664562Sgshapiro/* 1764562Sgshapiro** listener.c -- threaded network listener 1864562Sgshapiro*/ 1964562Sgshapiro 2064562Sgshapiro#include "libmilter.h" 2164562Sgshapiro 2264562Sgshapiro 2364562Sgshapiro# if NETINET || NETINET6 2464562Sgshapiro# include <arpa/inet.h> 2564562Sgshapiro# endif /* NETINET || NETINET6 */ 2664562Sgshapiro/* 2764562Sgshapiro** MI_MILTEROPEN -- setup socket to listen on 2864562Sgshapiro** 2964562Sgshapiro** Parameters: 3064562Sgshapiro** conn -- connection description 3164562Sgshapiro** backlog -- listen backlog 3264562Sgshapiro** socksize -- socksize of created socket 3373188Sgshapiro** family -- family of created socket 3473188Sgshapiro** name -- name for logging 3564562Sgshapiro** 3664562Sgshapiro** Returns: 3764562Sgshapiro** socket upon success, error code otherwise. 3864562Sgshapiro*/ 3964562Sgshapiro 4066494Sgshapirostatic socket_t 4173188Sgshapiromi_milteropen(conn, backlog, socksize, family, name) 4264562Sgshapiro char *conn; 4364562Sgshapiro int backlog; 4464562Sgshapiro SOCKADDR_LEN_T *socksize; 4573188Sgshapiro int *family; 4664562Sgshapiro char *name; 4764562Sgshapiro{ 4866494Sgshapiro socket_t sock; 4964562Sgshapiro int sockopt = 1; 5064562Sgshapiro char *p; 5164562Sgshapiro char *colon; 5264562Sgshapiro char *at; 5364562Sgshapiro SOCKADDR addr; 5464562Sgshapiro 5564562Sgshapiro if (conn == NULL || conn[0] == '\0') 5664562Sgshapiro { 5764562Sgshapiro smi_log(SMI_LOG_ERR, "%s: empty or missing socket information", 5864562Sgshapiro name); 5966494Sgshapiro return INVALID_SOCKET; 6064562Sgshapiro } 6164562Sgshapiro (void) memset(&addr, '\0', sizeof addr); 6264562Sgshapiro 6364562Sgshapiro /* protocol:filename or protocol:port@host */ 6464562Sgshapiro p = conn; 6564562Sgshapiro colon = strchr(p, ':'); 6664562Sgshapiro if (colon != NULL) 6764562Sgshapiro { 6864562Sgshapiro *colon = '\0'; 6964562Sgshapiro 7064562Sgshapiro if (*p == '\0') 7164562Sgshapiro { 7264562Sgshapiro#if NETUNIX 7364562Sgshapiro /* default to AF_UNIX */ 7464562Sgshapiro addr.sa.sa_family = AF_UNIX; 7564562Sgshapiro *socksize = sizeof (struct sockaddr_un); 7664562Sgshapiro#else /* NETUNIX */ 7764562Sgshapiro# if NETINET 7864562Sgshapiro /* default to AF_INET */ 7964562Sgshapiro addr.sa.sa_family = AF_INET; 8064562Sgshapiro *socksize = sizeof addr.sin; 8164562Sgshapiro# else /* NETINET */ 8264562Sgshapiro# if NETINET6 8364562Sgshapiro /* default to AF_INET6 */ 8464562Sgshapiro addr.sa.sa_family = AF_INET6; 8564562Sgshapiro *socksize = sizeof addr.sin6; 8664562Sgshapiro# else /* NETINET6 */ 8764562Sgshapiro /* no protocols available */ 8864562Sgshapiro smi_log(SMI_LOG_ERR, 8964562Sgshapiro "%s: no valid socket protocols available", 9064562Sgshapiro name); 9166494Sgshapiro return INVALID_SOCKET; 9264562Sgshapiro# endif /* NETINET6 */ 9364562Sgshapiro# endif /* NETINET */ 9464562Sgshapiro#endif /* NETUNIX */ 9564562Sgshapiro } 9664562Sgshapiro#if NETUNIX 9764562Sgshapiro else if (strcasecmp(p, "unix") == 0 || 9864562Sgshapiro strcasecmp(p, "local") == 0) 9964562Sgshapiro { 10064562Sgshapiro addr.sa.sa_family = AF_UNIX; 10164562Sgshapiro *socksize = sizeof (struct sockaddr_un); 10264562Sgshapiro } 10364562Sgshapiro#endif /* NETUNIX */ 10464562Sgshapiro#if NETINET 10564562Sgshapiro else if (strcasecmp(p, "inet") == 0) 10664562Sgshapiro { 10764562Sgshapiro addr.sa.sa_family = AF_INET; 10864562Sgshapiro *socksize = sizeof addr.sin; 10964562Sgshapiro } 11064562Sgshapiro#endif /* NETINET */ 11164562Sgshapiro#if NETINET6 11264562Sgshapiro else if (strcasecmp(p, "inet6") == 0) 11364562Sgshapiro { 11464562Sgshapiro addr.sa.sa_family = AF_INET6; 11564562Sgshapiro *socksize = sizeof addr.sin6; 11664562Sgshapiro } 11764562Sgshapiro#endif /* NETINET6 */ 11864562Sgshapiro else 11964562Sgshapiro { 12064562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 12164562Sgshapiro name, p); 12266494Sgshapiro return INVALID_SOCKET; 12364562Sgshapiro } 12464562Sgshapiro *colon++ = ':'; 12564562Sgshapiro } 12664562Sgshapiro else 12764562Sgshapiro { 12864562Sgshapiro colon = p; 12964562Sgshapiro#if NETUNIX 13064562Sgshapiro /* default to AF_UNIX */ 13164562Sgshapiro addr.sa.sa_family = AF_UNIX; 13264562Sgshapiro *socksize = sizeof (struct sockaddr_un); 13364562Sgshapiro#else /* NETUNIX */ 13464562Sgshapiro# if NETINET 13564562Sgshapiro /* default to AF_INET */ 13664562Sgshapiro addr.sa.sa_family = AF_INET; 13764562Sgshapiro *socksize = sizeof addr.sin; 13864562Sgshapiro# else /* NETINET */ 13964562Sgshapiro# if NETINET6 14064562Sgshapiro /* default to AF_INET6 */ 14164562Sgshapiro addr.sa.sa_family = AF_INET6; 14264562Sgshapiro *socksize = sizeof addr.sin6; 14364562Sgshapiro# else /* NETINET6 */ 14464562Sgshapiro smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 14564562Sgshapiro name, p); 14666494Sgshapiro return INVALID_SOCKET; 14764562Sgshapiro# endif /* NETINET6 */ 14864562Sgshapiro# endif /* NETINET */ 14964562Sgshapiro#endif /* NETUNIX */ 15064562Sgshapiro } 15164562Sgshapiro 15264562Sgshapiro#if NETUNIX 15364562Sgshapiro if (addr.sa.sa_family == AF_UNIX) 15464562Sgshapiro { 15564562Sgshapiro# if 0 15664562Sgshapiro long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 15764562Sgshapiro# endif /* 0 */ 15864562Sgshapiro 15964562Sgshapiro at = colon; 16064562Sgshapiro if (strlcpy(addr.sunix.sun_path, colon, 16164562Sgshapiro sizeof addr.sunix.sun_path) >= 16264562Sgshapiro sizeof addr.sunix.sun_path) 16364562Sgshapiro { 16464562Sgshapiro errno = EINVAL; 16564562Sgshapiro smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", 16664562Sgshapiro name, colon); 16766494Sgshapiro return INVALID_SOCKET; 16864562Sgshapiro } 16964562Sgshapiro# if 0 17064562Sgshapiro errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 17164562Sgshapiro S_IRUSR|S_IWUSR, NULL); 17264562Sgshapiro 17364562Sgshapiro /* if not safe, don't create */ 17464562Sgshapiro if (errno != 0) 17564562Sgshapiro { 17664562Sgshapiro smi_log(SMI_LOG_ERR, 17764562Sgshapiro "%s: UNIX socket name %s unsafe", 17864562Sgshapiro name, colon); 17966494Sgshapiro return INVALID_SOCKET; 18064562Sgshapiro } 18164562Sgshapiro# endif /* 0 */ 18264562Sgshapiro 18364562Sgshapiro } 18464562Sgshapiro#endif /* NETUNIX */ 18564562Sgshapiro 18664562Sgshapiro#if NETINET || NETINET6 18764562Sgshapiro if ( 18864562Sgshapiro# if NETINET 18964562Sgshapiro addr.sa.sa_family == AF_INET 19064562Sgshapiro# endif /* NETINET */ 19164562Sgshapiro# if NETINET && NETINET6 19264562Sgshapiro || 19364562Sgshapiro# endif /* NETINET && NETINET6 */ 19464562Sgshapiro# if NETINET6 19564562Sgshapiro addr.sa.sa_family == AF_INET6 19664562Sgshapiro# endif /* NETINET6 */ 19764562Sgshapiro ) 19864562Sgshapiro { 19964562Sgshapiro u_short port; 20064562Sgshapiro 20164562Sgshapiro /* Parse port@host */ 20264562Sgshapiro at = strchr(colon, '@'); 20364562Sgshapiro if (at == NULL) 20464562Sgshapiro { 20564562Sgshapiro switch (addr.sa.sa_family) 20664562Sgshapiro { 20764562Sgshapiro# if NETINET 20864562Sgshapiro case AF_INET: 20964562Sgshapiro addr.sin.sin_addr.s_addr = INADDR_ANY; 21064562Sgshapiro break; 21164562Sgshapiro# endif /* NETINET */ 21264562Sgshapiro 21364562Sgshapiro# if NETINET6 21464562Sgshapiro case AF_INET6: 21564562Sgshapiro addr.sin6.sin6_addr = in6addr_any; 21664562Sgshapiro break; 21764562Sgshapiro# endif /* NETINET6 */ 21864562Sgshapiro } 21964562Sgshapiro } 22064562Sgshapiro else 22164562Sgshapiro *at = '\0'; 22264562Sgshapiro 22364562Sgshapiro if (isascii(*colon) && isdigit(*colon)) 22464562Sgshapiro port = htons((u_short) atoi(colon)); 22564562Sgshapiro else 22664562Sgshapiro { 22764562Sgshapiro# ifdef NO_GETSERVBYNAME 22864562Sgshapiro smi_log(SMI_LOG_ERR, "%s: invalid port number %s", 22964562Sgshapiro name, colon); 23066494Sgshapiro return INVALID_SOCKET; 23164562Sgshapiro# else /* NO_GETSERVBYNAME */ 23264562Sgshapiro register struct servent *sp; 23364562Sgshapiro 23464562Sgshapiro sp = getservbyname(colon, "tcp"); 23564562Sgshapiro if (sp == NULL) 23664562Sgshapiro { 23764562Sgshapiro smi_log(SMI_LOG_ERR, 23864562Sgshapiro "%s: unknown port name %s", 23964562Sgshapiro name, colon); 24066494Sgshapiro return INVALID_SOCKET; 24164562Sgshapiro } 24264562Sgshapiro port = sp->s_port; 24364562Sgshapiro# endif /* NO_GETSERVBYNAME */ 24464562Sgshapiro } 24564562Sgshapiro if (at != NULL) 24664562Sgshapiro { 24764562Sgshapiro *at++ = '@'; 24864562Sgshapiro if (*at == '[') 24964562Sgshapiro { 25064562Sgshapiro char *end; 25164562Sgshapiro 25264562Sgshapiro end = strchr(at, ']'); 25364562Sgshapiro if (end != NULL) 25464562Sgshapiro { 25564562Sgshapiro bool found = FALSE; 25664562Sgshapiro# if NETINET 25764562Sgshapiro unsigned long hid = INADDR_NONE; 25864562Sgshapiro# endif /* NETINET */ 25964562Sgshapiro# if NETINET6 26064562Sgshapiro struct sockaddr_in6 hid6; 26164562Sgshapiro# endif /* NETINET6 */ 26264562Sgshapiro 26364562Sgshapiro *end = '\0'; 26464562Sgshapiro# if NETINET 26564562Sgshapiro if (addr.sa.sa_family == AF_INET && 26664562Sgshapiro (hid = inet_addr(&at[1])) != 26764562Sgshapiro INADDR_NONE) 26864562Sgshapiro { 26964562Sgshapiro addr.sin.sin_addr.s_addr = hid; 27064562Sgshapiro addr.sin.sin_port = port; 27164562Sgshapiro found = TRUE; 27264562Sgshapiro } 27364562Sgshapiro# endif /* NETINET */ 27464562Sgshapiro# if NETINET6 27564562Sgshapiro (void) memset(&hid6, '\0', sizeof hid6); 27664562Sgshapiro if (addr.sa.sa_family == AF_INET6 && 27764562Sgshapiro inet_pton(AF_INET6, &at[1], 27864562Sgshapiro &hid6.sin6_addr) == 1) 27964562Sgshapiro { 28064562Sgshapiro addr.sin6.sin6_addr = hid6.sin6_addr; 28164562Sgshapiro addr.sin6.sin6_port = port; 28264562Sgshapiro found = TRUE; 28364562Sgshapiro } 28464562Sgshapiro# endif /* NETINET6 */ 28564562Sgshapiro *end = ']'; 28664562Sgshapiro if (!found) 28764562Sgshapiro { 28864562Sgshapiro smi_log(SMI_LOG_ERR, 28964562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 29064562Sgshapiro name, at); 29166494Sgshapiro return INVALID_SOCKET; 29264562Sgshapiro } 29364562Sgshapiro } 29464562Sgshapiro else 29564562Sgshapiro { 29664562Sgshapiro smi_log(SMI_LOG_ERR, 29764562Sgshapiro "%s: Invalid numeric domain spec \"%s\"", 29864562Sgshapiro name, at); 29966494Sgshapiro return INVALID_SOCKET; 30064562Sgshapiro } 30164562Sgshapiro } 30264562Sgshapiro else 30364562Sgshapiro { 30471345Sgshapiro struct hostent *hp = NULL; 30571345Sgshapiro 30664562Sgshapiro hp = mi_gethostbyname(at, addr.sa.sa_family); 30764562Sgshapiro if (hp == NULL) 30864562Sgshapiro { 30964562Sgshapiro smi_log(SMI_LOG_ERR, 31064562Sgshapiro "%s: Unknown host name %s", 31164562Sgshapiro name, at); 31266494Sgshapiro return INVALID_SOCKET; 31364562Sgshapiro } 31464562Sgshapiro addr.sa.sa_family = hp->h_addrtype; 31564562Sgshapiro switch (hp->h_addrtype) 31664562Sgshapiro { 31764562Sgshapiro# if NETINET 31864562Sgshapiro case AF_INET: 31964562Sgshapiro memmove(&addr.sin.sin_addr, 32064562Sgshapiro hp->h_addr, 32164562Sgshapiro INADDRSZ); 32264562Sgshapiro addr.sin.sin_port = port; 32364562Sgshapiro break; 32464562Sgshapiro# endif /* NETINET */ 32564562Sgshapiro 32664562Sgshapiro# if NETINET6 32764562Sgshapiro case AF_INET6: 32864562Sgshapiro memmove(&addr.sin6.sin6_addr, 32964562Sgshapiro hp->h_addr, 33064562Sgshapiro IN6ADDRSZ); 33164562Sgshapiro addr.sin6.sin6_port = port; 33264562Sgshapiro break; 33364562Sgshapiro# endif /* NETINET6 */ 33464562Sgshapiro 33564562Sgshapiro default: 33664562Sgshapiro smi_log(SMI_LOG_ERR, 33764562Sgshapiro "%s: Unknown protocol for %s (%d)", 33864562Sgshapiro name, at, hp->h_addrtype); 33966494Sgshapiro return INVALID_SOCKET; 34064562Sgshapiro } 34171345Sgshapiro# if _FFR_FREEHOSTENT && NETINET6 34271345Sgshapiro freehostent(hp); 34371345Sgshapiro# endif /* _FFR_FREEHOSTENT && NETINET6 */ 34464562Sgshapiro } 34564562Sgshapiro } 34664562Sgshapiro else 34764562Sgshapiro { 34864562Sgshapiro switch (addr.sa.sa_family) 34964562Sgshapiro { 35064562Sgshapiro# if NETINET 35164562Sgshapiro case AF_INET: 35264562Sgshapiro addr.sin.sin_port = port; 35364562Sgshapiro break; 35464562Sgshapiro# endif /* NETINET */ 35564562Sgshapiro# if NETINET6 35664562Sgshapiro case AF_INET6: 35764562Sgshapiro addr.sin6.sin6_port = port; 35864562Sgshapiro break; 35964562Sgshapiro# endif /* NETINET6 */ 36064562Sgshapiro } 36164562Sgshapiro } 36264562Sgshapiro } 36364562Sgshapiro#endif /* NETINET || NETINET6 */ 36464562Sgshapiro 36564562Sgshapiro sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 36664562Sgshapiro if (!ValidSocket(sock)) 36764562Sgshapiro { 36864562Sgshapiro smi_log(SMI_LOG_ERR, 36964562Sgshapiro "%s: Unable to create new socket: %s", 37064562Sgshapiro name, strerror(errno)); 37166494Sgshapiro return INVALID_SOCKET; 37264562Sgshapiro } 37364562Sgshapiro 37464562Sgshapiro if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 37564562Sgshapiro sizeof(sockopt)) == -1) 37664562Sgshapiro { 37764562Sgshapiro smi_log(SMI_LOG_ERR, 37864562Sgshapiro "%s: Unable to setsockopt: %s", name, strerror(errno)); 37964562Sgshapiro (void) close(sock); 38066494Sgshapiro return INVALID_SOCKET; 38164562Sgshapiro } 38264562Sgshapiro 38364562Sgshapiro if (bind(sock, &addr.sa, *socksize) < 0) 38464562Sgshapiro { 38564562Sgshapiro smi_log(SMI_LOG_ERR, 38664562Sgshapiro "%s: Unable to bind to port %s: %s", 38764562Sgshapiro name, conn, strerror(errno)); 38864562Sgshapiro (void) close(sock); 38966494Sgshapiro return INVALID_SOCKET; 39064562Sgshapiro } 39164562Sgshapiro 39264562Sgshapiro if (listen(sock, backlog) < 0) 39364562Sgshapiro { 39464562Sgshapiro smi_log(SMI_LOG_ERR, 39564562Sgshapiro "%s: listen call failed: %s", name, strerror(errno)); 39664562Sgshapiro (void) close(sock); 39766494Sgshapiro return INVALID_SOCKET; 39864562Sgshapiro } 39973188Sgshapiro *family = addr.sa.sa_family; 40064562Sgshapiro return sock; 40164562Sgshapiro} 40264562Sgshapiro/* 40364562Sgshapiro** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 40464562Sgshapiro** 40564562Sgshapiro** Parameters: 40664562Sgshapiro** arg -- argument to pass to mi_handle_session() 40764562Sgshapiro** 40864562Sgshapiro** Returns: 40964562Sgshapiro** results from mi_handle_session() 41064562Sgshapiro*/ 41164562Sgshapiro 41264562Sgshapirovoid * 41364562Sgshapiromi_thread_handle_wrapper(arg) 41464562Sgshapiro void *arg; 41564562Sgshapiro{ 41664562Sgshapiro return (void *) mi_handle_session(arg); 41764562Sgshapiro} 41864562Sgshapiro 41966494Sgshapirostatic socket_t listenfd = INVALID_SOCKET; 42066494Sgshapiro 42171345Sgshapirostatic smutex_t L_Mutex; 42271345Sgshapiro 42364562Sgshapiro/* 42466494Sgshapiro** MI_CLOSENER -- close listen socket 42564562Sgshapiro** 42666494Sgshapiro** Parameters: 42766494Sgshapiro** none. 42866494Sgshapiro** 42966494Sgshapiro** Returns: 43066494Sgshapiro** none. 43166494Sgshapiro*/ 43266494Sgshapiro 43366494Sgshapirovoid 43466494Sgshapiromi_closener() 43566494Sgshapiro{ 43671345Sgshapiro (void) smutex_lock(&L_Mutex); 43766494Sgshapiro if (ValidSocket(listenfd)) 43866494Sgshapiro { 43966494Sgshapiro (void) close(listenfd); 44066494Sgshapiro listenfd = INVALID_SOCKET; 44166494Sgshapiro } 44271345Sgshapiro (void) smutex_unlock(&L_Mutex); 44366494Sgshapiro} 44466494Sgshapiro 44566494Sgshapiro/* 44666494Sgshapiro** MI_LISTENER -- Generic listener harness 44766494Sgshapiro** 44864562Sgshapiro** Open up listen port 44964562Sgshapiro** Wait for connections 45064562Sgshapiro** 45164562Sgshapiro** Parameters: 45264562Sgshapiro** conn -- connection description 45364562Sgshapiro** dbg -- debug level 45464562Sgshapiro** smfi -- filter structure to use 45564562Sgshapiro** timeout -- timeout for reads/writes 45664562Sgshapiro** 45764562Sgshapiro** Returns: 45864562Sgshapiro** MI_SUCCESS -- Exited normally 45964562Sgshapiro** (session finished or we were told to exit) 46064562Sgshapiro** MI_FAILURE -- Network initialization failed. 46164562Sgshapiro*/ 46264562Sgshapiro 46373188Sgshapiro# if BROKEN_PTHREAD_SLEEP 46473188Sgshapiro 46573188Sgshapiro/* 46673188Sgshapiro** Solaris 2.6, perhaps others, gets an internal threads library panic 46773188Sgshapiro** when sleep() is used: 46873188Sgshapiro** 46973188Sgshapiro** thread_create() failed, returned 11 (EINVAL) 47073188Sgshapiro** co_enable, thr_create() returned error = 24 47173188Sgshapiro** libthread panic: co_enable failed (PID: 17793 LWP 1) 47273188Sgshapiro** stacktrace: 47373188Sgshapiro** ef526b10 47473188Sgshapiro** ef52646c 47573188Sgshapiro** ef534cbc 47673188Sgshapiro** 156a4 47773188Sgshapiro** 14644 47873188Sgshapiro** 1413c 47973188Sgshapiro** 135e0 48073188Sgshapiro** 0 48173188Sgshapiro*/ 48273188Sgshapiro 48373188Sgshapiro# define MI_SLEEP(s) \ 48473188Sgshapiro{ \ 48573188Sgshapiro int rs = 0; \ 48673188Sgshapiro struct timeval st; \ 48773188Sgshapiro \ 48873188Sgshapiro st.tv_sec = (s); \ 48973188Sgshapiro st.tv_usec = 0; \ 49073188Sgshapiro if (st.tv_sec > 0) \ 49173188Sgshapiro rs = select(0, NULL, NULL, NULL, &st); \ 49273188Sgshapiro if (rs != 0) \ 49373188Sgshapiro { \ 49473188Sgshapiro smi_log(SMI_LOG_ERR, \ 49573188Sgshapiro "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \ 49673188Sgshapiro rs, errno); \ 49773188Sgshapiro } \ 49873188Sgshapiro} 49973188Sgshapiro# else /* BROKEN_PTHREAD_SLEEP */ 50073188Sgshapiro# define MI_SLEEP(s) sleep((s)) 50173188Sgshapiro# endif /* BROKEN_PTHREAD_SLEEP */ 50273188Sgshapiro 50364562Sgshapiroint 50466494Sgshapiromi_listener(conn, dbg, smfi, timeout, backlog) 50564562Sgshapiro char *conn; 50664562Sgshapiro int dbg; 50764562Sgshapiro smfiDesc_ptr smfi; 50864562Sgshapiro time_t timeout; 50966494Sgshapiro int backlog; 51064562Sgshapiro{ 51166494Sgshapiro socket_t connfd = INVALID_SOCKET; 51273188Sgshapiro int family = AF_UNSPEC; 51364562Sgshapiro int sockopt = 1; 51464562Sgshapiro int r; 51564562Sgshapiro int ret = MI_SUCCESS; 51673188Sgshapiro int mcnt = 0; 51773188Sgshapiro int tcnt = 0; 51877349Sgshapiro int acnt = 0; 51977349Sgshapiro int save_errno = 0; 52064562Sgshapiro sthread_t thread_id; 52164562Sgshapiro _SOCK_ADDR cliaddr; 52264562Sgshapiro SOCKADDR_LEN_T socksize; 52364562Sgshapiro SOCKADDR_LEN_T clilen; 52464562Sgshapiro SMFICTX_PTR ctx; 52564562Sgshapiro fd_set readset, excset; 52664562Sgshapiro struct timeval chktime; 52764562Sgshapiro 52864562Sgshapiro if (dbg > 0) 52964562Sgshapiro smi_log(SMI_LOG_DEBUG, 53064562Sgshapiro "%s: Opening listen socket on conn %s", 53164562Sgshapiro smfi->xxfi_name, conn); 53271345Sgshapiro (void) smutex_init(&L_Mutex); 53371345Sgshapiro (void) smutex_lock(&L_Mutex); 53473188Sgshapiro listenfd = mi_milteropen(conn, backlog, &socksize, &family, 53573188Sgshapiro smfi->xxfi_name); 53666494Sgshapiro if (!ValidSocket(listenfd)) 53764562Sgshapiro { 53864562Sgshapiro smi_log(SMI_LOG_FATAL, 53964562Sgshapiro "%s: Unable to create listening socket on conn %s", 54064562Sgshapiro smfi->xxfi_name, conn); 54171345Sgshapiro (void) smutex_unlock(&L_Mutex); 54264562Sgshapiro return MI_FAILURE; 54364562Sgshapiro } 54464562Sgshapiro clilen = socksize; 54571345Sgshapiro 54664562Sgshapiro if (listenfd >= FD_SETSIZE) 54764562Sgshapiro { 54864562Sgshapiro smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 54964562Sgshapiro smfi->xxfi_name, listenfd, FD_SETSIZE); 55071345Sgshapiro (void) smutex_unlock(&L_Mutex); 55164562Sgshapiro return MI_FAILURE; 55264562Sgshapiro } 55371345Sgshapiro (void) smutex_unlock(&L_Mutex); 55464562Sgshapiro 55564562Sgshapiro while (mi_stop() == MILTER_CONT) 55664562Sgshapiro { 55771345Sgshapiro (void) smutex_lock(&L_Mutex); 55871345Sgshapiro if (!ValidSocket(listenfd)) 55971345Sgshapiro { 56071345Sgshapiro (void) smutex_unlock(&L_Mutex); 56171345Sgshapiro break; 56271345Sgshapiro } 56371345Sgshapiro 56464562Sgshapiro /* select on interface ports */ 56564562Sgshapiro FD_ZERO(&readset); 56671345Sgshapiro FD_ZERO(&excset); 56764562Sgshapiro FD_SET((u_int) listenfd, &readset); 56864562Sgshapiro FD_SET((u_int) listenfd, &excset); 56964562Sgshapiro chktime.tv_sec = MI_CHK_TIME; 57064562Sgshapiro chktime.tv_usec = 0; 57164562Sgshapiro r = select(listenfd + 1, &readset, NULL, &excset, &chktime); 57264562Sgshapiro if (r == 0) /* timeout */ 57371345Sgshapiro { 57471345Sgshapiro (void) smutex_unlock(&L_Mutex); 57564562Sgshapiro continue; /* just check mi_stop() */ 57671345Sgshapiro } 57764562Sgshapiro if (r < 0) 57864562Sgshapiro { 57977349Sgshapiro save_errno = errno; 58071345Sgshapiro (void) smutex_unlock(&L_Mutex); 58177349Sgshapiro if (save_errno == EINTR) 58264562Sgshapiro continue; 58364562Sgshapiro ret = MI_FAILURE; 58464562Sgshapiro break; 58564562Sgshapiro } 58664562Sgshapiro if (!FD_ISSET(listenfd, &readset)) 58764562Sgshapiro { 58864562Sgshapiro /* some error: just stop for now... */ 58964562Sgshapiro ret = MI_FAILURE; 59071345Sgshapiro (void) smutex_unlock(&L_Mutex); 59164562Sgshapiro break; 59264562Sgshapiro } 59364562Sgshapiro 59473188Sgshapiro memset(&cliaddr, '\0', sizeof cliaddr); 59564562Sgshapiro connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 59664562Sgshapiro &clilen); 59777349Sgshapiro save_errno = errno; 59871345Sgshapiro (void) smutex_unlock(&L_Mutex); 59964562Sgshapiro 60073188Sgshapiro /* 60173188Sgshapiro ** If remote side closes before 60273188Sgshapiro ** accept() finishes, sockaddr 60373188Sgshapiro ** might not be fully filled in. 60473188Sgshapiro */ 60573188Sgshapiro 60673188Sgshapiro if (ValidSocket(connfd) && 60773188Sgshapiro (clilen == 0 || 60873188Sgshapiro# ifdef BSD4_4_SOCKADDR 60973188Sgshapiro cliaddr.sa.sa_len == 0 || 61073188Sgshapiro# endif /* BSD4_4_SOCKADDR */ 61173188Sgshapiro cliaddr.sa.sa_family != family)) 61273188Sgshapiro { 61373188Sgshapiro (void) close(connfd); 61473188Sgshapiro connfd = INVALID_SOCKET; 61577349Sgshapiro save_errno = EINVAL; 61673188Sgshapiro } 61773188Sgshapiro 61866494Sgshapiro if (!ValidSocket(connfd)) 61964562Sgshapiro { 62064562Sgshapiro smi_log(SMI_LOG_ERR, 62177349Sgshapiro "%s: accept() returned invalid socket (%s)", 62277349Sgshapiro smfi->xxfi_name, strerror(save_errno)); 62377349Sgshapiro if (save_errno == EINTR) 62477349Sgshapiro continue; 62577349Sgshapiro acnt++; 62677349Sgshapiro MI_SLEEP(acnt); 62777349Sgshapiro if (acnt >= MAX_FAILS_A) 62877349Sgshapiro { 62977349Sgshapiro ret = MI_FAILURE; 63077349Sgshapiro break; 63177349Sgshapiro } 63264562Sgshapiro continue; 63364562Sgshapiro } 63464562Sgshapiro 63564562Sgshapiro if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 63664562Sgshapiro (void *) &sockopt, sizeof sockopt) < 0) 63764562Sgshapiro { 63864562Sgshapiro smi_log(SMI_LOG_WARN, "%s: setsockopt() failed", 63964562Sgshapiro smfi->xxfi_name); 64064562Sgshapiro /* XXX: continue? */ 64164562Sgshapiro } 64264562Sgshapiro if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 64364562Sgshapiro { 64464562Sgshapiro (void) close(connfd); 64564562Sgshapiro smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed", 64664562Sgshapiro smfi->xxfi_name); 64773188Sgshapiro mcnt++; 64873188Sgshapiro MI_SLEEP(mcnt); 64973188Sgshapiro if (mcnt >= MAX_FAILS_M) 65064562Sgshapiro { 65164562Sgshapiro ret = MI_FAILURE; 65264562Sgshapiro break; 65364562Sgshapiro } 65464562Sgshapiro continue; 65564562Sgshapiro } 65673188Sgshapiro mcnt = 0; 65777349Sgshapiro acnt = 0; 65864562Sgshapiro memset(ctx, '\0', sizeof *ctx); 65964562Sgshapiro ctx->ctx_sd = connfd; 66064562Sgshapiro ctx->ctx_dbg = dbg; 66164562Sgshapiro ctx->ctx_timeout = timeout; 66264562Sgshapiro ctx->ctx_smfi = smfi; 66364562Sgshapiro#if 0 66464562Sgshapiro if (smfi->xxfi_eoh == NULL) 66564562Sgshapiro if (smfi->xxfi_eom == NULL) 66664562Sgshapiro if (smfi->xxfi_abort == NULL) 66764562Sgshapiro if (smfi->xxfi_close == NULL) 66864562Sgshapiro#endif /* 0 */ 66964562Sgshapiro if (smfi->xxfi_connect == NULL) 67064562Sgshapiro ctx->ctx_pflags |= SMFIP_NOCONNECT; 67164562Sgshapiro if (smfi->xxfi_helo == NULL) 67264562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHELO; 67364562Sgshapiro if (smfi->xxfi_envfrom == NULL) 67464562Sgshapiro ctx->ctx_pflags |= SMFIP_NOMAIL; 67564562Sgshapiro if (smfi->xxfi_envrcpt == NULL) 67664562Sgshapiro ctx->ctx_pflags |= SMFIP_NORCPT; 67764562Sgshapiro if (smfi->xxfi_header == NULL) 67864562Sgshapiro ctx->ctx_pflags |= SMFIP_NOHDRS; 67964562Sgshapiro if (smfi->xxfi_eoh == NULL) 68064562Sgshapiro ctx->ctx_pflags |= SMFIP_NOEOH; 68164562Sgshapiro if (smfi->xxfi_body == NULL) 68264562Sgshapiro ctx->ctx_pflags |= SMFIP_NOBODY; 68364562Sgshapiro 68464562Sgshapiro if ((r = thread_create(&thread_id, 68564562Sgshapiro mi_thread_handle_wrapper, 68671345Sgshapiro (void *) ctx)) != 0) 68764562Sgshapiro { 68864562Sgshapiro smi_log(SMI_LOG_ERR, 68964562Sgshapiro "%s: thread_create() failed: %d", 69064562Sgshapiro smfi->xxfi_name, r); 69173188Sgshapiro tcnt++; 69273188Sgshapiro MI_SLEEP(tcnt); 69364562Sgshapiro (void) close(connfd); 69464562Sgshapiro free(ctx); 69573188Sgshapiro if (tcnt >= MAX_FAILS_T) 69664562Sgshapiro { 69764562Sgshapiro ret = MI_FAILURE; 69864562Sgshapiro break; 69964562Sgshapiro } 70064562Sgshapiro continue; 70164562Sgshapiro } 70273188Sgshapiro tcnt = 0; 70364562Sgshapiro } 70464562Sgshapiro if (ret != MI_SUCCESS) 70564562Sgshapiro mi_stop_milters(MILTER_ABRT); 70671345Sgshapiro else 70771345Sgshapiro mi_closener(); 70871345Sgshapiro (void) smutex_destroy(&L_Mutex); 70964562Sgshapiro return ret; 71064562Sgshapiro} 71164562Sgshapiro#endif /* _FFR_MILTER */ 712