Index: address_check.c =================================================================== RCS file: /share/cvsroot/net/smtpd/address_check.c,v retrieving revision 1.1.1.1 retrieving revision 1.5 diff -u -p -r1.1.1.1 -r1.5 --- address_check.c 1997/10/13 01:40:34 1.1.1.1 +++ address_check.c 2001/03/26 08:41:43 1.5 @@ -1,5 +1,5 @@ /* - * $Id: address_check.c,v 1.1.1.1 1997/10/13 01:40:34 sjg Exp $ + * $Id: address_check.c,v 1.5 2001/03/26 08:41:43 sjg Exp $ * * Copyright (c) 1996, 1997 Obtuse Systems Corporation. All rights * reserved. @@ -68,6 +68,8 @@ #define CHECK_ADDRESS 0 #endif +#define DN_EXPAND_FAILURE 42 + #if CHECK_ADDRESS /* this encases everything */ #include #include @@ -147,6 +149,7 @@ int Failure = 0; int line = 0; extern char *victim; +extern int VerboseSyslog; #if NS_MATCH #define NSLIMIT 100 @@ -588,6 +591,40 @@ int masked_ip_match(char *tok, char *str return(madt == adt); } +/* do a Vixie style rbl lookup for dotquad addr in rbl domain + * rbl_domain. + */ +int vixie_rbl_lookup(char * rbl_domain, char * addr) { + char *t, *d, *a; + t = strdup(addr); + if (t==NULL) { + syslog(LOG_ERR, "Malloc failed!"); + Failure = 1; + return(0); + } + d = (char *) malloc(strlen(t)+strlen(rbl_domain)+1); + if (d==NULL) { + syslog(LOG_ERR, "Malloc failed!"); + free(t); + Failure = 1; + return(0); + } + *d='\0'; + while((a = strrchr(t, '.'))) { + strcat(d, a+1); + strcat(d, "."); + *a='\0'; + } + strcat(d, t); + strcat(d, rbl_domain); + if (gethostbyname(d) != NULL) { + free(t); free(d); + return(1); + } + free(t); free(d); + return(0); +} + static int ip_match(char *tok, char *string) { /* @@ -601,6 +638,12 @@ static int ip_match(char *tok, char *str else if ((string == NULL)) { return(0); } + else if (strncmp(tok, "RBL.", 4) == 0) { + /* do an rbl style lookup on the IP address in string usind + * rbl domain of whatever followed RBL in tok + */ + return(vixie_rbl_lookup(tok+3, string)); + } else { return(masked_ip_match(tok, string)); } @@ -628,7 +671,7 @@ u_char *endOfMsg; if((n = dn_expand(startOfMsg, endOfMsg, cp, buf, MAXDNAME)) < 0){ syslog (LOG_ERR, "dn_expand failed in skipName"); - Failure = 1; + Failure = DN_EXPAND_FAILURE; return(0); } return(n); @@ -858,7 +901,7 @@ void findNameServers(char * string, stru #ifdef DN_EXPAND_NAME_FAIL syslog (LOG_ERR, "dn_expand failed to expand %s record in findNameServers",(type == T_NS)?"NS":"MX" ); - Failure = 1; + Failure = DN_EXPAND_FAILURE; return; #else syslog (LOG_DEBUG, "dn_expand failed to expand %s record in findNameServers - ignored record", (type == T_NS)?"NS":"MX" ); @@ -1345,17 +1388,11 @@ static int source_list_match(char *list, return (0); } -/* Parse an address check file to see if a particular message from - * "fromaddr" to "toaddr" is allowed from a particular peer. Minimally, - * pi must have filled in pi->my_sa, pi->peer_sa, and pi->peer_ok_addr. - * When used, this gets from smtpd for each recipient of a message. - */ - -int smtpd_addr_check(const char * checkfname, - struct peer_info *pi, - const char * from, - const char * to, - char ** return_message) { +static int smtpd_addr_check1(const char * checkfname, + struct peer_info *pi, + const char * from, + const char * to, + char ** return_message) { FILE *fp; char buf[1024]; char *action, *sourcepat, *fromaddrpat, *toaddrpat, *junk; @@ -1365,21 +1402,6 @@ int smtpd_addr_check(const char * checkf static struct ns_match NS_rcpt = { NULL, 0, NULL, 0, NULL, 0 }; #endif - line=0; - - /* force user ident (if supplied) to lower case. */ - if (pi->peer_clean_ident != NULL) { - int i; - /* force to lower case */ - for (i=0; i < strlen(pi->peer_clean_ident); i++) { - pi->peer_clean_ident[i]=tolower(pi->peer_clean_ident[i]); - } - do_ident = 0; - } - else { - do_ident = 1; - } - if ((fp = fopen(checkfname, "r")) != 0) { while(fgets(buf, sizeof(buf) - 1, fp) != NULL) { buf[sizeof(buf) - 1]='\0'; @@ -1401,6 +1423,25 @@ int smtpd_addr_check(const char * checkf } buf[strlen(buf) - 1] = '\0'; + + /* + * handle an include file + */ + if (strncmp(buf, "include", 7) == 0) { + char *cp = &buf[7]; + int ret = 2; + + while (*cp == ' ' || *cp == '\t') + cp++; + if (*cp) + ret = smtpd_addr_check1(cp, pi, from, to, + return_message); + if (ret != 2) { + fclose(fp); + return ret; + } + continue; + } /* parse out fields in line */ if (!(action = strtok(buf, sep)) @@ -1478,40 +1519,37 @@ int smtpd_addr_check(const char * checkf return(CHECK_FAILURE); } /* we've matched this rule. is it an allow or deny? */ + /* first log it... */ + if (!VerboseSyslog) + accumlog(LOG_INFO, " %s=%s:%d", action, checkfname, line); if (strcmp(action, "allow") == 0) { /* allows succeed silently */ - syslog(LOG_DEBUG, "smtp connection from %s@%s(%s) MAIL FROM: %s RCPT TO: %s, allowed by line %d of %s", - (pi->peer_clean_ident != NULL)? - pi->peer_clean_ident:"UNKNOWN", - (pi->peer_clean_reverse_name != NULL)? - pi->peer_clean_reverse_name:"UNKNOWN", - pi->peer_ok_addr, from, to, line, checkfname); + if (VerboseSyslog) { + syslog(LOG_DEBUG, "smtp connection from %s@%s(%s) MAIL FROM: %s RCPT TO: %s, allowed by line %d of %s", + (pi->peer_clean_ident != NULL)? + pi->peer_clean_ident:"UNKNOWN", + (pi->peer_clean_reverse_name != NULL)? + pi->peer_clean_reverse_name:"UNKNOWN", + pi->peer_ok_addr, from, to, line, checkfname); + } fclose(fp); return (1); } - else if (strcmp(action, "noto") == 0) { + else if (strncmp(action, "noto", 4) == 0) { /* notos generate a log message */ - syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, noto by line %d of %s", - (pi->peer_clean_ident != NULL)? - pi->peer_clean_ident:"UNKNOWN", - (pi->peer_clean_reverse_name != NULL)? - pi->peer_clean_reverse_name:"UNKNOWN", - pi->peer_ok_addr, from, to, line, checkfname); - fclose(fp); - return (-1); - } - else if (strcmp(action, "deny") == 0) { - /* denys generate a log message */ - syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, denied by line %d of %s", - (pi->peer_clean_ident != NULL)? - pi->peer_clean_ident:"UNKNOWN", - (pi->peer_clean_reverse_name != NULL)? - pi->peer_clean_reverse_name:"UNKNOWN", - pi->peer_ok_addr, from, to, line, checkfname); - fclose(fp); - return (0); - } - + if (VerboseSyslog) { + syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, noto by line %d of %s", + (pi->peer_clean_ident != NULL)? + pi->peer_clean_ident:"UNKNOWN", + (pi->peer_clean_reverse_name != NULL)? + pi->peer_clean_reverse_name:"UNKNOWN", + pi->peer_ok_addr, from, to, line, checkfname); + } + /* + * moved the delay bit here to keep log + * munging simpler + */ + /* noto_delay and deny_delay. Ok, I admit they aren't very * nice, but I like to be able to do the same thing to * spammers that I do to phone telemarketers (Feign interest, @@ -1528,32 +1566,29 @@ int smtpd_addr_check(const char * checkf * * - Bob's Evil Twin. */ - - else if (strcmp(action, "deny_delay") == 0) { - /* denys generate a log message */ - syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, denied with delay by line %d of %s", - (pi->peer_clean_ident != NULL)? - pi->peer_clean_ident:"UNKNOWN", - (pi->peer_clean_reverse_name != NULL)? - pi->peer_clean_reverse_name:"UNKNOWN", - pi->peer_ok_addr, from, to, line, checkfname); - syslog(LOG_INFO, "Sleeping for a deny_delay of %d seconds", DENY_DELAY); - sleep(DENY_DELAY); + if (strcmp(action, "noto_delay") == 0) { + if (VerboseSyslog) + syslog(LOG_INFO, "Sleeping for a noto_delay of %d seconds", NOTO_DELAY); + sleep(NOTO_DELAY); + } fclose(fp); - return (0); + return (-1); } - else if (strcmp(action, "noto_delay") == 0) { - /* notos generate a log message */ - syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, noto with delay by line %d of %s", - (pi->peer_clean_ident != NULL)? - pi->peer_clean_ident:"UNKNOWN", - (pi->peer_clean_reverse_name != NULL)? - pi->peer_clean_reverse_name:"UNKNOWN", - pi->peer_ok_addr, from, to, line, checkfname); - syslog(LOG_INFO, "Sleeping for a noto_delay of %d seconds", NOTO_DELAY); - sleep(NOTO_DELAY); + else if (strncmp(action, "deny", 4) == 0) { + /* denys generate a log message */ + if (VerboseSyslog) { + syslog(LOG_INFO, "smtp connection from %s@%s(%s) attempted MAIL FROM: %s RCPT TO: %s, denied by line %d of %s", + (pi->peer_clean_ident != NULL)? + pi->peer_clean_ident:"UNKNOWN", + (pi->peer_clean_reverse_name != NULL)? + pi->peer_clean_reverse_name:"UNKNOWN", + pi->peer_ok_addr, from, to, line, checkfname); + } + if (strcmp(action, "deny_delay") == 0) { + sleep(DENY_DELAY); + } fclose(fp); - return (-1); + return (0); } else if (strcmp(action, "debug") == 0) { syslog (LOG_INFO, "DEBUG: Matched line %d, connection from %s@%s(%s) MAIL FROM: %s RCPT TO: %s, continuing.", @@ -1576,13 +1611,61 @@ int smtpd_addr_check(const char * checkf * call anything in here that will set Failure. It is left here in case * of future modifications */ - if (Failure) { + switch (Failure) { + case 0: /* all ok */ + break; + case DN_EXPAND_FAILURE: + syslog(LOG_ERR, "dn_expand failed: from='%s' to='%s'", from, to); + fclose(fp); + return 0; + default: /* Something died while parsing */ syslog (LOG_ERR, "Returning default of %d due to previous failure", CHECK_FAILURE); fclose(fp); return(CHECK_FAILURE); } } + fclose(fp); + return 2; /* continue */ + } else { + /* fopen() coughed up a lung */ + syslog(LOG_ERR, "Can not open from address check file %s (%m)!", checkfname); + return(CHECK_FAILURE); + } +} + +/* Parse an address check file to see if a particular message from + * "fromaddr" to "toaddr" is allowed from a particular peer. Minimally, + * pi must have filled in pi->my_sa, pi->peer_sa, and pi->peer_ok_addr. + * When used, this gets from smtpd for each recipient of a message. + */ + +int smtpd_addr_check(const char * checkfname, + struct peer_info *pi, + const char * from, + const char * to, + char ** return_message) { + int ret; + line=0; + + /* force user ident (if supplied) to lower case. */ + if (pi->peer_clean_ident != NULL) { + int i; + /* force to lower case */ + for (i=0; i < strlen(pi->peer_clean_ident); i++) { + pi->peer_clean_ident[i]=tolower(pi->peer_clean_ident[i]); + } + do_ident = 0; + } + else { + do_ident = 1; + } + + if ((ret = smtpd_addr_check1(checkfname, + pi, + from, + to, + return_message)) == 2) { /* we've parsed the whole file, and no match. as such we return * CHECK_FAILURE, our choice of fail-on behaviour at compile time * determining if this is an allowed or denied message. @@ -1593,15 +1676,74 @@ int smtpd_addr_check(const char * checkf (pi->peer_clean_reverse_name != NULL)? pi->peer_clean_reverse_name:"UNKNOWN", pi->peer_ok_addr, from, to, checkfname); - fclose(fp); return (CHECK_FAILURE); } - else { - /* fopen() coughed up a lung */ - syslog(LOG_ERR, "Can not open from address check file %s (%m)!", checkfname); - return(CHECK_FAILURE); - } + return ret; +} + +#ifdef MAIN +unsigned char * +cleanitup(const unsigned char *s) +{ + return s; } +int main(argc, argv) + int argc; + char **argv; +{ + struct sockaddr_in me, he; + struct peer_info pi; + char **av = 0, *cp, buf[1024], **s2av(); + int ac, x = -1; + char *rules = "/var/smtpd/etc/smtpd_check_rules"; + char *err; + + memset(&pi, 0, sizeof(pi)); + + sock_dest("tcp::25", &me, &x); + pi.my_sa = &me; + pi.peer_sa = &he; + pi.peer_dirty_forward_name = pi.peer_dirty_reverse_name = "UNKNOWN"; + + while (fgets(buf, sizeof(buf), stdin)) { + if (av) + free(av); + av = s2av(buf, " \t\n", &ac); + if (!av) + continue; + x = -1; + if (strcmp(av[0], "q") == 0 || strncmp(av[0], "quit", 4) == 0) + exit(0); + + if (strncmp(av[0], "me", 2) == 0) { + sock_dest(av[1], &me, &x); + continue; + } + if (strncmp(av[0], "he", 2) == 0) { + sock_dest(av[1], &he, &x); + continue; + } + if (strncmp(av[0], "rules", 5) == 0) { + rules = strdup(av[1]); + continue; + } + /* + * default is FROM TO [peer_name] [peer_addr] + */ + if (ac > 2) + pi.peer_dirty_forward_name = pi.peer_dirty_reverse_name = av[2]; + if (ac > 3) + pi.peer_ok_addr = av[3]; + err = 0; + switch(smtpd_addr_check(rules, &pi, av[0], av[1], &err)) { + case 1: printf("allowed: %s\n", err); break; + case 0: printf("denied: %s\n", err); break; + case -1: printf("noto: %s\n", err); break; + } + } + exit(0); +} +#endif #endif /* CHECK_ADDRESS */ Index: smtpd.c =================================================================== RCS file: /share/cvsroot/net/smtpd/smtpd.c,v retrieving revision 1.1.1.4 diff -u -p -r1.1.1.4 smtpd.c --- smtpd.c 1997/10/13 01:40:34 1.1.1.4 +++ smtpd.c 2001/03/26 08:54:15 @@ -1,8 +1,17 @@ /* + * Copyright (c) 1998, Simon Gerraty. + * + * Hacked to reduce the syslog volume. + * The goal is to log one entry per message indicating: + * relay,from,to,result + * where result is either bytes accepted or an indication of + * why we dropped it. We use accumlog() to do this. + */ +/* * smtpd, Obtuse SMTP daemon, storing agent. does simple collection of * mail messages, for later forwarding by smtpfwdd. * - * $Id: smtpd.c,v 1.1.1.4 1997/10/13 01:40:34 sjg Exp $ + * $Id: smtpd.c,v 1.6 2001/03/25 10:17:16 sjg Exp $ * * Copyright (c) 1996, 1997 Obtuse Systems Corporation. All rights * reserved. @@ -40,7 +49,7 @@ char *obtuse_copyright = "Copyright 1996 - Obtuse Systems Corporation - All rights reserved."; -char *obtuse_rcsid = "$Id: smtpd.c,v 1.1.1.4 1997/10/13 01:40:34 sjg Exp $"; +char *obtuse_rcsid = "$Id: smtpd.c,v 1.6 2001/03/25 10:17:16 sjg Exp $"; #include #include @@ -124,6 +133,10 @@ extern int gethostname(char *name, int l #define CHECK_FILE "/etc/smtpd_check_rules" #endif +#ifndef DNS_TEST +#define DNS_TEST 0 +#endif + #if SET_LOCALE #include #endif @@ -141,6 +154,7 @@ char *spoolfile = NULL; char *spooldir = SPOOLSUBDIR; /* this is relative to our chroot. */ int read_timeout = READ_TIMEOUT; int maxsize = 0; +char *minfree = 0; int outfd, replyfd; #ifdef SUNOS_GETOPT extern char *optarg; @@ -154,12 +168,16 @@ int NoHostChecks = NO_HOSTCHECKS; int Paranoid_Smtp = PARANOID_SMTP; int Paranoid_Dns = PARANOID_DNS; int exiting = 0; +int VerboseSyslog = 1; + +#ifndef _PATH_SMTPD_PID +# define _PATH_SMTPD_PID ".smtpd.pid" +#endif /* * Generate the usual cryptic usage statement */ - void show_usage() { @@ -209,6 +227,12 @@ char * make_check_fail_reply(char *user, int len; c++; switch (*c) { + case '%': + add = "%"; + break; + case 'C': + add = ":"; + break; case 'F': add = from; break; @@ -797,7 +821,10 @@ smtp_exit(int val) if (exiting++<3) { flush_smtp_mbuf(reply_buf, replyfd, reply_buf->offset); } - + + if (!VerboseSyslog) { + accumlog(LOG_INFO, 0); /* flush? */ + } exit(val); } @@ -957,7 +984,12 @@ smtp_cleanitup(const unsigned char *s) *dst++ = '^'; *dst++ = '='; } - if (isalnum(ch) || strchr(" -,:=.@_!<>()[]/+%", ch) != NULL) { + /* + * RFC822 allows both ' and " in local-part. + * " is infact _required_ if local-part contains spaces as is + * common in x400 (yuk). + */ + if (isalnum(ch) || strchr(" -,:=.@_!<>()[]/+%'\"", ch) != NULL) { if (firstone && (ch == '-')) { arg_attempt = 1; *dst++ = '^'; @@ -1365,6 +1397,19 @@ smtp_parse_cmd(unsigned char *inbuf, smtp_exit(EX_PROTOCOL); } } + if (minfree) { + switch ((int) spacefor(spooldir, minfree)) { + case -1: + syslog(LOG_CRIT, "Spooldir %s is bogus: %m", spooldir); + smtp_exit(EX_CONFIG); + break; + case 0: + syslog(LOG_DEBUG, "Need %s bytes in %s", minfree, spooldir); + sleep(10); /* keep inetd from getting upset? */ + smtp_exit(EX_OSERR); + break; + } + } writereply(replybuf, 250, 0, peerinfo.my_clean_reverse_name, " ", @@ -1375,8 +1420,16 @@ smtp_parse_cmd(unsigned char *inbuf, /* * log the connection */ - syslog(LOG_INFO, "SMTP HELO from %s(%s) as \"%s\"", - peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr, client_claimed_name); + if (VerboseSyslog) { + syslog(LOG_INFO, "SMTP HELO from %s(%s) as \"%s\"", + peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr, client_claimed_name); + } else { + accumlog(LOG_INFO, 0); /* flush anything left */ + accumlog(LOG_INFO, "relay=%s/%s", + peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr); + if (strcasecmp(peerinfo.peer_clean_reverse_name, client_claimed_name)) + accumlog(LOG_INFO, " as \"%s\"", client_claimed_name); + } state_change(state, HELO, SUCCESS); } else if (strcasecmp(verb, "MAIL") == 0) { if (!cmd_ok(MAIL, state)) { @@ -1407,8 +1460,15 @@ smtp_parse_cmd(unsigned char *inbuf, } buf += 5; SPANBLANK(buf); + /* + * if local-part contains ", then spaces are allowed + */ cp = NULL; - cp = strchr(buf, ' '); + if (buf[0] == '"' || buf[1] == '"') + cp = strrchr(buf, '"'); /* REVISIT: find last " */ + if (cp == NULL) + cp = buf; + cp = strchr(cp+1, ' '); if (cp != NULL) { /* stuff on the end */ *cp = '\0'; @@ -1453,8 +1513,13 @@ smtp_parse_cmd(unsigned char *inbuf, /* * log the connection */ - syslog(LOG_INFO, "mail from %s", - current_from_mailpath); + if (VerboseSyslog) { + syslog(LOG_INFO, "mail from %s", + current_from_mailpath); + } else { + accumlog(LOG_INFO, " from=%s", current_from_mailpath); + } + /* * put our output in the outbuf */ @@ -1499,7 +1564,15 @@ smtp_parse_cmd(unsigned char *inbuf, buf += 3; SPANBLANK(buf); cp = NULL; - cp = strchr(buf, ' '); + /* + * if local-part contains ", then spaces are allowed + */ + cp = NULL; + if (buf[0] == '"' || buf[1] == '"') + cp = strrchr(buf, '"'); /* REVISIT: find last " */ + if (cp == NULL) + cp = buf; + cp = strchr(cp+1, ' '); if (cp != NULL) { /* stuff on the end */ *cp = '\0'; @@ -1614,7 +1687,12 @@ smtp_parse_cmd(unsigned char *inbuf, break; case 0: /* we matched a "deny" rule. syslog and send back failure message */ - syslog(LOG_INFO, "Forbidden FROM or RCPT for host %s(%s) - Abandoning session", peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr); + if (VerboseSyslog) { + syslog(LOG_INFO, "Forbidden FROM or RCPT for host %s(%s) - Abandoning session", peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr); + } else { + accumlog(LOG_INFO, " forbidden FROM or RCPT"); + accumlog(LOG_INFO, 0); /* flush it */ + } { char *c; c = make_check_fail_reply(peerinfo.peer_clean_ident, @@ -1645,7 +1723,11 @@ smtp_parse_cmd(unsigned char *inbuf, } } badrcpt = 1; - syslog(LOG_INFO, "Discarded bad recipient %s", victim); + if (VerboseSyslog) { + syslog(LOG_INFO, "Discarded bad recipient %s", victim); + } else { + accumlog(LOG_INFO, " discarded bad recipient=%s", victim); + } state_change(state, RCPT, ERROR); break; @@ -1666,7 +1748,11 @@ smtp_parse_cmd(unsigned char *inbuf, /* * log the recipient. */ - syslog(LOG_INFO, "Recipient %s", victim); + if (VerboseSyslog) { + syslog(LOG_INFO, "Recipient %s", victim); + } else { + accumlog(LOG_INFO, " to=%s", victim); + } if (write_smtp_mbuf(outbuf, "RCPT ", strlen("RCPT ")) && write_smtp_mbuf(outbuf, victim, strlen(victim)) && write_smtp_mbuf(outbuf, "\n", 1)) { @@ -1768,11 +1854,19 @@ snarfdata(int in, int out, long *size, i snarfed = read_smtp_mbuf(buf, in, 1024); if (snarfed < 0) { - syslog(LOG_INFO, "read error receiving message body: %m"); + if (VerboseSyslog) { + syslog(LOG_INFO, "read error receiving message body: %m"); + } else { + accumlog(LOG_INFO, " read error receiving message body: %s", strerror(errno)); + } return (2); } if (snarfed == 0) { - syslog(LOG_INFO, "EOF while receiving message body"); + if (VerboseSyslog) { + syslog(LOG_INFO, "EOF while receiving message body"); + } else { + accumlog(LOG_INFO, " EOF while receiving message body"); + } return (2); } if (outbuf->size < buf->size) { @@ -1949,7 +2043,8 @@ void main(int argc, char **argv) { int opt; - char *optstring = "xc:d:u:s:g:m:HPDL"; + int smtp_port = 25; + char *optstring = "p:qxc:d:u:s:g:m:F:HPR:DL"; int i, k; smtp_state_set last_state_s, current_state_s; /* The real state vector. */ smtp_state last_state, current_state; /* Pointers to the state vector. */ @@ -1957,6 +2052,7 @@ main(int argc, char **argv) char *chrootdir = SPOOLDIR; char *username = SMTP_USER; char *groupname = SMTP_GROUP; + char *dns_test = DNS_TEST; struct passwd *user = NULL; struct group *group = NULL; struct sigaction new_sa, old_sa; @@ -1993,6 +2089,22 @@ main(int argc, char **argv) while ((opt = getopt(argc, argv, optstring)) > 0) { #endif switch (opt) { + case 'R': + dns_test = optarg; + break; + case 'F': + minfree = optarg; + if ((int)spacefor(".", minfree) < 0) { + fprintf(stderr, "The \"-F\" option requires a positive integer argument, \"%s\" is bogus\n", optarg); + show_usage(); + exit(EX_CONFIG); + } + case 'p': + smtp_port = atoi(optarg); + break; + case 'q': + VerboseSyslog = 0; + break; case 'c': if (optarg[0] != '/') { syslog(LOG_ERR, "The \"-c\" option requires an absolute pathname argument\n"); @@ -2218,13 +2330,14 @@ main(int argc, char **argv) memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; - sa.sin_port = htons(25); + sa.sin_port = htons(smtp_port); sa.sin_addr.s_addr = INADDR_ANY; /* Need to do this while we're still root */ if ( bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0 ) { - syslog(LOG_ERR, "Can't bind listen socket to port 25 in daemon mode (%m)"); + syslog(LOG_ERR, "Can't bind listen socket to port %d in daemon mode (%m)", + smtp_port); exit(EX_OSERR); } @@ -2242,6 +2355,18 @@ main(int argc, char **argv) syslog(LOG_INFO, "Log reopened."); } + /* + * Solve the Solaris chroot dns hassels, by forcing + * the dns to be exercised before we chroot. + */ + if (dns_test != NULL) { + /* + * its better to check something that won't be found + * (but quickly), so no point reporting failure. + */ + (void) gethostbyname(dns_test); + } + if (chrootdir != NULL) { if (chdir(chrootdir) != 0) { syslog(LOG_CRIT, "Couldn't chdir to directory %s! (%m)", @@ -2277,6 +2402,8 @@ main(int argc, char **argv) */ if ( daemon_mode ) { + FILE *fp; + char *cp; int failures; int rval; @@ -2298,6 +2425,17 @@ main(int argc, char **argv) failures = 0; syslog(LOG_INFO,"smtpd running in daemon mode - ready to accept connections"); + if (cp = malloc(sizeof(_PATH_SMTPD_PID) + strlen(spooldir) + 4)) { + (void) sprintf(cp, "%s/%s", spooldir, _PATH_SMTPD_PID); + if (fp = fopen(cp, "w")) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } else { + syslog(LOG_ERR, "cannot record pid in %s: %m", _PATH_SMTPD_PID); + } + free(cp); + } + while (1) { int fd; int slen; @@ -2588,7 +2726,11 @@ main(int argc, char **argv) /* * eof */ - syslog(LOG_INFO, "EOF on client fd. At least they could say goodbye!"); + if (VerboseSyslog) { + syslog(LOG_INFO, "EOF on client fd. At least they could say goodbye!"); + } else { + accumlog(LOG_INFO, "EOF on client fd."); + } smtp_exit(EX_OSERR); } offset = 0; @@ -2646,8 +2788,13 @@ main(int argc, char **argv) smtp_close_spoolfile(outfd); writereply(reply_buf, 250, 0, m250gotit, NULL); flush_smtp_mbuf(reply_buf, replyfd, reply_buf->offset); - syslog(LOG_INFO, "Received %ld bytes of message body from %s(%s)", - msize, peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr); + if (VerboseSyslog) { + syslog(LOG_INFO, "Received %ld bytes of message body from %s(%s)", + msize, peerinfo.peer_clean_reverse_name, peerinfo.peer_ok_addr); + } else { + accumlog(LOG_INFO, " bytes=%ld", msize); + accumlog(LOG_INFO, 0); /* flush */ + } clear_state(SNARF_DATA, current_state); clear_state(OK_RCPT, current_state); clear_state(OK_MAIL, current_state); Index: smtpfwdd.c =================================================================== RCS file: /share/cvsroot/net/smtpd/smtpfwdd.c,v retrieving revision 1.1.1.3 retrieving revision 1.5 diff -u -p -r1.1.1.3 -r1.5 --- smtpfwdd.c 1997/10/13 01:40:34 1.1.1.3 +++ smtpfwdd.c 2001/03/25 10:18:43 1.5 @@ -1,10 +1,19 @@ /* + * Copyright (c) 1998, Simon Gerraty. + * + * Hacked to reduce the syslog volume. + * The goal is to log one entry per message indicating: + * peer,from,to,result + * where result is either bytes accepted or an indication of + * why we dropped it. We use accumlog() to do this. + */ +/* * smtpfwdd, Obtuse SMTP forward daemon, master process watches spool * directory for files spooled by smtpd. On seeing one, spawns a child * to pick it up and invokes sendmail (or sendmail-like agent) to * deliver it. * - * $Id: smtpfwdd.c,v 1.1.1.3 1997/10/13 01:40:34 sjg Exp $ + * $Id: smtpfwdd.c,v 1.5 2001/03/25 10:18:43 sjg Exp $ * * Copyright (c) 1996, 1997 Obtuse Systems Corporation. All rights * reserved. @@ -41,7 +50,7 @@ */ char *obtuse_copyright = "Copyright 1996 - Obtuse Systems Corporation - All rights reserved."; -char *obtuse_rcsid = "$Id: smtpfwdd.c,v 1.1.1.3 1997/10/13 01:40:34 sjg Exp $"; +char *obtuse_rcsid = "$Id: smtpfwdd.c,v 1.5 2001/03/25 10:18:43 sjg Exp $"; #include #include @@ -116,12 +125,16 @@ char *obtuse_rcsid = "$Id: smtpfwdd.c,v #define COMPLETION_WAIT 86400 #endif +char *mailqueuedir = NULL; +char *minfree = 0; char *spooldir = NULL; char *mailagent = MAIL_AGENT; int children = 0; int maxchildren = MAXCHILDREN; int poll_time = POLL_TIME; int gc_int = COMPLETION_WAIT; +int expensive = 0; +int VerboseSyslog = 1; #ifdef SUNOS_GETOPT extern char *optarg; @@ -276,6 +289,13 @@ smtp_spoolfile_complete(const char *fnam syslog(LOG_DEBUG, "Retrying delivery of file %s, last attempted at %s.", fname, ctime(&(buf.st_mtime))); } } + if (mailqueuedir != 0) { + if (space_for_bytes(mailqueuedir, buf.st_size * 2) == 0) { + syslog(LOG_DEBUG, "Skipping file %s, not enough space in %s", + fname, mailqueuedir); + return (0); + } + } return (1); } @@ -287,6 +307,8 @@ smtp_spoolfile_complete(const char *fnam void reap_children(void) { + int x; + while (1) { pid_t pid; int status; @@ -304,7 +326,7 @@ reap_children(void) } children--; if ((!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) { - switch (WEXITSTATUS(status)) { + switch (x = WEXITSTATUS(status)) { case EX_TEMPFAIL: /* @@ -325,7 +347,7 @@ reap_children(void) /* * permanent failure or something unusual */ - syslog(LOG_DEBUG, "Child process (%d) failed - no retry", pid); + syslog(LOG_DEBUG, "Child process (%d) failed(%d) - no retry", pid, x); break; } } @@ -390,6 +412,7 @@ void forward(char *fname) { FILE *f = NULL; + char smtpd_id[SMTP_MAX_CMD_LINE]; char line[SMTP_MAX_CMD_LINE]; char *c, *from; int sentout; @@ -489,6 +512,19 @@ forward(char *fname) exit(EX_TEMPFAIL); } + + /* + * when filtering we'd like our filter to be able to avoid the + * overhead of finding a unique name to store its input in, so + * telling it the unique name we have can help. + */ + if (c = strrchr(fname, '/')) + ++c; + else + c = fname; + sprintf(smtpd_id, "SMTPD_ID=%.*s", sizeof(smtpd_id) - 12, c); + putenv(smtpd_id); + /* * parse file */ @@ -591,6 +627,9 @@ forward(char *fname) body = ftell(f); victim = victims; sentout = 0; + if (!VerboseSyslog) { + accumlog(LOG_INFO, "forwarding %s", fname); + } while (victim != NULL) { int status, pid, pidw, i, rstart; struct smtp_victim *sv = victim; @@ -610,13 +649,19 @@ forward(char *fname) * doesn't need this). */ av[i++] = "-oiTrue"; + if (expensive) + av[i++] = "-odq"; /* enqueue only */ } #endif av[i++] = "-f"; av[i++] = from; rstart = i; while (i < MAXARGS - 2) { - syslog(LOG_INFO, "forwarding to recipient %s", victim->name); + if (VerboseSyslog) { + syslog(LOG_INFO, "forwarding to recipient %s", victim->name); + } else { + accumlog(LOG_INFO, " to=%s", victim->name); + } av[i++] = victim->name; victim = victim->next; if (victim == NULL) { @@ -732,7 +777,12 @@ forward(char *fname) * All seems to have worked */ - syslog(LOG_INFO, "%s forwarded to %d recipients", fname, sentout); + if (VerboseSyslog) { + syslog(LOG_INFO, "%s forwarded to %d recipients", fname, sentout); + } else { + accumlog(LOG_INFO, ", forwarded to %d recipients", sentout); + accumlog(LOG_INFO, 0); /* flush */ + } if (unlink(fname) != 0) { syslog(LOG_CRIT, "Couldn't remove spool file %s! (%m)", fname); exit(EX_CONFIG); @@ -764,7 +814,7 @@ void main(int argc, char **argv) { int opt; - char *optstring = "u:g:d:s:C:M:P:"; + char *optstring = "qu:g:d:s:C:M:P:Q:F:e"; int pid; char *username = SMTP_USER; @@ -783,6 +833,24 @@ main(int argc, char **argv) while ((opt = getopt(argc, argv, optstring)) > 0) { #endif switch (opt) { + case 'Q': + if (optarg[0] != '/') { + fprintf(stderr, "The \"-Q\" option requires an absolute pathname argument, \"%s\" is bogus\n", optarg); + show_usage(); + exit(EX_CONFIG); + } + mailqueuedir = optarg; /* will check it for free space */ + break; + case 'F': + minfree = optarg; + if ((int)spacefor(".", minfree) < 0) { + fprintf(stderr, "The \"-F\" option requires a positive integer argument, \"%s\" is bogus\n", optarg); + show_usage(); + exit(EX_CONFIG); + } + case 'q': + VerboseSyslog = 0; + break; case 'd': if (optarg[0] != '/') { fprintf(stderr, "The \"-d\" option requires an absolute pathname argument, \"%s\" is bogus\n", optarg); @@ -791,6 +859,9 @@ main(int argc, char **argv) } spooldir = optarg; break; + case 'e': + expensive = 1; + break; case 's': if (optarg[0] != '/') { fprintf(stderr, "The \"-s\" option requires an absolute pathname argument, \"%s\" is bogus\n", optarg); @@ -1089,6 +1160,28 @@ main(int argc, char **argv) struct dirent *direct; int cpid; + if (minfree != 0 && mailqueuedir != 0) { + int told = 0; + + do { + switch (cpid = (int) spacefor(mailqueuedir, minfree)) { + case -1: + syslog(LOG_CRIT, "mailqueuedir '%s' is bogus: %m", + mailqueuedir); + exit(EX_CONFIG); + break; + case 0: + if (!told) { + told = 1; + syslog(LOG_DEBUG, "waiting for %d bytes free in %s", + minfree, mailqueuedir); + } + reap_children(); + sleep(300); + break; + } + } while (cpid == 0) ; + } while ((direct = readdir(dir)) != NULL) { int groks = 0; @@ -1101,6 +1194,10 @@ main(int argc, char **argv) } sleep(1); reap_children(); + } + if (!VerboseSyslog) { + /* should be empty - but just in case */ + accumlog(LOG_INFO, 0); } /* * If we have a file with an appropriate name and it is