listener.c revision 64562
1/* 2 * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11#ifndef lint 12static char id[] = "@(#)$Id: listener.c,v 8.38.2.1.2.7 2000/05/25 21:44:26 gshapiro Exp $"; 13#endif /* ! lint */ 14 15#if _FFR_MILTER 16/* 17** listener.c -- threaded network listener 18*/ 19 20#include "libmilter.h" 21 22 23# if NETINET || NETINET6 24# include <arpa/inet.h> 25# endif /* NETINET || NETINET6 */ 26/* 27** MI_MILTEROPEN -- setup socket to listen on 28** 29** Parameters: 30** conn -- connection description 31** backlog -- listen backlog 32** socksize -- socksize of created socket 33** 34** Returns: 35** socket upon success, error code otherwise. 36*/ 37 38static int 39mi_milteropen(conn, backlog, socksize, name) 40 char *conn; 41 int backlog; 42 SOCKADDR_LEN_T *socksize; 43 char *name; 44{ 45 int sock = 0; 46 int sockopt = 1; 47 char *p; 48 char *colon; 49 char *at; 50 struct hostent *hp = NULL; 51 SOCKADDR addr; 52 53 if (conn == NULL || conn[0] == '\0') 54 { 55 smi_log(SMI_LOG_ERR, "%s: empty or missing socket information", 56 name); 57 return MI_INVALID_SOCKET; 58 } 59 (void) memset(&addr, '\0', sizeof addr); 60 61 /* protocol:filename or protocol:port@host */ 62 p = conn; 63 colon = strchr(p, ':'); 64 if (colon != NULL) 65 { 66 *colon = '\0'; 67 68 if (*p == '\0') 69 { 70#if NETUNIX 71 /* default to AF_UNIX */ 72 addr.sa.sa_family = AF_UNIX; 73 *socksize = sizeof (struct sockaddr_un); 74#else /* NETUNIX */ 75# if NETINET 76 /* default to AF_INET */ 77 addr.sa.sa_family = AF_INET; 78 *socksize = sizeof addr.sin; 79# else /* NETINET */ 80# if NETINET6 81 /* default to AF_INET6 */ 82 addr.sa.sa_family = AF_INET6; 83 *socksize = sizeof addr.sin6; 84# else /* NETINET6 */ 85 /* no protocols available */ 86 smi_log(SMI_LOG_ERR, 87 "%s: no valid socket protocols available", 88 name); 89 return MI_INVALID_SOCKET; 90# endif /* NETINET6 */ 91# endif /* NETINET */ 92#endif /* NETUNIX */ 93 } 94#if NETUNIX 95 else if (strcasecmp(p, "unix") == 0 || 96 strcasecmp(p, "local") == 0) 97 { 98 addr.sa.sa_family = AF_UNIX; 99 *socksize = sizeof (struct sockaddr_un); 100 } 101#endif /* NETUNIX */ 102#if NETINET 103 else if (strcasecmp(p, "inet") == 0) 104 { 105 addr.sa.sa_family = AF_INET; 106 *socksize = sizeof addr.sin; 107 } 108#endif /* NETINET */ 109#if NETINET6 110 else if (strcasecmp(p, "inet6") == 0) 111 { 112 addr.sa.sa_family = AF_INET6; 113 *socksize = sizeof addr.sin6; 114 } 115#endif /* NETINET6 */ 116 else 117 { 118 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 119 name, p); 120 return MI_INVALID_SOCKET; 121 } 122 *colon++ = ':'; 123 } 124 else 125 { 126 colon = p; 127#if NETUNIX 128 /* default to AF_UNIX */ 129 addr.sa.sa_family = AF_UNIX; 130 *socksize = sizeof (struct sockaddr_un); 131#else /* NETUNIX */ 132# if NETINET 133 /* default to AF_INET */ 134 addr.sa.sa_family = AF_INET; 135 *socksize = sizeof addr.sin; 136# else /* NETINET */ 137# if NETINET6 138 /* default to AF_INET6 */ 139 addr.sa.sa_family = AF_INET6; 140 *socksize = sizeof addr.sin6; 141# else /* NETINET6 */ 142 smi_log(SMI_LOG_ERR, "%s: unknown socket type %s", 143 name, p); 144 return MI_INVALID_SOCKET; 145# endif /* NETINET6 */ 146# endif /* NETINET */ 147#endif /* NETUNIX */ 148 } 149 150#if NETUNIX 151 if (addr.sa.sa_family == AF_UNIX) 152 { 153# if 0 154 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 155# endif /* 0 */ 156 157 at = colon; 158 if (strlcpy(addr.sunix.sun_path, colon, 159 sizeof addr.sunix.sun_path) >= 160 sizeof addr.sunix.sun_path) 161 { 162 errno = EINVAL; 163 smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long", 164 name, colon); 165 return MI_INVALID_SOCKET; 166 } 167# if 0 168 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 169 S_IRUSR|S_IWUSR, NULL); 170 171 /* if not safe, don't create */ 172 if (errno != 0) 173 { 174 smi_log(SMI_LOG_ERR, 175 "%s: UNIX socket name %s unsafe", 176 name, colon); 177 return MI_INVALID_SOCKET; 178 } 179# endif /* 0 */ 180 181 } 182#endif /* NETUNIX */ 183 184#if NETINET || NETINET6 185 if ( 186# if NETINET 187 addr.sa.sa_family == AF_INET 188# endif /* NETINET */ 189# if NETINET && NETINET6 190 || 191# endif /* NETINET && NETINET6 */ 192# if NETINET6 193 addr.sa.sa_family == AF_INET6 194# endif /* NETINET6 */ 195 ) 196 { 197 u_short port; 198 199 /* Parse port@host */ 200 at = strchr(colon, '@'); 201 if (at == NULL) 202 { 203 switch (addr.sa.sa_family) 204 { 205# if NETINET 206 case AF_INET: 207 addr.sin.sin_addr.s_addr = INADDR_ANY; 208 break; 209# endif /* NETINET */ 210 211# if NETINET6 212 case AF_INET6: 213 addr.sin6.sin6_addr = in6addr_any; 214 break; 215# endif /* NETINET6 */ 216 } 217 } 218 else 219 *at = '\0'; 220 221 if (isascii(*colon) && isdigit(*colon)) 222 port = htons((u_short) atoi(colon)); 223 else 224 { 225# ifdef NO_GETSERVBYNAME 226 smi_log(SMI_LOG_ERR, "%s: invalid port number %s", 227 name, colon); 228 return MI_INVALID_SOCKET; 229# else /* NO_GETSERVBYNAME */ 230 register struct servent *sp; 231 232 sp = getservbyname(colon, "tcp"); 233 if (sp == NULL) 234 { 235 smi_log(SMI_LOG_ERR, 236 "%s: unknown port name %s", 237 name, colon); 238 return MI_INVALID_SOCKET; 239 } 240 port = sp->s_port; 241# endif /* NO_GETSERVBYNAME */ 242 } 243 if (at != NULL) 244 { 245 *at++ = '@'; 246 if (*at == '[') 247 { 248 char *end; 249 250 end = strchr(at, ']'); 251 if (end != NULL) 252 { 253 bool found = FALSE; 254# if NETINET 255 unsigned long hid = INADDR_NONE; 256# endif /* NETINET */ 257# if NETINET6 258 struct sockaddr_in6 hid6; 259# endif /* NETINET6 */ 260 261 *end = '\0'; 262# if NETINET 263 if (addr.sa.sa_family == AF_INET && 264 (hid = inet_addr(&at[1])) != 265 INADDR_NONE) 266 { 267 addr.sin.sin_addr.s_addr = hid; 268 addr.sin.sin_port = port; 269 found = TRUE; 270 } 271# endif /* NETINET */ 272# if NETINET6 273 (void) memset(&hid6, '\0', sizeof hid6); 274 if (addr.sa.sa_family == AF_INET6 && 275 inet_pton(AF_INET6, &at[1], 276 &hid6.sin6_addr) == 1) 277 { 278 addr.sin6.sin6_addr = hid6.sin6_addr; 279 addr.sin6.sin6_port = port; 280 found = TRUE; 281 } 282# endif /* NETINET6 */ 283 *end = ']'; 284 if (!found) 285 { 286 smi_log(SMI_LOG_ERR, 287 "%s: Invalid numeric domain spec \"%s\"", 288 name, at); 289 return MI_INVALID_SOCKET; 290 } 291 } 292 else 293 { 294 smi_log(SMI_LOG_ERR, 295 "%s: Invalid numeric domain spec \"%s\"", 296 name, at); 297 return MI_INVALID_SOCKET; 298 } 299 } 300 else 301 { 302 hp = mi_gethostbyname(at, addr.sa.sa_family); 303 if (hp == NULL) 304 { 305 smi_log(SMI_LOG_ERR, 306 "%s: Unknown host name %s", 307 name, at); 308 return MI_INVALID_SOCKET; 309 } 310 addr.sa.sa_family = hp->h_addrtype; 311 switch (hp->h_addrtype) 312 { 313# if NETINET 314 case AF_INET: 315 memmove(&addr.sin.sin_addr, 316 hp->h_addr, 317 INADDRSZ); 318 addr.sin.sin_port = port; 319 break; 320# endif /* NETINET */ 321 322# if NETINET6 323 case AF_INET6: 324 memmove(&addr.sin6.sin6_addr, 325 hp->h_addr, 326 IN6ADDRSZ); 327 addr.sin6.sin6_port = port; 328 break; 329# endif /* NETINET6 */ 330 331 default: 332 smi_log(SMI_LOG_ERR, 333 "%s: Unknown protocol for %s (%d)", 334 name, at, hp->h_addrtype); 335 return MI_INVALID_SOCKET; 336 } 337 } 338 } 339 else 340 { 341 switch (addr.sa.sa_family) 342 { 343# if NETINET 344 case AF_INET: 345 addr.sin.sin_port = port; 346 break; 347# endif /* NETINET */ 348# if NETINET6 349 case AF_INET6: 350 addr.sin6.sin6_port = port; 351 break; 352# endif /* NETINET6 */ 353 } 354 } 355 } 356#endif /* NETINET || NETINET6 */ 357 358 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 359 if (!ValidSocket(sock)) 360 { 361 smi_log(SMI_LOG_ERR, 362 "%s: Unable to create new socket: %s", 363 name, strerror(errno)); 364 return MI_INVALID_SOCKET; 365 } 366 367 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt, 368 sizeof(sockopt)) == -1) 369 { 370 smi_log(SMI_LOG_ERR, 371 "%s: Unable to setsockopt: %s", name, strerror(errno)); 372 (void) close(sock); 373 return MI_INVALID_SOCKET; 374 } 375 376 if (bind(sock, &addr.sa, *socksize) < 0) 377 { 378 smi_log(SMI_LOG_ERR, 379 "%s: Unable to bind to port %s: %s", 380 name, conn, strerror(errno)); 381 (void) close(sock); 382 return MI_INVALID_SOCKET; 383 } 384 385 if (listen(sock, backlog) < 0) 386 { 387 smi_log(SMI_LOG_ERR, 388 "%s: listen call failed: %s", name, strerror(errno)); 389 (void) close(sock); 390 return MI_INVALID_SOCKET; 391 } 392 393 return sock; 394} 395/* 396** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session 397** 398** Parameters: 399** arg -- argument to pass to mi_handle_session() 400** 401** Returns: 402** results from mi_handle_session() 403*/ 404 405void * 406mi_thread_handle_wrapper(arg) 407 void *arg; 408{ 409 return (void *) mi_handle_session(arg); 410} 411 412/* 413** MI_MILTER_LISTENER -- Generic listener harness 414** 415** Open up listen port 416** Wait for connections 417** 418** Parameters: 419** conn -- connection description 420** dbg -- debug level 421** smfi -- filter structure to use 422** timeout -- timeout for reads/writes 423** 424** Returns: 425** MI_SUCCESS -- Exited normally 426** (session finished or we were told to exit) 427** MI_FAILURE -- Network initialization failed. 428*/ 429 430int 431mi_listener(conn, dbg, smfi, timeout) 432 char *conn; 433 int dbg; 434 smfiDesc_ptr smfi; 435 time_t timeout; 436{ 437 int connfd = -1; 438 int listenfd = -1; 439 int sockopt = 1; 440 int r; 441 int ret = MI_SUCCESS; 442 int cnt_m = 0; 443 int cnt_t = 0; 444 sthread_t thread_id; 445 _SOCK_ADDR cliaddr; 446 SOCKADDR_LEN_T socksize; 447 SOCKADDR_LEN_T clilen; 448 SMFICTX_PTR ctx; 449 fd_set readset, excset; 450 struct timeval chktime; 451 452 if (dbg > 0) 453 smi_log(SMI_LOG_DEBUG, 454 "%s: Opening listen socket on conn %s", 455 smfi->xxfi_name, conn); 456 if ((listenfd = mi_milteropen(conn, SOMAXCONN, &socksize, 457 smfi->xxfi_name)) < 0) 458 { 459 smi_log(SMI_LOG_FATAL, 460 "%s: Unable to create listening socket on conn %s", 461 smfi->xxfi_name, conn); 462 return MI_FAILURE; 463 } 464 clilen = socksize; 465 if (listenfd >= FD_SETSIZE) 466 { 467 smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d", 468 smfi->xxfi_name, listenfd, FD_SETSIZE); 469 return MI_FAILURE; 470 } 471 472 while (mi_stop() == MILTER_CONT) 473 { 474 /* select on interface ports */ 475 FD_ZERO(&readset); 476 FD_SET((u_int) listenfd, &readset); 477 FD_ZERO(&excset); 478 FD_SET((u_int) listenfd, &excset); 479 chktime.tv_sec = MI_CHK_TIME; 480 chktime.tv_usec = 0; 481 r = select(listenfd + 1, &readset, NULL, &excset, &chktime); 482 if (r == 0) /* timeout */ 483 continue; /* just check mi_stop() */ 484 if (r < 0) 485 { 486 if (errno == EINTR) 487 continue; 488 ret = MI_FAILURE; 489 break; 490 } 491 if (!FD_ISSET(listenfd, &readset)) 492 { 493 /* some error: just stop for now... */ 494 ret = MI_FAILURE; 495 break; 496 } 497 498 connfd = accept(listenfd, (struct sockaddr *) &cliaddr, 499 &clilen); 500 501 if (connfd < 0) 502 { 503 smi_log(SMI_LOG_ERR, 504 "%s: accept() returned invalid socket", 505 smfi->xxfi_name); 506 continue; 507 } 508 509 if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE, 510 (void *) &sockopt, sizeof sockopt) < 0) 511 { 512 smi_log(SMI_LOG_WARN, "%s: setsockopt() failed", 513 smfi->xxfi_name); 514 /* XXX: continue? */ 515 } 516 if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL) 517 { 518 (void) close(connfd); 519 smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed", 520 smfi->xxfi_name); 521 sleep(++cnt_m); 522 if (cnt_m >= MAX_FAILS_M) 523 { 524 ret = MI_FAILURE; 525 break; 526 } 527 continue; 528 } 529 cnt_m = 0; 530 memset(ctx, '\0', sizeof *ctx); 531 ctx->ctx_sd = connfd; 532 ctx->ctx_dbg = dbg; 533 ctx->ctx_timeout = timeout; 534 ctx->ctx_smfi = smfi; 535#if 0 536 if (smfi->xxfi_eoh == NULL) 537 if (smfi->xxfi_eom == NULL) 538 if (smfi->xxfi_abort == NULL) 539 if (smfi->xxfi_close == NULL) 540#endif /* 0 */ 541 if (smfi->xxfi_connect == NULL) 542 ctx->ctx_pflags |= SMFIP_NOCONNECT; 543 if (smfi->xxfi_helo == NULL) 544 ctx->ctx_pflags |= SMFIP_NOHELO; 545 if (smfi->xxfi_envfrom == NULL) 546 ctx->ctx_pflags |= SMFIP_NOMAIL; 547 if (smfi->xxfi_envrcpt == NULL) 548 ctx->ctx_pflags |= SMFIP_NORCPT; 549 if (smfi->xxfi_header == NULL) 550 ctx->ctx_pflags |= SMFIP_NOHDRS; 551 if (smfi->xxfi_eoh == NULL) 552 ctx->ctx_pflags |= SMFIP_NOEOH; 553 if (smfi->xxfi_body == NULL) 554 ctx->ctx_pflags |= SMFIP_NOBODY; 555 556 if ((r = thread_create(&thread_id, 557 mi_thread_handle_wrapper, 558 (void *) ctx)) != MI_SUCCESS) 559 { 560 smi_log(SMI_LOG_ERR, 561 "%s: thread_create() failed: %d", 562 smfi->xxfi_name, r); 563 sleep(++cnt_t); 564 (void) close(connfd); 565 free(ctx); 566 if (cnt_t >= MAX_FAILS_T) 567 { 568 ret = MI_FAILURE; 569 break; 570 } 571 continue; 572 } 573 cnt_t = 0; 574 } 575 if (ret != MI_SUCCESS) 576 mi_stop_milters(MILTER_ABRT); 577 if (listenfd >= 0) 578 (void) close(listenfd); 579 return ret; 580} 581#endif /* _FFR_MILTER */ 582