Index: head/contrib/sendmail/makemap/makemap.c =================================================================== --- head/contrib/sendmail/makemap/makemap.c (revision 173342) +++ head/contrib/sendmail/makemap/makemap.c (revision 173343) @@ -1,528 +1,528 @@ /* * Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1992 Eric P. Allman. All rights reserved. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1992 Eric P. Allman. All rights reserved.\n\ Copyright (c) 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n") -SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.177 2004/08/03 23:57:24 ca Exp $") +SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.178 2007/05/11 18:45:39 ca Exp $") #include #ifndef ISC_UNIX # include #endif /* ! ISC_UNIX */ #include #include #include #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif /* EX_OK */ #include #include #include #include uid_t RealUid; gid_t RealGid; char *RealUserName; uid_t RunAsUid; -uid_t RunAsGid; +gid_t RunAsGid; char *RunAsUserName; int Verbose = 2; bool DontInitGroups = false; uid_t TrustedUid = 0; BITMAP256 DontBlameSendmail; #define BUFSIZE 1024 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep) static void usage __P((char *)); static void usage(progname) char *progname; { sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n", progname); sm_io_fprintf(smioerr, SM_TIME_DEFAULT, " %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n", (int) strlen(progname), ""); sm_io_fprintf(smioerr, SM_TIME_DEFAULT, " %*s [-u] [-v] type mapname\n", (int) strlen(progname), ""); exit(EX_USAGE); } int main(argc, argv) int argc; char **argv; { char *progname; char *cfile; bool inclnull = false; bool notrunc = false; bool allowreplace = false; bool allowempty = false; bool verbose = false; bool foldcase = true; bool unmake = false; char sep = '\0'; char comment = '#'; int exitstat; int opt; char *typename = NULL; char *mapname = NULL; unsigned int lineno; int st; int mode; int smode; int putflags = 0; long sff = SFF_ROOTOK|SFF_REGONLY; struct passwd *pw; SMDB_DATABASE *database; SMDB_CURSOR *cursor; SMDB_DBENT db_key, db_val; SMDB_DBPARAMS params; SMDB_USER_INFO user_info; char ibuf[BUFSIZE]; #if HASFCHOWN SM_FILE_T *cfp; char buf[MAXLINE]; #endif /* HASFCHOWN */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ extern char *optarg; extern int optind; memset(¶ms, '\0', sizeof params); params.smdbp_cache_size = 1024 * 1024; progname = strrchr(argv[0], '/'); if (progname != NULL) progname++; else progname = argv[0]; cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); clrbitmap(DontBlameSendmail); RunAsUid = RealUid = getuid(); RunAsGid = RealGid = getgid(); pw = getpwuid(RealUid); if (pw != NULL) (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); else (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", (int) RealUid); RunAsUserName = RealUserName = rnamebuf; user_info.smdbu_id = RunAsUid; user_info.smdbu_group_id = RunAsGid; (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, SMDB_MAX_USER_NAME_LEN); #define OPTIONS "C:D:Nc:deflorst:uv" while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'C': cfile = optarg; break; case 'N': inclnull = true; break; case 'c': params.smdbp_cache_size = atol(optarg); break; case 'd': params.smdbp_allow_dup = true; break; case 'e': allowempty = true; break; case 'f': foldcase = false; break; case 'D': comment = *optarg; break; case 'l': smdb_print_available_types(); exit(EX_OK); break; case 'o': notrunc = true; break; case 'r': allowreplace = true; break; case 's': setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail); setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail); setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail); setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail); break; case 't': if (optarg == NULL || *optarg == '\0') { sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "Invalid separator\n"); break; } sep = *optarg; break; case 'u': unmake = true; break; case 'v': verbose = true; break; default: usage(progname); /* NOTREACHED */ } } if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) sff |= SFF_NOSLINK; if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) sff |= SFF_NOHLINK; if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) sff |= SFF_NOWLINK; argc -= optind; argv += optind; if (argc != 2) { usage(progname); /* NOTREACHED */ } else { typename = argv[0]; mapname = argv[1]; } #if HASFCHOWN /* Find TrustedUser value in sendmail.cf */ if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, NULL)) == NULL) { sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s", cfile, sm_errstring(errno)); exit(EX_NOINPUT); } while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *b; if ((b = strchr(buf, '\n')) != NULL) *b = '\0'; b = buf; switch (*b++) { case 'O': /* option */ if (strncasecmp(b, " TrustedUser", 12) == 0 && !(isascii(b[12]) && isalnum(b[12]))) { b = strchr(b, '='); if (b == NULL) continue; while (isascii(*++b) && isspace(*b)) continue; if (isascii(*b) && isdigit(*b)) TrustedUid = atoi(b); else { TrustedUid = 0; pw = getpwnam(b); if (pw == NULL) (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "TrustedUser: unknown user %s\n", b); else TrustedUid = pw->pw_uid; } # ifdef UID_MAX if (TrustedUid > UID_MAX) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "TrustedUser: uid value (%ld) > UID_MAX (%ld)", (long) TrustedUid, (long) UID_MAX); TrustedUid = 0; } # endif /* UID_MAX */ break; } default: continue; } } (void) sm_io_close(cfp, SM_TIME_DEFAULT); #endif /* HASFCHOWN */ if (!params.smdbp_allow_dup && !allowreplace) putflags = SMDBF_NO_OVERWRITE; if (unmake) { mode = O_RDONLY; smode = S_IRUSR; } else { mode = O_RDWR; if (!notrunc) { mode |= O_CREAT|O_TRUNC; sff |= SFF_CREAT; } smode = S_IWUSR; } params.smdbp_num_elements = 4096; errno = smdb_open_database(&database, mapname, mode, smode, sff, typename, &user_info, ¶ms); if (errno != SMDBE_OK) { char *hint; if (errno == SMDBE_UNSUPPORTED_DB_TYPE && (hint = smdb_db_definition(typename)) != NULL) (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: Need to recompile with -D%s for %s support\n", progname, hint, typename); else (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: error opening type %s map %s: %s\n", progname, typename, mapname, sm_errstring(errno)); exit(EX_CANTCREAT); } (void) database->smdb_sync(database, 0); if (!unmake && geteuid() == 0 && TrustedUid != 0) { errno = database->smdb_set_owner(database, TrustedUid, -1); if (errno != SMDBE_OK) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "WARNING: ownership change on %s failed %s", mapname, sm_errstring(errno)); } } /* ** Copy the data */ exitstat = EX_OK; if (unmake) { errno = database->smdb_cursor(database, &cursor, 0); if (errno != SMDBE_OK) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: cannot make cursor for type %s map %s\n", progname, typename, mapname); exit(EX_SOFTWARE); } memset(&db_key, '\0', sizeof db_key); memset(&db_val, '\0', sizeof db_val); for (lineno = 0; ; lineno++) { errno = cursor->smdbc_get(cursor, &db_key, &db_val, SMDB_CURSOR_GET_NEXT); if (errno != SMDBE_OK) break; (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\t%.*s\n", (int) db_key.size, (char *) db_key.data, (int) db_val.size, (char *)db_val.data); } (void) cursor->smdbc_close(cursor); } else { lineno = 0; while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) != NULL) { register char *p; lineno++; /* ** Parse the line. */ p = strchr(ibuf, '\n'); if (p != NULL) *p = '\0'; else if (!sm_io_eof(smioin)) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: line %u: line too long (%ld bytes max)\n", progname, mapname, lineno, (long) sizeof ibuf); exitstat = EX_DATAERR; continue; } if (ibuf[0] == '\0' || ibuf[0] == comment) continue; if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0])) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: line %u: syntax error (leading space)\n", progname, mapname, lineno); exitstat = EX_DATAERR; continue; } memset(&db_key, '\0', sizeof db_key); memset(&db_val, '\0', sizeof db_val); db_key.data = ibuf; for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++) { if (foldcase && isascii(*p) && isupper(*p)) *p = tolower(*p); } db_key.size = p - ibuf; if (inclnull) db_key.size++; if (*p != '\0') *p++ = '\0'; while (*p != '\0' && ISSEP(*p)) p++; if (!allowempty && *p == '\0') { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: line %u: no RHS for LHS %s\n", progname, mapname, lineno, (char *) db_key.data); exitstat = EX_DATAERR; continue; } db_val.data = p; db_val.size = strlen(p); if (inclnull) db_val.size++; /* ** Do the database insert. */ if (verbose) { (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "key=`%s', val=`%s'\n", (char *) db_key.data, (char *) db_val.data); } errno = database->smdb_put(database, &db_key, &db_val, putflags); switch (errno) { case SMDBE_KEY_EXIST: st = 1; break; case 0: st = 0; break; default: st = -1; break; } if (st < 0) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: line %u: key %s: put error: %s\n", progname, mapname, lineno, (char *) db_key.data, sm_errstring(errno)); exitstat = EX_IOERR; } else if (st > 0) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: line %u: key %s: duplicate key\n", progname, mapname, lineno, (char *) db_key.data); exitstat = EX_DATAERR; } } } /* ** Now close the database. */ errno = database->smdb_close(database); if (errno != SMDBE_OK) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: close(%s): %s\n", progname, mapname, sm_errstring(errno)); exitstat = EX_IOERR; } smdb_free_database(database); exit(exitstat); /* NOTREACHED */ return exitstat; } Index: head/contrib/sendmail/praliases/praliases.c =================================================================== --- head/contrib/sendmail/praliases/praliases.c (revision 173342) +++ head/contrib/sendmail/praliases/praliases.c (revision 173343) @@ -1,397 +1,397 @@ /* * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include SM_IDSTR(copyright, "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ All rights reserved.\n\ Copyright (c) 1983 Eric P. Allman. All rights reserved.\n\ Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n") -SM_IDSTR(id, "@(#)$Id: praliases.c,v 8.93 2001/09/11 04:05:07 gshapiro Exp $") +SM_IDSTR(id, "@(#)$Id: praliases.c,v 8.94 2007/05/11 18:50:36 ca Exp $") #include #include #include #include #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif /* EX_OK */ #include #ifndef NOT_SENDMAIL # define NOT_SENDMAIL #endif /* ! NOT_SENDMAIL */ #include #include #include static void praliases __P((char *, int, char **)); uid_t RealUid; gid_t RealGid; char *RealUserName; uid_t RunAsUid; -uid_t RunAsGid; +gid_t RunAsGid; char *RunAsUserName; int Verbose = 2; bool DontInitGroups = false; uid_t TrustedUid = 0; BITMAP256 DontBlameSendmail; # define DELIMITERS " ,/" # define PATH_SEPARATOR ':' int main(argc, argv) int argc; char **argv; { char *cfile; char *filename = NULL; SM_FILE_T *cfp; int ch; char afilebuf[MAXLINE]; char buf[MAXLINE]; struct passwd *pw; static char rnamebuf[MAXNAME]; extern char *optarg; extern int optind; clrbitmap(DontBlameSendmail); RunAsUid = RealUid = getuid(); RunAsGid = RealGid = getgid(); pw = getpwuid(RealUid); if (pw != NULL) { if (strlen(pw->pw_name) > MAXNAME - 1) pw->pw_name[MAXNAME] = 0; sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); } else (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", (int) RealUid); RunAsUserName = RealUserName = rnamebuf; cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); while ((ch = getopt(argc, argv, "C:f:")) != -1) { switch ((char)ch) { case 'C': cfile = optarg; break; case 'f': filename = optarg; break; case '?': default: (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "usage: praliases [-C cffile] [-f aliasfile]\n"); exit(EX_USAGE); } } argc -= optind; argv += optind; if (filename != NULL) { praliases(filename, argc, argv); exit(EX_OK); } if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, NULL)) == NULL) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: %s: %s\n", cfile, sm_errstring(errno)); exit(EX_NOINPUT); } while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *b, *p; b = strchr(buf, '\n'); if (b != NULL) *b = '\0'; b = buf; switch (*b++) { case 'O': /* option -- see if alias file */ if (sm_strncasecmp(b, " AliasFile", 10) == 0 && !(isascii(b[10]) && isalnum(b[10]))) { /* new form -- find value */ b = strchr(b, '='); if (b == NULL) continue; while (isascii(*++b) && isspace(*b)) continue; } else if (*b++ != 'A') { /* something else boring */ continue; } /* this is the A or AliasFile option -- save it */ if (sm_strlcpy(afilebuf, b, sizeof afilebuf) >= sizeof afilebuf) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: AliasFile filename too long: %.30s\n", b); (void) sm_io_close(cfp, SM_TIME_DEFAULT); exit(EX_CONFIG); } b = afilebuf; for (p = b; p != NULL; ) { while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; b = p; p = strpbrk(p, DELIMITERS); /* find end of spec */ if (p != NULL) { bool quoted = false; for (; *p != '\0'; p++) { /* ** Don't break into a quoted ** string. */ if (*p == '"') quoted = !quoted; else if (*p == ',' && !quoted) break; } /* No more alias specs follow */ if (*p == '\0') { /* chop trailing whitespace */ while (isascii(*p) && isspace(*p) && p > b) p--; *p = '\0'; p = NULL; } } if (p != NULL) { char *e = p - 1; /* chop trailing whitespace */ while (isascii(*e) && isspace(*e) && e > b) e--; *++e = '\0'; *p++ = '\0'; } praliases(b, argc, argv); } default: continue; } } (void) sm_io_close(cfp, SM_TIME_DEFAULT); exit(EX_OK); /* NOTREACHED */ return EX_OK; } static void praliases(filename, argc, argv) char *filename; int argc; char **argv; { int result; char *colon; char *db_name; char *db_type; SMDB_DATABASE *database = NULL; SMDB_CURSOR *cursor = NULL; SMDB_DBENT db_key, db_value; SMDB_DBPARAMS params; SMDB_USER_INFO user_info; colon = strchr(filename, PATH_SEPARATOR); if (colon == NULL) { db_name = filename; db_type = SMDB_TYPE_DEFAULT; } else { *colon = '\0'; db_name = colon + 1; db_type = filename; } /* clean off arguments */ for (;;) { while (isascii(*db_name) && isspace(*db_name)) db_name++; if (*db_name != '-') break; while (*db_name != '\0' && !(isascii(*db_name) && isspace(*db_name))) db_name++; } /* Skip non-file based DB types */ if (db_type != NULL && *db_type != '\0') { if (db_type != SMDB_TYPE_DEFAULT && strcmp(db_type, "hash") != 0 && strcmp(db_type, "btree") != 0 && strcmp(db_type, "dbm") != 0) { sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: Skipping non-file based alias type %s\n", db_type); return; } } if (*db_name == '\0' || (db_type != NULL && *db_type == '\0')) { if (colon != NULL) *colon = ':'; (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: illegal alias specification: %s\n", filename); goto fatal; } memset(¶ms, '\0', sizeof params); params.smdbp_cache_size = 1024 * 1024; user_info.smdbu_id = RunAsUid; user_info.smdbu_group_id = RunAsGid; (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, SMDB_MAX_USER_NAME_LEN); result = smdb_open_database(&database, db_name, O_RDONLY, 0, SFF_ROOTOK, db_type, &user_info, ¶ms); if (result != SMDBE_OK) { sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: %s: open: %s\n", db_name, sm_errstring(result)); goto fatal; } if (argc == 0) { memset(&db_key, '\0', sizeof db_key); memset(&db_value, '\0', sizeof db_value); result = database->smdb_cursor(database, &cursor, 0); if (result != SMDBE_OK) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: %s: set cursor: %s\n", db_name, sm_errstring(result)); goto fatal; } while ((result = cursor->smdbc_get(cursor, &db_key, &db_value, SMDB_CURSOR_GET_NEXT)) == SMDBE_OK) { #if 0 /* skip magic @:@ entry */ if (db_key.size == 2 && db_key.data[0] == '@' && db_key.data[1] == '\0' && db_value.size == 2 && db_value.data[0] == '@' && db_value.data[1] == '\0') continue; #endif /* 0 */ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s:%.*s\n", (int) db_key.size, (char *) db_key.data, (int) db_value.size, (char *) db_value.data); } if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY) { (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "praliases: %s: get value at cursor: %s\n", db_name, sm_errstring(result)); goto fatal; } } else for (; *argv != NULL; ++argv) { int get_res; memset(&db_key, '\0', sizeof db_key); memset(&db_value, '\0', sizeof db_value); db_key.data = *argv; db_key.size = strlen(*argv); get_res = database->smdb_get(database, &db_key, &db_value, 0); if (get_res == SMDBE_NOT_FOUND) { db_key.size++; get_res = database->smdb_get(database, &db_key, &db_value, 0); } if (get_res == SMDBE_OK) { (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s:%.*s\n", (int) db_key.size, (char *) db_key.data, (int) db_value.size, (char *) db_value.data); } else (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s: No such key\n", (char *)db_key.data); } fatal: if (cursor != NULL) (void) cursor->smdbc_close(cursor); if (database != NULL) (void) database->smdb_close(database); if (colon != NULL) *colon = ':'; return; } Index: head/contrib/sendmail/src/conf.c =================================================================== --- head/contrib/sendmail/src/conf.c (revision 173342) +++ head/contrib/sendmail/src/conf.c (revision 173343) @@ -1,6358 +1,6377 @@ /* * Copyright (c) 1998-2007 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * $FreeBSD$ */ #include -SM_RCSID("@(#)$Id: conf.c,v 8.1128 2007/04/03 21:32:29 ca Exp $") +SM_RCSID("@(#)$Id: conf.c,v 8.1136 2007/10/10 00:06:45 ca Exp $") #include #include #if NEWDB # include "sm/bdb.h" #endif /* NEWDB */ #include #include "map.h" #ifdef DEC # if NETINET6 /* for the IPv6 device lookup */ # define _SOCKADDR_LEN # include # endif /* NETINET6 */ #endif /* DEC */ # include # include #include #if NETINET || NETINET6 # include #endif /* NETINET || NETINET6 */ #if HASULIMIT && defined(HPUX11) # include #endif /* HASULIMIT && defined(HPUX11) */ static void setupmaps __P((void)); static void setupmailers __P((void)); static void setupqueues __P((void)); static int get_num_procs_online __P((void)); static int add_hostnames __P((SOCKADDR *)); #if NETINET6 && NEEDSGETIPNODE static struct hostent *getipnodebyname __P((char *, int, int, int *)); static struct hostent *getipnodebyaddr __P((char *, int, int, int *)); #endif /* NETINET6 && NEEDSGETIPNODE */ /* ** CONF.C -- Sendmail Configuration Tables. ** ** Defines the configuration of this installation. ** ** Configuration Variables: ** HdrInfo -- a table describing well-known header fields. ** Each entry has the field name and some flags, ** which are described in sendmail.h. ** ** Notes: ** I have tried to put almost all the reasonable ** configuration information into the configuration ** file read at runtime. My intent is that anything ** here is a function of the version of UNIX you ** are running, or is really static -- for example ** the headers are a superset of widely used ** protocols. If you find yourself playing with ** this file too much, you may be making a mistake! */ /* ** Header info table ** Final (null) entry contains the flags used for any other field. ** ** Not all of these are actually handled specially by sendmail ** at this time. They are included as placeholders, to let ** you know that "someday" I intend to have sendmail do ** something with them. */ struct hdrinfo HdrInfo[] = { /* originator fields, most to least significant */ { "resent-sender", H_FROM|H_RESENT, NULL }, { "resent-from", H_FROM|H_RESENT, NULL }, { "resent-reply-to", H_FROM|H_RESENT, NULL }, { "sender", H_FROM, NULL }, { "from", H_FROM, NULL }, { "reply-to", H_FROM, NULL }, { "errors-to", H_FROM|H_ERRORSTO, NULL }, { "full-name", H_ACHECK, NULL }, { "return-receipt-to", H_RECEIPTTO, NULL }, { "delivery-receipt-to", H_RECEIPTTO, NULL }, { "disposition-notification-to", H_FROM, NULL }, /* destination fields */ { "to", H_RCPT, NULL }, { "resent-to", H_RCPT|H_RESENT, NULL }, { "cc", H_RCPT, NULL }, { "resent-cc", H_RCPT|H_RESENT, NULL }, { "bcc", H_RCPT|H_BCC, NULL }, { "resent-bcc", H_RCPT|H_BCC|H_RESENT, NULL }, { "apparently-to", H_RCPT, NULL }, /* message identification and control */ { "message-id", 0, NULL }, { "resent-message-id", H_RESENT, NULL }, { "message", H_EOH, NULL }, { "text", H_EOH, NULL }, /* date fields */ { "date", 0, NULL }, { "resent-date", H_RESENT, NULL }, /* trace fields */ { "received", H_TRACE|H_FORCE, NULL }, { "x400-received", H_TRACE|H_FORCE, NULL }, { "via", H_TRACE|H_FORCE, NULL }, { "mail-from", H_TRACE|H_FORCE, NULL }, /* miscellaneous fields */ { "comments", H_FORCE|H_ENCODABLE, NULL }, { "return-path", H_FORCE|H_ACHECK|H_BINDLATE, NULL }, { "content-transfer-encoding", H_CTE, NULL }, { "content-type", H_CTYPE, NULL }, { "content-length", H_ACHECK, NULL }, { "subject", H_ENCODABLE, NULL }, { "x-authentication-warning", H_FORCE, NULL }, { NULL, 0, NULL } }; /* ** Privacy values */ struct prival PrivacyValues[] = { { "public", PRIV_PUBLIC }, { "needmailhelo", PRIV_NEEDMAILHELO }, { "needexpnhelo", PRIV_NEEDEXPNHELO }, { "needvrfyhelo", PRIV_NEEDVRFYHELO }, { "noexpn", PRIV_NOEXPN }, { "novrfy", PRIV_NOVRFY }, { "restrictexpand", PRIV_RESTRICTEXPAND }, { "restrictmailq", PRIV_RESTRICTMAILQ }, { "restrictqrun", PRIV_RESTRICTQRUN }, { "noetrn", PRIV_NOETRN }, { "noverb", PRIV_NOVERB }, { "authwarnings", PRIV_AUTHWARNINGS }, { "noreceipts", PRIV_NORECEIPTS }, { "nobodyreturn", PRIV_NOBODYRETN }, { "goaway", PRIV_GOAWAY }, { "noactualrecipient", PRIV_NOACTUALRECIPIENT }, { NULL, 0 } }; /* ** DontBlameSendmail values */ struct dbsval DontBlameSendmailValues[] = { { "safe", DBS_SAFE }, { "assumesafechown", DBS_ASSUMESAFECHOWN }, { "groupwritabledirpathsafe", DBS_GROUPWRITABLEDIRPATHSAFE }, { "groupwritableforwardfilesafe", DBS_GROUPWRITABLEFORWARDFILESAFE }, { "groupwritableincludefilesafe", DBS_GROUPWRITABLEINCLUDEFILESAFE }, { "groupwritablealiasfile", DBS_GROUPWRITABLEALIASFILE }, { "worldwritablealiasfile", DBS_WORLDWRITABLEALIASFILE }, { "forwardfileinunsafedirpath", DBS_FORWARDFILEINUNSAFEDIRPATH }, { "includefileinunsafedirpath", DBS_INCLUDEFILEINUNSAFEDIRPATH }, { "mapinunsafedirpath", DBS_MAPINUNSAFEDIRPATH }, { "linkedaliasfileinwritabledir", DBS_LINKEDALIASFILEINWRITABLEDIR }, { "linkedclassfileinwritabledir", DBS_LINKEDCLASSFILEINWRITABLEDIR }, { "linkedforwardfileinwritabledir", DBS_LINKEDFORWARDFILEINWRITABLEDIR }, { "linkedincludefileinwritabledir", DBS_LINKEDINCLUDEFILEINWRITABLEDIR }, { "linkedmapinwritabledir", DBS_LINKEDMAPINWRITABLEDIR }, { "linkedserviceswitchfileinwritabledir", DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR }, { "filedeliverytohardlink", DBS_FILEDELIVERYTOHARDLINK }, { "filedeliverytosymlink", DBS_FILEDELIVERYTOSYMLINK }, { "writemaptohardlink", DBS_WRITEMAPTOHARDLINK }, { "writemaptosymlink", DBS_WRITEMAPTOSYMLINK }, { "writestatstohardlink", DBS_WRITESTATSTOHARDLINK }, { "writestatstosymlink", DBS_WRITESTATSTOSYMLINK }, { "forwardfileingroupwritabledirpath", DBS_FORWARDFILEINGROUPWRITABLEDIRPATH }, { "includefileingroupwritabledirpath", DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH }, { "classfileinunsafedirpath", DBS_CLASSFILEINUNSAFEDIRPATH }, { "errorheaderinunsafedirpath", DBS_ERRORHEADERINUNSAFEDIRPATH }, { "helpfileinunsafedirpath", DBS_HELPFILEINUNSAFEDIRPATH }, { "forwardfileinunsafedirpathsafe", DBS_FORWARDFILEINUNSAFEDIRPATHSAFE }, { "includefileinunsafedirpathsafe", DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE }, { "runprograminunsafedirpath", DBS_RUNPROGRAMINUNSAFEDIRPATH }, { "runwritableprogram", DBS_RUNWRITABLEPROGRAM }, { "nonrootsafeaddr", DBS_NONROOTSAFEADDR }, { "truststickybit", DBS_TRUSTSTICKYBIT }, { "dontwarnforwardfileinunsafedirpath", DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH }, { "insufficiententropy", DBS_INSUFFICIENTENTROPY }, { "groupreadablesasldbfile", DBS_GROUPREADABLESASLDBFILE }, { "groupwritablesasldbfile", DBS_GROUPWRITABLESASLDBFILE }, { "groupwritableforwardfile", DBS_GROUPWRITABLEFORWARDFILE }, { "groupwritableincludefile", DBS_GROUPWRITABLEINCLUDEFILE }, { "worldwritableforwardfile", DBS_WORLDWRITABLEFORWARDFILE }, { "worldwritableincludefile", DBS_WORLDWRITABLEINCLUDEFILE }, { "groupreadablekeyfile", DBS_GROUPREADABLEKEYFILE }, #if _FFR_GROUPREADABLEAUTHINFOFILE { "groupreadableadefaultauthinfofile", DBS_GROUPREADABLEAUTHINFOFILE }, #endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ { NULL, 0 } }; /* ** Miscellaneous stuff. */ int DtableSize = 50; /* max open files; reset in 4.2bsd */ /* ** SETDEFAULTS -- set default values ** ** Some of these must be initialized using direct code since they ** depend on run-time values. So let's do all of them this way. ** ** Parameters: ** e -- the default envelope. ** ** Returns: ** none. ** ** Side Effects: ** Initializes a bunch of global variables to their ** default values. */ #define MINUTES * 60 #define HOURS * 60 MINUTES #define DAYS * 24 HOURS #ifndef MAXRULERECURSION # define MAXRULERECURSION 50 /* max ruleset recursion depth */ #endif /* ! MAXRULERECURSION */ void setdefaults(e) register ENVELOPE *e; { int i; int numprocs; struct passwd *pw; numprocs = get_num_procs_online(); SpaceSub = ' '; /* option B */ QueueLA = 8 * numprocs; /* option x */ RefuseLA = 12 * numprocs; /* option X */ WkRecipFact = 30000L; /* option y */ WkClassFact = 1800L; /* option z */ WkTimeFact = 90000L; /* option Z */ QueueFactor = WkRecipFact * 20; /* option q */ QueueMode = QM_NORMAL; /* what queue items to act upon */ FileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option F */ QueueFileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option QueueFileMode */ if (((pw = sm_getpwnam("mailnull")) != NULL && pw->pw_uid != 0) || ((pw = sm_getpwnam("sendmail")) != NULL && pw->pw_uid != 0) || ((pw = sm_getpwnam("daemon")) != NULL && pw->pw_uid != 0)) { DefUid = pw->pw_uid; /* option u */ DefGid = pw->pw_gid; /* option g */ DefUser = newstr(pw->pw_name); } else { DefUid = 1; /* option u */ DefGid = 1; /* option g */ setdefuser(); } TrustedUid = 0; if (tTd(37, 4)) sm_dprintf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n", DefUser != NULL ? DefUser : "<1:1>", (int) DefUid, (int) DefGid); CheckpointInterval = 10; /* option C */ MaxHopCount = 25; /* option h */ set_delivery_mode(SM_FORK, e); /* option d */ e->e_errormode = EM_PRINT; /* option e */ e->e_qgrp = NOQGRP; e->e_qdir = NOQDIR; e->e_xfqgrp = NOQGRP; e->e_xfqdir = NOQDIR; e->e_ctime = curtime(); SevenBitInput = false; /* option 7 */ MaxMciCache = 1; /* option k */ MciCacheTimeout = 5 MINUTES; /* option K */ LogLevel = 9; /* option L */ #if MILTER MilterLogLevel = -1; #endif /* MILTER */ inittimeouts(NULL, false); /* option r */ PrivacyFlags = PRIV_PUBLIC; /* option p */ MeToo = true; /* option m */ SendMIMEErrors = true; /* option f */ SuperSafe = SAFE_REALLY; /* option s */ clrbitmap(DontBlameSendmail); /* DontBlameSendmail option */ #if MIME8TO7 MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */ #else /* MIME8TO7 */ MimeMode = MM_PASS8BIT; #endif /* MIME8TO7 */ for (i = 0; i < MAXTOCLASS; i++) { TimeOuts.to_q_return[i] = 5 DAYS; /* option T */ TimeOuts.to_q_warning[i] = 0; /* option T */ } ServiceSwitchFile = "/etc/mail/service.switch"; ServiceCacheMaxAge = (time_t) 10; HostsFile = _PATH_HOSTS; PidFile = newstr(_PATH_SENDMAILPID); MustQuoteChars = "@,;:\\()[].'"; MciInfoTimeout = 30 MINUTES; MaxRuleRecursion = MAXRULERECURSION; MaxAliasRecursion = 10; MaxMacroRecursion = 10; ColonOkInAddr = true; DontLockReadFiles = true; DontProbeInterfaces = DPI_PROBEALL; DoubleBounceAddr = "postmaster"; MaxHeadersLength = MAXHDRSLEN; MaxMimeHeaderLength = MAXLINE; MaxMimeFieldLength = MaxMimeHeaderLength / 2; MaxForwardEntries = 0; FastSplit = 1; MaxNOOPCommands = MAXNOOPCOMMANDS; #if SASL AuthMechanisms = newstr(AUTH_MECHANISMS); AuthRealm = NULL; MaxSLBits = INT_MAX; #endif /* SASL */ #if STARTTLS TLS_Srv_Opts = TLS_I_SRV; #endif /* STARTTLS */ #ifdef HESIOD_INIT HesiodContext = NULL; #endif /* HESIOD_INIT */ #if NETINET6 /* Detect if IPv6 is available at run time */ i = socket(AF_INET6, SOCK_STREAM, 0); if (i >= 0) { InetMode = AF_INET6; (void) close(i); } else InetMode = AF_INET; #else /* NETINET6 */ InetMode = AF_INET; #endif /* NETINET6 */ ControlSocketName = NULL; memset(&ConnectOnlyTo, '\0', sizeof(ConnectOnlyTo)); DataFileBufferSize = 4096; XscriptFileBufferSize = 4096; for (i = 0; i < MAXRWSETS; i++) RuleSetNames[i] = NULL; #if MILTER InputFilters[0] = NULL; #endif /* MILTER */ RejectLogInterval = 3 HOURS; #if REQUIRES_DIR_FSYNC RequiresDirfsync = true; #endif /* REQUIRES_DIR_FSYNC */ ConnectionRateWindowSize = 60; setupmaps(); setupqueues(); setupmailers(); setupheaders(); } /* ** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups()) */ void setdefuser() { struct passwd *defpwent; static char defuserbuf[40]; DefUser = defuserbuf; defpwent = sm_getpwuid(DefUid); (void) sm_strlcpy(defuserbuf, (defpwent == NULL || defpwent->pw_name == NULL) ? "nobody" : defpwent->pw_name, sizeof(defuserbuf)); if (tTd(37, 4)) sm_dprintf("setdefuser: DefUid=%d, DefUser=%s\n", (int) DefUid, DefUser); } /* ** SETUPQUEUES -- initialize default queues ** ** The mqueue QUEUE structure gets filled in after readcf() but ** we need something to point to now for the mailer setup, ** which use "mqueue" as default queue. */ static void setupqueues() { char buf[100]; MaxRunnersPerQueue = 1; (void) sm_strlcpy(buf, "mqueue, P=/var/spool/mqueue", sizeof(buf)); makequeue(buf, false); } /* ** SETUPMAILERS -- initialize default mailers */ static void setupmailers() { char buf[100]; (void) sm_strlcpy(buf, "prog, P=/bin/sh, F=lsouDq9, T=X-Unix/X-Unix/X-Unix, A=sh -c \201u", sizeof(buf)); makemailer(buf); (void) sm_strlcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=X-Unix/X-Unix/X-Unix, A=FILE \201u", sizeof(buf)); makemailer(buf); (void) sm_strlcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u", sizeof(buf)); makemailer(buf); initerrmailers(); } /* ** SETUPMAPS -- set up map classes */ #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ { \ extern bool parse __P((MAP *, char *)); \ extern bool open __P((MAP *, int)); \ extern void close __P((MAP *)); \ extern char *lookup __P((MAP *, char *, char **, int *)); \ extern void store __P((MAP *, char *, char *)); \ s = stab(name, ST_MAPCLASS, ST_ENTER); \ s->s_mapclass.map_cname = name; \ s->s_mapclass.map_ext = ext; \ s->s_mapclass.map_cflags = flags; \ s->s_mapclass.map_parse = parse; \ s->s_mapclass.map_open = open; \ s->s_mapclass.map_close = close; \ s->s_mapclass.map_lookup = lookup; \ s->s_mapclass.map_store = store; \ } static void setupmaps() { register STAB *s; #if NEWDB # if DB_VERSION_MAJOR > 1 int major_v, minor_v, patch_v; (void) db_version(&major_v, &minor_v, &patch_v); if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR) { errno = 0; syserr("Berkeley DB version mismatch: compiled against %d.%d.%d, run-time linked against %d.%d.%d", DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, major_v, minor_v, patch_v); } # endif /* DB_VERSION_MAJOR > 1 */ MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, hash_map_open, db_map_close, db_map_lookup, db_map_store); MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, bt_map_open, db_map_close, db_map_lookup, db_map_store); #endif /* NEWDB */ #if NDBM MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, ndbm_map_open, ndbm_map_close, ndbm_map_lookup, ndbm_map_store); #endif /* NDBM */ #if NIS MAPDEF("nis", NULL, MCF_ALIASOK, map_parseargs, nis_map_open, null_map_close, nis_map_lookup, null_map_store); #endif /* NIS */ #if NISPLUS MAPDEF("nisplus", NULL, MCF_ALIASOK, map_parseargs, nisplus_map_open, null_map_close, nisplus_map_lookup, null_map_store); #endif /* NISPLUS */ #if LDAPMAP MAPDEF("ldap", NULL, MCF_ALIASOK|MCF_NOTPERSIST, ldapmap_parseargs, ldapmap_open, ldapmap_close, ldapmap_lookup, null_map_store); #endif /* LDAPMAP */ #if PH_MAP MAPDEF("ph", NULL, MCF_NOTPERSIST, ph_map_parseargs, ph_map_open, ph_map_close, ph_map_lookup, null_map_store); #endif /* PH_MAP */ #if MAP_NSD /* IRIX 6.5 nsd support */ MAPDEF("nsd", NULL, MCF_ALIASOK, map_parseargs, null_map_open, null_map_close, nsd_map_lookup, null_map_store); #endif /* MAP_NSD */ #if HESIOD MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, hes_map_open, hes_map_close, hes_map_lookup, null_map_store); #endif /* HESIOD */ #if NETINFO MAPDEF("netinfo", NULL, MCF_ALIASOK, map_parseargs, ni_map_open, null_map_close, ni_map_lookup, null_map_store); #endif /* NETINFO */ #if 0 MAPDEF("dns", NULL, 0, dns_map_init, null_map_open, null_map_close, dns_map_lookup, null_map_store); #endif /* 0 */ #if NAMED_BIND # if DNSMAP # if _FFR_DNSMAP_ALIASABLE MAPDEF("dns", NULL, MCF_ALIASOK, dns_map_parseargs, dns_map_open, null_map_close, dns_map_lookup, null_map_store); # else /* _FFR_DNSMAP_ALIASABLE */ MAPDEF("dns", NULL, 0, dns_map_parseargs, dns_map_open, null_map_close, dns_map_lookup, null_map_store); # endif /* _FFR_DNSMAP_ALIASABLE */ # endif /* DNSMAP */ #endif /* NAMED_BIND */ #if NAMED_BIND /* best MX DNS lookup */ MAPDEF("bestmx", NULL, MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, bestmx_map_lookup, null_map_store); #endif /* NAMED_BIND */ MAPDEF("host", NULL, 0, host_map_init, null_map_open, null_map_close, host_map_lookup, null_map_store); MAPDEF("text", NULL, MCF_ALIASOK, map_parseargs, text_map_open, null_map_close, text_map_lookup, null_map_store); MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, stab_map_open, null_map_close, stab_map_lookup, stab_map_store); MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, map_parseargs, impl_map_open, impl_map_close, impl_map_lookup, impl_map_store); /* access to system passwd file */ MAPDEF("user", NULL, MCF_OPTFILE, map_parseargs, user_map_open, null_map_close, user_map_lookup, null_map_store); /* dequote map */ MAPDEF("dequote", NULL, 0, dequote_init, null_map_open, null_map_close, dequote_map, null_map_store); #if MAP_REGEX MAPDEF("regex", NULL, 0, regex_map_init, null_map_open, null_map_close, regex_map_lookup, null_map_store); #endif /* MAP_REGEX */ #if USERDB /* user database */ MAPDEF("userdb", ".db", 0, map_parseargs, null_map_open, null_map_close, udb_map_lookup, null_map_store); #endif /* USERDB */ /* arbitrary programs */ MAPDEF("program", NULL, MCF_ALIASOK, map_parseargs, null_map_open, null_map_close, prog_map_lookup, null_map_store); /* sequenced maps */ MAPDEF("sequence", NULL, MCF_ALIASOK, seq_map_parse, null_map_open, null_map_close, seq_map_lookup, seq_map_store); /* switched interface to sequenced maps */ MAPDEF("switch", NULL, MCF_ALIASOK, map_parseargs, switch_map_open, null_map_close, seq_map_lookup, seq_map_store); /* null map lookup -- really for internal use only */ MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, null_map_lookup, null_map_store); /* syslog map -- logs information to syslog */ MAPDEF("syslog", NULL, 0, syslog_map_parseargs, null_map_open, null_map_close, syslog_map_lookup, null_map_store); /* macro storage map -- rulesets can set macros */ MAPDEF("macro", NULL, 0, dequote_init, null_map_open, null_map_close, macro_map_lookup, null_map_store); /* arithmetic map -- add/subtract/compare */ MAPDEF("arith", NULL, 0, dequote_init, null_map_open, null_map_close, arith_map_lookup, null_map_store); #if SOCKETMAP /* arbitrary daemons */ MAPDEF("socket", NULL, MCF_ALIASOK, map_parseargs, socket_map_open, socket_map_close, socket_map_lookup, null_map_store); #endif /* SOCKETMAP */ #if _FFR_DPRINTF_MAP /* dprintf map -- logs information to syslog */ MAPDEF("dprintf", NULL, 0, dprintf_map_parseargs, null_map_open, null_map_close, dprintf_map_lookup, null_map_store); #endif /* _FFR_DPRINTF_MAP */ if (tTd(38, 2)) { /* bogus map -- always return tempfail */ MAPDEF("bogus", NULL, MCF_ALIASOK|MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, bogus_map_lookup, null_map_store); } } #undef MAPDEF /* ** INITHOSTMAPS -- initial host-dependent maps ** ** This should act as an interface to any local service switch ** provided by the host operating system. ** ** Parameters: ** none ** ** Returns: ** none ** ** Side Effects: ** Should define maps "host" and "users" as necessary ** for this OS. If they are not defined, they will get ** a default value later. It should check to make sure ** they are not defined first, since it's possible that ** the config file has provided an override. */ void inithostmaps() { register int i; int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char buf[MAXLINE]; /* ** Make sure we have a host map. */ if (stab("host", ST_MAP, ST_FIND) == NULL) { /* user didn't initialize: set up host map */ (void) sm_strlcpy(buf, "host host", sizeof(buf)); #if NAMED_BIND if (ConfigLevel >= 2) (void) sm_strlcat(buf, " -a. -D", sizeof(buf)); #endif /* NAMED_BIND */ (void) makemapentry(buf); } /* ** Set up default aliases maps */ nmaps = switch_map_find("aliases", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("aliases.files", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases.files null", sizeof(buf)); (void) makemapentry(buf); } #if NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion mail_aliases.org_dir", sizeof(buf)); (void) makemapentry(buf); } #endif /* NISPLUS */ #if NIS else if (strcmp(maptype[i], "nis") == 0 && stab("aliases.nis", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases.nis nis mail.aliases", sizeof(buf)); (void) makemapentry(buf); } #endif /* NIS */ #if NETINFO else if (strcmp(maptype[i], "netinfo") == 0 && stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases.netinfo netinfo -z, /aliases", sizeof(buf)); (void) makemapentry(buf); } #endif /* NETINFO */ #if HESIOD else if (strcmp(maptype[i], "hesiod") == 0 && stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases.hesiod hesiod aliases", sizeof(buf)); (void) makemapentry(buf); } #endif /* HESIOD */ #if LDAPMAP && defined(SUN_EXTENSIONS) && \ defined(SUN_SIMPLIFIED_LDAP) && HASLDAPGETALIASBYNAME else if (strcmp(maptype[i], "ldap") == 0 && stab("aliases.ldap", ST_MAP, ST_FIND) == NULL) { (void) strlcpy(buf, "aliases.ldap ldap -b . -h localhost -k mail=%0 -v mailgroup", sizeof buf); (void) makemapentry(buf); } #endif /* LDAPMAP && defined(SUN_EXTENSIONS) && ... */ } if (stab("aliases", ST_MAP, ST_FIND) == NULL) { (void) sm_strlcpy(buf, "aliases switch aliases", sizeof(buf)); (void) makemapentry(buf); } } /* ** SWITCH_MAP_FIND -- find the list of types associated with a map ** ** This is the system-dependent interface to the service switch. ** ** Parameters: ** service -- the name of the service of interest. ** maptype -- an out-array of strings containing the types ** of access to use for this service. There can ** be at most MAXMAPSTACK types for a single service. ** mapreturn -- an out-array of return information bitmaps ** for the map. ** ** Returns: ** The number of map types filled in, or -1 for failure. ** ** Side effects: ** Preserves errno so nothing in the routine clobbers it. */ #if defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) # define _USE_SUN_NSSWITCH_ #endif /* defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) */ #if _FFR_HPUX_NSSWITCH # ifdef __hpux # define _USE_SUN_NSSWITCH_ # endif /* __hpux */ #endif /* _FFR_HPUX_NSSWITCH */ #ifdef _USE_SUN_NSSWITCH_ # include #endif /* _USE_SUN_NSSWITCH_ */ #if defined(ultrix) || (defined(__osf__) && defined(__alpha)) # define _USE_DEC_SVC_CONF_ #endif /* defined(ultrix) || (defined(__osf__) && defined(__alpha)) */ #ifdef _USE_DEC_SVC_CONF_ # include #endif /* _USE_DEC_SVC_CONF_ */ int switch_map_find(service, maptype, mapreturn) char *service; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; { int svcno = 0; int save_errno = errno; #ifdef _USE_SUN_NSSWITCH_ struct __nsw_switchconfig *nsw_conf; enum __nsw_parse_err pserr; struct __nsw_lookup *lk; static struct __nsw_lookup lkp0 = { "files", {1, 0, 0, 0}, NULL, NULL }; static struct __nsw_switchconfig lkp_default = { 0, "sendmail", 3, &lkp0 }; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL) lk = lkp_default.lookups; else lk = nsw_conf->lookups; svcno = 0; while (lk != NULL && svcno < MAXMAPSTACK) { maptype[svcno] = lk->service_name; if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN) mapreturn[MA_NOTFOUND] |= 1 << svcno; if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; svcno++; lk = lk->next; } errno = save_errno; return svcno; #endif /* _USE_SUN_NSSWITCH_ */ #ifdef _USE_DEC_SVC_CONF_ struct svcinfo *svcinfo; int svc; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcinfo = getsvc(); if (svcinfo == NULL) goto punt; if (strcmp(service, "hosts") == 0) svc = SVC_HOSTS; else if (strcmp(service, "aliases") == 0) svc = SVC_ALIASES; else if (strcmp(service, "passwd") == 0) svc = SVC_PASSWD; else { errno = save_errno; return -1; } for (svcno = 0; svcno < SVC_PATHSIZE && svcno < MAXMAPSTACK; svcno++) { switch (svcinfo->svcpath[svc][svcno]) { case SVC_LOCAL: maptype[svcno] = "files"; break; case SVC_YP: maptype[svcno] = "nis"; break; case SVC_BIND: maptype[svcno] = "dns"; break; # ifdef SVC_HESIOD case SVC_HESIOD: maptype[svcno] = "hesiod"; break; # endif /* SVC_HESIOD */ case SVC_LAST: errno = save_errno; return svcno; } } errno = save_errno; return svcno; #endif /* _USE_DEC_SVC_CONF_ */ #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) /* ** Fall-back mechanism. */ STAB *st; static time_t servicecachetime; /* time service switch was cached */ time_t now = curtime(); for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((now - servicecachetime) > (time_t) ServiceCacheMaxAge) { /* (re)read service switch */ register SM_FILE_T *fp; long sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK; if (!bitnset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail)) sff |= SFF_NOWLINK; if (ConfigFileRead) servicecachetime = now; fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff); if (fp != NULL) { char buf[MAXLINE]; while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *p; p = strpbrk(buf, "#\n"); if (p != NULL) *p = '\0'; p = strpbrk(buf, " \t"); if (p != NULL) *p++ = '\0'; if (buf[0] == '\0') continue; if (p == NULL) { sm_syslog(LOG_ERR, NOQID, "Bad line on %.100s: %.100s", ServiceSwitchFile, buf); continue; } while (isspace(*p)) p++; if (*p == '\0') continue; /* ** Find/allocate space for this service entry. ** Space for all of the service strings ** are allocated at once. This means ** that we only have to free the first ** one to free all of them. */ st = stab(buf, ST_SERVICE, ST_ENTER); if (st->s_service[0] != NULL) sm_free((void *) st->s_service[0]); /* XXX */ p = newstr(p); for (svcno = 0; svcno < MAXMAPSTACK; ) { if (*p == '\0') break; st->s_service[svcno++] = p; p = strpbrk(p, " \t"); if (p == NULL) break; *p++ = '\0'; while (isspace(*p)) p++; } if (svcno < MAXMAPSTACK) st->s_service[svcno] = NULL; } (void) sm_io_close(fp, SM_TIME_DEFAULT); } } /* look up entry in cache */ st = stab(service, ST_SERVICE, ST_FIND); if (st != NULL && st->s_service[0] != NULL) { /* extract data */ svcno = 0; while (svcno < MAXMAPSTACK) { maptype[svcno] = st->s_service[svcno]; if (maptype[svcno++] == NULL) break; } errno = save_errno; return --svcno; } #endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ #if !defined(_USE_SUN_NSSWITCH_) /* if the service file doesn't work, use an absolute fallback */ # ifdef _USE_DEC_SVC_CONF_ punt: # endif /* _USE_DEC_SVC_CONF_ */ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcno = 0; if (strcmp(service, "aliases") == 0) { maptype[svcno++] = "files"; # if defined(AUTO_NETINFO_ALIASES) && defined (NETINFO) maptype[svcno++] = "netinfo"; # endif /* defined(AUTO_NETINFO_ALIASES) && defined (NETINFO) */ # ifdef AUTO_NIS_ALIASES # if NISPLUS maptype[svcno++] = "nisplus"; # endif /* NISPLUS */ # if NIS maptype[svcno++] = "nis"; # endif /* NIS */ # endif /* AUTO_NIS_ALIASES */ errno = save_errno; return svcno; } if (strcmp(service, "hosts") == 0) { # if NAMED_BIND maptype[svcno++] = "dns"; # else /* NAMED_BIND */ # if defined(sun) && !defined(BSD) /* SunOS */ maptype[svcno++] = "nis"; # endif /* defined(sun) && !defined(BSD) */ # endif /* NAMED_BIND */ # if defined(AUTO_NETINFO_HOSTS) && defined (NETINFO) maptype[svcno++] = "netinfo"; # endif /* defined(AUTO_NETINFO_HOSTS) && defined (NETINFO) */ maptype[svcno++] = "files"; errno = save_errno; return svcno; } errno = save_errno; return -1; #endif /* !defined(_USE_SUN_NSSWITCH_) */ } /* ** USERNAME -- return the user id of the logged in user. ** ** Parameters: ** none. ** ** Returns: ** The login name of the logged in user. ** ** Side Effects: ** none. ** ** Notes: ** The return value is statically allocated. */ char * username() { static char *myname = NULL; extern char *getlogin(); register struct passwd *pw; /* cache the result */ if (myname == NULL) { myname = getlogin(); if (myname == NULL || myname[0] == '\0') { pw = sm_getpwuid(RealUid); if (pw != NULL) myname = pw->pw_name; } else { uid_t uid = RealUid; if ((pw = sm_getpwnam(myname)) == NULL || (uid != 0 && uid != pw->pw_uid)) { pw = sm_getpwuid(uid); if (pw != NULL) myname = pw->pw_name; } } if (myname == NULL || myname[0] == '\0') { syserr("554 5.3.0 Who are you?"); myname = "postmaster"; } else if (strpbrk(myname, ",;:/|\"\\") != NULL) myname = addquotes(myname, NULL); else myname = sm_pstrdup_x(myname); } return myname; } /* ** TTYPATH -- Get the path of the user's tty ** ** Returns the pathname of the user's tty. Returns NULL if ** the user is not logged in or if s/he has write permission ** denied. ** ** Parameters: ** none ** ** Returns: ** pathname of the user's tty. ** NULL if not logged in or write permission denied. ** ** Side Effects: ** none. ** ** WARNING: ** Return value is in a local buffer. ** ** Called By: ** savemail */ char * ttypath() { struct stat stbuf; register char *pathn; extern char *ttyname(); extern char *getlogin(); /* compute the pathname of the controlling tty */ if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL && (pathn = ttyname(0)) == NULL) { errno = 0; return NULL; } /* see if we have write permission */ if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode)) { errno = 0; return NULL; } /* see if the user is logged in */ if (getlogin() == NULL) return NULL; /* looks good */ return pathn; } /* ** CHECKCOMPAT -- check for From and To person compatible. ** ** This routine can be supplied on a per-installation basis ** to determine whether a person is allowed to send a message. ** This allows restriction of certain types of internet ** forwarding or registration of users. ** ** If the hosts are found to be incompatible, an error ** message should be given using "usrerr" and an EX_ code ** should be returned. You can also set to->q_status to ** a DSN-style status code. ** ** EF_NO_BODY_RETN can be set in e->e_flags to suppress the ** body during the return-to-sender function; this should be done ** on huge messages. This bit may already be set by the ESMTP ** protocol. ** ** Parameters: ** to -- the person being sent to. ** ** Returns: ** an exit status ** ** Side Effects: ** none (unless you include the usrerr stuff) */ int checkcompat(to, e) register ADDRESS *to; register ENVELOPE *e; { if (tTd(49, 1)) sm_dprintf("checkcompat(to=%s, from=%s)\n", to->q_paddr, e->e_from.q_paddr); #ifdef EXAMPLE_CODE /* this code is intended as an example only */ register STAB *s; s = stab("arpa", ST_MAILER, ST_FIND); if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 && to->q_mailer == s->s_mailer) { usrerr("553 No ARPA mail through this machine: see your system administration"); /* e->e_flags |= EF_NO_BODY_RETN; to suppress body on return */ to->q_status = "5.7.1"; return EX_UNAVAILABLE; } #endif /* EXAMPLE_CODE */ return EX_OK; } #ifdef SUN_EXTENSIONS static void init_md_sun() { struct stat sbuf; /* Check for large file descriptor */ if (fstat(fileno(stdin), &sbuf) < 0) { if (errno == EOVERFLOW) { perror("stdin"); exit(EX_NOINPUT); } } } #endif /* SUN_EXTENSIONS */ /* ** INIT_MD -- do machine dependent initializations ** ** Systems that have global modes that should be set should do ** them here rather than in main. */ #ifdef _AUX_SOURCE # include #endif /* _AUX_SOURCE */ #if SHARE_V1 # include #endif /* SHARE_V1 */ void init_md(argc, argv) int argc; char **argv; { #ifdef _AUX_SOURCE setcompat(getcompat() | COMPAT_BSDPROT); #endif /* _AUX_SOURCE */ #ifdef SUN_EXTENSIONS init_md_sun(); #endif /* SUN_EXTENSIONS */ #if _CONVEX_SOURCE /* keep gethostby*() from stripping the local domain name */ set_domain_trim_off(); #endif /* _CONVEX_SOURCE */ #if defined(__QNX__) && !defined(__QNXNTO__) /* ** Due to QNX's network distributed nature, you can target a tcpip ** stack on a different node in the qnx network; this patch lets ** this feature work. The __sock_locate() must be done before the ** environment is clear. */ __sock_locate(); #endif /* __QNX__ */ #if SECUREWARE || defined(_SCO_unix_) set_auth_parameters(argc, argv); # ifdef _SCO_unix_ /* ** This is required for highest security levels (the kernel ** won't let it call set*uid() or run setuid binaries without ** it). It may be necessary on other SECUREWARE systems. */ if (getluid() == -1) setluid(0); # endif /* _SCO_unix_ */ #endif /* SECUREWARE || defined(_SCO_unix_) */ #ifdef VENDOR_DEFAULT VendorCode = VENDOR_DEFAULT; #else /* VENDOR_DEFAULT */ VendorCode = VENDOR_BERKELEY; #endif /* VENDOR_DEFAULT */ } /* ** INIT_VENDOR_MACROS -- vendor-dependent macro initializations ** ** Called once, on startup. ** ** Parameters: ** e -- the global envelope. ** ** Returns: ** none. ** ** Side Effects: ** vendor-dependent. */ void init_vendor_macros(e) register ENVELOPE *e; { } /* ** GETLA -- get the current load average ** ** This code stolen from la.c. ** ** Parameters: ** none. ** ** Returns: ** The current load average as an integer. ** ** Side Effects: ** none. */ /* try to guess what style of load average we have */ #define LA_ZERO 1 /* always return load average as zero */ #define LA_INT 2 /* read kmem for avenrun; interpret as long */ #define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ #define LA_SUBR 4 /* call getloadavg */ #define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */ #define LA_SHORT 6 /* read kmem for avenrun; interpret as short */ #define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */ #define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */ #define LA_DGUX 9 /* special DGUX implementation */ #define LA_HPUX 10 /* special HPUX implementation */ #define LA_IRIX6 11 /* special IRIX 6.2 implementation */ #define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */ #define LA_DEVSHORT 13 /* read short from a device */ #define LA_ALPHAOSF 14 /* Digital UNIX (OSF/1 on Alpha) table() call */ #define LA_PSET 15 /* Solaris per-processor-set load average */ #define LA_LONGLONG 17 /* read kmem for avenrun; interpret as long long */ /* do guesses based on general OS type */ #ifndef LA_TYPE # define LA_TYPE LA_ZERO #endif /* ! LA_TYPE */ #ifndef FSHIFT # if defined(unixpc) # define FSHIFT 5 # endif /* defined(unixpc) */ # if defined(__alpha) || defined(IRIX) # define FSHIFT 10 # endif /* defined(__alpha) || defined(IRIX) */ #endif /* ! FSHIFT */ #ifndef FSHIFT # define FSHIFT 8 #endif /* ! FSHIFT */ #ifndef FSCALE # define FSCALE (1 << FSHIFT) #endif /* ! FSCALE */ #ifndef LA_AVENRUN # ifdef SYSTEM5 # define LA_AVENRUN "avenrun" # else /* SYSTEM5 */ # define LA_AVENRUN "_avenrun" # endif /* SYSTEM5 */ #endif /* ! LA_AVENRUN */ /* _PATH_KMEM should be defined in */ #ifndef _PATH_KMEM # define _PATH_KMEM "/dev/kmem" #endif /* ! _PATH_KMEM */ #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) # include /* _PATH_UNIX should be defined in */ # ifndef _PATH_UNIX # if defined(SYSTEM5) # define _PATH_UNIX "/unix" # else /* defined(SYSTEM5) */ # define _PATH_UNIX "/vmunix" # endif /* defined(SYSTEM5) */ # endif /* ! _PATH_UNIX */ # ifdef _AUX_SOURCE struct nlist Nl[2]; # else /* _AUX_SOURCE */ struct nlist Nl[] = { { LA_AVENRUN }, { 0 }, }; # endif /* _AUX_SOURCE */ # define X_AVENRUN 0 int getla() { int j; static int kmem = -1; # if LA_TYPE == LA_INT long avenrun[3]; # else /* LA_TYPE == LA_INT */ # if LA_TYPE == LA_SHORT short avenrun[3]; # else # if LA_TYPE == LA_LONGLONG long long avenrun[3]; # else /* LA_TYPE == LA_LONGLONG */ double avenrun[3]; # endif /* LA_TYPE == LA_LONGLONG */ # endif /* LA_TYPE == LA_SHORT */ # endif /* LA_TYPE == LA_INT */ extern off_t lseek(); if (kmem < 0) { # ifdef _AUX_SOURCE (void) sm_strlcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN, sizeof(Nl[X_AVENRUN].n_name)); Nl[1].n_name[0] = '\0'; # endif /* _AUX_SOURCE */ # if defined(_AIX3) || defined(_AIX4) if (knlist(Nl, 1, sizeof(Nl[0])) < 0) # else /* defined(_AIX3) || defined(_AIX4) */ if (nlist(_PATH_UNIX, Nl) < 0) # endif /* defined(_AIX3) || defined(_AIX4) */ { if (tTd(3, 1)) sm_dprintf("getla: nlist(%s): %s\n", _PATH_UNIX, sm_errstring(errno)); return -1; } if (Nl[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) sm_dprintf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); return -1; } # ifdef NAMELISTMASK Nl[X_AVENRUN].n_value &= NAMELISTMASK; # endif /* NAMELISTMASK */ kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) sm_dprintf("getla: open(/dev/kmem): %s\n", sm_errstring(errno)); return -1; } if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) { if (tTd(3, 1)) sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", sm_errstring(errno)); (void) close(kmem); kmem = -1; return -1; } } if (tTd(3, 20)) sm_dprintf("getla: symbol address = %#lx\n", (unsigned long) Nl[X_AVENRUN].n_value); if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { /* thank you Ian */ if (tTd(3, 1)) sm_dprintf("getla: lseek or read: %s\n", sm_errstring(errno)); return -1; } # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) if (tTd(3, 5)) { # if LA_TYPE == LA_SHORT sm_dprintf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %d, %d", avenrun[1], avenrun[2]); # else /* LA_TYPE == LA_SHORT */ # if LA_TYPE == LA_LONGLONG sm_dprintf("getla: avenrun = %lld", avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %lld, %lld", avenrun[1], avenrun[2]); # else /* LA_TYPE == LA_LONGLONG */ sm_dprintf("getla: avenrun = %ld", avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %ld, %ld", avenrun[1], avenrun[2]); # endif /* LA_TYPE == LA_LONGLONG */ # endif /* LA_TYPE == LA_SHORT */ sm_dprintf("\n"); } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); # else /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */ if (tTd(3, 5)) { sm_dprintf("getla: avenrun = %g", avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %g, %g", avenrun[1], avenrun[2]); sm_dprintf("\n"); } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); # endif /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */ } #endif /* (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) || (LA_TYPE == LA_LONGLONG) */ #if LA_TYPE == LA_READKSYM # include int getla() { int j; static int kmem = -1; long avenrun[3]; struct mioc_rksym mirk; if (kmem < 0) { kmem = open("/dev/kmem", 0, 0); if (kmem < 0) { if (tTd(3, 1)) sm_dprintf("getla: open(/dev/kmem): %s\n", sm_errstring(errno)); return -1; } if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) { if (tTd(3, 1)) sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", sm_errstring(errno)); (void) close(kmem); kmem = -1; return -1; } } mirk.mirk_symname = LA_AVENRUN; mirk.mirk_buf = avenrun; mirk.mirk_buflen = sizeof(avenrun); if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0) { if (tTd(3, 1)) sm_dprintf("getla: ioctl(MIOC_READKSYM) failed: %s\n", sm_errstring(errno)); return -1; } if (tTd(3, 5)) { sm_dprintf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %d, %d", avenrun[1], avenrun[2]); sm_dprintf("\n"); } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } #endif /* LA_TYPE == LA_READKSYM */ #if LA_TYPE == LA_DGUX # include int getla() { struct dg_sys_info_load_info load_info; dg_sys_info((long *)&load_info, DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0); if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (load_info.one_minute + 0.5)); return ((int) (load_info.one_minute + 0.5)); } #endif /* LA_TYPE == LA_DGUX */ #if LA_TYPE == LA_HPUX /* forward declarations to keep gcc from complaining */ struct pst_dynamic; struct pst_status; struct pst_static; struct pst_vminfo; struct pst_diskinfo; struct pst_processor; struct pst_lv; struct pst_swapinfo; # include # include int getla() { struct pst_dynamic pstd; if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic), (size_t) 1, 0) == -1) return 0; if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); return (int) (pstd.psd_avg_1_min + 0.5); } #endif /* LA_TYPE == LA_HPUX */ #if LA_TYPE == LA_SUBR int getla() { double avenrun[3]; if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) sm_dprintf("getla: getloadavg failed: %s", sm_errstring(errno)); return -1; } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } #endif /* LA_TYPE == LA_SUBR */ #if LA_TYPE == LA_MACH /* ** This has been tested on NEXTSTEP release 2.1/3.X. */ # if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 # include # else /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */ # include # endif /* defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 */ int getla() { processor_set_t default_set; kern_return_t error; unsigned int info_count; struct processor_set_basic_info info; host_t host; error = processor_set_default(host_self(), &default_set); if (error != KERN_SUCCESS) { if (tTd(3, 1)) sm_dprintf("getla: processor_set_default failed: %s", sm_errstring(errno)); return -1; } info_count = PROCESSOR_SET_BASIC_INFO_COUNT; if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, &host, (processor_set_info_t)&info, &info_count) != KERN_SUCCESS) { if (tTd(3, 1)) sm_dprintf("getla: processor_set_info failed: %s", sm_errstring(errno)); return -1; } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) ((info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE)); return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; } #endif /* LA_TYPE == LA_MACH */ #if LA_TYPE == LA_PROCSTR # if SM_CONF_BROKEN_STRTOD ERROR: This OS has most likely a broken strtod() implemenentation. ERROR: The function is required for getla(). ERROR: Check the compilation options _LA_PROCSTR and ERROR: _SM_CONF_BROKEN_STRTOD (without the leading _). # endif /* SM_CONF_BROKEN_STRTOD */ /* ** Read /proc/loadavg for the load average. This is assumed to be ** in a format like "0.15 0.12 0.06". ** ** Initially intended for Linux. This has been in the kernel ** since at least 0.99.15. */ # ifndef _PATH_LOADAVG # define _PATH_LOADAVG "/proc/loadavg" # endif /* ! _PATH_LOADAVG */ int getla() { double avenrun; register int result; SM_FILE_T *fp; fp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_LOADAVG, SM_IO_RDONLY, NULL); if (fp == NULL) { if (tTd(3, 1)) sm_dprintf("getla: sm_io_open(%s): %s\n", _PATH_LOADAVG, sm_errstring(errno)); return -1; } result = sm_io_fscanf(fp, SM_TIME_DEFAULT, "%lf", &avenrun); (void) sm_io_close(fp, SM_TIME_DEFAULT); if (result != 1) { if (tTd(3, 1)) sm_dprintf("getla: sm_io_fscanf() = %d: %s\n", result, sm_errstring(errno)); return -1; } if (tTd(3, 1)) sm_dprintf("getla(): %.2f\n", avenrun); return ((int) (avenrun + 0.5)); } #endif /* LA_TYPE == LA_PROCSTR */ #if LA_TYPE == LA_IRIX6 # include # ifdef _UNICOSMP # define CAST_SYSMP(x) (x) # else /* _UNICOSMP */ # define CAST_SYSMP(x) ((x) & 0x7fffffff) # endif /* _UNICOSMP */ int getla(void) { int j; static int kmem = -1; int avenrun[3]; if (kmem < 0) { kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) sm_dprintf("getla: open(%s): %s\n", _PATH_KMEM, sm_errstring(errno)); return -1; } if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) { if (tTd(3, 1)) sm_dprintf("getla: fcntl(/dev/kmem, FD_CLOEXEC): %s\n", sm_errstring(errno)); (void) close(kmem); kmem = -1; return -1; } } if (lseek(kmem, CAST_SYSMP(sysmp(MP_KERNADDR, MPKA_AVENRUN)), SEEK_SET) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { if (tTd(3, 1)) sm_dprintf("getla: lseek or read: %s\n", sm_errstring(errno)); return -1; } if (tTd(3, 5)) { sm_dprintf("getla: avenrun = %ld", (long int) avenrun[0]); if (tTd(3, 15)) sm_dprintf(", %ld, %ld", (long int) avenrun[1], (long int) avenrun[2]); sm_dprintf("\n"); } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } #endif /* LA_TYPE == LA_IRIX6 */ #if LA_TYPE == LA_KSTAT # include int getla() { static kstat_ctl_t *kc = NULL; static kstat_t *ksp = NULL; kstat_named_t *ksn; int la; if (kc == NULL) /* if not initialized before */ kc = kstat_open(); if (kc == NULL) { if (tTd(3, 1)) sm_dprintf("getla: kstat_open(): %s\n", sm_errstring(errno)); return -1; } if (ksp == NULL) ksp = kstat_lookup(kc, "unix", 0, "system_misc"); if (ksp == NULL) { if (tTd(3, 1)) sm_dprintf("getla: kstat_lookup(): %s\n", sm_errstring(errno)); return -1; } if (kstat_read(kc, ksp, NULL) < 0) { if (tTd(3, 1)) sm_dprintf("getla: kstat_read(): %s\n", sm_errstring(errno)); return -1; } ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min"); la = ((double) ksn->value.ul + FSCALE/2) / FSCALE; /* kstat_close(kc); /o do not close for fast access */ return la; } #endif /* LA_TYPE == LA_KSTAT */ #if LA_TYPE == LA_DEVSHORT /* ** Read /dev/table/avenrun for the load average. This should contain ** three shorts for the 1, 5, and 15 minute loads. We only read the ** first, since that's all we care about. ** ** Intended for SCO OpenServer 5. */ # ifndef _PATH_AVENRUN # define _PATH_AVENRUN "/dev/table/avenrun" # endif /* ! _PATH_AVENRUN */ int getla() { static int afd = -1; short avenrun; int loadav; int r; errno = EBADF; if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1) { if (errno != EBADF) return -1; afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC); if (afd < 0) { sm_syslog(LOG_ERR, NOQID, "can't open %s: %s", _PATH_AVENRUN, sm_errstring(errno)); return -1; } } r = read(afd, &avenrun, sizeof(avenrun)); if (tTd(3, 5)) sm_dprintf("getla: avenrun = %d\n", avenrun); loadav = (int) (avenrun + FSCALE/2) >> FSHIFT; if (tTd(3, 1)) sm_dprintf("getla: %d\n", loadav); return loadav; } #endif /* LA_TYPE == LA_DEVSHORT */ #if LA_TYPE == LA_ALPHAOSF struct rtentry; struct mbuf; # include int getla() { int ave = 0; struct tbl_loadavg tab; if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1) { if (tTd(3, 1)) sm_dprintf("getla: table %s\n", sm_errstring(errno)); return -1; } if (tTd(3, 1)) sm_dprintf("getla: scale = %d\n", tab.tl_lscale); if (tab.tl_lscale) ave = ((tab.tl_avenrun.l[2] + (tab.tl_lscale/2)) / tab.tl_lscale); else ave = (int) (tab.tl_avenrun.d[2] + 0.5); if (tTd(3, 1)) sm_dprintf("getla: %d\n", ave); return ave; } #endif /* LA_TYPE == LA_ALPHAOSF */ #if LA_TYPE == LA_PSET int getla() { double avenrun[3]; if (pset_getloadavg(PS_MYID, avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) sm_dprintf("getla: pset_getloadavg failed: %s", sm_errstring(errno)); return -1; } if (tTd(3, 1)) sm_dprintf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } #endif /* LA_TYPE == LA_PSET */ #if LA_TYPE == LA_ZERO int getla() { if (tTd(3, 1)) sm_dprintf("getla: ZERO\n"); return 0; } #endif /* LA_TYPE == LA_ZERO */ /* * Copyright 1989 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: Many and varied... */ /* Non Apollo stuff removed by Don Lewis 11/15/93 */ #ifndef lint SM_UNUSED(static char rcsid[]) = "@(#)$OrigId: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $"; #endif /* ! lint */ #ifdef apollo # undef volatile # include /* ARGSUSED */ int getloadavg( call_data ) caddr_t call_data; /* pointer to (double) return value */ { double *avenrun = (double *) call_data; int i; status_$t st; long loadav[3]; proc1_$get_loadav(loadav, &st); *avenrun = loadav[0] / (double) (1 << 16); return 0; } #endif /* apollo */ /* ** SM_GETLA -- get the current load average ** ** Parameters: ** none ** ** Returns: ** none ** ** Side Effects: ** Set CurrentLA to the current load average. ** Set {load_avg} in GlobalMacros to the current load average. */ void sm_getla() { char labuf[8]; CurrentLA = getla(); (void) sm_snprintf(labuf, sizeof(labuf), "%d", CurrentLA); macdefine(&GlobalMacros, A_TEMP, macid("{load_avg}"), labuf); } /* ** SHOULDQUEUE -- should this message be queued or sent? ** ** Compares the message cost to the load average to decide. ** ** Note: Do NOT change this API! It is documented in op.me ** and theoretically the user can change this function... ** ** Parameters: ** pri -- the priority of the message in question. ** ct -- the message creation time (unused, but see above). ** ** Returns: ** true -- if this message should be queued up for the ** time being. ** false -- if the load is low enough to send this message. ** ** Side Effects: ** none. */ /* ARGSUSED1 */ bool shouldqueue(pri, ct) long pri; time_t ct; { bool rval; #if _FFR_MEMSTAT long memfree; #endif /* _FFR_MEMSTAT */ if (tTd(3, 30)) sm_dprintf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri); #if _FFR_MEMSTAT if (QueueLowMem > 0 && sm_memstat_get(MemoryResource, &memfree) >= 0 && memfree < QueueLowMem) { if (tTd(3, 30)) sm_dprintf("true (memfree=%ld < QueueLowMem=%ld)\n", memfree, QueueLowMem); return true; } #endif /* _FFR_MEMSTAT */ if (CurrentLA < QueueLA) { if (tTd(3, 30)) sm_dprintf("false (CurrentLA < QueueLA)\n"); return false; } rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1)); if (tTd(3, 30)) sm_dprintf("%s (by calculation)\n", rval ? "true" : "false"); return rval; } /* ** REFUSECONNECTIONS -- decide if connections should be refused ** ** Parameters: ** e -- the current envelope. ** dn -- number of daemon. ** active -- was this daemon actually active? ** ** Returns: ** true if incoming SMTP connections should be refused ** (for now). ** false if we should accept new work. ** ** Side Effects: ** Sets process title when it is rejecting connections. */ bool refuseconnections(e, dn, active) ENVELOPE *e; int dn; bool active; { static time_t lastconn[MAXDAEMONS]; static int conncnt[MAXDAEMONS]; static time_t firstrejtime[MAXDAEMONS]; static time_t nextlogtime[MAXDAEMONS]; int limit; #if _FFR_MEMSTAT long memfree; #endif /* _FFR_MEMSTAT */ #if XLA if (!xla_smtp_ok()) return true; #endif /* XLA */ SM_ASSERT(dn >= 0); SM_ASSERT(dn < MAXDAEMONS); if (ConnRateThrottle > 0) { time_t now; now = curtime(); if (active) { if (now != lastconn[dn]) { lastconn[dn] = now; conncnt[dn] = 1; } else if (conncnt[dn]++ > ConnRateThrottle) { #define D_MSG_CRT "deferring connections on daemon %s: %d per second" /* sleep to flatten out connection load */ sm_setproctitle(true, e, D_MSG_CRT, Daemons[dn].d_name, ConnRateThrottle); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, D_MSG_CRT, Daemons[dn].d_name, ConnRateThrottle); (void) sleep(1); } } else if (now != lastconn[dn]) conncnt[dn] = 0; } #if _FFR_MEMSTAT if (RefuseLowMem > 0 && sm_memstat_get(MemoryResource, &memfree) >= 0 && memfree < RefuseLowMem) { # define R_MSG_LM "rejecting connections on daemon %s: free memory: %ld" sm_setproctitle(true, e, R_MSG_LM, Daemons[dn].d_name, memfree); if (LogLevel > 8) sm_syslog(LOG_NOTICE, NOQID, R_MSG_LM, Daemons[dn].d_name, memfree); return true; } #endif /* _FFR_MEMSTAT */ sm_getla(); limit = (Daemons[dn].d_refuseLA != DPO_NOTSET) ? Daemons[dn].d_refuseLA : RefuseLA; if (limit > 0 && CurrentLA >= limit) { time_t now; # define R_MSG_LA "rejecting connections on daemon %s: load average: %d" # define R2_MSG_LA "have been rejecting connections on daemon %s for %s" sm_setproctitle(true, e, R_MSG_LA, Daemons[dn].d_name, CurrentLA); if (LogLevel > 8) sm_syslog(LOG_NOTICE, NOQID, R_MSG_LA, Daemons[dn].d_name, CurrentLA); now = curtime(); if (firstrejtime[dn] == 0) { firstrejtime[dn] = now; nextlogtime[dn] = now + RejectLogInterval; } else if (nextlogtime[dn] < now) { sm_syslog(LOG_ERR, NOQID, R2_MSG_LA, Daemons[dn].d_name, pintvl(now - firstrejtime[dn], true)); nextlogtime[dn] = now + RejectLogInterval; } return true; } else firstrejtime[dn] = 0; limit = (Daemons[dn].d_delayLA != DPO_NOTSET) ? Daemons[dn].d_delayLA : DelayLA; if (limit > 0 && CurrentLA >= limit) { time_t now; static time_t log_delay = (time_t) 0; # define MIN_DELAY_LOG 90 /* wait before logging this again */ # define D_MSG_LA "delaying connections on daemon %s: load average=%d >= %d" /* sleep to flatten out connection load */ sm_setproctitle(true, e, D_MSG_LA, Daemons[dn].d_name, limit); if (LogLevel > 8 && (now = curtime()) > log_delay) { sm_syslog(LOG_INFO, NOQID, D_MSG_LA, Daemons[dn].d_name, CurrentLA, limit); log_delay = now + MIN_DELAY_LOG; } (void) sleep(1); } limit = (Daemons[dn].d_maxchildren != DPO_NOTSET) ? Daemons[dn].d_maxchildren : MaxChildren; if (limit > 0 && CurChildren >= limit) { proc_list_probe(); if (CurChildren >= limit) { #define R_MSG_CHILD "rejecting connections on daemon %s: %d children, max %d" sm_setproctitle(true, e, R_MSG_CHILD, Daemons[dn].d_name, CurChildren, limit); if (LogLevel > 8) sm_syslog(LOG_INFO, NOQID, R_MSG_CHILD, Daemons[dn].d_name, CurChildren, limit); return true; } } return false; } /* ** SETPROCTITLE -- set process title for ps ** ** Parameters: ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. ** ** Side Effects: ** Clobbers argv of our main procedure so ps(1) will ** display the title. */ #define SPT_NONE 0 /* don't use it at all */ #define SPT_REUSEARGV 1 /* cover argv with title information */ #define SPT_BUILTIN 2 /* use libc builtin */ #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */ #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */ #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */ #define SPT_SCO 6 /* write kernel u. area */ #define SPT_CHANGEARGV 7 /* write our own strings into argv[] */ #ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV #endif /* ! SPT_TYPE */ #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN # if SPT_TYPE == SPT_PSTAT # include # endif /* SPT_TYPE == SPT_PSTAT */ # if SPT_TYPE == SPT_PSSTRINGS # include # include # ifndef PS_STRINGS /* hmmmm.... apparently not available after all */ # undef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # else /* ! PS_STRINGS */ # ifndef NKPDE /* FreeBSD 2.0 */ # define NKPDE 63 typedef unsigned int *pt_entry_t; # endif /* ! NKPDE */ # endif /* ! PS_STRINGS */ # endif /* SPT_TYPE == SPT_PSSTRINGS */ # if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV # define SETPROC_STATIC static # else /* SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV */ # define SETPROC_STATIC # endif /* SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV */ # if SPT_TYPE == SPT_SYSMIPS # include # include # endif /* SPT_TYPE == SPT_SYSMIPS */ # if SPT_TYPE == SPT_SCO # include # include # include # include # if PSARGSZ > MAXLINE # define SPT_BUFSIZE PSARGSZ # endif /* PSARGSZ > MAXLINE */ # endif /* SPT_TYPE == SPT_SCO */ # ifndef SPT_PADCHAR # define SPT_PADCHAR ' ' # endif /* ! SPT_PADCHAR */ #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ #ifndef SPT_BUFSIZE # define SPT_BUFSIZE MAXLINE #endif /* ! SPT_BUFSIZE */ #if _FFR_SPT_ALIGN /* ** It looks like the Compaq Tru64 5.1A now aligns argv and envp to ** 64 bit alignment, so unless each piece of argv and envp is a multiple ** of 8 bytes (including terminating NULL), initsetproctitle() won't use ** any of the space beyond argv[0]. Be sure to set SPT_ALIGN_SIZE if ** you use this FFR. */ # ifdef SPT_ALIGN_SIZE # define SPT_ALIGN(x, align) (((((x) + SPT_ALIGN_SIZE) >> (align)) << (align)) - 1) # else /* SPT_ALIGN_SIZE */ # define SPT_ALIGN(x, align) (x) # endif /* SPT_ALIGN_SIZE */ #else /* _FFR_SPT_ALIGN */ # define SPT_ALIGN(x, align) (x) #endif /* _FFR_SPT_ALIGN */ /* ** Pointers for setproctitle. ** This allows "ps" listings to give more useful information. */ static char **Argv = NULL; /* pointer to argument vector */ static char *LastArgv = NULL; /* end of argv */ #if SPT_TYPE != SPT_BUILTIN static void setproctitle __P((const char *, ...)); #endif /* SPT_TYPE != SPT_BUILTIN */ void initsetproctitle(argc, argv, envp) int argc; char **argv; char **envp; { register int i; int align; extern char **environ; /* ** Move the environment so setproctitle can use the space at ** the top of memory. */ if (envp != NULL) { for (i = 0; envp[i] != NULL; i++) continue; environ = (char **) xalloc(sizeof(char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = newstr(envp[i]); environ[i] = NULL; } /* ** Save start and extent of argv for setproctitle. */ Argv = argv; /* ** Determine how much space we can use for setproctitle. ** Use all contiguous argv and envp pointers starting at argv[0] */ align = -1; # if _FFR_SPT_ALIGN # ifdef SPT_ALIGN_SIZE for (i = SPT_ALIGN_SIZE; i > 0; i >>= 1) align++; # endif /* SPT_ALIGN_SIZE */ # endif /* _FFR_SPT_ALIGN */ for (i = 0; i < argc; i++) { if (i == 0 || LastArgv + 1 == argv[i]) LastArgv = argv[i] + SPT_ALIGN(strlen(argv[i]), align); } for (i = 0; LastArgv != NULL && envp != NULL && envp[i] != NULL; i++) { if (LastArgv + 1 == envp[i]) LastArgv = envp[i] + SPT_ALIGN(strlen(envp[i]), align); } } #if SPT_TYPE != SPT_BUILTIN /*VARARGS1*/ static void # ifdef __STDC__ setproctitle(const char *fmt, ...) # else /* __STDC__ */ setproctitle(fmt, va_alist) const char *fmt; va_dcl # endif /* __STDC__ */ { # if SPT_TYPE != SPT_NONE register int i; register char *p; SETPROC_STATIC char buf[SPT_BUFSIZE]; SM_VA_LOCAL_DECL # if SPT_TYPE == SPT_PSTAT union pstun pst; # endif /* SPT_TYPE == SPT_PSTAT */ # if SPT_TYPE == SPT_SCO int j; off_t seek_off; static int kmem = -1; static pid_t kmempid = -1; struct user u; # endif /* SPT_TYPE == SPT_SCO */ p = buf; /* print sendmail: heading for grep */ (void) sm_strlcpy(p, "sendmail: ", SPACELEFT(buf, p)); p += strlen(p); /* print the argument string */ SM_VA_START(ap, fmt); (void) sm_vsnprintf(p, SPACELEFT(buf, p), fmt, ap); SM_VA_END(ap); i = (int) strlen(buf); if (i < 0) return; # if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; pstat(PSTAT_SETCMD, pst, i, 0, 0); # endif /* SPT_TYPE == SPT_PSTAT */ # if SPT_TYPE == SPT_PSSTRINGS PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = buf; # endif /* SPT_TYPE == SPT_PSSTRINGS */ # if SPT_TYPE == SPT_SYSMIPS sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); # endif /* SPT_TYPE == SPT_SYSMIPS */ # if SPT_TYPE == SPT_SCO if (kmem < 0 || kmempid != CurrentPid) { if (kmem >= 0) (void) close(kmem); kmem = open(_PATH_KMEM, O_RDWR, 0); if (kmem < 0) return; if ((j = fcntl(kmem, F_GETFD, 0)) < 0 || fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0) { (void) close(kmem); kmem = -1; return; } kmempid = CurrentPid; } buf[PSARGSZ - 1] = '\0'; seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u; if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off) (void) write(kmem, buf, PSARGSZ); # endif /* SPT_TYPE == SPT_SCO */ # if SPT_TYPE == SPT_REUSEARGV if (LastArgv == NULL) return; if (i > LastArgv - Argv[0] - 2) { i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } (void) sm_strlcpy(Argv[0], buf, i + 1); p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; Argv[1] = NULL; # endif /* SPT_TYPE == SPT_REUSEARGV */ # if SPT_TYPE == SPT_CHANGEARGV Argv[0] = buf; Argv[1] = 0; # endif /* SPT_TYPE == SPT_CHANGEARGV */ # endif /* SPT_TYPE != SPT_NONE */ } #endif /* SPT_TYPE != SPT_BUILTIN */ /* ** SM_SETPROCTITLE -- set process task and set process title for ps ** ** Possibly set process status and call setproctitle() to ** change the ps display. ** ** Parameters: ** status -- whether or not to store as process status ** e -- the current envelope. ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. */ /*VARARGS2*/ void #ifdef __STDC__ sm_setproctitle(bool status, ENVELOPE *e, const char *fmt, ...) #else /* __STDC__ */ sm_setproctitle(status, e, fmt, va_alist) bool status; ENVELOPE *e; const char *fmt; va_dcl #endif /* __STDC__ */ { char buf[SPT_BUFSIZE]; SM_VA_LOCAL_DECL /* print the argument string */ SM_VA_START(ap, fmt); (void) sm_vsnprintf(buf, sizeof(buf), fmt, ap); SM_VA_END(ap); if (status) proc_list_set(CurrentPid, buf); if (ProcTitlePrefix != NULL) { char prefix[SPT_BUFSIZE]; expand(ProcTitlePrefix, prefix, sizeof(prefix), e); setproctitle("%s: %s", prefix, buf); } else setproctitle("%s", buf); } /* ** WAITFOR -- wait for a particular process id. ** ** Parameters: ** pid -- process id to wait for. ** ** Returns: ** status of pid. ** -1 if pid never shows up. ** ** Side Effects: ** none. */ int waitfor(pid) pid_t pid; { int st; pid_t i; do { errno = 0; i = sm_wait(&st); if (i > 0) proc_list_drop(i, st, NULL); } while ((i >= 0 || errno == EINTR) && i != pid); if (i < 0) return -1; return st; } /* ** SM_WAIT -- wait ** ** Parameters: ** status -- pointer to status (return value) ** ** Returns: ** pid */ pid_t sm_wait(status) int *status; { # ifdef WAITUNION union wait st; # else /* WAITUNION */ auto int st; # endif /* WAITUNION */ pid_t i; # if defined(ISC_UNIX) || defined(_SCO_unix_) int savesig; # endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ # if defined(ISC_UNIX) || defined(_SCO_unix_) savesig = sm_releasesignal(SIGCHLD); # endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ i = wait(&st); # if defined(ISC_UNIX) || defined(_SCO_unix_) if (savesig > 0) sm_blocksignal(SIGCHLD); # endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */ # ifdef WAITUNION *status = st.w_status; # else /* WAITUNION */ *status = st; # endif /* WAITUNION */ return i; } /* ** REAPCHILD -- pick up the body of my child, lest it become a zombie ** ** Parameters: ** sig -- the signal that got us here (unused). ** ** Returns: ** none. ** ** Side Effects: ** Picks up extant zombies. ** Control socket exits may restart/shutdown daemon. ** ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** DOING. */ /* ARGSUSED0 */ SIGFUNC_DECL reapchild(sig) int sig; { int save_errno = errno; int st; pid_t pid; # if HASWAITPID auto int status; int count; count = 0; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { st = status; if (count++ > 1000) break; # else /* HASWAITPID */ # ifdef WNOHANG union wait status; while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) { st = status.w_status; # else /* WNOHANG */ auto int status; /* ** Catch one zombie -- we will be re-invoked (we hope) if there ** are more. Unreliable signals probably break this, but this ** is the "old system" situation -- waitpid or wait3 are to be ** strongly preferred. */ if ((pid = wait(&status)) > 0) { st = status; # endif /* WNOHANG */ # endif /* HASWAITPID */ /* Drop PID and check if it was a control socket child */ proc_list_drop(pid, st, NULL); } FIX_SYSV_SIGNAL(sig, reapchild); errno = save_errno; return SIGFUNC_RETURN; } /* ** GETDTABLESIZE -- return number of file descriptors ** ** Only on non-BSD systems ** ** Parameters: ** none ** ** Returns: ** size of file descriptor table ** ** Side Effects: ** none */ #ifdef SOLARIS # include #endif /* SOLARIS */ int getdtsize() { # ifdef RLIMIT_NOFILE struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) return rl.rlim_cur; # endif /* RLIMIT_NOFILE */ # if HASGETDTABLESIZE return getdtablesize(); # else /* HASGETDTABLESIZE */ # ifdef _SC_OPEN_MAX return sysconf(_SC_OPEN_MAX); # else /* _SC_OPEN_MAX */ return NOFILE; # endif /* _SC_OPEN_MAX */ # endif /* HASGETDTABLESIZE */ } /* ** UNAME -- get the UUCP name of this system. */ #if !HASUNAME int uname(name) struct utsname *name; { SM_FILE_T *file; char *n; name->nodename[0] = '\0'; /* try /etc/whoami -- one line with the node name */ if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, "/etc/whoami", SM_IO_RDONLY, NULL)) != NULL) { (void) sm_io_fgets(file, SM_TIME_DEFAULT, name->nodename, NODE_LENGTH + 1); (void) sm_io_close(file, SM_TIME_DEFAULT); n = strchr(name->nodename, '\n'); if (n != NULL) *n = '\0'; if (name->nodename[0] != '\0') return 0; } /* try /usr/include/whoami.h -- has a #define somewhere */ if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, "/usr/include/whoami.h", SM_IO_RDONLY, NULL)) != NULL) { char buf[MAXLINE]; while (sm_io_fgets(file, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { if (sm_io_sscanf(buf, "#define sysname \"%*[^\"]\"", NODE_LENGTH, name->nodename) > 0) break; } (void) sm_io_close(file, SM_TIME_DEFAULT); if (name->nodename[0] != '\0') return 0; } return -1; } #endif /* !HASUNAME */ /* ** INITGROUPS -- initialize groups ** ** Stub implementation for System V style systems */ #if !HASINITGROUPS initgroups(name, basegid) char *name; int basegid; { return 0; } #endif /* !HASINITGROUPS */ /* ** SETGROUPS -- set group list ** ** Stub implementation for systems that don't have group lists */ #ifndef NGROUPS_MAX int setgroups(ngroups, grouplist) int ngroups; GIDSET_T grouplist[]; { return 0; } #endif /* ! NGROUPS_MAX */ /* ** SETSID -- set session id (for non-POSIX systems) */ #if !HASSETSID pid_t setsid __P ((void)) { # ifdef TIOCNOTTY int fd; fd = open("/dev/tty", O_RDWR, 0); if (fd >= 0) { (void) ioctl(fd, TIOCNOTTY, (char *) 0); (void) close(fd); } # endif /* TIOCNOTTY */ # ifdef SYS5SETPGRP return setpgrp(); # else /* SYS5SETPGRP */ return setpgid(0, CurrentPid); # endif /* SYS5SETPGRP */ } #endif /* !HASSETSID */ /* ** FSYNC -- dummy fsync */ #if NEEDFSYNC fsync(fd) int fd; { # ifdef O_SYNC return fcntl(fd, F_SETFL, O_SYNC); # else /* O_SYNC */ /* nothing we can do */ return 0; # endif /* O_SYNC */ } #endif /* NEEDFSYNC */ /* ** DGUX_INET_ADDR -- inet_addr for DG/UX ** ** Data General DG/UX version of inet_addr returns a struct in_addr ** instead of a long. This patches things. Only needed on versions ** prior to 5.4.3. */ #ifdef DGUX_5_4_2 # undef inet_addr long dgux_inet_addr(host) char *host; { struct in_addr haddr; haddr = inet_addr(host); return haddr.s_addr; } #endif /* DGUX_5_4_2 */ /* ** GETOPT -- for old systems or systems with bogus implementations */ #if !SM_CONF_GETOPT /* * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* ** this version hacked to add `atend' flag to allow state machine ** to reset if invoked by the program to scan args for a 2nd time */ # if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; # endif /* defined(LIBC_SCCS) && !defined(lint) */ /* ** get option letter from argument vector */ # ifdef _CONVEX_SOURCE extern int optind, opterr, optopt; extern char *optarg; # else /* _CONVEX_SOURCE */ int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = 0; /* character checked for validity */ char *optarg = NULL; /* argument associated with option */ # endif /* _CONVEX_SOURCE */ # define BADCH (int)'?' # define EMSG "" # define tell(s) if (opterr) \ {sm_io_fputs(smioerr, SM_TIME_DEFAULT, *nargv); \ (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, s); \ (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, optopt); \ (void) sm_io_putc(smioerr, SM_TIME_DEFAULT, '\n'); \ return BADCH;} int getopt(nargc,nargv,ostr) int nargc; char *const *nargv; const char *ostr; { static char *place = EMSG; /* option letter processing */ static char atend = 0; register char *oli = NULL; /* option letter list index */ if (atend) { atend = 0; place = EMSG; } if(!*place) { /* update scanning pointer */ if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) { atend++; return -1; } if (*place == '-') { /* found "--" */ ++optind; atend++; return -1; } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) { if (!*place) ++optind; tell(": illegal option -- "); } if (oli && *++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return optopt; /* dump back option letter */ } #endif /* !SM_CONF_GETOPT */ /* ** USERSHELLOK -- tell if a user's shell is ok for unrestricted use ** ** Parameters: ** user -- the name of the user we are checking. ** shell -- the user's shell from /etc/passwd ** ** Returns: ** true -- if it is ok to use this for unrestricted access. ** false -- if the shell is restricted. */ #if !HASGETUSERSHELL # ifndef _PATH_SHELLS # define _PATH_SHELLS "/etc/shells" # endif /* ! _PATH_SHELLS */ # if defined(_AIX3) || defined(_AIX4) # include # if _AIX4 >= 40200 # include # endif /* _AIX4 >= 40200 */ # include # endif /* defined(_AIX3) || defined(_AIX4) */ static char *DefaultUserShells[] = { "/bin/sh", /* standard shell */ # ifdef MPE "/SYS/PUB/CI", # else /* MPE */ "/usr/bin/sh", "/bin/csh", /* C shell */ "/usr/bin/csh", # endif /* MPE */ # ifdef __hpux # ifdef V4FS "/usr/bin/rsh", /* restricted Bourne shell */ "/usr/bin/ksh", /* Korn shell */ "/usr/bin/rksh", /* restricted Korn shell */ "/usr/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/usr/bin/posix/sh", # else /* V4FS */ "/bin/rsh", /* restricted Bourne shell */ "/bin/ksh", /* Korn shell */ "/bin/rksh", /* restricted Korn shell */ "/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/bin/posix/sh", "/sbin/sh", # endif /* V4FS */ # endif /* __hpux */ # if defined(_AIX3) || defined(_AIX4) "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", "/bin/tsh", /* trusted shell */ "/usr/bin/tsh", "/bin/bsh", /* Bourne shell */ "/usr/bin/bsh", # endif /* defined(_AIX3) || defined(_AIX4) */ # if defined(__svr4__) || defined(__svr5__) "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", # endif /* defined(__svr4__) || defined(__svr5__) */ # ifdef sgi "/sbin/sh", /* SGI's shells really live in /sbin */ "/usr/bin/sh", "/sbin/bsh", /* classic Bourne shell */ "/bin/bsh", "/usr/bin/bsh", "/sbin/csh", /* standard csh */ "/bin/csh", "/usr/bin/csh", "/sbin/jsh", /* classic Bourne shell w/ job control*/ "/bin/jsh", "/usr/bin/jsh", "/bin/ksh", /* Korn shell */ "/sbin/ksh", "/usr/bin/ksh", "/sbin/tcsh", /* Extended csh */ "/bin/tcsh", "/usr/bin/tcsh", # endif /* sgi */ NULL }; #endif /* !HASGETUSERSHELL */ #define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/" bool usershellok(user, shell) char *user; char *shell; { # if HASGETUSERSHELL register char *p; extern char *getusershell(); if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) return true; setusershell(); while ((p = getusershell()) != NULL) if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0) break; endusershell(); return p != NULL; # else /* HASGETUSERSHELL */ # if USEGETCONFATTR auto char *v; # endif /* USEGETCONFATTR */ register SM_FILE_T *shellf; char buf[MAXLINE]; if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) return true; # if USEGETCONFATTR /* ** Naturally IBM has a "better" idea..... ** ** What a crock. This interface isn't documented, it is ** considered part of the security library (-ls), and it ** only works if you are running as root (since the list ** of valid shells is obviously a source of great concern). ** I recommend that you do NOT define USEGETCONFATTR, ** especially since you are going to have to set up an ** /etc/shells anyhow to handle the cases where getconfattr ** fails. */ if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL) { while (*v != '\0') { if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0) return true; v += strlen(v) + 1; } return false; } # endif /* USEGETCONFATTR */ shellf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_SHELLS, SM_IO_RDONLY, NULL); if (shellf == NULL) { /* no /etc/shells; see if it is one of the std shells */ char **d; if (errno != ENOENT && LogLevel > 3) sm_syslog(LOG_ERR, NOQID, "usershellok: cannot open %s: %s", _PATH_SHELLS, sm_errstring(errno)); for (d = DefaultUserShells; *d != NULL; d++) { if (strcmp(shell, *d) == 0) return true; } return false; } while (sm_io_fgets(shellf, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) { register char *p, *q; p = buf; while (*p != '\0' && *p != '#' && *p != '/') p++; if (*p == '#' || *p == '\0') continue; q = p; while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p))) p++; *p = '\0'; if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0) { (void) sm_io_close(shellf, SM_TIME_DEFAULT); return true; } } (void) sm_io_close(shellf, SM_TIME_DEFAULT); return false; # endif /* HASGETUSERSHELL */ } /* ** FREEDISKSPACE -- see how much free space is on the queue filesystem ** ** Only implemented if you have statfs. ** ** Parameters: ** dir -- the directory in question. ** bsize -- a variable into which the filesystem ** block size is stored. ** ** Returns: ** The number of blocks free on the queue filesystem. ** -1 if the statfs call fails. ** ** Side effects: ** Puts the filesystem block size into bsize. */ /* statfs types */ # define SFS_NONE 0 /* no statfs implementation */ # define SFS_USTAT 1 /* use ustat */ # define SFS_4ARGS 2 /* use four-argument statfs call */ # define SFS_VFS 3 /* use implementation */ # define SFS_MOUNT 4 /* use implementation */ # define SFS_STATFS 5 /* use implementation */ # define SFS_STATVFS 6 /* use implementation */ # ifndef SFS_TYPE # define SFS_TYPE SFS_NONE # endif /* ! SFS_TYPE */ # if SFS_TYPE == SFS_USTAT # include # endif /* SFS_TYPE == SFS_USTAT */ # if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS # include # endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */ # if SFS_TYPE == SFS_VFS # include # endif /* SFS_TYPE == SFS_VFS */ # if SFS_TYPE == SFS_MOUNT # include # endif /* SFS_TYPE == SFS_MOUNT */ # if SFS_TYPE == SFS_STATVFS # include # endif /* SFS_TYPE == SFS_STATVFS */ long freediskspace(dir, bsize) const char *dir; long *bsize; { # if SFS_TYPE == SFS_NONE if (bsize != NULL) *bsize = 4096L; /* assume free space is plentiful */ return (long) LONG_MAX; # else /* SFS_TYPE == SFS_NONE */ # if SFS_TYPE == SFS_USTAT struct ustat fs; struct stat statbuf; # define FSBLOCKSIZE DEV_BSIZE # define SFS_BAVAIL f_tfree # else /* SFS_TYPE == SFS_USTAT */ # if defined(ultrix) struct fs_data fs; # define SFS_BAVAIL fd_bfreen # define FSBLOCKSIZE 1024L # else /* defined(ultrix) */ # if SFS_TYPE == SFS_STATVFS struct statvfs fs; # define FSBLOCKSIZE fs.f_frsize # else /* SFS_TYPE == SFS_STATVFS */ struct statfs fs; # define FSBLOCKSIZE fs.f_bsize # endif /* SFS_TYPE == SFS_STATVFS */ # endif /* defined(ultrix) */ # endif /* SFS_TYPE == SFS_USTAT */ # ifndef SFS_BAVAIL # define SFS_BAVAIL f_bavail # endif /* ! SFS_BAVAIL */ # if SFS_TYPE == SFS_USTAT if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) # else /* SFS_TYPE == SFS_USTAT */ # if SFS_TYPE == SFS_4ARGS if (statfs(dir, &fs, sizeof(fs), 0) == 0) # else /* SFS_TYPE == SFS_4ARGS */ # if SFS_TYPE == SFS_STATVFS if (statvfs(dir, &fs) == 0) # else /* SFS_TYPE == SFS_STATVFS */ # if defined(ultrix) if (statfs(dir, &fs) > 0) # else /* defined(ultrix) */ if (statfs(dir, &fs) == 0) # endif /* defined(ultrix) */ # endif /* SFS_TYPE == SFS_STATVFS */ # endif /* SFS_TYPE == SFS_4ARGS */ # endif /* SFS_TYPE == SFS_USTAT */ { if (bsize != NULL) *bsize = FSBLOCKSIZE; if (fs.SFS_BAVAIL <= 0) return 0; else if (fs.SFS_BAVAIL > LONG_MAX) return (long) LONG_MAX; else return (long) fs.SFS_BAVAIL; } return -1; # endif /* SFS_TYPE == SFS_NONE */ } /* ** ENOUGHDISKSPACE -- is there enough free space on the queue file systems? ** ** Parameters: ** msize -- the size to check against. If zero, we don't yet ** know how big the message will be, so just check for ** a "reasonable" amount. ** e -- envelope, or NULL -- controls logging ** ** Returns: ** true if in every queue group there is at least one ** queue directory whose file system contains enough free space. ** false otherwise. ** ** Side Effects: ** If there is not enough disk space and e != NULL ** then sm_syslog is called. */ bool enoughdiskspace(msize, e) long msize; ENVELOPE *e; { int i; if (MinBlocksFree <= 0 && msize <= 0) { if (tTd(4, 80)) sm_dprintf("enoughdiskspace: no threshold\n"); return true; } filesys_update(); for (i = 0; i < NumQueue; ++i) { if (pickqdir(Queue[i], msize, e) < 0) return false; } return true; } /* ** TRANSIENTERROR -- tell if an error code indicates a transient failure ** ** This looks at an errno value and tells if this is likely to ** go away if retried later. ** ** Parameters: ** err -- the errno code to classify. ** ** Returns: ** true if this is probably transient. ** false otherwise. */ bool transienterror(err) int err; { switch (err) { case EIO: /* I/O error */ case ENXIO: /* Device not configured */ case EAGAIN: /* Resource temporarily unavailable */ case ENOMEM: /* Cannot allocate memory */ case ENODEV: /* Operation not supported by device */ case ENFILE: /* Too many open files in system */ case EMFILE: /* Too many open files */ case ENOSPC: /* No space left on device */ case ETIMEDOUT: /* Connection timed out */ #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif /* ESTALE */ #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif /* ENETDOWN */ #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif /* ENETUNREACH */ #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif /* ENETRESET */ #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif /* ECONNABORTED */ #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif /* ECONNRESET */ #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif /* ENOBUFS */ #ifdef ESHUTDOWN case ESHUTDOWN: /* Can't send after socket shutdown */ #endif /* ESHUTDOWN */ #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif /* ECONNREFUSED */ #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif /* EHOSTDOWN */ #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif /* EHOSTUNREACH */ #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif /* EDQUOT */ #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif /* EPROCLIM */ #ifdef EUSERS case EUSERS: /* Too many users */ #endif /* EUSERS */ #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif /* EDEADLK */ #ifdef EISCONN case EISCONN: /* Socket already connected */ #endif /* EISCONN */ #ifdef EINPROGRESS case EINPROGRESS: /* Operation now in progress */ #endif /* EINPROGRESS */ #ifdef EALREADY case EALREADY: /* Operation already in progress */ #endif /* EALREADY */ #ifdef EADDRINUSE case EADDRINUSE: /* Address already in use */ #endif /* EADDRINUSE */ #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: /* Can't assign requested address */ #endif /* EADDRNOTAVAIL */ #ifdef ETXTBSY case ETXTBSY: /* (Apollo) file locked */ #endif /* ETXTBSY */ #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) case ENOSR: /* Out of streams resources */ #endif /* defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) */ #ifdef ENOLCK case ENOLCK: /* No locks available */ #endif /* ENOLCK */ case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */ return true; } /* nope, must be permanent */ return false; } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** ** Parameters: ** fd -- the file descriptor of the file. ** filename -- the file name (for error messages). ** ext -- the filename extension. ** type -- type of the lock. Bits can be: ** LOCK_EX -- exclusive lock. ** LOCK_NB -- non-blocking. ** LOCK_UN -- unlock. ** ** Returns: ** true if the lock was acquired. ** false otherwise. */ bool lockfile(fd, filename, ext, type) int fd; char *filename; char *ext; int type; { int i; int save_errno; # if !HASFLOCK int action; struct flock lfd; if (ext == NULL) ext = ""; memset(&lfd, '\0', sizeof(lfd)); if (bitset(LOCK_UN, type)) lfd.l_type = F_UNLCK; else if (bitset(LOCK_EX, type)) lfd.l_type = F_WRLCK; else lfd.l_type = F_RDLCK; if (bitset(LOCK_NB, type)) action = F_SETLK; else action = F_SETLKW; if (tTd(55, 60)) sm_dprintf("lockfile(%s%s, action=%d, type=%d): ", filename, ext, action, lfd.l_type); while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) continue; if (i >= 0) { if (tTd(55, 60)) sm_dprintf("SUCCESS\n"); return true; } save_errno = errno; if (tTd(55, 60)) sm_dprintf("(%s) ", sm_errstring(save_errno)); /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or ** -oA/tmp/aliases or anything like that, and /tmp is mounted ** as type "tmp" (that is, served from swap space), the ** previous fcntl will fail with "Invalid argument" errors. ** Since this is fairly common during testing, we will assume ** that this indicates that the lock is successfully grabbed. */ if (save_errno == EINVAL) { if (tTd(55, 60)) sm_dprintf("SUCCESS\n"); return true; } if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN)) { int omode = fcntl(fd, F_GETFL, 0); uid_t euid = geteuid(); errno = save_errno; syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, euid); dumpfd(fd, true, true); } # else /* !HASFLOCK */ if (ext == NULL) ext = ""; if (tTd(55, 60)) sm_dprintf("lockfile(%s%s, type=%o): ", filename, ext, type); while ((i = flock(fd, type)) < 0 && errno == EINTR) continue; if (i >= 0) { if (tTd(55, 60)) sm_dprintf("SUCCESS\n"); return true; } save_errno = errno; if (tTd(55, 60)) sm_dprintf("(%s) ", sm_errstring(save_errno)); if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK) { int omode = fcntl(fd, F_GETFL, 0); uid_t euid = geteuid(); errno = save_errno; syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, euid); dumpfd(fd, true, true); } # endif /* !HASFLOCK */ if (tTd(55, 60)) sm_dprintf("FAIL\n"); errno = save_errno; return false; } /* ** CHOWNSAFE -- tell if chown is "safe" (executable only by root) ** ** Unfortunately, given that we can't predict other systems on which ** a remote mounted (NFS) filesystem will be mounted, the answer is ** almost always that this is unsafe. ** ** Note also that many operating systems have non-compliant ** implementations of the _POSIX_CHOWN_RESTRICTED variable and the ** fpathconf() routine. According to IEEE 1003.1-1990, if ** _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then ** no non-root process can give away the file. However, vendors ** don't take NFS into account, so a comfortable value of ** _POSIX_CHOWN_RESTRICTED tells us nothing. ** ** Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf() ** even on files where chown is not restricted. Many systems get ** this wrong on NFS-based filesystems (that is, they say that chown ** is restricted [safe] on NFS filesystems where it may not be, since ** other systems can access the same filesystem and do file giveaway; ** only the NFS server knows for sure!) Hence, it is important to ** get the value of SAFENFSPATHCONF correct -- it should be defined ** _only_ after testing (see test/t_pathconf.c) a system on an unsafe ** NFS-based filesystem to ensure that you can get meaningful results. ** If in doubt, assume unsafe! ** ** You may also need to tweak IS_SAFE_CHOWN -- it should be a ** condition indicating whether the return from pathconf indicates ** that chown is safe (typically either > 0 or >= 0 -- there isn't ** even any agreement about whether a zero return means that a file ** is or is not safe). It defaults to "> 0". ** ** If the parent directory is safe (writable only by owner back ** to the root) then we can relax slightly and trust fpathconf ** in more circumstances. This is really a crock -- if this is an ** NFS mounted filesystem then we really know nothing about the ** underlying implementation. However, most systems pessimize and ** return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which ** we interpret as unsafe, as we should. Thus, this heuristic gets ** us into a possible problem only on systems that have a broken ** pathconf implementation and which are also poorly configured ** (have :include: files in group- or world-writable directories). ** ** Parameters: ** fd -- the file descriptor to check. ** safedir -- set if the parent directory is safe. ** ** Returns: ** true -- if the chown(2) operation is "safe" -- that is, ** only root can chown the file to an arbitrary user. ** false -- if an arbitrary user can give away a file. */ #ifndef IS_SAFE_CHOWN # define IS_SAFE_CHOWN > 0 #endif /* ! IS_SAFE_CHOWN */ bool chownsafe(fd, safedir) int fd; bool safedir; { # if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \ (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H)) int rval; /* give the system administrator a chance to override */ if (bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail)) return true; /* ** Some systems (e.g., SunOS) seem to have the call and the ** #define _PC_CHOWN_RESTRICTED, but don't actually implement ** the call. This heuristic checks for that. */ errno = 0; rval = fpathconf(fd, _PC_CHOWN_RESTRICTED); # if SAFENFSPATHCONF return errno == 0 && rval IS_SAFE_CHOWN; # else /* SAFENFSPATHCONF */ return safedir && errno == 0 && rval IS_SAFE_CHOWN; # endif /* SAFENFSPATHCONF */ # else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */ return bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail); # endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */ } /* ** RESETLIMITS -- reset system controlled resource limits ** ** This is to avoid denial-of-service attacks ** ** Parameters: ** none ** ** Returns: ** none */ #if HASSETRLIMIT # ifdef RLIMIT_NEEDS_SYS_TIME_H # include # endif /* RLIMIT_NEEDS_SYS_TIME_H */ # include #endif /* HASSETRLIMIT */ void resetlimits() { #if HASSETRLIMIT struct rlimit lim; lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CPU, &lim); (void) setrlimit(RLIMIT_FSIZE, &lim); # ifdef RLIMIT_NOFILE lim.rlim_cur = lim.rlim_max = FD_SETSIZE; (void) setrlimit(RLIMIT_NOFILE, &lim); # endif /* RLIMIT_NOFILE */ #else /* HASSETRLIMIT */ # if HASULIMIT (void) ulimit(2, 0x3fffff); (void) ulimit(4, FD_SETSIZE); # endif /* HASULIMIT */ #endif /* HASSETRLIMIT */ errno = 0; } /* ** SETVENDOR -- process vendor code from V configuration line ** ** Parameters: ** vendor -- string representation of vendor. ** ** Returns: ** true -- if ok. ** false -- if vendor code could not be processed. ** ** Side Effects: ** It is reasonable to set mode flags here to tweak ** processing in other parts of the code if necessary. ** For example, if you are a vendor that uses $%y to ** indicate YP lookups, you could enable that here. */ bool setvendor(vendor) char *vendor; { if (sm_strcasecmp(vendor, "Berkeley") == 0) { VendorCode = VENDOR_BERKELEY; return true; } /* add vendor extensions here */ #ifdef SUN_EXTENSIONS if (sm_strcasecmp(vendor, "Sun") == 0) { VendorCode = VENDOR_SUN; return true; } #endif /* SUN_EXTENSIONS */ #ifdef DEC if (sm_strcasecmp(vendor, "Digital") == 0) { VendorCode = VENDOR_DEC; return true; } #endif /* DEC */ #if defined(VENDOR_NAME) && defined(VENDOR_CODE) if (sm_strcasecmp(vendor, VENDOR_NAME) == 0) { VendorCode = VENDOR_CODE; return true; } #endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */ return false; } /* ** GETVENDOR -- return vendor name based on vendor code ** ** Parameters: ** vendorcode -- numeric representation of vendor. ** ** Returns: ** string containing vendor name. */ char * getvendor(vendorcode) int vendorcode; { #if defined(VENDOR_NAME) && defined(VENDOR_CODE) /* ** Can't have the same switch case twice so need to ** handle VENDOR_CODE outside of switch. It might ** match one of the existing VENDOR_* codes. */ if (vendorcode == VENDOR_CODE) return VENDOR_NAME; #endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */ switch (vendorcode) { case VENDOR_BERKELEY: return "Berkeley"; case VENDOR_SUN: return "Sun"; case VENDOR_HP: return "HP"; case VENDOR_IBM: return "IBM"; case VENDOR_SENDMAIL: return "Sendmail"; default: return "Unknown"; } } /* ** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults ** ** Vendor_pre_defaults is called before reading the configuration ** file; vendor_post_defaults is called immediately after. ** ** Parameters: ** e -- the global environment to initialize. ** ** Returns: ** none. */ #if SHARE_V1 int DefShareUid; /* default share uid to run as -- unused??? */ #endif /* SHARE_V1 */ void vendor_pre_defaults(e) ENVELOPE *e; { #if SHARE_V1 /* OTHERUID is defined in shares.h, do not be alarmed */ DefShareUid = OTHERUID; #endif /* SHARE_V1 */ #if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) sun_pre_defaults(e); #endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */ #ifdef apollo /* ** stupid domain/os can't even open ** /etc/mail/sendmail.cf without this */ sm_setuserenv("ISP", NULL); sm_setuserenv("SYSTYPE", NULL); #endif /* apollo */ } void vendor_post_defaults(e) ENVELOPE *e; { #ifdef __QNX__ /* Makes sure the SOCK environment variable remains */ sm_setuserenv("SOCK", NULL); #endif /* __QNX__ */ #if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) sun_post_defaults(e); #endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */ } /* ** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode */ void vendor_daemon_setup(e) ENVELOPE *e; { #if HASSETLOGIN (void) setlogin(RunAsUserName); #endif /* HASSETLOGIN */ #if SECUREWARE if (getluid() != -1) { usrerr("Daemon cannot have LUID"); finis(false, true, EX_USAGE); } #endif /* SECUREWARE */ } /* ** VENDOR_SET_UID -- do setup for setting a user id ** ** This is called when we are still root. ** ** Parameters: ** uid -- the uid we are about to become. ** ** Returns: ** none. */ void vendor_set_uid(uid) UID_T uid; { /* ** We need to setup the share groups (lnodes) ** and add auditing information (luid's) ** before we loose our ``root''ness. */ #if SHARE_V1 if (setupshares(uid, syserr) != 0) syserr("Unable to set up shares"); #endif /* SHARE_V1 */ #if SECUREWARE (void) setup_secure(uid); #endif /* SECUREWARE */ } /* ** VALIDATE_CONNECTION -- check connection for rationality ** ** If the connection is rejected, this routine should log an ** appropriate message -- but should never issue any SMTP protocol. ** ** Parameters: ** sap -- a pointer to a SOCKADDR naming the peer. ** hostname -- the name corresponding to sap. ** e -- the current envelope. ** ** Returns: ** error message from rejection. ** NULL if not rejected. */ #if TCPWRAPPERS # include /* tcpwrappers does no logging, but you still have to declare these -- ugh */ int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; #endif /* TCPWRAPPERS */ char * validate_connection(sap, hostname, e) SOCKADDR *sap; char *hostname; ENVELOPE *e; { #if TCPWRAPPERS char *host; char *addr; extern int hosts_ctl(); #endif /* TCPWRAPPERS */ if (tTd(48, 3)) sm_dprintf("validate_connection(%s, %s)\n", hostname, anynet_ntoa(sap)); connection_rate_check(sap, e); if (rscheck("check_relay", hostname, anynet_ntoa(sap), e, RSF_RMCOMM|RSF_COUNT, 3, NULL, NOQID, NULL) != EX_OK) { static char reject[BUFSIZ*2]; extern char MsgBuf[]; if (tTd(48, 4)) sm_dprintf(" ... validate_connection: BAD (rscheck)\n"); if (strlen(MsgBuf) >= 3) (void) sm_strlcpy(reject, MsgBuf, sizeof(reject)); else (void) sm_strlcpy(reject, "Access denied", sizeof(reject)); return reject; } #if TCPWRAPPERS if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']') host = "unknown"; else host = hostname; addr = anynet_ntoa(sap); # if NETINET6 /* TCP/Wrappers don't want the IPv6: protocol label */ if (addr != NULL && sm_strncasecmp(addr, "IPv6:", 5) == 0) addr += 5; # endif /* NETINET6 */ if (!hosts_ctl("sendmail", host, addr, STRING_UNKNOWN)) { if (tTd(48, 4)) sm_dprintf(" ... validate_connection: BAD (tcpwrappers)\n"); if (LogLevel > 3) sm_syslog(LOG_NOTICE, e->e_id, "tcpwrappers (%s, %s) rejection", host, addr); return "Access denied"; } #endif /* TCPWRAPPERS */ if (tTd(48, 4)) sm_dprintf(" ... validate_connection: OK\n"); return NULL; } /* ** STRTOL -- convert string to long integer ** ** For systems that don't have it in the C library. ** ** This is taken verbatim from the 4.4-Lite C library. */ #if NEEDSTRTOL # if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; # endif /* defined(LIBC_SCCS) && !defined(lint) */ /* ** Convert a string to a long integer. ** ** Ignores `locale' stuff. Assumes that the upper and lower case ** alphabets and digits are each contiguous. */ long strtol(nptr, endptr, base) const char *nptr; char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* ** Skip white space and pick up leading +/- sign if any. ** If base is 0, allow 0x for hex and 0 for octal, else ** assume decimal; if base is already 16, allow 0x. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; /* ** Compute the cutoff value between legal numbers and illegal ** numbers. That is the largest legal value, divided by the ** base. An input number that is greater than this value, if ** followed by a legal input character, is too big. One that ** is equal to this value may be valid or not; the limit ** between valid and invalid numbers is then based on the last ** digit. For instance, if the range for longs is ** [-2147483648..2147483647] and the input base is 10, ** cutoff will be set to 214748364 and cutlim to either ** 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated ** a value > 214748364, or equal but the next digit is > 7 (or 8), ** the number is too big, and we will return a range error. ** ** Set any if any `digits' consumed; make it negative to indicate ** overflow. */ cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX; cutlim = cutoff % (unsigned long) base; cutoff /= (unsigned long) base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = neg ? LONG_MIN : LONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); return acc; } #endif /* NEEDSTRTOL */ /* ** STRSTR -- find first substring in string ** ** Parameters: ** big -- the big (full) string. ** little -- the little (sub) string. ** ** Returns: ** A pointer to the first instance of little in big. ** big if little is the null string. ** NULL if little is not contained in big. */ #if NEEDSTRSTR char * strstr(big, little) char *big; char *little; { register char *p = big; int l; if (*little == '\0') return big; l = strlen(little); while ((p = strchr(p, *little)) != NULL) { if (strncmp(p, little, l) == 0) return p; p++; } return NULL; } #endif /* NEEDSTRSTR */ /* ** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX ** ** Some operating systems have wierd problems with the gethostbyXXX ** routines. For example, Solaris versions at least through 2.3 ** don't properly deliver a canonical h_name field. This tries to ** work around these problems. ** ** Support IPv6 as well as IPv4. */ #if NETINET6 && NEEDSGETIPNODE # ifndef AI_DEFAULT # define AI_DEFAULT 0 /* dummy */ # endif /* ! AI_DEFAULT */ # ifndef AI_ADDRCONFIG # define AI_ADDRCONFIG 0 /* dummy */ # endif /* ! AI_ADDRCONFIG */ # ifndef AI_V4MAPPED # define AI_V4MAPPED 0 /* dummy */ # endif /* ! AI_V4MAPPED */ # ifndef AI_ALL # define AI_ALL 0 /* dummy */ # endif /* ! AI_ALL */ static struct hostent * getipnodebyname(name, family, flags, err) char *name; int family; int flags; int *err; { bool resv6 = true; struct hostent *h; if (family == AF_INET6) { /* From RFC2133, section 6.1 */ resv6 = bitset(RES_USE_INET6, _res.options); _res.options |= RES_USE_INET6; } SM_SET_H_ERRNO(0); h = gethostbyname(name); if (!resv6) _res.options &= ~RES_USE_INET6; *err = h_errno; return h; } static struct hostent * getipnodebyaddr(addr, len, family, err) char *addr; int len; int family; int *err; { struct hostent *h; SM_SET_H_ERRNO(0); h = gethostbyaddr(addr, len, family); *err = h_errno; return h; } void freehostent(h) struct hostent *h; { /* ** Stub routine -- if they don't have getipnodeby*(), ** they probably don't have the free routine either. */ return; } #endif /* NETINET6 && NEEDSGETIPNODE */ struct hostent * sm_gethostbyname(name, family) char *name; int family; { int save_errno; struct hostent *h = NULL; #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) # if SOLARIS == 20300 || SOLARIS == 203 static struct hostent hp; static char buf[1000]; extern struct hostent *_switch_gethostbyname_r(); if (tTd(61, 10)) sm_dprintf("_switch_gethostbyname_r(%s)... ", name); h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); save_errno = errno; # else /* SOLARIS == 20300 || SOLARIS == 203 */ extern struct hostent *__switch_gethostbyname(); if (tTd(61, 10)) sm_dprintf("__switch_gethostbyname(%s)... ", name); h = __switch_gethostbyname(name); save_errno = errno; # endif /* SOLARIS == 20300 || SOLARIS == 203 */ #else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */ int nmaps; # if NETINET6 int flags = AI_DEFAULT|AI_ALL; int err; # endif /* NETINET6 */ char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char hbuf[MAXNAME]; if (tTd(61, 10)) sm_dprintf("sm_gethostbyname(%s, %d)... ", name, family); # if NETINET6 # if ADDRCONFIG_IS_BROKEN flags &= ~AI_ADDRCONFIG; # endif /* ADDRCONFIG_IS_BROKEN */ h = getipnodebyname(name, family, flags, &err); SM_SET_H_ERRNO(err); # else /* NETINET6 */ h = gethostbyname(name); # endif /* NETINET6 */ save_errno = errno; if (h == NULL) { if (tTd(61, 10)) sm_dprintf("failure\n"); nmaps = switch_map_find("hosts", maptype, mapreturn); while (--nmaps >= 0) { if (strcmp(maptype[nmaps], "nis") == 0 || strcmp(maptype[nmaps], "files") == 0) break; } if (nmaps >= 0) { /* try short name */ if (strlen(name) > sizeof(hbuf) - 1) { errno = save_errno; return NULL; } (void) sm_strlcpy(hbuf, name, sizeof(hbuf)); (void) shorten_hostname(hbuf); /* if it hasn't been shortened, there's no point */ if (strcmp(hbuf, name) != 0) { if (tTd(61, 10)) sm_dprintf("sm_gethostbyname(%s, %d)... ", hbuf, family); # if NETINET6 h = getipnodebyname(hbuf, family, flags, &err); SM_SET_H_ERRNO(err); save_errno = errno; # else /* NETINET6 */ h = gethostbyname(hbuf); save_errno = errno; # endif /* NETINET6 */ } } } #endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */ if (tTd(61, 10)) { if (h == NULL) sm_dprintf("failure\n"); else { sm_dprintf("%s\n", h->h_name); if (tTd(61, 11)) { #if NETINET6 struct in6_addr ia6; char buf6[INET6_ADDRSTRLEN]; #else /* NETINET6 */ struct in_addr ia; #endif /* NETINET6 */ size_t i; if (h->h_aliases != NULL) for (i = 0; h->h_aliases[i] != NULL; i++) sm_dprintf("\talias: %s\n", h->h_aliases[i]); for (i = 0; h->h_addr_list[i] != NULL; i++) { char *addr; #if NETINET6 memmove(&ia6, h->h_addr_list[i], IN6ADDRSZ); addr = anynet_ntop(&ia6, buf6, sizeof(buf6)); #else /* NETINET6 */ memmove(&ia, h->h_addr_list[i], INADDRSZ); addr = (char *) inet_ntoa(ia); #endif /* NETINET6 */ if (addr != NULL) sm_dprintf("\taddr: %s\n", addr); } } } } errno = save_errno; return h; } struct hostent * sm_gethostbyaddr(addr, len, type) char *addr; int len; int type; { struct hostent *hp; #if NETINET6 if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) addr)) { /* Avoid reverse lookup for IPv6 unspecified address */ SM_SET_H_ERRNO(HOST_NOT_FOUND); return NULL; } #endif /* NETINET6 */ #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) # if SOLARIS == 20300 || SOLARIS == 203 { static struct hostent he; static char buf[1000]; extern struct hostent *_switch_gethostbyaddr_r(); hp = _switch_gethostbyaddr_r(addr, len, type, &he, buf, sizeof(buf), &h_errno); } # else /* SOLARIS == 20300 || SOLARIS == 203 */ { extern struct hostent *__switch_gethostbyaddr(); hp = __switch_gethostbyaddr(addr, len, type); } # endif /* SOLARIS == 20300 || SOLARIS == 203 */ #else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */ # if NETINET6 { int err; hp = getipnodebyaddr(addr, len, type, &err); SM_SET_H_ERRNO(err); } # else /* NETINET6 */ hp = gethostbyaddr(addr, len, type); # endif /* NETINET6 */ #endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */ return hp; } /* ** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid */ struct passwd * sm_getpwnam(user) char *user; { #ifdef _AIX4 extern struct passwd *_getpwnam_shadow(const char *, const int); return _getpwnam_shadow(user, 0); #else /* _AIX4 */ return getpwnam(user); #endif /* _AIX4 */ } struct passwd * sm_getpwuid(uid) UID_T uid; { #if defined(_AIX4) && 0 extern struct passwd *_getpwuid_shadow(const int, const int); return _getpwuid_shadow(uid,0); #else /* defined(_AIX4) && 0 */ return getpwuid(uid); #endif /* defined(_AIX4) && 0 */ } /* ** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup ** ** Set up the trusted computing environment for C2 level security ** under SecureWare. ** ** Parameters: ** uid -- uid of the user to initialize in the TCB ** ** Returns: ** none ** ** Side Effects: ** Initialized the user in the trusted computing base */ #if SECUREWARE # include # include void secureware_setup_secure(uid) UID_T uid; { int rc; if (getluid() != -1) return; if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN) { switch (rc) { case SSI_NO_PRPW_ENTRY: syserr("No protected passwd entry, uid = %d", (int) uid); break; case SSI_LOCKED: syserr("Account has been disabled, uid = %d", (int) uid); break; case SSI_RETIRED: syserr("Account has been retired, uid = %d", (int) uid); break; case SSI_BAD_SET_LUID: syserr("Could not set LUID, uid = %d", (int) uid); break; case SSI_BAD_SET_PRIVS: syserr("Could not set kernel privs, uid = %d", (int) uid); default: syserr("Unknown return code (%d) from set_secure_info(%d)", rc, (int) uid); break; } finis(false, true, EX_NOPERM); } } #endif /* SECUREWARE */ /* ** ADD_HOSTNAMES -- Add a hostname to class 'w' based on IP address ** ** Add hostnames to class 'w' based on the IP address read from ** the network interface. ** ** Parameters: ** sa -- a pointer to a SOCKADDR containing the address ** ** Returns: ** 0 if successful, -1 if host lookup fails. */ static int add_hostnames(sa) SOCKADDR *sa; { struct hostent *hp; char **ha; char hnb[MAXHOSTNAMELEN]; /* lookup name with IP address */ switch (sa->sa.sa_family) { #if NETINET case AF_INET: hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr, sizeof(sa->sin.sin_addr), sa->sa.sa_family); break; #endif /* NETINET */ #if NETINET6 case AF_INET6: hp = sm_gethostbyaddr((char *) &sa->sin6.sin6_addr, sizeof(sa->sin6.sin6_addr), sa->sa.sa_family); break; #endif /* NETINET6 */ default: /* Give warning about unsupported family */ if (LogLevel > 3) sm_syslog(LOG_WARNING, NOQID, "Unsupported address family %d: %.100s", sa->sa.sa_family, anynet_ntoa(sa)); return -1; } if (hp == NULL) { int save_errno = errno; if (LogLevel > 3 && #if NETINET6 !(sa->sa.sa_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr)) && #endif /* NETINET6 */ true) sm_syslog(LOG_WARNING, NOQID, "gethostbyaddr(%.100s) failed: %d", anynet_ntoa(sa), #if NAMED_BIND h_errno #else /* NAMED_BIND */ -1 #endif /* NAMED_BIND */ ); errno = save_errno; return -1; } /* save its cname */ if (!wordinclass((char *) hp->h_name, 'w')) { setclass('w', (char *) hp->h_name); if (tTd(0, 4)) sm_dprintf("\ta.k.a.: %s\n", hp->h_name); if (sm_snprintf(hnb, sizeof(hnb), "[%s]", hp->h_name) < sizeof(hnb) && !wordinclass((char *) hnb, 'w')) setclass('w', hnb); } else { if (tTd(0, 43)) sm_dprintf("\ta.k.a.: %s (already in $=w)\n", hp->h_name); } /* save all it aliases name */ for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++) { if (!wordinclass(*ha, 'w')) { setclass('w', *ha); if (tTd(0, 4)) sm_dprintf("\ta.k.a.: %s\n", *ha); if (sm_snprintf(hnb, sizeof(hnb), "[%s]", *ha) < sizeof(hnb) && !wordinclass((char *) hnb, 'w')) setclass('w', hnb); } else { if (tTd(0, 43)) sm_dprintf("\ta.k.a.: %s (already in $=w)\n", *ha); } } #if NETINET6 freehostent(hp); #endif /* NETINET6 */ return 0; } /* ** LOAD_IF_NAMES -- load interface-specific names into $=w ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Loads $=w with the names of all the interfaces. */ #if !NETINET # define SIOCGIFCONF_IS_BROKEN 1 /* XXX */ #endif /* !NETINET */ #if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN struct rtentry; struct mbuf; # ifndef SUNOS403 # include # endif /* ! SUNOS403 */ # if (_AIX4 >= 40300) && !defined(_NET_IF_H) # undef __P # endif /* (_AIX4 >= 40300) && !defined(_NET_IF_H) */ # include #endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */ void load_if_names() { # if NETINET6 && defined(SIOCGLIFCONF) # ifdef __hpux /* ** Unfortunately, HP has changed all of the structures, ** making life difficult for implementors. */ # define lifconf if_laddrconf # define lifc_len iflc_len # define lifc_buf iflc_buf # define lifreq if_laddrreq # define lifr_addr iflr_addr # define lifr_name iflr_name # define lifr_flags iflr_flags # define ss_family sa_family # undef SIOCGLIFNUM # endif /* __hpux */ int s; int i; size_t len; int numifs; char *buf; struct lifconf lifc; # ifdef SIOCGLIFNUM struct lifnum lifn; # endif /* SIOCGLIFNUM */ s = socket(InetMode, SOCK_DGRAM, 0); if (s == -1) return; /* get the list of known IP address from the kernel */ # ifdef __hpux i = ioctl(s, SIOCGIFNUM, (char *) &numifs); # endif /* __hpux */ # ifdef SIOCGLIFNUM lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = 0; i = ioctl(s, SIOCGLIFNUM, (char *)&lifn); numifs = lifn.lifn_count; # endif /* SIOCGLIFNUM */ # if defined(__hpux) || defined(SIOCGLIFNUM) if (i < 0) { /* can't get number of interfaces -- fall back */ if (tTd(0, 4)) sm_dprintf("SIOCGLIFNUM failed: %s\n", sm_errstring(errno)); numifs = -1; } else if (tTd(0, 42)) sm_dprintf("system has %d interfaces\n", numifs); if (numifs < 0) # endif /* defined(__hpux) || defined(SIOCGLIFNUM) */ numifs = MAXINTERFACES; if (numifs <= 0) { (void) close(s); return; } len = lifc.lifc_len = numifs * sizeof(struct lifreq); buf = lifc.lifc_buf = xalloc(lifc.lifc_len); # ifndef __hpux lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = 0; # endif /* ! __hpux */ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { if (tTd(0, 4)) sm_dprintf("SIOCGLIFCONF failed: %s\n", sm_errstring(errno)); (void) close(s); sm_free(buf); return; } /* scan the list of IP address */ if (tTd(0, 40)) sm_dprintf("scanning for interface specific names, lifc_len=%ld\n", (long) len); for (i = 0; i < len && i >= 0; ) { int flags; struct lifreq *ifr = (struct lifreq *)&buf[i]; SOCKADDR *sa = (SOCKADDR *) &ifr->lifr_addr; int af = ifr->lifr_addr.ss_family; char *addr; char *name; struct in6_addr ia6; struct in_addr ia; # ifdef SIOCGLIFFLAGS struct lifreq ifrf; # endif /* SIOCGLIFFLAGS */ char ip_addr[256]; char buf6[INET6_ADDRSTRLEN]; /* ** We must close and recreate the socket each time ** since we don't know what type of socket it is now ** (each status function may change it). */ (void) close(s); s = socket(af, SOCK_DGRAM, 0); if (s == -1) { sm_free(buf); /* XXX */ return; } /* ** If we don't have a complete ifr structure, ** don't try to use it. */ if ((len - i) < sizeof(*ifr)) break; # ifdef BSD4_4_SOCKADDR if (sa->sa.sa_len > sizeof(ifr->lifr_addr)) i += sizeof(ifr->lifr_name) + sa->sa.sa_len; else # endif /* BSD4_4_SOCKADDR */ # ifdef DEC /* fix for IPv6 size differences */ i += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len); # else /* DEC */ i += sizeof(*ifr); # endif /* DEC */ if (tTd(0, 20)) sm_dprintf("%s\n", anynet_ntoa(sa)); if (af != AF_INET && af != AF_INET6) continue; # ifdef SIOCGLIFFLAGS memset(&ifrf, '\0', sizeof(struct lifreq)); (void) sm_strlcpy(ifrf.lifr_name, ifr->lifr_name, sizeof(ifrf.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (char *) &ifrf) < 0) { if (tTd(0, 4)) sm_dprintf("SIOCGLIFFLAGS failed: %s\n", sm_errstring(errno)); continue; } name = ifr->lifr_name; flags = ifrf.lifr_flags; if (tTd(0, 41)) sm_dprintf("\tflags: %lx\n", (unsigned long) flags); if (!bitset(IFF_UP, flags)) continue; # endif /* SIOCGLIFFLAGS */ ip_addr[0] = '\0'; /* extract IP address from the list*/ switch (af) { case AF_INET6: # ifdef __KAME__ /* convert into proper scoped address */ if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) && sa->sin6.sin6_scope_id == 0) { struct in6_addr *ia6p; ia6p = &sa->sin6.sin6_addr; sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] | ((unsigned int)ia6p->s6_addr[2] << 8)); ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0; } # endif /* __KAME__ */ ia6 = sa->sin6.sin6_addr; if (IN6_IS_ADDR_UNSPECIFIED(&ia6)) { addr = anynet_ntop(&ia6, buf6, sizeof(buf6)); message("WARNING: interface %s is UP with %s address", name, addr == NULL ? "(NULL)" : addr); continue; } /* save IP address in text from */ addr = anynet_ntop(&ia6, buf6, sizeof(buf6)); if (addr != NULL) (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]", (int) sizeof(ip_addr) - 3, addr); break; case AF_INET: ia = sa->sin.sin_addr; if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE) { message("WARNING: interface %s is UP with %s address", name, inet_ntoa(ia)); continue; } /* save IP address in text from */ (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]", (int) sizeof(ip_addr) - 3, inet_ntoa(ia)); break; } if (*ip_addr == '\0') continue; if (!wordinclass(ip_addr, 'w')) { setclass('w', ip_addr); if (tTd(0, 4)) sm_dprintf("\ta.k.a.: %s\n", ip_addr); } # ifdef SIOCGLIFFLAGS /* skip "loopback" interface "lo" */ if (DontProbeInterfaces == DPI_SKIPLOOPBACK && bitset(IFF_LOOPBACK, flags)) continue; # endif /* SIOCGLIFFLAGS */ (void) add_hostnames(sa); } sm_free(buf); /* XXX */ (void) close(s); # else /* NETINET6 && defined(SIOCGLIFCONF) */ # if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN int s; int i; struct ifconf ifc; int numifs; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) return; /* get the list of known IP address from the kernel */ # if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0) { /* can't get number of interfaces -- fall back */ if (tTd(0, 4)) sm_dprintf("SIOCGIFNUM failed: %s\n", sm_errstring(errno)); numifs = -1; } else if (tTd(0, 42)) sm_dprintf("system has %d interfaces\n", numifs); if (numifs < 0) # endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */ numifs = MAXINTERFACES; if (numifs <= 0) { (void) close(s); return; } ifc.ifc_len = numifs * sizeof(struct ifreq); ifc.ifc_buf = xalloc(ifc.ifc_len); if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { if (tTd(0, 4)) sm_dprintf("SIOCGIFCONF failed: %s\n", sm_errstring(errno)); (void) close(s); return; } /* scan the list of IP address */ if (tTd(0, 40)) sm_dprintf("scanning for interface specific names, ifc_len=%d\n", ifc.ifc_len); for (i = 0; i < ifc.ifc_len && i >= 0; ) { int af; struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i]; SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr; # if NETINET6 char *addr; struct in6_addr ia6; # endif /* NETINET6 */ struct in_addr ia; # ifdef SIOCGIFFLAGS struct ifreq ifrf; # endif /* SIOCGIFFLAGS */ char ip_addr[256]; # if NETINET6 char buf6[INET6_ADDRSTRLEN]; # endif /* NETINET6 */ /* ** If we don't have a complete ifr structure, ** don't try to use it. */ if ((ifc.ifc_len - i) < sizeof(*ifr)) break; # ifdef BSD4_4_SOCKADDR if (sa->sa.sa_len > sizeof(ifr->ifr_addr)) i += sizeof(ifr->ifr_name) + sa->sa.sa_len; else # endif /* BSD4_4_SOCKADDR */ i += sizeof(*ifr); if (tTd(0, 20)) sm_dprintf("%s\n", anynet_ntoa(sa)); af = ifr->ifr_addr.sa_family; if (af != AF_INET # if NETINET6 && af != AF_INET6 # endif /* NETINET6 */ ) continue; # ifdef SIOCGIFFLAGS memset(&ifrf, '\0', sizeof(struct ifreq)); (void) sm_strlcpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name)); (void) ioctl(s, SIOCGIFFLAGS, (char *) &ifrf); if (tTd(0, 41)) sm_dprintf("\tflags: %lx\n", (unsigned long) ifrf.ifr_flags); # define IFRFREF ifrf # else /* SIOCGIFFLAGS */ # define IFRFREF (*ifr) # endif /* SIOCGIFFLAGS */ if (!bitset(IFF_UP, IFRFREF.ifr_flags)) continue; ip_addr[0] = '\0'; /* extract IP address from the list*/ switch (af) { case AF_INET: ia = sa->sin.sin_addr; if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE) { message("WARNING: interface %s is UP with %s address", ifr->ifr_name, inet_ntoa(ia)); continue; } /* save IP address in text from */ (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]", (int) sizeof(ip_addr) - 3, inet_ntoa(ia)); break; # if NETINET6 case AF_INET6: # ifdef __KAME__ /* convert into proper scoped address */ if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) || IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) && sa->sin6.sin6_scope_id == 0) { struct in6_addr *ia6p; ia6p = &sa->sin6.sin6_addr; sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] | ((unsigned int)ia6p->s6_addr[2] << 8)); ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0; } # endif /* __KAME__ */ ia6 = sa->sin6.sin6_addr; if (IN6_IS_ADDR_UNSPECIFIED(&ia6)) { addr = anynet_ntop(&ia6, buf6, sizeof(buf6)); message("WARNING: interface %s is UP with %s address", ifr->ifr_name, addr == NULL ? "(NULL)" : addr); continue; } /* save IP address in text from */ addr = anynet_ntop(&ia6, buf6, sizeof(buf6)); if (addr != NULL) (void) sm_snprintf(ip_addr, sizeof(ip_addr), "[%.*s]", (int) sizeof(ip_addr) - 3, addr); break; # endif /* NETINET6 */ } if (ip_addr[0] == '\0') continue; if (!wordinclass(ip_addr, 'w')) { setclass('w', ip_addr); if (tTd(0, 4)) sm_dprintf("\ta.k.a.: %s\n", ip_addr); } /* skip "loopback" interface "lo" */ if (DontProbeInterfaces == DPI_SKIPLOOPBACK && bitset(IFF_LOOPBACK, IFRFREF.ifr_flags)) continue; (void) add_hostnames(sa); } sm_free(ifc.ifc_buf); /* XXX */ (void) close(s); # undef IFRFREF # endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */ # endif /* NETINET6 && defined(SIOCGLIFCONF) */ } /* ** ISLOOPBACK -- is socket address in the loopback net? ** ** Parameters: ** sa -- socket address. ** ** Returns: ** true -- is socket address in the loopback net? ** false -- otherwise ** */ bool isloopback(sa) SOCKADDR sa; { #if NETINET6 if (IN6_IS_ADDR_LOOPBACK(&sa.sin6.sin6_addr)) return true; #else /* NETINET6 */ /* XXX how to correctly extract IN_LOOPBACKNET part? */ if (((ntohl(sa.sin.sin_addr.s_addr) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) return true; #endif /* NETINET6 */ return false; } /* ** GET_NUM_PROCS_ONLINE -- return the number of processors currently online ** ** Parameters: ** none. ** ** Returns: ** The number of processors online. */ static int get_num_procs_online() { int nproc = 0; #ifdef USESYSCTL # if defined(CTL_HW) && defined(HW_NCPU) size_t sz; int mib[2]; mib[0] = CTL_HW; mib[1] = HW_NCPU; sz = (size_t) sizeof(nproc); (void) sysctl(mib, 2, &nproc, &sz, NULL, 0); # endif /* defined(CTL_HW) && defined(HW_NCPU) */ #else /* USESYSCTL */ # ifdef _SC_NPROCESSORS_ONLN nproc = (int) sysconf(_SC_NPROCESSORS_ONLN); # else /* _SC_NPROCESSORS_ONLN */ # ifdef __hpux # include struct pst_dynamic psd; if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1) nproc = psd.psd_proc_cnt; # endif /* __hpux */ # endif /* _SC_NPROCESSORS_ONLN */ #endif /* USESYSCTL */ if (nproc <= 0) nproc = 1; return nproc; } /* ** SM_CLOSEFROM -- close file descriptors ** ** Parameters: ** lowest -- first fd to close ** highest -- last fd + 1 to close ** ** Returns: ** none */ void sm_closefrom(lowest, highest) int lowest, highest; { #if HASCLOSEFROM closefrom(lowest); #else /* HASCLOSEFROM */ int i; for (i = lowest; i < highest; i++) (void) close(i); #endif /* HASCLOSEFROM */ } #if HASFDWALK /* ** CLOSEFD_WALK -- walk fd's arranging to close them ** Callback for fdwalk() ** ** Parameters: ** lowest -- first fd to arrange to be closed ** fd -- fd to arrange to be closed ** ** Returns: ** zero */ static int closefd_walk(lowest, fd) void *lowest; int fd; { if (fd >= *(int *)lowest) (void) fcntl(fd, F_SETFD, FD_CLOEXEC); return 0; } #endif /* HASFDWALK */ /* ** SM_CLOSE_ON_EXEC -- arrange for file descriptors to be closed ** ** Parameters: ** lowest -- first fd to arrange to be closed ** highest -- last fd + 1 to arrange to be closed ** ** Returns: ** none */ void sm_close_on_exec(highest, lowest) int highest, lowest; { #if HASFDWALK (void) fdwalk(closefd_walk, &lowest); #else /* HASFDWALK */ int i, j; for (i = lowest; i < highest; i++) { if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); } #endif /* HASFDWALK */ } /* ** SEED_RANDOM -- seed the random number generator ** ** Parameters: ** none ** ** Returns: ** none */ void seed_random() { #if HASSRANDOMDEV srandomdev(); #else /* HASSRANDOMDEV */ long seed; struct timeval t; seed = (long) CurrentPid; if (gettimeofday(&t, NULL) >= 0) seed += t.tv_sec + t.tv_usec; # if HASRANDOM (void) srandom(seed); # else /* HASRANDOM */ (void) srand((unsigned int) seed); # endif /* HASRANDOM */ #endif /* HASSRANDOMDEV */ } /* ** SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE ** ** Parameters: ** level -- syslog level ** id -- envelope ID or NULL (NOQUEUE) ** fmt -- format string ** arg... -- arguments as implied by fmt. ** ** Returns: ** none */ /* VARARGS3 */ void #ifdef __STDC__ sm_syslog(int level, const char *id, const char *fmt, ...) #else /* __STDC__ */ sm_syslog(level, id, fmt, va_alist) int level; const char *id; const char *fmt; va_dcl #endif /* __STDC__ */ { char *buf; size_t bufsize; char *begin, *end; int save_errno; int seq = 1; int idlen; char buf0[MAXLINE]; char *newstring; extern int SyslogPrefixLen; SM_VA_LOCAL_DECL save_errno = errno; if (id == NULL) - { id = "NOQUEUE"; - idlen = strlen(id) + SyslogPrefixLen; - } - else if (strcmp(id, NOQID) == 0) - { - id = ""; - idlen = SyslogPrefixLen; - } - else - idlen = strlen(id) + SyslogPrefixLen; + idlen = strlen(id) + SyslogPrefixLen; buf = buf0; bufsize = sizeof(buf0); for (;;) { int n; /* print log message into buf */ SM_VA_START(ap, fmt); n = sm_vsnprintf(buf, bufsize, fmt, ap); SM_VA_END(ap); SM_ASSERT(n > 0); if (n < bufsize) break; /* String too small, redo with correct size */ bufsize = n + 1; if (buf != buf0) { sm_free(buf); buf = NULL; } buf = sm_malloc_x(bufsize); } /* clean up buf after it has been expanded with args */ newstring = str2prt(buf); if ((strlen(newstring) + idlen + 1) < SYSLOG_BUFSIZE) { #if LOG if (*id == '\0') { if (tTd(89, 8)) sm_dprintf("%s\n", newstring); else syslog(level, "%s", newstring); } else { if (tTd(89, 8)) sm_dprintf("%s: %s\n", id, newstring); else syslog(level, "%s: %s", id, newstring); } #else /* LOG */ /*XXX should do something more sensible */ if (*id == '\0') (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", newstring); else (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s\n", id, newstring); #endif /* LOG */ if (buf != buf0) sm_free(buf); errno = save_errno; return; } /* ** additional length for splitting: " ..." + 3, where 3 is magic to ** have some data for the next entry. */ #define SL_SPLIT 7 begin = newstring; idlen += 5; /* strlen("[999]"), see below */ while (*begin != '\0' && (strlen(begin) + idlen) > SYSLOG_BUFSIZE) { char save; if (seq >= 999) { /* Too many messages */ break; } end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT; while (end > begin) { /* Break on comma or space */ if (*end == ',' || *end == ' ') { end++; /* Include separator */ break; } end--; } /* No separator, break midstring... */ if (end == begin) end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT; save = *end; *end = 0; #if LOG if (tTd(89, 8)) sm_dprintf("%s[%d]: %s ...\n", id, seq++, begin); else syslog(level, "%s[%d]: %s ...", id, seq++, begin); #else /* LOG */ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s[%d]: %s ...\n", id, seq++, begin); #endif /* LOG */ *end = save; begin = end; } if (seq >= 999) { #if LOG if (tTd(89, 8)) sm_dprintf("%s[%d]: log terminated, too many parts\n", id, seq); else syslog(level, "%s[%d]: log terminated, too many parts", id, seq); #else /* LOG */ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s[%d]: log terminated, too many parts\n", id, seq); #endif /* LOG */ } else if (*begin != '\0') { #if LOG if (tTd(89, 8)) sm_dprintf("%s[%d]: %s\n", id, seq, begin); else syslog(level, "%s[%d]: %s", id, seq, begin); #else /* LOG */ (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s[%d]: %s\n", id, seq, begin); #endif /* LOG */ } if (buf != buf0) sm_free(buf); errno = save_errno; } /* ** HARD_SYSLOG -- call syslog repeatedly until it works ** ** Needed on HP-UX, which apparently doesn't guarantee that ** syslog succeeds during interrupt handlers. */ #if defined(__hpux) && !defined(HPUX11) # define MAXSYSLOGTRIES 100 # undef syslog # ifdef V4FS # define XCNST const # define CAST (const char *) # else /* V4FS */ # define XCNST # define CAST # endif /* V4FS */ void # ifdef __STDC__ hard_syslog(int pri, XCNST char *msg, ...) # else /* __STDC__ */ hard_syslog(pri, msg, va_alist) int pri; XCNST char *msg; va_dcl # endif /* __STDC__ */ { int i; char buf[SYSLOG_BUFSIZE]; SM_VA_LOCAL_DECL SM_VA_START(ap, msg); (void) sm_vsnprintf(buf, sizeof(buf), msg, ap); SM_VA_END(ap); for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; ) continue; } # undef CAST #endif /* defined(__hpux) && !defined(HPUX11) */ #if NEEDLOCAL_HOSTNAME_LENGTH /* ** LOCAL_HOSTNAME_LENGTH ** ** This is required to get sendmail to compile against BIND 4.9.x ** on Ultrix. ** ** Unfortunately, a Compaq Y2K patch kit provides it without ** bumping __RES in /usr/include/resolv.h so we can't automatically ** figure out whether it is needed. */ int local_hostname_length(hostname) char *hostname; { size_t len_host, len_domain; if (!*_res.defdname) res_init(); len_host = strlen(hostname); len_domain = strlen(_res.defdname); if (len_host > len_domain && (sm_strcasecmp(hostname + len_host - len_domain, _res.defdname) == 0) && hostname[len_host - len_domain - 1] == '.') return len_host - len_domain - 1; else return 0; } #endif /* NEEDLOCAL_HOSTNAME_LENGTH */ #if NEEDLINK /* ** LINK -- clone a file ** ** Some OS's lacks link() and hard links. Since sendmail is using ** link() as an efficient way to clone files, this implementation ** will simply do a file copy. ** ** NOTE: This link() replacement is not a generic replacement as it ** does not handle all of the semantics of the real link(2). ** ** Parameters: ** source -- pathname of existing file. ** target -- pathname of link (clone) to be created. ** ** Returns: ** 0 -- success. ** -1 -- failure, see errno for details. */ int link(source, target) const char *source; const char *target; { int save_errno; int sff; int src = -1, dst = -1; ssize_t readlen; ssize_t writelen; char buf[BUFSIZ]; struct stat st; sff = SFF_REGONLY|SFF_OPENASROOT; if (DontLockReadFiles) sff |= SFF_NOLOCK; /* Open the original file */ src = safeopen((char *)source, O_RDONLY, 0, sff); if (src < 0) goto fail; /* Obtain the size and the mode */ if (fstat(src, &st) < 0) goto fail; /* Create the duplicate copy */ sff &= ~SFF_NOLOCK; sff |= SFF_CREAT; dst = safeopen((char *)target, O_CREAT|O_EXCL|O_WRONLY, st.st_mode, sff); if (dst < 0) goto fail; /* Copy all of the bytes one buffer at a time */ while ((readlen = read(src, &buf, sizeof(buf))) > 0) { ssize_t left = readlen; char *p = buf; while (left > 0 && (writelen = write(dst, p, (size_t) left)) >= 0) { left -= writelen; p += writelen; } if (writelen < 0) break; } /* Any trouble reading? */ if (readlen < 0 || writelen < 0) goto fail; /* Close the input file */ if (close(src) < 0) { src = -1; goto fail; } src = -1; /* Close the output file */ if (close(dst) < 0) { /* don't set dst = -1 here so we unlink the file */ goto fail; } /* Success */ return 0; fail: save_errno = errno; if (src >= 0) (void) close(src); if (dst >= 0) { (void) unlink(target); (void) close(dst); } errno = save_errno; return -1; } #endif /* NEEDLINK */ /* ** Compile-Time options */ char *CompileOptions[] = { #if ALLOW_255 "ALLOW_255", #endif /* ALLOW_255 */ #if NAMED_BIND # if DNSMAP "DNSMAP", # endif /* DNSMAP */ #endif /* NAMED_BIND */ #if EGD "EGD", #endif /* EGD */ #if HESIOD "HESIOD", #endif /* HESIOD */ #if HES_GETMAILHOST "HES_GETMAILHOST", #endif /* HES_GETMAILHOST */ #if LDAPMAP "LDAPMAP", #endif /* LDAPMAP */ #if LDAP_REFERRALS "LDAP_REFERRALS", #endif /* LDAP_REFERRALS */ #if LOG "LOG", #endif /* LOG */ #if MAP_NSD "MAP_NSD", #endif /* MAP_NSD */ #if MAP_REGEX "MAP_REGEX", #endif /* MAP_REGEX */ #if MATCHGECOS "MATCHGECOS", #endif /* MATCHGECOS */ #if MILTER "MILTER", #endif /* MILTER */ #if MIME7TO8 "MIME7TO8", #endif /* MIME7TO8 */ #if MIME7TO8_OLD "MIME7TO8_OLD", #endif /* MIME7TO8_OLD */ #if MIME8TO7 "MIME8TO7", #endif /* MIME8TO7 */ #if NAMED_BIND "NAMED_BIND", #endif /* NAMED_BIND */ #if NDBM "NDBM", #endif /* NDBM */ #if NETINET "NETINET", #endif /* NETINET */ #if NETINET6 "NETINET6", #endif /* NETINET6 */ #if NETINFO "NETINFO", #endif /* NETINFO */ #if NETISO "NETISO", #endif /* NETISO */ #if NETNS "NETNS", #endif /* NETNS */ #if NETUNIX "NETUNIX", #endif /* NETUNIX */ #if NETX25 "NETX25", #endif /* NETX25 */ #if NEWDB "NEWDB", #endif /* NEWDB */ #if NIS "NIS", #endif /* NIS */ #if NISPLUS "NISPLUS", #endif /* NISPLUS */ #if NO_DH "NO_DH", #endif /* NO_DH */ #if PH_MAP "PH_MAP", #endif /* PH_MAP */ #ifdef PICKY_HELO_CHECK "PICKY_HELO_CHECK", #endif /* PICKY_HELO_CHECK */ #if PIPELINING "PIPELINING", #endif /* PIPELINING */ #if SASL # if SASL >= 20000 "SASLv2", # else /* SASL >= 20000 */ "SASL", # endif /* SASL >= 20000 */ #endif /* SASL */ #if SCANF "SCANF", #endif /* SCANF */ #if SM_LDAP_ERROR_ON_MISSING_ARGS "SM_LDAP_ERROR_ON_MISSING_ARGS", #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */ #if SMTPDEBUG "SMTPDEBUG", #endif /* SMTPDEBUG */ #if SOCKETMAP "SOCKETMAP", #endif /* SOCKETMAP */ #if STARTTLS "STARTTLS", #endif /* STARTTLS */ #if SUID_ROOT_FILES_OK "SUID_ROOT_FILES_OK", #endif /* SUID_ROOT_FILES_OK */ #if TCPWRAPPERS "TCPWRAPPERS", #endif /* TCPWRAPPERS */ #if TLS_NO_RSA "TLS_NO_RSA", #endif /* TLS_NO_RSA */ #if TLS_VRFY_PER_CTX "TLS_VRFY_PER_CTX", #endif /* TLS_VRFY_PER_CTX */ #if USERDB "USERDB", #endif /* USERDB */ #if USE_LDAP_INIT "USE_LDAP_INIT", #endif /* USE_LDAP_INIT */ #if USE_TTYPATH "USE_TTYPATH", #endif /* USE_TTYPATH */ #if XDEBUG "XDEBUG", #endif /* XDEBUG */ #if XLA "XLA", #endif /* XLA */ NULL }; /* ** OS compile options. */ char *OsCompileOptions[] = { #if ADDRCONFIG_IS_BROKEN "ADDRCONFIG_IS_BROKEN", #endif /* ADDRCONFIG_IS_BROKEN */ #ifdef AUTO_NETINFO_HOSTS "AUTO_NETINFO_HOSTS", #endif /* AUTO_NETINFO_HOSTS */ #ifdef AUTO_NIS_ALIASES "AUTO_NIS_ALIASES", #endif /* AUTO_NIS_ALIASES */ #if BROKEN_RES_SEARCH "BROKEN_RES_SEARCH", #endif /* BROKEN_RES_SEARCH */ #ifdef BSD4_4_SOCKADDR "BSD4_4_SOCKADDR", #endif /* BSD4_4_SOCKADDR */ #if BOGUS_O_EXCL "BOGUS_O_EXCL", #endif /* BOGUS_O_EXCL */ #if DEC_OSF_BROKEN_GETPWENT "DEC_OSF_BROKEN_GETPWENT", #endif /* DEC_OSF_BROKEN_GETPWENT */ #if FAST_PID_RECYCLE "FAST_PID_RECYCLE", #endif /* FAST_PID_RECYCLE */ #if HASCLOSEFROM "HASCLOSEFROM", #endif /* HASCLOSEFROM */ #if HASFCHOWN "HASFCHOWN", #endif /* HASFCHOWN */ #if HASFCHMOD "HASFCHMOD", #endif /* HASFCHMOD */ #if HASFDWALK "HASFDWALK", #endif /* HASFDWALK */ #if HASFLOCK "HASFLOCK", #endif /* HASFLOCK */ #if HASGETDTABLESIZE "HASGETDTABLESIZE", #endif /* HASGETDTABLESIZE */ #if HASGETUSERSHELL "HASGETUSERSHELL", #endif /* HASGETUSERSHELL */ #if HASINITGROUPS "HASINITGROUPS", #endif /* HASINITGROUPS */ #if HASLDAPGETALIASBYNAME "HASLDAPGETALIASBYNAME", #endif /* HASLDAPGETALIASBYNAME */ #if HASLSTAT "HASLSTAT", #endif /* HASLSTAT */ #if HASNICE "HASNICE", #endif /* HASNICE */ #if HASRANDOM "HASRANDOM", #endif /* HASRANDOM */ #if HASRRESVPORT "HASRRESVPORT", #endif /* HASRRESVPORT */ #if HASSETEGID "HASSETEGID", #endif /* HASSETEGID */ #if HASSETLOGIN "HASSETLOGIN", #endif /* HASSETLOGIN */ #if HASSETREGID "HASSETREGID", #endif /* HASSETREGID */ #if HASSETRESGID "HASSETRESGID", #endif /* HASSETRESGID */ #if HASSETREUID "HASSETREUID", #endif /* HASSETREUID */ #if HASSETRLIMIT "HASSETRLIMIT", #endif /* HASSETRLIMIT */ #if HASSETSID "HASSETSID", #endif /* HASSETSID */ #if HASSETUSERCONTEXT "HASSETUSERCONTEXT", #endif /* HASSETUSERCONTEXT */ #if HASSETVBUF "HASSETVBUF", #endif /* HASSETVBUF */ #if HAS_ST_GEN "HAS_ST_GEN", #endif /* HAS_ST_GEN */ #if HASSRANDOMDEV "HASSRANDOMDEV", #endif /* HASSRANDOMDEV */ #if HASURANDOMDEV "HASURANDOMDEV", #endif /* HASURANDOMDEV */ #if HASSTRERROR "HASSTRERROR", #endif /* HASSTRERROR */ #if HASULIMIT "HASULIMIT", #endif /* HASULIMIT */ #if HASUNAME "HASUNAME", #endif /* HASUNAME */ #if HASUNSETENV "HASUNSETENV", #endif /* HASUNSETENV */ #if HASWAITPID "HASWAITPID", #endif /* HASWAITPID */ #if IDENTPROTO "IDENTPROTO", #endif /* IDENTPROTO */ #if IP_SRCROUTE "IP_SRCROUTE", #endif /* IP_SRCROUTE */ #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL "LOCK_ON_OPEN", #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ #if MILTER_NO_NAGLE "MILTER_NO_NAGLE ", #endif /* MILTER_NO_NAGLE */ #if NEEDFSYNC "NEEDFSYNC", #endif /* NEEDFSYNC */ #if NEEDLINK "NEEDLINK", #endif /* NEEDLINK */ #if NEEDLOCAL_HOSTNAME_LENGTH "NEEDLOCAL_HOSTNAME_LENGTH", #endif /* NEEDLOCAL_HOSTNAME_LENGTH */ #if NEEDSGETIPNODE "NEEDSGETIPNODE", #endif /* NEEDSGETIPNODE */ #if NEEDSTRSTR "NEEDSTRSTR", #endif /* NEEDSTRSTR */ #if NEEDSTRTOL "NEEDSTRTOL", #endif /* NEEDSTRTOL */ #ifdef NO_GETSERVBYNAME "NO_GETSERVBYNAME", #endif /* NO_GETSERVBYNAME */ #if NOFTRUNCATE "NOFTRUNCATE", #endif /* NOFTRUNCATE */ #if REQUIRES_DIR_FSYNC "REQUIRES_DIR_FSYNC", #endif /* REQUIRES_DIR_FSYNC */ #if RLIMIT_NEEDS_SYS_TIME_H "RLIMIT_NEEDS_SYS_TIME_H", #endif /* RLIMIT_NEEDS_SYS_TIME_H */ #if SAFENFSPATHCONF "SAFENFSPATHCONF", #endif /* SAFENFSPATHCONF */ #if SECUREWARE "SECUREWARE", #endif /* SECUREWARE */ #if SHARE_V1 "SHARE_V1", #endif /* SHARE_V1 */ #if SIOCGIFCONF_IS_BROKEN "SIOCGIFCONF_IS_BROKEN", #endif /* SIOCGIFCONF_IS_BROKEN */ #if SIOCGIFNUM_IS_BROKEN "SIOCGIFNUM_IS_BROKEN", #endif /* SIOCGIFNUM_IS_BROKEN */ #if SNPRINTF_IS_BROKEN "SNPRINTF_IS_BROKEN", #endif /* SNPRINTF_IS_BROKEN */ #if SO_REUSEADDR_IS_BROKEN "SO_REUSEADDR_IS_BROKEN", #endif /* SO_REUSEADDR_IS_BROKEN */ #if SYS5SETPGRP "SYS5SETPGRP", #endif /* SYS5SETPGRP */ #if SYSTEM5 "SYSTEM5", #endif /* SYSTEM5 */ #if USE_DOUBLE_FORK "USE_DOUBLE_FORK", #endif /* USE_DOUBLE_FORK */ #if USE_ENVIRON "USE_ENVIRON", #endif /* USE_ENVIRON */ #if USE_SA_SIGACTION "USE_SA_SIGACTION", #endif /* USE_SA_SIGACTION */ #if USE_SIGLONGJMP "USE_SIGLONGJMP", #endif /* USE_SIGLONGJMP */ #if USEGETCONFATTR "USEGETCONFATTR", #endif /* USEGETCONFATTR */ #if USESETEUID "USESETEUID", #endif /* USESETEUID */ #ifdef USESYSCTL "USESYSCTL", #endif /* USESYSCTL */ #if USING_NETSCAPE_LDAP "USING_NETSCAPE_LDAP", #endif /* USING_NETSCAPE_LDAP */ #ifdef WAITUNION "WAITUNION", #endif /* WAITUNION */ NULL }; /* ** FFR compile options. */ char *FFRCompileOptions[] = { +#if _FFR_ADDR_TYPE_MODES + /* more info in {addr_type}, requires m4 changes! */ + "_FFR_ADDR_TYPE_MODES", +#endif /* _FFR_ADDR_TYPE_MODES */ #if _FFR_ALLOW_SASLINFO /* DefaultAuthInfo can be specified by user. */ /* DefaultAuthInfo doesn't really work in 8.13 anymore. */ "_FFR_ALLOW_SASLINFO", #endif /* _FFR_ALLOW_SASLINFO */ #if _FFR_BESTMX_BETTER_TRUNCATION /* Better truncation of list of MX records for dns map. */ "_FFR_BESTMX_BETTER_TRUNCATION", #endif /* _FFR_BESTMX_BETTER_TRUNCATION */ #if _FFR_CATCH_BROKEN_MTAS /* Deal with MTAs that send a reply during the DATA phase. */ "_FFR_CATCH_BROKEN_MTAS", #endif /* _FFR_CATCH_BROKEN_MTAS */ #if _FFR_CHK_QUEUE /* Stricter checks about queue directory permissions. */ "_FFR_CHK_QUEUE", #endif /* _FFR_CHK_QUEUE */ #if _FFR_CLIENT_SIZE /* Don't try to send mail if its size exceeds SIZE= of server. */ "_FFR_CLIENT_SIZE", #endif /* _FFR_CLIENT_SIZE */ #if _FFR_CRLPATH /* CRLPath; needs documentation; Al Smith */ "_FFR_CRLPATH", #endif /* _FFR_CRLPATH */ #if _FFR_DAEMON_NETUNIX /* Allow local (not just TCP) socket connection to server. */ "_FFR_DAEMON_NETUNIX", #endif /* _FFR_DAEMON_NETUNIX */ #if _FFR_DEPRECATE_MAILER_FLAG_I /* What it says :-) */ "_FFR_DEPRECATE_MAILER_FLAG_I", #endif /* _FFR_DEPRECATE_MAILER_FLAG_I */ #if _FFR_DM_ONE /* deliver first TA in background, then queue */ "_FFR_DM_ONE", #endif /* _FFR_DM_ONE */ #if _FFR_DIGUNIX_SAFECHOWN /* Properly set SAFECHOWN (include/sm/conf.h) for Digital UNIX */ /* Problem noted by Anne Bennett of Concordia University */ "_FFR_DIGUNIX_SAFECHOWN", #endif /* _FFR_DIGUNIX_SAFECHOWN */ #if _FFR_DNSMAP_ALIASABLE /* Allow dns map type to be used for aliases. */ /* Don Lewis of TDK */ "_FFR_DNSMAP_ALIASABLE", #endif /* _FFR_DNSMAP_ALIASABLE */ #if _FFR_DONTLOCKFILESFORREAD_OPTION /* Enable DontLockFilesForRead option. */ "_FFR_DONTLOCKFILESFORREAD_OPTION", #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ #if _FFR_DOTTED_USERNAMES /* Allow usernames with '.' */ "_FFR_DOTTED_USERNAMES", #endif /* _FFR_DOTTED_USERNAMES */ #if _FFR_DPO_CS /* ** Make DaemonPortOptions case sensitive. ** For some unknown reasons the code converted every option ** to uppercase (first letter only, as that's the only one that ** is actually checked). This prevented all new lower case options ** from working... ** The documentation doesn't say anything about case (in)sensitivity, ** which means it should be case sensitive by default, ** but it's not a good idea to change this within a patch release, ** so let's delay this to 8.15. */ "_FFR_DPO_CS", #endif /* _FFR_DPO_CS */ +#if _FFR_DPRINTF_MAP + /* dprintf map for logging */ + "_FFR_DPRINTF_MAP", +#endif /* _FFR_DPRINTF_MAP */ #if _FFR_DROP_TRUSTUSER_WARNING /* ** Don't issue this warning: ** "readcf: option TrustedUser may cause problems on systems ** which do not support fchown() if UseMSP is not set. */ "_FFR_DROP_TRUSTUSER_WARNING", #endif /* _FFR_DROP_TRUSTUSER_WARNING */ #if _FFR_EIGHT_BIT_ADDR_OK /* EightBitAddrOK: allow 8-bit e-mail addresses */ "_FFR_EIGHT_BIT_ADDR_OK", #endif /* _FFR_EIGHT_BIT_ADDR_OK */ #if _FFR_EXTRA_MAP_CHECK /* perform extra checks on $( $) in R lines */ "_FFR_EXTRA_MAP_CHECK", #endif /* _FFR_EXTRA_MAP_CHECK */ #if _FFR_GETHBN_ExFILE /* ** According to Motonori Nakamura some gethostbyname() ** implementations (TurboLinux?) may (temporarily) fail ** due to a lack of file discriptors. Enabling this FFR ** will check errno for EMFILE and ENFILE and in case of a match ** cause a temporary error instead of a permanent error. ** The right solution is of course to file a bug against those ** systems such that they actually set h_errno = TRY_AGAIN. */ "_FFR_GETHBN_ExFILE", #endif /* _FFR_GETHBN_ExFILE */ #if _FFR_FIX_DASHT /* ** If using -t, force not sending to argv recipients, even ** if they are mentioned in the headers. */ "_FFR_FIX_DASHT", #endif /* _FFR_FIX_DASHT */ #if _FFR_FORWARD_SYSERR /* Cause a "syserr" if forward file isn't "safe". */ "_FFR_FORWARD_SYSERR", #endif /* _FFR_FORWARD_SYSERR */ #if _FFR_GEN_ORCPT /* Generate a ORCPT DSN arg if not already provided */ "_FFR_GEN_ORCPT", #endif /* _FFR_GEN_ORCPT */ #if _FFR_GROUPREADABLEAUTHINFOFILE /* Allow group readable DefaultAuthInfo file. */ "_FFR_GROUPREADABLEAUTHINFOFILE", #endif /* _FFR_GROUPREADABLEAUTHINFOFILE */ #if _FFR_HANDLE_ISO8859_GECOS /* ** Allow ISO 8859 characters in GECOS field: replace them ** ith ASCII "equivalent". */ /* Peter Eriksson of Linkopings universitet */ "_FFR_HANDLE_ISO8859_GECOS", #endif /* _FFR_HANDLE_ISO8859_GECOS */ #if _FFR_HPUX_NSSWITCH /* Use nsswitch on HP-UX */ "_FFR_HPUX_NSSWITCH", #endif /* _FFR_HPUX_NSSWITCH */ #if _FFR_IGNORE_BOGUS_ADDR /* Ignore addresses for which prescan() failed */ "_FFR_IGNORE_BOGUS_ADDR", #endif /* _FFR_IGNORE_BOGUS_ADDR */ #if _FFR_IGNORE_EXT_ON_HELO /* Ignore extensions offered in response to HELO */ "_FFR_IGNORE_EXT_ON_HELO", #endif /* _FFR_IGNORE_EXT_ON_HELO */ #if _FFR_MAXDATASIZE /* ** It is possible that a header is larger than MILTER_CHUNK_SIZE, ** hence this shouldn't be used as limit for milter communication. ** see also libmilter/comm.c ** Gurusamy Sarathy of ActiveState */ "_FFR_MAXDATASIZE", #endif /* _FFR_MAXDATASIZE */ #if _FFR_MAX_FORWARD_ENTRIES /* Try to limit number of .forward entries */ /* (doesn't work) */ /* Randall S. Winchester of the University of Maryland */ "_FFR_MAX_FORWARD_ENTRIES", #endif /* _FFR_MAX_FORWARD_ENTRIES */ #if _FFR_MAX_SLEEP_TIME /* Limit sleep(2) time in libsm/clock.c */ "_FFR_MAX_SLEEP_TIME", #endif /* _FFR_MAX_SLEEP_TIME */ #if _FFR_MEMSTAT /* Check free memory */ "_FFR_MEMSTAT", #endif /* _FFR_MEMSTAT */ #if _FFR_MILTER_CHECK "_FFR_MILTER_CHECK", #endif /* _FFR_MILTER_CHECK */ #if _FFR_MILTER_CONVERT_ALL_LF_TO_CRLF /* ** milter_body() uses the same conversion algorithm as putbody() ** to translate the "local" df format (\n) to SMTP format (\r\n). ** However, putbody() and mime8to7() use different conversion ** algorithms. ** If the input date does not follow the SMTP standard ** (e.g., if it has "naked \r"s), then the output from putbody() ** and mime8to7() will most likely be different. ** By turning on this FFR milter_body() will try to "imitate" ** mime8to7(). ** Note: there is no (simple) way to deal with both conversions ** in a consistent manner. Moreover, as the "GiGo" principle applies, ** it's not really worth to fix it. */ "_FFR_MILTER_CONVERT_ALL_LF_TO_CRLF", #endif /* _FFR_MILTER_CONVERT_ALL_LF_TO_CRLF */ #if _FFR_MILTER_CHECK_REJECTIONS_TOO /* ** Also send RCPTs that are rejected by check_rcpt to a milter ** (if requested during option negotiation). */ "_FFR_MILTER_CHECK_REJECTIONS_TOO", #endif /* _FFR_MILTER_CHECK_REJECTIONS_TOO */ #if _FFR_MIME7TO8_OLD /* Old mime7to8 code, the new is broken for at least one example. */ "_FFR_MIME7TO8_OLD", #endif /* _FFR_MAX_SLEEP_TIME */ +#if _FFR_MORE_MACROS + /* allow more long macro names ("unprintable" characters). */ + "_FFR_MORE_MACROS", +#endif /* _FFR_MORE_MACROS */ #if _FFR_MSG_ACCEPT /* allow to override "Message accepted for delivery" */ "_FFR_MSG_ACCEPT", #endif /* _FFR_MSG_ACCEPT */ #if _FFR_NODELAYDSN_ON_HOLD /* Do not issue a DELAY DSN for mailers that use the hold flag. */ /* Steven Pitzl */ "_FFR_NODELAYDSN_ON_HOLD", #endif /* _FFR_NODELAYDSN_ON_HOLD */ #if _FFR_NO_PIPE /* Disable PIPELINING, delay client if used. */ "_FFR_NO_PIPE", #endif /* _FFR_NO_PIPE */ +#if _FFR_LDAP_NETWORK_TIMEOUT + /* set LDAP_OPT_NETWORK_TIMEOUT if available (-c) */ + "_FFR_LDAP_NETWORK_TIMEOUT", +#endif /* _FFR_LDAP_NETWORK_TIMEOUT */ #if _FFR_LOG_NTRIES /* log ntries=, from Nik Clayton of FreeBSD */ "_FFR_LOG_NTRIES", #endif /* _FFR_LOG_NTRIES */ #if _FFR_QF_PARANOIA "_FFR_QF_PARANOIA", #endif /* _FFR_QF_PARANOIA */ #if _FFR_QUEUEDELAY /* Exponential queue delay; disabled in 8.13 since it isn't used. */ "_FFR_QUEUEDELAY", #endif /* _FFR_QUEUEDELAY */ #if _FFR_QUEUE_GROUP_SORTORDER /* Allow QueueSortOrder per queue group. */ /* XXX: Still need to actually use qgrp->qg_sortorder */ "_FFR_QUEUE_GROUP_SORTORDER", #endif /* _FFR_QUEUE_GROUP_SORTORDER */ #if _FFR_QUEUE_MACRO /* Define {queue} macro. */ "_FFR_QUEUE_MACRO", #endif /* _FFR_QUEUE_MACRO */ #if _FFR_QUEUE_RUN_PARANOIA /* Additional checks when doing queue runs; interval of checks */ "_FFR_QUEUE_RUN_PARANOIA", #endif /* _FFR_QUEUE_RUN_PARANOIA */ #if _FFR_QUEUE_SCHED_DBG /* Debug output for the queue scheduler. */ "_FFR_QUEUE_SCHED_DBG", #endif /* _FFR_QUEUE_SCHED_DBG */ #if _FFR_REDIRECTEMPTY /* ** envelope <> can't be sent to mailing lists, only owner- ** send spam of this type to owner- of the list ** ---- to stop spam from going to mailing lists. */ "_FFR_REDIRECTEMPTY", #endif /* _FFR_REDIRECTEMPTY */ #if _FFR_RESET_MACRO_GLOBALS /* Allow macro 'j' to be set dynamically via rulesets. */ "_FFR_RESET_MACRO_GLOBALS", #endif /* _FFR_RESET_MACRO_GLOBALS */ #if _FFR_RHS /* Random shuffle for queue sorting. */ "_FFR_RHS", #endif /* _FFR_RHS */ +#if _FFR_RUNPQG + /* + ** allow -qGqueue_group -qp to work, i.e., + ** restrict a persistent queue runner to a queue group. + */ + + "_FFR_RUNPQG", +#endif /* _FFR_RUNPQG */ +#if _FFR_SESSID + /* session id (for logging) */ + "_FFR_SESSID", +#endif /* _FFR_SESSID */ #if _FFR_SHM_STATUS /* Donated code (unused). */ "_FFR_SHM_STATUS", #endif /* _FFR_SHM_STATUS */ #if _FFR_LDAP_SINGLEDN /* ** The LDAP database map code in Sendmail 8.12.10, when ** given the -1 switch, would match only a single DN, ** but was able to return multiple attributes for that ** DN. In Sendmail 8.13 this "bug" was corrected to ** only return if exactly one attribute matched. ** ** Unfortunately, our configuration uses the former ** behaviour. Attached is a relatively simple patch ** to 8.13.4 which adds a -2 switch (for lack of a ** better option) which returns the single dn/multiple ** attributes. ** ** Jeffrey T. Eaton, Carnegie-Mellon University */ "_FFR_LDAP_SINGLEDN", #endif /* _FFR_LDAP_SINGLEDN */ #if _FFR_SKIP_DOMAINS /* process every N'th domain instead of every N'th message */ "_FFR_SKIP_DOMAINS", #endif /* _FFR_SKIP_DOMAINS */ #if _FFR_SLEEP_USE_SELECT /* Use select(2) in libsm/clock.c to emulate sleep(2) */ "_FFR_SLEEP_USE_SELECT ", #endif /* _FFR_SLEEP_USE_SELECT */ #if _FFR_SPT_ALIGN /* ** It looks like the Compaq Tru64 5.1A now aligns argv and envp to 64 ** bit alignment, so unless each piece of argv and envp is a multiple ** of 8 bytes (including terminating NULL), initsetproctitle() won't ** use any of the space beyond argv[0]. Be sure to set SPT_ALIGN_SIZE ** if you use this FFR. */ /* Chris Adams of HiWAAY Informations Services */ "_FFR_SPT_ALIGN", #endif /* _FFR_SPT_ALIGN */ #if _FFR_SS_PER_DAEMON /* SuperSafe per DaemonPortOptions: 'T' (better letter?) */ "_FFR_SS_PER_DAEMON", #endif /* _FFR_SS_PER_DAEMON */ #if _FFR_TIMERS /* Donated code (unused). */ "_FFR_TIMERS", #endif /* _FFR_TIMERS */ #if _FFR_TLS_1 /* More STARTTLS options, e.g., secondary certs. */ "_FFR_TLS_1", #endif /* _FFR_TLS_1 */ #if _FFR_TRUSTED_QF /* ** If we don't own the file mark it as unsafe. ** However, allow TrustedUser to own it as well ** in case TrustedUser manipulates the queue. */ "_FFR_TRUSTED_QF", #endif /* _FFR_TRUSTED_QF */ #if _FFR_USE_SEM_LOCKING "_FFR_USE_SEM_LOCKING", #endif /* _FFR_USE_SEM_LOCKING */ #if _FFR_USE_SETLOGIN /* Use setlogin() */ /* Peter Philipp */ "_FFR_USE_SETLOGIN", #endif /* _FFR_USE_SETLOGIN */ NULL }; Index: head/contrib/sendmail/src/headers.c =================================================================== --- head/contrib/sendmail/src/headers.c (revision 173342) +++ head/contrib/sendmail/src/headers.c (revision 173343) @@ -1,2310 +1,2312 @@ /* * Copyright (c) 1998-2004, 2006, 2007 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * $FreeBSD$ */ #include #include -SM_RCSID("@(#)$Id: headers.c,v 8.310 2007/02/07 22:44:35 ca Exp $") +SM_RCSID("@(#)$Id: headers.c,v 8.312 2007/06/19 18:52:11 ca Exp $") static HDR *allocheader __P((char *, char *, int, SM_RPOOL_T *, bool)); static size_t fix_mime_header __P((HDR *, ENVELOPE *)); static int priencode __P((char *)); static bool put_vanilla_header __P((HDR *, char *, MCI *)); /* ** SETUPHEADERS -- initialize headers in symbol table ** ** Parameters: ** none ** ** Returns: ** none */ void setupheaders() { struct hdrinfo *hi; STAB *s; for (hi = HdrInfo; hi->hi_field != NULL; hi++) { s = stab(hi->hi_field, ST_HEADER, ST_ENTER); s->s_header.hi_flags = hi->hi_flags; s->s_header.hi_ruleset = NULL; } } /* ** DOCHOMPHEADER -- process and save a header line. ** ** Called by chompheader. ** ** Parameters: ** line -- header as a text line. ** pflag -- flags for chompheader() (from sendmail.h) ** hdrp -- a pointer to the place to save the header. ** e -- the envelope including this header. ** ** Returns: ** flags for this header. ** ** Side Effects: ** The header is saved on the header list. ** Contents of 'line' are destroyed. */ static struct hdrinfo NormalHeader = { NULL, 0, NULL }; static unsigned long dochompheader __P((char *, int, HDR **, ENVELOPE *)); static unsigned long dochompheader(line, pflag, hdrp, e) char *line; int pflag; HDR **hdrp; ENVELOPE *e; { unsigned char mid = '\0'; register char *p; register HDR *h; HDR **hp; char *fname; char *fvalue; bool cond = false; bool dropfrom; bool headeronly; STAB *s; struct hdrinfo *hi; bool nullheader = false; BITMAP256 mopts; headeronly = hdrp != NULL; if (!headeronly) hdrp = &e->e_header; /* strip off options */ clrbitmap(mopts); p = line; if (!bitset(pflag, CHHDR_USER) && *p == '?') { int c; register char *q; q = strchr(++p, '?'); if (q == NULL) goto hse; *q = '\0'; c = *p & 0377; /* possibly macro conditional */ if (c == MACROEXPAND) { /* catch ?$? */ if (*++p == '\0') { *q = '?'; goto hse; } mid = (unsigned char) *p++; /* catch ?$abc? */ if (*p != '\0') { *q = '?'; goto hse; } } else if (*p == '$') { /* catch ?$? */ if (*++p == '\0') { *q = '?'; goto hse; } mid = (unsigned char) macid(p); if (bitset(0200, mid)) { p += strlen(macname(mid)) + 2; SM_ASSERT(p <= q); } else p++; /* catch ?$abc? */ if (*p != '\0') { *q = '?'; goto hse; } } else { while (*p != '\0') { if (!isascii(*p)) { *q = '?'; goto hse; } setbitn(bitidx(*p), mopts); cond = true; p++; } } p = q + 1; } /* find canonical name */ fname = p; while (isascii(*p) && isgraph(*p) && *p != ':') p++; fvalue = p; while (isascii(*p) && isspace(*p)) p++; if (*p++ != ':' || fname == fvalue) { hse: syserr("553 5.3.0 header syntax error, line \"%s\"", line); return 0; } *fvalue = '\0'; fvalue = p; /* if the field is null, go ahead and use the default */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') nullheader = true; /* security scan: long field names are end-of-header */ if (strlen(fname) > 100) return H_EOH; /* check to see if it represents a ruleset call */ if (bitset(pflag, CHHDR_DEF)) { char hbuf[50]; (void) expand(fvalue, hbuf, sizeof(hbuf), e); for (p = hbuf; isascii(*p) && isspace(*p); ) p++; if ((*p++ & 0377) == CALLSUBR) { auto char *endp; bool strc; strc = *p == '+'; /* strip comments? */ if (strc) ++p; if (strtorwset(p, &endp, ST_ENTER) > 0) { *endp = '\0'; s = stab(fname, ST_HEADER, ST_ENTER); if (LogLevel > 9 && s->s_header.hi_ruleset != NULL) sm_syslog(LOG_WARNING, NOQID, "Warning: redefined ruleset for header=%s, old=%s, new=%s", fname, s->s_header.hi_ruleset, p); s->s_header.hi_ruleset = newstr(p); if (!strc) s->s_header.hi_flags |= H_STRIPCOMM; } return 0; } } /* see if it is a known type */ s = stab(fname, ST_HEADER, ST_FIND); if (s != NULL) hi = &s->s_header; else hi = &NormalHeader; if (tTd(31, 9)) { if (s == NULL) sm_dprintf("no header flags match\n"); else sm_dprintf("header match, flags=%lx, ruleset=%s\n", hi->hi_flags, hi->hi_ruleset == NULL ? "" : hi->hi_ruleset); } /* see if this is a resent message */ if (!bitset(pflag, CHHDR_DEF) && !headeronly && bitset(H_RESENT, hi->hi_flags)) e->e_flags |= EF_RESENT; /* if this is an Errors-To: header keep track of it now */ if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly && bitset(H_ERRORSTO, hi->hi_flags)) (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); /* if this means "end of header" quit now */ if (!headeronly && bitset(H_EOH, hi->hi_flags)) return hi->hi_flags; /* ** Horrible hack to work around problem with Lotus Notes SMTP ** mail gateway, which generates From: headers with newlines in ** them and the
on the second line. Although this is ** legal RFC 822, many MUAs don't handle this properly and thus ** never find the actual address. */ if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader) { while ((p = strchr(fvalue, '\n')) != NULL) *p = ' '; } /* ** If there is a check ruleset, verify it against the header. */ if (bitset(pflag, CHHDR_CHECK)) { int rscheckflags; char *rs; rscheckflags = RSF_COUNT; if (!bitset(hi->hi_flags, H_FROM|H_RCPT)) rscheckflags |= RSF_UNSTRUCTURED; /* no ruleset? look for default */ rs = hi->hi_ruleset; if (rs == NULL) { s = stab("*", ST_HEADER, ST_FIND); if (s != NULL) { rs = (&s->s_header)->hi_ruleset; if (bitset((&s->s_header)->hi_flags, H_STRIPCOMM)) rscheckflags |= RSF_RMCOMM; } } else if (bitset(hi->hi_flags, H_STRIPCOMM)) rscheckflags |= RSF_RMCOMM; if (rs != NULL) { int l, k; char qval[MAXNAME]; l = 0; qval[l++] = '"'; /* - 3 to avoid problems with " at the end */ /* should be sizeof(qval), not MAXNAME */ for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++) { switch (fvalue[k]) { /* XXX other control chars? */ case '\011': /* ht */ case '\012': /* nl */ case '\013': /* vt */ case '\014': /* np */ case '\015': /* cr */ qval[l++] = ' '; break; case '"': qval[l++] = '\\'; /* FALLTHROUGH */ default: qval[l++] = fvalue[k]; break; } } qval[l++] = '"'; qval[l] = '\0'; k += strlen(fvalue + k); if (k >= MAXNAME) { if (LogLevel > 9) sm_syslog(LOG_WARNING, e->e_id, "Warning: truncated header '%s' before check with '%s' len=%d max=%d", fname, rs, k, MAXNAME - 1); } macdefine(&e->e_macro, A_TEMP, macid("{currHeader}"), qval); macdefine(&e->e_macro, A_TEMP, macid("{hdr_name}"), fname); (void) sm_snprintf(qval, sizeof(qval), "%d", k); macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval); if (bitset(H_FROM, hi->hi_flags)) macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "h s"); else if (bitset(H_RCPT, hi->hi_flags)) macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "h r"); else macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "h"); (void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3, NULL, e->e_id, NULL); } } /* ** Drop explicit From: if same as what we would generate. ** This is to make MH (which doesn't always give a full name) ** insert the full name information in all circumstances. */ dropfrom = false; p = "resent-from"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; if (!bitset(pflag, CHHDR_DEF) && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0) { if (tTd(31, 2)) { sm_dprintf("comparing header from (%s) against default (%s or %s)\n", fvalue, e->e_from.q_paddr, e->e_from.q_user); } if (e->e_from.q_paddr != NULL && e->e_from.q_mailer != NULL && bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && (strcmp(fvalue, e->e_from.q_paddr) == 0 || strcmp(fvalue, e->e_from.q_user) == 0)) dropfrom = true; } /* delete default value for this header */ for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) { if (sm_strcasecmp(fname, h->h_field) == 0 && !bitset(H_USER, h->h_flags) && !bitset(H_FORCE, h->h_flags)) { if (nullheader) { /* user-supplied value was null */ return 0; } if (dropfrom) { /* make this look like the user entered it */ h->h_flags |= H_USER; return hi->hi_flags; } h->h_value = NULL; if (!cond) { /* copy conditions from default case */ memmove((char *) mopts, (char *) h->h_mflags, sizeof(mopts)); } h->h_macro = mid; } } /* create a new node */ h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h)); h->h_field = sm_rpool_strdup_x(e->e_rpool, fname); h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue); h->h_link = NULL; memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts)); h->h_macro = mid; *hp = h; h->h_flags = hi->hi_flags; if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE)) h->h_flags |= H_USER; /* strip EOH flag if parsing MIME headers */ if (headeronly) h->h_flags &= ~H_EOH; if (bitset(pflag, CHHDR_DEF)) h->h_flags |= H_DEFAULT; if (cond || mid != '\0') h->h_flags |= H_CHECK; /* hack to see if this is a new format message */ if (!bitset(pflag, CHHDR_DEF) && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) && (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) { e->e_flags &= ~EF_OLDSTYLE; } return h->h_flags; } /* ** CHOMPHEADER -- process and save a header line. ** ** Called by collect, readcf, and readqf to deal with header lines. ** This is just a wrapper for dochompheader(). ** ** Parameters: ** line -- header as a text line. ** pflag -- flags for chompheader() (from sendmail.h) ** hdrp -- a pointer to the place to save the header. ** e -- the envelope including this header. ** ** Returns: ** flags for this header. ** ** Side Effects: ** The header is saved on the header list. ** Contents of 'line' are destroyed. */ unsigned long chompheader(line, pflag, hdrp, e) char *line; int pflag; HDR **hdrp; register ENVELOPE *e; { unsigned long rval; if (tTd(31, 6)) { sm_dprintf("chompheader: "); xputs(sm_debug_file(), line); sm_dprintf("\n"); } /* quote this if user (not config file) input */ if (bitset(pflag, CHHDR_USER)) { char xbuf[MAXLINE]; char *xbp = NULL; int xbufs; xbufs = sizeof(xbuf); xbp = quote_internal_chars(line, xbuf, &xbufs); if (tTd(31, 7)) { sm_dprintf("chompheader: quoted: "); xputs(sm_debug_file(), xbp); sm_dprintf("\n"); } rval = dochompheader(xbp, pflag, hdrp, e); if (xbp != xbuf) sm_free(xbp); } else rval = dochompheader(line, pflag, hdrp, e); return rval; } /* ** ALLOCHEADER -- allocate a header entry ** ** Parameters: ** field -- the name of the header field (will not be copied). ** value -- the value of the field (will be copied). ** flags -- flags to add to h_flags. ** rp -- resource pool for allocations ** space -- add leading space? ** ** Returns: ** Pointer to a newly allocated and populated HDR. ** ** Notes: ** o field and value must be in internal format, i.e., ** metacharacters must be "quoted", see quote_internal_chars(). ** o maybe add more flags to decide: ** - what to copy (field/value) ** - whether to convert value to an internal format */ static HDR * allocheader(field, value, flags, rp, space) char *field; char *value; int flags; SM_RPOOL_T *rp; bool space; { HDR *h; STAB *s; /* find info struct */ s = stab(field, ST_HEADER, ST_FIND); /* allocate space for new header */ h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h)); h->h_field = field; if (space) { size_t l; char *n; l = strlen(value); SM_ASSERT(l + 2 > l); n = sm_rpool_malloc_x(rp, l + 2); n[0] = ' '; n[1] = '\0'; sm_strlcpy(n + 1, value, l + 1); h->h_value = n; } else h->h_value = sm_rpool_strdup_x(rp, value); h->h_flags = flags; if (s != NULL) h->h_flags |= s->s_header.hi_flags; clrbitmap(h->h_mflags); h->h_macro = '\0'; return h; } /* ** ADDHEADER -- add a header entry to the end of the queue. ** ** This bypasses the special checking of chompheader. ** ** Parameters: ** field -- the name of the header field (will not be copied). ** value -- the value of the field (will be copied). ** flags -- flags to add to h_flags. ** e -- envelope. ** space -- add leading space? ** ** Returns: ** none. ** ** Side Effects: ** adds the field on the list of headers for this envelope. ** ** Notes: field and value must be in internal format, i.e., ** metacharacters must be "quoted", see quote_internal_chars(). */ void addheader(field, value, flags, e, space) char *field; char *value; int flags; ENVELOPE *e; bool space; { register HDR *h; HDR **hp; HDR **hdrlist = &e->e_header; /* find current place in list -- keep back pointer? */ for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) { if (sm_strcasecmp(field, h->h_field) == 0) break; } /* allocate space for new header */ h = allocheader(field, value, flags, e->e_rpool, space); h->h_link = *hp; *hp = h; } /* ** INSHEADER -- insert a header entry at the specified index ** This bypasses the special checking of chompheader. ** ** Parameters: ** idx -- index into the header list at which to insert ** field -- the name of the header field (will be copied). ** value -- the value of the field (will be copied). ** flags -- flags to add to h_flags. ** e -- envelope. ** space -- add leading space? ** ** Returns: ** none. ** ** Side Effects: ** inserts the field on the list of headers for this envelope. ** ** Notes: ** - field and value must be in internal format, i.e., ** metacharacters must be "quoted", see quote_internal_chars(). ** - the header list contains headers that might not be ** sent "out" (see putheader(): "skip"), hence there is no ** reliable way to insert a header at an exact position ** (except at the front or end). */ void insheader(idx, field, value, flags, e, space) int idx; char *field; char *value; int flags; ENVELOPE *e; bool space; { HDR *h, *srch, *last = NULL; /* allocate space for new header */ h = allocheader(field, value, flags, e->e_rpool, space); /* find insertion position */ for (srch = e->e_header; srch != NULL && idx > 0; srch = srch->h_link, idx--) last = srch; if (e->e_header == NULL) { e->e_header = h; h->h_link = NULL; } else if (srch == NULL) { SM_ASSERT(last != NULL); last->h_link = h; h->h_link = NULL; } else { h->h_link = srch->h_link; srch->h_link = h; } } /* ** HVALUE -- return value of a header. ** ** Only "real" fields (i.e., ones that have not been supplied ** as a default) are used. ** ** Parameters: ** field -- the field name. ** header -- the header list. ** ** Returns: ** pointer to the value part (internal format). ** NULL if not found. ** ** Side Effects: ** none. */ char * hvalue(field, header) char *field; HDR *header; { register HDR *h; for (h = header; h != NULL; h = h->h_link) { if (!bitset(H_DEFAULT, h->h_flags) && sm_strcasecmp(h->h_field, field) == 0) return h->h_value; } return NULL; } /* ** ISHEADER -- predicate telling if argument is a header. ** ** A line is a header if it has a single word followed by ** optional white space followed by a colon. ** ** Header fields beginning with two dashes, although technically ** permitted by RFC822, are automatically rejected in order ** to make MIME work out. Without this we could have a technically ** legal header such as ``--"foo:bar"'' that would also be a legal ** MIME separator. ** ** Parameters: ** h -- string to check for possible headerness. ** ** Returns: ** true if h is a header. ** false otherwise. ** ** Side Effects: ** none. */ bool isheader(h) char *h; { char *s; s = h; if (s[0] == '-' && s[1] == '-') return false; while (*s > ' ' && *s != ':' && *s != '\0') s++; if (h == s) return false; /* following technically violates RFC822 */ while (isascii(*s) && isspace(*s)) s++; return (*s == ':'); } /* ** EATHEADER -- run through the stored header and extract info. ** ** Parameters: ** e -- the envelope to process. ** full -- if set, do full processing (e.g., compute ** message priority). This should not be set ** when reading a queue file because some info ** needed to compute the priority is wrong. ** log -- call logsender()? ** ** Returns: ** none. ** ** Side Effects: ** Sets a bunch of global variables from information ** in the collected header. */ void eatheader(e, full, log) register ENVELOPE *e; bool full; bool log; { register HDR *h; register char *p; int hopcnt = 0; char buf[MAXLINE]; /* ** Set up macros for possible expansion in headers. */ macdefine(&e->e_macro, A_PERM, 'f', e->e_sender); macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt); else macdefine(&e->e_macro, A_PERM, 'u', NULL); /* full name of from person */ p = hvalue("full-name", e->e_header); if (p != NULL) { if (!rfc822_string(p)) { /* ** Quote a full name with special characters ** as a comment so crackaddr() doesn't destroy ** the name portion of the address. */ p = addquotes(p, e->e_rpool); } macdefine(&e->e_macro, A_PERM, 'x', p); } if (tTd(32, 1)) sm_dprintf("----- collected header -----\n"); e->e_msgid = NULL; for (h = e->e_header; h != NULL; h = h->h_link) { if (tTd(32, 1)) sm_dprintf("%s:", h->h_field); if (h->h_value == NULL) { if (tTd(32, 1)) sm_dprintf("\n"); continue; } /* do early binding */ if (bitset(H_DEFAULT, h->h_flags) && !bitset(H_BINDLATE, h->h_flags)) { if (tTd(32, 1)) { sm_dprintf("("); xputs(sm_debug_file(), h->h_value); sm_dprintf(") "); } expand(h->h_value, buf, sizeof(buf), e); if (buf[0] != '\0' && (buf[0] != ' ' || buf[1] != '\0')) { if (bitset(H_FROM, h->h_flags)) expand(crackaddr(buf, e), buf, sizeof(buf), e); h->h_value = sm_rpool_strdup_x(e->e_rpool, buf); h->h_flags &= ~H_DEFAULT; } } if (tTd(32, 1)) { xputs(sm_debug_file(), h->h_value); sm_dprintf("\n"); } /* count the number of times it has been processed */ if (bitset(H_TRACE, h->h_flags)) hopcnt++; /* send to this person if we so desire */ if (GrabTo && bitset(H_RCPT, h->h_flags) && !bitset(H_DEFAULT, h->h_flags) && (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) { #if 0 int saveflags = e->e_flags; #endif /* 0 */ (void) sendtolist(denlstring(h->h_value, true, false), NULLADDR, &e->e_sendqueue, 0, e); #if 0 /* ** Change functionality so a fatal error on an ** address doesn't affect the entire envelope. */ /* delete fatal errors generated by this address */ if (!bitset(EF_FATALERRS, saveflags)) e->e_flags &= ~EF_FATALERRS; #endif /* 0 */ } /* save the message-id for logging */ p = "resent-message-id"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; if (sm_strcasecmp(h->h_field, p) == 0) { e->e_msgid = h->h_value; while (isascii(*e->e_msgid) && isspace(*e->e_msgid)) e->e_msgid++; macdefine(&e->e_macro, A_PERM, macid("{msg_id}"), e->e_msgid); } } if (tTd(32, 1)) sm_dprintf("----------------------------\n"); /* if we are just verifying (that is, sendmail -t -bv), drop out now */ if (OpMode == MD_VERIFY) return; /* store hop count */ if (hopcnt > e->e_hopcount) { e->e_hopcount = hopcnt; (void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount); macdefine(&e->e_macro, A_TEMP, 'c', buf); } /* message priority */ p = hvalue("precedence", e->e_header); if (p != NULL) e->e_class = priencode(p); if (e->e_class < 0) e->e_timeoutclass = TOC_NONURGENT; else if (e->e_class > 0) e->e_timeoutclass = TOC_URGENT; if (full) { e->e_msgpriority = e->e_msgsize - e->e_class * WkClassFact + e->e_nrcpts * WkRecipFact; } /* check for DSN to properly set e_timeoutclass */ p = hvalue("content-type", e->e_header); if (p != NULL) { bool oldsupr; char **pvp; char pvpbuf[MAXLINE]; extern unsigned char MimeTokenTab[256]; /* tokenize header */ oldsupr = SuprErrs; SuprErrs = true; pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, MimeTokenTab, false); SuprErrs = oldsupr; /* Check if multipart/report */ if (pvp != NULL && pvp[0] != NULL && pvp[1] != NULL && pvp[2] != NULL && sm_strcasecmp(*pvp++, "multipart") == 0 && strcmp(*pvp++, "/") == 0 && sm_strcasecmp(*pvp++, "report") == 0) { /* Look for report-type=delivery-status */ while (*pvp != NULL) { /* skip to semicolon separator */ while (*pvp != NULL && strcmp(*pvp, ";") != 0) pvp++; /* skip semicolon */ if (*pvp++ == NULL || *pvp == NULL) break; /* look for report-type */ if (sm_strcasecmp(*pvp++, "report-type") != 0) continue; /* skip equal */ if (*pvp == NULL || strcmp(*pvp, "=") != 0) continue; /* check value */ if (*++pvp != NULL && sm_strcasecmp(*pvp, "delivery-status") == 0) e->e_timeoutclass = TOC_DSN; /* found report-type, no need to continue */ break; } } } /* message timeout priority */ p = hvalue("priority", e->e_header); if (p != NULL) { /* (this should be in the configuration file) */ if (sm_strcasecmp(p, "urgent") == 0) e->e_timeoutclass = TOC_URGENT; else if (sm_strcasecmp(p, "normal") == 0) e->e_timeoutclass = TOC_NORMAL; else if (sm_strcasecmp(p, "non-urgent") == 0) e->e_timeoutclass = TOC_NONURGENT; else if (bitset(EF_RESPONSE, e->e_flags)) e->e_timeoutclass = TOC_DSN; } else if (bitset(EF_RESPONSE, e->e_flags)) e->e_timeoutclass = TOC_DSN; /* date message originated */ p = hvalue("posted-date", e->e_header); if (p == NULL) p = hvalue("date", e->e_header); if (p != NULL) macdefine(&e->e_macro, A_PERM, 'a', p); /* check to see if this is a MIME message */ if ((e->e_bodytype != NULL && sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) || hvalue("MIME-Version", e->e_header) != NULL) { e->e_flags |= EF_IS_MIME; if (HasEightBits) e->e_bodytype = "8BITMIME"; } else if ((p = hvalue("Content-Type", e->e_header)) != NULL) { /* this may be an RFC 1049 message */ p = strpbrk(p, ";/"); if (p == NULL || *p == ';') { /* yep, it is */ e->e_flags |= EF_DONT_MIME; } } /* ** From person in antiquated ARPANET mode ** required by UK Grey Book e-mail gateways (sigh) */ if (OpMode == MD_ARPAFTP) { register struct hdrinfo *hi; for (hi = HdrInfo; hi->hi_field != NULL; hi++) { if (bitset(H_FROM, hi->hi_flags) && (!bitset(H_RESENT, hi->hi_flags) || bitset(EF_RESENT, e->e_flags)) && (p = hvalue(hi->hi_field, e->e_header)) != NULL) break; } if (hi->hi_field != NULL) { if (tTd(32, 2)) sm_dprintf("eatheader: setsender(*%s == %s)\n", hi->hi_field, p); setsender(p, e, NULL, '\0', true); } } /* ** Log collection information. */ if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) { logsender(e, e->e_msgid); e->e_flags &= ~EF_LOGSENDER; } } /* ** LOGSENDER -- log sender information ** ** Parameters: ** e -- the envelope to log ** msgid -- the message id ** ** Returns: ** none */ void logsender(e, msgid) register ENVELOPE *e; char *msgid; { char *name; register char *sbp; register char *p; char hbuf[MAXNAME + 1]; char sbuf[MAXLINE + 1]; char mbuf[MAXNAME + 1]; /* don't allow newlines in the message-id */ /* XXX do we still need this? sm_syslog() replaces control chars */ if (msgid != NULL) { size_t l; l = strlen(msgid); if (l > sizeof(mbuf) - 1) l = sizeof(mbuf) - 1; memmove(mbuf, msgid, l); mbuf[l] = '\0'; p = mbuf; while ((p = strchr(p, '\n')) != NULL) *p++ = ' '; } if (bitset(EF_RESPONSE, e->e_flags)) name = "[RESPONSE]"; else if ((name = macvalue('_', e)) != NULL) /* EMPTY */ ; else if (RealHostName == NULL) name = "localhost"; else if (RealHostName[0] == '[') name = RealHostName; else { name = hbuf; (void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName); if (RealHostAddr.sa.sa_family != 0) { p = &hbuf[strlen(hbuf)]; (void) sm_snprintf(p, SPACELEFT(hbuf, p), " (%.100s)", anynet_ntoa(&RealHostAddr)); } } /* some versions of syslog only take 5 printf args */ #if (SYSLOG_BUFSIZE) >= 256 sbp = sbuf; (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), "from=%.200s, size=%ld, class=%d, nrcpts=%d", e->e_from.q_paddr == NULL ? "" : e->e_from.q_paddr, e->e_msgsize, e->e_class, e->e_nrcpts); sbp += strlen(sbp); if (msgid != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf); sbp += strlen(sbp); } if (e->e_bodytype != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p); sbp += strlen(sbp); } p = macvalue(macid("{daemon_name}"), e); if (p != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), ", daemon=%.20s", p); sbp += strlen(sbp); } sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%s", sbuf, name); #else /* (SYSLOG_BUFSIZE) >= 256 */ sm_syslog(LOG_INFO, e->e_id, "from=%s", e->e_from.q_paddr == NULL ? "" : shortenstring(e->e_from.q_paddr, 83)); sm_syslog(LOG_INFO, e->e_id, "size=%ld, class=%ld, nrcpts=%d", e->e_msgsize, e->e_class, e->e_nrcpts); if (msgid != NULL) sm_syslog(LOG_INFO, e->e_id, "msgid=%s", shortenstring(mbuf, 83)); sbp = sbuf; *sbp = '\0'; if (e->e_bodytype != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) { (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); sbp += strlen(sbp); } sm_syslog(LOG_INFO, e->e_id, "%.400srelay=%s", sbuf, name); #endif /* (SYSLOG_BUFSIZE) >= 256 */ } /* ** PRIENCODE -- encode external priority names into internal values. ** ** Parameters: ** p -- priority in ascii. ** ** Returns: ** priority as a numeric level. ** ** Side Effects: ** none. */ static int priencode(p) char *p; { register int i; for (i = 0; i < NumPriorities; i++) { if (sm_strcasecmp(p, Priorities[i].pri_name) == 0) return Priorities[i].pri_val; } /* unknown priority */ return 0; } /* ** CRACKADDR -- parse an address and turn it into a macro ** ** This doesn't actually parse the address -- it just extracts ** it and replaces it with "$g". The parse is totally ad hoc ** and isn't even guaranteed to leave something syntactically ** identical to what it started with. However, it does leave ** something semantically identical if possible, else at least ** syntactically correct. ** ** For example, it changes "Real Name (Comment)" ** to "Real Name <$g> (Comment)". ** ** This algorithm has been cleaned up to handle a wider range ** of cases -- notably quoted and backslash escaped strings. ** This modification makes it substantially better at preserving ** the original syntax. ** ** Parameters: ** addr -- the address to be cracked. ** e -- the current envelope. ** ** Returns: ** a pointer to the new version. ** ** Side Effects: ** none. ** ** Warning: ** The return value is saved in local storage and should ** be copied if it is to be reused. */ #define SM_HAVE_ROOM ((bp < buflim) && (buflim <= bufend)) /* ** Append a character to bp if we have room. ** If not, punt and return $g. */ #define SM_APPEND_CHAR(c) \ do \ { \ if (SM_HAVE_ROOM) \ *bp++ = (c); \ else \ goto returng; \ } while (0) #if MAXNAME < 10 ERROR MAXNAME must be at least 10 #endif /* MAXNAME < 10 */ char * crackaddr(addr, e) register char *addr; ENVELOPE *e; { register char *p; register char c; int cmtlev; /* comment level in input string */ int realcmtlev; /* comment level in output string */ int anglelev; /* angle level in input string */ int copylev; /* 0 == in address, >0 copying */ int bracklev; /* bracket level for IPv6 addr check */ bool addangle; /* put closing angle in output */ bool qmode; /* quoting in original string? */ bool realqmode; /* quoting in output string? */ bool putgmac = false; /* already wrote $g */ bool quoteit = false; /* need to quote next character */ bool gotangle = false; /* found first '<' */ bool gotcolon = false; /* found a ':' */ register char *bp; char *buflim; char *bufhead; char *addrhead; char *bufend; static char buf[MAXNAME + 1]; if (tTd(33, 1)) sm_dprintf("crackaddr(%s)\n", addr); buflim = bufend = &buf[sizeof(buf) - 1]; bp = bufhead = buf; /* skip over leading spaces but preserve them */ while (*addr != '\0' && isascii(*addr) && isspace(*addr)) { SM_APPEND_CHAR(*addr); addr++; } bufhead = bp; /* ** Start by assuming we have no angle brackets. This will be ** adjusted later if we find them. */ p = addrhead = addr; copylev = anglelev = cmtlev = realcmtlev = 0; bracklev = 0; qmode = realqmode = addangle = false; while ((c = *p++) != '\0') { /* ** Try to keep legal syntax using spare buffer space ** (maintained by buflim). */ if (copylev > 0) SM_APPEND_CHAR(c); /* check for backslash escapes */ if (c == '\\') { /* arrange to quote the address */ if (cmtlev <= 0 && !qmode) quoteit = true; if ((c = *p++) == '\0') { /* too far */ p--; goto putg; } if (copylev > 0) SM_APPEND_CHAR(c); goto putg; } /* check for quoted strings */ if (c == '"' && cmtlev <= 0) { qmode = !qmode; if (copylev > 0 && SM_HAVE_ROOM) { if (realqmode) buflim--; else buflim++; realqmode = !realqmode; } continue; } if (qmode) goto putg; /* check for comments */ if (c == '(') { cmtlev++; /* allow space for closing paren */ if (SM_HAVE_ROOM) { buflim--; realcmtlev++; if (copylev++ <= 0) { if (bp != bufhead) SM_APPEND_CHAR(' '); SM_APPEND_CHAR(c); } } } if (cmtlev > 0) { if (c == ')') { cmtlev--; copylev--; if (SM_HAVE_ROOM) { realcmtlev--; buflim++; } } continue; } else if (c == ')') { /* syntax error: unmatched ) */ if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead) bp--; } /* count nesting on [ ... ] (for IPv6 domain literals) */ if (c == '[') bracklev++; else if (c == ']') bracklev--; /* check for group: list; syntax */ if (c == ':' && anglelev <= 0 && bracklev <= 0 && !gotcolon && !ColonOkInAddr) { register char *q; /* ** Check for DECnet phase IV ``::'' (host::user) ** or DECnet phase V ``:.'' syntaxes. The latter ** covers ``user@DEC:.tay.myhost'' and ** ``DEC:.tay.myhost::user'' syntaxes (bletch). */ if (*p == ':' || *p == '.') { if (cmtlev <= 0 && !qmode) quoteit = true; if (copylev > 0) { SM_APPEND_CHAR(c); SM_APPEND_CHAR(*p); } p++; goto putg; } gotcolon = true; bp = bufhead; if (quoteit) { SM_APPEND_CHAR('"'); /* back up over the ':' and any spaces */ --p; while (p > addr && isascii(*--p) && isspace(*p)) continue; p++; } for (q = addrhead; q < p; ) { c = *q++; if (quoteit && c == '"') SM_APPEND_CHAR('\\'); SM_APPEND_CHAR(c); } if (quoteit) { if (bp == &bufhead[1]) bp--; else SM_APPEND_CHAR('"'); while ((c = *p++) != ':') SM_APPEND_CHAR(c); SM_APPEND_CHAR(c); } /* any trailing white space is part of group: */ while (isascii(*p) && isspace(*p)) { SM_APPEND_CHAR(*p); p++; } copylev = 0; putgmac = quoteit = false; bufhead = bp; addrhead = p; continue; } if (c == ';' && copylev <= 0 && !ColonOkInAddr) SM_APPEND_CHAR(c); /* check for characters that may have to be quoted */ if (strchr(MustQuoteChars, c) != NULL) { /* ** If these occur as the phrase part of a <> ** construct, but are not inside of () or already ** quoted, they will have to be quoted. Note that ** now (but don't actually do the quoting). */ if (cmtlev <= 0 && !qmode) quoteit = true; } /* check for angle brackets */ if (c == '<') { register char *q; /* assume first of two angles is bogus */ if (gotangle) quoteit = true; gotangle = true; /* oops -- have to change our mind */ anglelev = 1; if (SM_HAVE_ROOM) { if (!addangle) buflim--; addangle = true; } bp = bufhead; if (quoteit) { SM_APPEND_CHAR('"'); /* back up over the '<' and any spaces */ --p; while (p > addr && isascii(*--p) && isspace(*p)) continue; p++; } for (q = addrhead; q < p; ) { c = *q++; if (quoteit && c == '"') { SM_APPEND_CHAR('\\'); SM_APPEND_CHAR(c); } else SM_APPEND_CHAR(c); } if (quoteit) { if (bp == &buf[1]) bp--; else SM_APPEND_CHAR('"'); while ((c = *p++) != '<') SM_APPEND_CHAR(c); SM_APPEND_CHAR(c); } copylev = 0; putgmac = quoteit = false; continue; } if (c == '>') { if (anglelev > 0) { anglelev--; if (SM_HAVE_ROOM) { if (addangle) buflim++; addangle = false; } } else if (SM_HAVE_ROOM) { /* syntax error: unmatched > */ if (copylev > 0 && bp > bufhead) bp--; quoteit = true; continue; } if (copylev++ <= 0) SM_APPEND_CHAR(c); continue; } /* must be a real address character */ putg: if (copylev <= 0 && !putgmac) { if (bp > buf && bp[-1] == ')') SM_APPEND_CHAR(' '); SM_APPEND_CHAR(MACROEXPAND); SM_APPEND_CHAR('g'); putgmac = true; } } /* repair any syntactic damage */ if (realqmode && bp < bufend) *bp++ = '"'; while (realcmtlev-- > 0 && bp < bufend) *bp++ = ')'; if (addangle && bp < bufend) *bp++ = '>'; *bp = '\0'; if (bp < bufend) goto success; returng: /* String too long, punt */ buf[0] = '<'; buf[1] = MACROEXPAND; buf[2]= 'g'; buf[3] = '>'; buf[4]= '\0'; sm_syslog(LOG_ALERT, e->e_id, "Dropped invalid comments from header address"); success: if (tTd(33, 1)) { sm_dprintf("crackaddr=>`"); xputs(sm_debug_file(), buf); sm_dprintf("'\n"); } return buf; } /* ** PUTHEADER -- put the header part of a message from the in-core copy ** ** Parameters: ** mci -- the connection information. ** hdr -- the header to put. ** e -- envelope to use. ** flags -- MIME conversion flags. ** ** Returns: ** true iff header part was written successfully ** ** Side Effects: ** none. */ bool putheader(mci, hdr, e, flags) register MCI *mci; HDR *hdr; register ENVELOPE *e; int flags; { register HDR *h; char buf[SM_MAX(MAXLINE,BUFSIZ)]; char obuf[MAXLINE]; if (tTd(34, 1)) sm_dprintf("--- putheader, mailer = %s ---\n", mci->mci_mailer->m_name); /* ** If we're in MIME mode, we're not really in the header of the ** message, just the header of one of the parts of the body of ** the message. Therefore MCIF_INHEADER should not be turned on. */ if (!bitset(MCIF_INMIME, mci->mci_flags)) mci->mci_flags |= MCIF_INHEADER; for (h = hdr; h != NULL; h = h->h_link) { register char *p = h->h_value; char *q; if (tTd(34, 11)) { sm_dprintf(" %s:", h->h_field); xputs(sm_debug_file(), p); } /* Skip empty headers */ if (h->h_value == NULL) continue; /* heuristic shortening of MIME fields to avoid MUA overflows */ if (MaxMimeFieldLength > 0 && wordinclass(h->h_field, macid("{checkMIMEFieldHeaders}"))) { size_t len; len = fix_mime_header(h, e); if (len > 0) { sm_syslog(LOG_ALERT, e->e_id, "Truncated MIME %s header due to field size (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) sm_dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n", h->h_field, (unsigned long) len); } } if (MaxMimeHeaderLength > 0 && wordinclass(h->h_field, macid("{checkMIMETextHeaders}"))) { size_t len; len = strlen(h->h_value); if (len > (size_t) MaxMimeHeaderLength) { h->h_value[MaxMimeHeaderLength - 1] = '\0'; sm_syslog(LOG_ALERT, e->e_id, "Truncated long MIME %s header (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", h->h_field, (unsigned long) len); } } if (MaxMimeHeaderLength > 0 && wordinclass(h->h_field, macid("{checkMIMEHeaders}"))) { size_t len; len = strlen(h->h_value); if (shorten_rfc822_string(h->h_value, MaxMimeHeaderLength)) { if (len < MaxMimeHeaderLength) { /* we only rebalanced a bogus header */ sm_syslog(LOG_ALERT, e->e_id, "Fixed MIME %s header (possible attack)", h->h_field); if (tTd(34, 11)) sm_dprintf(" fixed MIME %s header (possible attack)\n", h->h_field); } else { /* we actually shortened header */ sm_syslog(LOG_ALERT, e->e_id, "Truncated long MIME %s header (length = %ld) (possible attack)", h->h_field, (unsigned long) len); if (tTd(34, 11)) sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", h->h_field, (unsigned long) len); } } } /* ** Suppress Content-Transfer-Encoding: if we are MIMEing ** and we are potentially converting from 8 bit to 7 bit ** MIME. If converting, add a new CTE header in ** mime8to7(). */ if (bitset(H_CTE, h->h_flags) && bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) && !bitset(M87F_NO8TO7, flags)) { if (tTd(34, 11)) sm_dprintf(" (skipped (content-transfer-encoding))\n"); continue; } if (bitset(MCIF_INMIME, mci->mci_flags)) { if (tTd(34, 11)) sm_dprintf("\n"); if (!put_vanilla_header(h, p, mci)) goto writeerr; continue; } if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) && (h->h_macro == '\0' || (q = macvalue(bitidx(h->h_macro), e)) == NULL || *q == '\0')) { if (tTd(34, 11)) sm_dprintf(" (skipped)\n"); continue; } /* handle Resent-... headers specially */ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) { if (tTd(34, 11)) sm_dprintf(" (skipped (resent))\n"); continue; } /* suppress return receipts if requested */ if (bitset(H_RECEIPTTO, h->h_flags) && (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) { if (tTd(34, 11)) sm_dprintf(" (skipped (receipt))\n"); continue; } /* macro expand value if generated internally */ if (bitset(H_DEFAULT, h->h_flags) || bitset(H_BINDLATE, h->h_flags)) { expand(p, buf, sizeof(buf), e); p = buf; if (*p == '\0') { if (tTd(34, 11)) sm_dprintf(" (skipped -- null value)\n"); continue; } } if (bitset(H_BCC, h->h_flags)) { /* Bcc: field -- either truncate or delete */ if (bitset(EF_DELETE_BCC, e->e_flags)) { if (tTd(34, 11)) sm_dprintf(" (skipped -- bcc)\n"); } else { /* no other recipient headers: truncate value */ (void) sm_strlcpyn(obuf, sizeof(obuf), 2, h->h_field, ":"); if (!putline(obuf, mci)) goto writeerr; } continue; } if (tTd(34, 11)) sm_dprintf("\n"); if (bitset(H_FROM|H_RCPT, h->h_flags)) { /* address field */ bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); if (bitset(H_FROM, h->h_flags)) oldstyle = false; - commaize(h, p, oldstyle, mci, e); + commaize(h, p, oldstyle, mci, e, + PXLF_HEADER | PXLF_STRIPMQUOTE); } else { if (!put_vanilla_header(h, p, mci)) goto writeerr; } } /* ** If we are converting this to a MIME message, add the ** MIME headers (but not in MIME mode!). */ #if MIME8TO7 if (bitset(MM_MIME8BIT, MimeMode) && bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && !bitnset(M_8BITS, mci->mci_mailer->m_flags) && !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) && hvalue("MIME-Version", e->e_header) == NULL) { if (!putline("MIME-Version: 1.0", mci)) goto writeerr; if (hvalue("Content-Type", e->e_header) == NULL) { (void) sm_snprintf(obuf, sizeof(obuf), "Content-Type: text/plain; charset=%s", defcharset(e)); if (!putline(obuf, mci)) goto writeerr; } if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL && !putline("Content-Transfer-Encoding: 8bit", mci)) goto writeerr; } #endif /* MIME8TO7 */ return true; writeerr: return false; } /* ** PUT_VANILLA_HEADER -- output a fairly ordinary header ** ** Parameters: ** h -- the structure describing this header ** v -- the value of this header ** mci -- the connection info for output ** ** Returns: ** true iff header was written successfully */ static bool put_vanilla_header(h, v, mci) HDR *h; char *v; MCI *mci; { register char *nlp; register char *obp; int putflags; char obuf[MAXLINE + 256]; /* additional length for h_field */ putflags = PXLF_HEADER | PXLF_STRIPMQUOTE; if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; (void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field); obp = obuf + strlen(obuf); while ((nlp = strchr(v, '\n')) != NULL) { int l; l = nlp - v; /* ** XXX This is broken for SPACELEFT()==0 ** However, SPACELEFT() is always > 0 unless MAXLINE==1. */ if (SPACELEFT(obuf, obp) - 1 < (size_t) l) l = SPACELEFT(obuf, obp) - 1; (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); if (!putxline(obuf, strlen(obuf), mci, putflags)) goto writeerr; v += l + 1; obp = obuf; if (*v != ' ' && *v != '\t') *obp++ = ' '; } /* XXX This is broken for SPACELEFT()==0 */ (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", (int) (SPACELEFT(obuf, obp) - 1), v); return putxline(obuf, strlen(obuf), mci, putflags); writeerr: return false; } /* ** COMMAIZE -- output a header field, making a comma-translated list. ** ** Parameters: ** h -- the header field to output. ** p -- the value to put in it. ** oldstyle -- true if this is an old style header. ** mci -- the connection information. ** e -- the envelope containing the message. +** putflags -- flags for putxline() ** ** Returns: ** true iff header field was written successfully ** ** Side Effects: ** outputs "p" to "mci". */ bool -commaize(h, p, oldstyle, mci, e) +commaize(h, p, oldstyle, mci, e, putflags) register HDR *h; register char *p; bool oldstyle; register MCI *mci; register ENVELOPE *e; + int putflags; { register char *obp; int opos, omax, spaces; bool firstone = true; - int putflags = PXLF_HEADER | PXLF_STRIPMQUOTE; char **res; char obuf[MAXLINE + 3]; /* ** Output the address list translated by the ** mailer and with commas. */ if (tTd(14, 2)) sm_dprintf("commaize(%s:%s)\n", h->h_field, p); if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; obp = obuf; (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field); /* opos = strlen(obp); instead of the next 3 lines? */ opos = strlen(h->h_field) + 1; if (opos > 201) opos = 201; obp += opos; spaces = 0; while (*p != '\0' && isascii(*p) && isspace(*p)) { ++spaces; ++p; } if (spaces > 0) { SM_ASSERT(sizeof(obuf) > opos * 2); /* ** Restrict number of spaces to half the length of buffer ** so the header field body can be put in here too. ** Note: this is a hack... */ if (spaces > sizeof(obuf) / 2) spaces = sizeof(obuf) / 2; (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces, ""); opos += spaces; obp += spaces; SM_ASSERT(obp < &obuf[MAXLINE]); } omax = mci->mci_mailer->m_linelimit - 2; if (omax < 0 || omax > 78) omax = 78; /* ** Run through the list of values. */ while (*p != '\0') { register char *name; register int c; char savechar; int flags; auto int status; /* ** Find the end of the name. New style names ** end with a comma, old style names end with ** a space character. However, spaces do not ** necessarily delimit an old-style name -- at ** signs mean keep going. */ /* find end of name */ while ((isascii(*p) && isspace(*p)) || *p == ',') p++; name = p; res = NULL; for (;;) { auto char *oldp; char pvpbuf[PSBUFSIZE]; res = prescan(p, oldstyle ? ' ' : ',', pvpbuf, sizeof(pvpbuf), &oldp, ExtTokenTab, false); p = oldp; #if _FFR_IGNORE_BOGUS_ADDR /* ignore addresses that can't be parsed */ if (res == NULL) { name = p; continue; } #endif /* _FFR_IGNORE_BOGUS_ADDR */ /* look to see if we have an at sign */ while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p != '@') { p = oldp; break; } ++p; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; } /* at the end of one complete name */ /* strip off trailing white space */ while (p >= name && ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) p--; if (++p == name) continue; /* ** if prescan() failed go a bit backwards; this is a hack, ** there should be some better error recovery. */ if (res == NULL && p > name && !((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) --p; savechar = *p; *p = '\0'; /* translate the name to be relative */ flags = RF_HEADERADDR|RF_ADDDOMAIN; if (bitset(H_FROM, h->h_flags)) flags |= RF_SENDERADDR; #if USERDB else if (e->e_from.q_mailer != NULL && bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) { char *q; q = udbsender(name, e->e_rpool); if (q != NULL) name = q; } #endif /* USERDB */ status = EX_OK; name = remotename(name, mci->mci_mailer, flags, &status, e); if (*name == '\0') { *p = savechar; continue; } name = denlstring(name, false, true); /* output the name with nice formatting */ opos += strlen(name); if (!firstone) opos += 2; if (opos > omax && !firstone) { (void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp)); if (!putxline(obuf, strlen(obuf), mci, putflags)) goto writeerr; obp = obuf; (void) sm_strlcpy(obp, " ", sizeof(obuf)); opos = strlen(obp); obp += opos; opos += strlen(name); } else if (!firstone) { (void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp)); obp += 2; } while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) *obp++ = c; firstone = false; *p = savechar; } if (obp < &obuf[sizeof(obuf)]) *obp = '\0'; else obuf[sizeof(obuf) - 1] = '\0'; return putxline(obuf, strlen(obuf), mci, putflags); writeerr: return false; } /* ** COPYHEADER -- copy header list ** ** This routine is the equivalent of newstr for header lists ** ** Parameters: ** header -- list of header structures to copy. ** rpool -- resource pool, or NULL ** ** Returns: ** a copy of 'header'. ** ** Side Effects: ** none. */ HDR * copyheader(header, rpool) register HDR *header; SM_RPOOL_T *rpool; { register HDR *newhdr; HDR *ret; register HDR **tail = &ret; while (header != NULL) { newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr)); STRUCTCOPY(*header, *newhdr); *tail = newhdr; tail = &newhdr->h_link; header = header->h_link; } *tail = NULL; return ret; } /* ** FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header ** ** Run through all of the parameters of a MIME header and ** possibly truncate and rebalance the parameter according ** to MaxMimeFieldLength. ** ** Parameters: ** h -- the header to truncate/rebalance ** e -- the current envelope ** ** Returns: ** length of last offending field, 0 if all ok. ** ** Side Effects: ** string modified in place */ static size_t fix_mime_header(h, e) HDR *h; ENVELOPE *e; { char *begin = h->h_value; char *end; size_t len = 0; size_t retlen = 0; if (begin == NULL || *begin == '\0') return 0; /* Split on each ';' */ /* find_character() never returns NULL */ while ((end = find_character(begin, ';')) != NULL) { char save = *end; char *bp; *end = '\0'; len = strlen(begin); /* Shorten individual parameter */ if (shorten_rfc822_string(begin, MaxMimeFieldLength)) { if (len < MaxMimeFieldLength) { /* we only rebalanced a bogus field */ sm_syslog(LOG_ALERT, e->e_id, "Fixed MIME %s header field (possible attack)", h->h_field); if (tTd(34, 11)) sm_dprintf(" fixed MIME %s header field (possible attack)\n", h->h_field); } else { /* we actually shortened the header */ retlen = len; } } /* Collapse the possibly shortened string with rest */ bp = begin + strlen(begin); if (bp != end) { char *ep = end; *end = save; end = bp; /* copy character by character due to overlap */ while (*ep != '\0') *bp++ = *ep++; *bp = '\0'; } else *end = save; if (*end == '\0') break; /* Move past ';' */ begin = end + 1; } return retlen; } Index: head/contrib/sendmail/src/sendmail.8 =================================================================== --- head/contrib/sendmail/src/sendmail.8 (revision 173342) +++ head/contrib/sendmail/src/sendmail.8 (revision 173343) @@ -1,749 +1,749 @@ .\" Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. .\" All rights reserved. .\" Copyright (c) 1983, 1997 Eric P. Allman. All rights reserved. .\" Copyright (c) 1988, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" By using this file, you agree to the terms and conditions set .\" forth in the LICENSE file which can be found at the top level of .\" the sendmail distribution. .\" .\" -.\" $Id: sendmail.8,v 8.57 2003/12/01 17:02:41 ca Exp $ +.\" $Id: sendmail.8,v 8.58 2007/08/02 05:42:33 ca Exp $ .\" -.TH SENDMAIL 8 "$Date: 2003/12/01 17:02:41 $" +.TH SENDMAIL 8 "$Date: 2007/08/02 05:42:33 $" .SH NAME sendmail \- an electronic mail transport agent .SH SYNOPSIS .B sendmail .RI [ flags "] [" "address ..." ] .br .B newaliases .br .B mailq .RB [ \-v ] .br .B hoststat .br .B purgestat .br .B smtpd .SH DESCRIPTION .B Sendmail sends a message to one or more .I recipients, routing the message over whatever networks are necessary. .B Sendmail does internetwork forwarding as necessary to deliver the message to the correct place. .PP .B Sendmail is not intended as a user interface routine; other programs provide user-friendly front ends; .B sendmail is used only to deliver pre-formatted messages. .PP With no flags, .B sendmail reads its standard input up to an end-of-file or a line consisting only of a single dot and sends a copy of the message found there to all of the addresses listed. It determines the network(s) to use based on the syntax and contents of the addresses. .PP Local addresses are looked up in a file and aliased appropriately. Aliasing can be prevented by preceding the address with a backslash. Beginning with 8.10, the sender is included in any alias expansions, e.g., if `john' sends to `group', and `group' includes `john' in the expansion, then the letter will also be delivered to `john'. .SS Parameters .TP .B \-Ac Use submit.cf even if the operation mode does not indicate an initial mail submission. .TP .B \-Am Use sendmail.cf even if the operation mode indicates an initial mail submission. .TP .BI \-B type Set the body type to .IR type . Current legal values are 7BIT or 8BITMIME. .TP .B \-ba Go into ARPANET mode. All input lines must end with a CR-LF, and all messages will be generated with a CR-LF at the end. Also, the ``From:'' and ``Sender:'' fields are examined for the name of the sender. .TP .B \-bd Run as a daemon. .B Sendmail will fork and run in background listening on socket 25 for incoming SMTP connections. This is normally run from /etc/rc. .TP .B \-bD Same as .B \-bd except runs in foreground. .TP .B \-bh Print the persistent host status database. .TP .B \-bH Purge expired entries from the persistent host status database. .TP .B \-bi Initialize the alias database. .TP .B \-bm Deliver mail in the usual way (default). .TP .B \-bp Print a listing of the queue(s). .TP .B \-bP Print number of entries in the queue(s); only available with shared memory support. .TP .B \-bs Use the SMTP protocol as described in RFC821 on standard input and output. This flag implies all the operations of the .B \-ba flag that are compatible with SMTP. .TP .B \-bt Run in address test mode. This mode reads addresses and shows the steps in parsing; it is used for debugging configuration tables. .TP .B \-bv Verify names only \- do not try to collect or deliver a message. Verify mode is normally used for validating users or mailing lists. .TP .BI \-C file Use alternate configuration file. .B Sendmail gives up any enhanced (set-user-ID or set-group-ID) privileges if an alternate configuration file is specified. .TP .BI "\-D " logfile Send debugging output to the indicated log file instead of stdout. .TP .BI \-d category . level... Set the debugging flag for .I category to .IR level . .I Category is either an integer or a name specifying the topic, and .I level an integer specifying the level of debugging output desired. Higher levels generally mean more output. More than one flag can be specified by separating them with commas. A list of numeric debugging categories can be found in the TRACEFLAGS file in the sendmail source distribution. .br The option .B \-d0.1 prints the version of .B sendmail and the options it was compiled with. .br Most other categories are only useful with, and documented in, .BR sendmail 's source code. .ne 1i .TP .BI \-F fullname Set the full name of the sender. .TP .BI \-f name Sets the name of the ``from'' person (i.e., the envelope sender of the mail). This address may also be used in the From: header if that header is missing during initial submission. The envelope sender address is used as the recipient for delivery status notifications and may also appear in a Return-Path: header. .B \-f should only be used by ``trusted'' users (normally .IR root ", " daemon , and .IR network ) or if the person you are trying to become is the same as the person you are. Otherwise, an X-Authentication-Warning header will be added to the message. .TP .BI \-G Relay (gateway) submission of a message, e.g., when .BR rmail calls .B sendmail . .TP .BI \-h N Set the hop count to .IR N . The hop count is incremented every time the mail is processed. When it reaches a limit, the mail is returned with an error message, the victim of an aliasing loop. If not specified, ``Received:'' lines in the message are counted. .TP .B \-i Ignore dots alone on lines by themselves in incoming messages. This should be set if you are reading data from a file. .TP .BI "\-L " tag Set the identifier used in syslog messages to the supplied .IR tag . .TP .BI "\-N " dsn Set delivery status notification conditions to .IR dsn , which can be `never' for no notifications or a comma separated list of the values `failure' to be notified if delivery failed, `delay' to be notified if delivery is delayed, and `success' to be notified when the message is successfully delivered. .TP .B \-n Don't do aliasing. .TP \fB\-O\fP \fIoption\fR=\fIvalue\fR Set option .I option to the specified .IR value . This form uses long names. See below for more details. .TP .BI \-o "x value" Set option .I x to the specified .IR value . This form uses single character names only. The short names are not described in this manual page; see the .I "Sendmail Installation and Operation Guide" for details. .TP .BI \-p protocol Set the name of the protocol used to receive the message. This can be a simple protocol name such as ``UUCP'' or a protocol and hostname, such as ``UUCP:ucbvax''. .TP \fB\-q\fR[\fItime\fR] Process saved messages in the queue at given intervals. If .I time is omitted, process the queue once. .I Time is given as a tagged number, with `s' being seconds, `m' being minutes (default), `h' being hours, `d' being days, and `w' being weeks. For example, `\-q1h30m' or `\-q90m' would both set the timeout to one hour thirty minutes. By default, .B sendmail will run in the background. This option can be used safely with .BR \-bd . .TP \fB\-qp\fR[\fItime\fR] Similar to \fB\-q\fItime\fR, except that instead of periodically forking a child to process the queue, sendmail forks a single persistent child for each queue that alternates between processing the queue and sleeping. The sleep time is given as the argument; it defaults to 1 second. The process will always sleep at least 5 seconds if the queue was empty in the previous queue run. .TP \fB\-q\fRf Process saved messages in the queue once and do not fork(), but run in the foreground. .TP -\fB\-q\fRG name +\fB\-q\fRG\fIname\fR Process jobs in queue group called .I name only. .TP \fB\-q\fR[\fI!\fR]I\fIsubstr\fR Limit processed jobs to those containing .I substr as a substring of the queue id or not when .I ! is specified. .TP \fB\-q\fR[\fI!\fR]Q\fIsubstr\fR Limit processed jobs to quarantined jobs containing .I substr as a substring of the quarantine reason or not when .I ! is specified. .TP \fB\-q\fR[\fI!\fR]R\fIsubstr\fR Limit processed jobs to those containing .I substr as a substring of one of the recipients or not when .I ! is specified. .TP \fB\-q\fR[\fI!\fR]S\fIsubstr\fR Limit processed jobs to those containing .I substr as a substring of the sender or not when .I ! is specified. .TP \fB\-Q\fR[reason] Quarantine a normal queue items with the given reason or unquarantine quarantined queue items if no reason is given. This should only be used with some sort of item matching using as described above. .TP .BI "\-R " return Set the amount of the message to be returned if the message bounces. The .I return parameter can be `full' to return the entire message or `hdrs' to return only the headers. In the latter case also local bounces return only the headers. .TP .BI \-r name An alternate and obsolete form of the .B \-f flag. .TP .B \-t Read message for recipients. To:, Cc:, and Bcc: lines will be scanned for recipient addresses. The Bcc: line will be deleted before transmission. .TP .BI "\-V " envid Set the original envelope id. This is propagated across SMTP to servers that support DSNs and is returned in DSN-compliant error messages. .TP .B \-v Go into verbose mode. Alias expansions will be announced, etc. .TP .BI "\-X " logfile Log all traffic in and out of mailers in the indicated log file. This should only be used as a last resort for debugging mailer bugs. It will log a lot of data very quickly. .TP .B \-\- Stop processing command flags and use the rest of the arguments as addresses. .SS Options There are also a number of processing options that may be set. Normally these will only be used by a system administrator. Options may be set either on the command line using the .B \-o flag (for short names), the .B \-O flag (for long names), or in the configuration file. This is a partial list limited to those options that are likely to be useful on the command line and only shows the long names; for a complete list (and details), consult the .IR "Sendmail Installation and Operation Guide" . The options are: .TP .RI AliasFile= file Use alternate alias file. .TP HoldExpensive On mailers that are considered ``expensive'' to connect to, don't initiate immediate connection. This requires queueing. .TP .RI CheckpointInterval= N Checkpoint the queue file after every .I N successful deliveries (default 10). This avoids excessive duplicate deliveries when sending to long mailing lists interrupted by system crashes. .ne 1i .TP .RI DeliveryMode= x Set the delivery mode to .IR x . Delivery modes are `i' for interactive (synchronous) delivery, `b' for background (asynchronous) delivery, `q' for queue only \- i.e., actual delivery is done the next time the queue is run, and `d' for deferred \- the same as `q' except that database lookups for maps which have set the \-D option (default for the host map) are avoided. .TP .RI ErrorMode= x Set error processing to mode .IR x . Valid modes are `m' to mail back the error message, `w' to ``write'' back the error message (or mail it back if the sender is not logged in), `p' to print the errors on the terminal (default), `q' to throw away error messages (only exit status is returned), and `e' to do special processing for the BerkNet. If the text of the message is not mailed back by modes `m' or `w' and if the sender is local to this machine, a copy of the message is appended to the file .I dead.letter in the sender's home directory. .TP SaveFromLine Save UNIX-style From lines at the front of messages. .TP .RI MaxHopCount= N The maximum number of times a message is allowed to ``hop'' before we decide it is in a loop. .TP IgnoreDots Do not take dots on a line by themselves as a message terminator. .TP SendMimeErrors Send error messages in MIME format. If not set, the DSN (Delivery Status Notification) SMTP extension is disabled. .TP .RI ConnectionCacheTimeout= timeout Set connection cache timeout. .TP .RI ConnectionCacheSize= N Set connection cache size. .TP .RI LogLevel= n The log level. .TP .RI MeToo= False Don't send to ``me'' (the sender) if I am in an alias expansion. .TP CheckAliases Validate the right hand side of aliases during a newaliases(1) command. .TP OldStyleHeaders If set, this message may have old style headers. If not set, this message is guaranteed to have new style headers (i.e., commas instead of spaces between addresses). If set, an adaptive algorithm is used that will correctly determine the header format in most cases. .TP .RI QueueDirectory= queuedir Select the directory in which to queue messages. .TP .RI StatusFile= file Save statistics in the named file. .TP .RI Timeout.queuereturn= time Set the timeout on undelivered messages in the queue to the specified time. After delivery has failed (e.g., because of a host being down) for this amount of time, failed messages will be returned to the sender. The default is five days. .TP .RI UserDatabaseSpec= userdatabase If set, a user database is consulted to get forwarding information. You can consider this an adjunct to the aliasing mechanism, except that the database is intended to be distributed; aliases are local to a particular host. This may not be available if your sendmail does not have the USERDB option compiled in. .TP ForkEachJob Fork each job during queue runs. May be convenient on memory-poor machines. .TP SevenBitInput Strip incoming messages to seven bits. .TP .RI EightBitMode= mode Set the handling of eight bit input to seven bit destinations to .IR mode : m (mimefy) will convert to seven-bit MIME format, p (pass) will pass it as eight bits (but violates protocols), and s (strict) will bounce the message. .TP .RI MinQueueAge= timeout Sets how long a job must ferment in the queue between attempts to send it. .TP .RI DefaultCharSet= charset Sets the default character set used to label 8-bit data that is not otherwise labelled. .TP .RI DialDelay= sleeptime If opening a connection fails, sleep for .I sleeptime seconds and try again. Useful on dial-on-demand sites. .TP .RI NoRecipientAction= action Set the behaviour when there are no recipient headers (To:, Cc: or Bcc:) in the message to .IR action : none leaves the message unchanged, add-to adds a To: header with the envelope recipients, add-apparently-to adds an Apparently-To: header with the envelope recipients, add-bcc adds an empty Bcc: header, and add-to-undisclosed adds a header reading `To: undisclosed-recipients:;'. .TP .RI MaxDaemonChildren= N Sets the maximum number of children that an incoming SMTP daemon will allow to spawn at any time to .IR N . .TP .RI ConnectionRateThrottle= N Sets the maximum number of connections per second to the SMTP port to .IR N . .PP In aliases, the first character of a name may be a vertical bar to cause interpretation of the rest of the name as a command to pipe the mail to. It may be necessary to quote the name to keep .B sendmail from suppressing the blanks from between arguments. For example, a common alias is: .IP msgs: "|/usr/bin/msgs -s" .PP Aliases may also have the syntax .RI ``:include: filename '' to ask .B sendmail to read the named file for a list of recipients. For example, an alias such as: .IP poets: ":include:/usr/local/lib/poets.list" .PP would read .I /usr/local/lib/poets.list for the list of addresses making up the group. .PP .B Sendmail returns an exit status describing what it did. The codes are defined in .RI < sysexits.h >: .TP EX_OK Successful completion on all addresses. .TP EX_NOUSER User name not recognized. .TP EX_UNAVAILABLE Catchall meaning necessary resources were not available. .TP EX_SYNTAX Syntax error in address. .TP EX_SOFTWARE Internal software error, including bad arguments. .TP EX_OSERR Temporary operating system error, such as ``cannot fork''. .TP EX_NOHOST Host name not recognized. .TP EX_TEMPFAIL Message could not be sent immediately, but was queued. .PP If invoked as .BR newaliases , .B sendmail will rebuild the alias database. If invoked as .BR mailq , .B sendmail will print the contents of the mail queue. If invoked as .BR hoststat , .B sendmail will print the persistent host status database. If invoked as .BR purgestat , .B sendmail will purge expired entries from the persistent host status database. If invoked as .BR smtpd , .B sendmail will act as a daemon, as if the .B \-bd option were specified. .SH NOTES .B sendmail often gets blamed for many problems that are actually the result of other problems, such as overly permissive modes on directories. For this reason, .B sendmail checks the modes on system directories and files to determine if they can be trusted. Although these checks can be turned off and your system security reduced by setting the .BR DontBlameSendmail option, the permission problems should be fixed. For more information, see: .I http://www.sendmail.org/tips/DontBlameSendmail.html .SH FILES Except for the file .I /etc/mail/sendmail.cf itself the following pathnames are all specified in .IR /etc/mail/sendmail.cf . Thus, these values are only approximations. .PP .TP /etc/mail/aliases raw data for alias names .TP /etc/mail/aliases.db data base of alias names .TP /etc/mail/sendmail.cf configuration file .TP /etc/mail/helpfile help file .TP /etc/mail/statistics collected statistics .TP /var/spool/mqueue/* temp files .SH SEE ALSO mail(1), syslog(3), aliases(5), mailaddr(7), mail.local(8), rc(8), rmail(8) .PP DARPA Internet Request For Comments .IR RFC819 , .IR RFC821 , .IR RFC822 . .IR "Sendmail Installation and Operation Guide" , No. 8, SMM. .PP http://www.sendmail.org/ .SH HISTORY The .B sendmail command appeared in 4.2BSD. .\" $FreeBSD$