/* * Copyright (c) Joe Maimon, jmaimon@jmaimon.com New York,NY 2005 * * This milter is adapted from the sample milter included in the * open source sendmail distribution libmilter/README * * Portions may be copyright the Sendmail Consortium. * * No license was explicitly stated for that sample. In the event * that a License does apply, and this work is a significant deriviation * of the sample milter, the assumed license would be the license found * in the open source sendmail distribution LICENSE file. * * Additionaly, unless you received this file from all copyright holders under * any other arrangement, the GNU GPLv2 or later license may be assumed to apply, * as specified by the Free Software Foundation. */ /* * The following copyright applies to portions copied or derived from Sendmail.org sources */ /* * Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers. * All rights reserved. * Copyright (c) 1986, 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. * */ /*TODO * * verification caching * * connection caching * * hostmap interaction * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include # if NETINET || NETINET6 # include # include # endif /* NETINET || NETINET6 */ #ifndef _FFR_MILTER_REWRITE # define _FFR_MILTER_REWRITE 1 #endif /* _FFR_MILTER_REWRITE */ #ifndef _FFR_MILTER_SM_MAP # define _FFR_MILTER_SM_MAP 1 #endif /* _FFR_MILTER_SM_MAP */ #include "libmilter/mfapi.h" /* right hand side items */ #define CANONNET ((unsigned char)0226) /* canonical net, next token */ #define CANONHOST ((unsigned char)0227) /* canonical host, next token */ #define CANONUSER ((unsigned char)0230) /* canonical user, next N tokens */ #define CALLSUBR ((unsigned char)0231) /* call another rewriting set */ #ifndef true typedef int bool; # define false 0 # define true 1 #endif /* ! true */ #define VERSION "0.01" #define PROGNAME "callahead-milter" static char *progname = PROGNAME; static char *MyHname = NULL; static int MyHname_len; static int prevent_loop = 0; static int use_j_macro = 0; static int verify_sender = 0; static int callback_sender = 0; static FILE *logfile = NULL; static int verbosity = 0; static bool do_mx_lookups = 0; static int cache_memory_size = 0; static char *cache_disk_file = NULL; static char *cache_expire_spec = NULL; static char *timeouts_spec = "D:10s;X:10s;L:20s;C:15s;O:25s;K:30s;S:10s;N:30s;R:10s;E:1m;M:2m"; static char *host_connection_caching = NULL; static char *pt_host_connection_caching = NULL; static bool cleanup_reply = false; static bool use_syslog = false; static char *syslog_label = PROGNAME; static char *calm_envrcpt = NULL; static char *calm_envfrom = NULL; static int calm_envrcpt_len = 0; static int calm_envfrom_len = 0; static int only_mx_supressed = 0; static bool retry_without_dot = false; static bool getmx_detect_ipa = false; static char *hostname_sm_lookup = NULL; struct mlfiPriv { char * rewrite_val; char * sender_address; int sender_address_len; char * queue_id; char myhname[1024]; char *myhnamep; char *macro_j; int macro_j_len; int myhname_len; int consock; int consock_con; time_t milter_start; time_t dns_mx_start; /* recursive function */ time_t olookup_start; /* recursive function */ }; struct calm_hostent { struct hostent * host; char * buf; }; static char * fallbackdsthost = NULL; static bool rewrite_anyways = 0; static bool rewrite_maybe = 0; static char * lfallbackdsthost = NULL; #if _FFR_MILTER_SM_MAP static char * bestmx_sm_map = "bestmx"; static bool bestmx_sm_map_lookup = false; #endif /* _FFR_MILTER_SM_MAP */ static char noqueue_id[] = "NOQUEUE:"; static sfsistat smfis_tempfail = SMFIS_TEMPFAIL; static sfsistat smfis_remote_tempfail = SMFIS_TEMPFAIL; static sfsistat never_stop_anything = 0; #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) extern sfsistat mlfi_cleanup(SMFICTX *, bool); void calm_verbose(SMFICTX * ctx, int intro, char * fmt, ...) { va_list ap; struct mlfiPriv *priv = NULL; int syslog_prio = LOG_INFO; if (ctx) priv = MLFIPRIV; if (!verbosity) return; va_start(ap, fmt); if (logfile) fflush(logfile); if (intro) { fprintf((logfile) ? logfile : stderr, "%s : %s: %lu%s", progname, (priv && priv->queue_id) ? priv->queue_id : "NOQUEUE", (ctx)? (unsigned long) pthread_self() : 0, (fmt && *fmt) ? " : " : "\n"); } if (use_syslog) { if (verbosity > 3) syslog_prio = LOG_DEBUG; else if (verbosity > 2) syslog_prio = LOG_ERR; if (intro) syslog(syslog_prio, "%s: %lu%s", (priv && priv->queue_id) ? priv->queue_id : "NOQUEUE", (ctx)? (unsigned long) pthread_self() : 0, (fmt && *fmt) ? " : " : "\n"); } if(fmt && *fmt) { vfprintf((logfile) ? logfile : stderr, fmt,ap); if (use_syslog) vsyslog(syslog_prio, fmt, ap); } if (logfile) fflush(logfile); va_end(ap); return; } # define VERBOSE(a,b,...) do { \ if(verbosity >= a) \ calm_verbose(b, 1, __VA_ARGS__); \ } while (0); #if DEBUG # define VERBOSE_FUNCLINE(a,b) do { \ if(verbosity >= a) \ calm_verbose(b, 1, "%s(): line %d\n", \ __func__, __LINE__); \ } while (0); # define VERBOSE_FUNCLINE_MSG(a,b,...) do { \ if(verbosity >= a) \ { \ calm_verbose(b, 1, "%s(): line %d", \ __func__, __LINE__); \ calm_verbose(b, 0, __VA_ARGS__); \ } \ } while (0); #else # define VERBOSE_FUNCLINE(...) while(0) { ; } # define VERBOSE_FUNCLINE_MSG(...) while(0) { ; } #endif /* DEBUG */ /* lifted and modified from sendmail/convtime.c * props and copyrights to sendmail/org and consortium */ /* ** CONVTIME -- convert time ** ** Takes a time as an ascii string with a trailing character ** giving units: ** s -- seconds ** m -- minutes ** h -- hours ** d -- days (default) ** w -- weeks ** For example, "3d12h" is three and a half days. ** ** Parameters: ** p -- pointer to ascii time. ** units -- default units if none specified. ** ** Returns: ** time in seconds. ** ** Side Effects: ** none. */ time_t convtime(ctx, p, units) SMFICTX *ctx; char *p; int units; { register time_t t, r; register char c; bool pos = true; r = 0; if (strcasecmp(p, "now") == 0) return -1; if (*p == '-') { pos = false; ++p; } while (*p != '\0') { t = 0; while ((c = *p++) != '\0' && isascii(c) && isdigit(c)) t = t * 10 + (c - '0'); if (c == '\0') { c = units; p--; } else if (strchr("wdhms", c) == NULL) { VERBOSE(1, ctx, "Invalid time unit `%c'", c); c = units; } switch (c) { case 'w': /* weeks */ t *= 7; /* FALLTHROUGH */ case 'd': /* days */ /* FALLTHROUGH */ default: t *= 24; /* FALLTHROUGH */ case 'h': /* hours */ t *= 60; /* FALLTHROUGH */ case 'm': /* minutes */ t *= 60; /* FALLTHROUGH */ case 's': /* seconds */ break; } r += t; } return pos ? r : -r; } /* lifted and modified from sendmail/milter.c * props and copyrights to sendmail/org and consortium */ /* ** CALM_PARSE_TIMEOUTS -- parse timeout list ** ** Called when proccessing argv ** ** Parameters: ** ctx -- context ** spec -- the timeout list. ** calm_timeouts -- timeout array. ** ** Returns: ** 0 on error ** 1 on success */ #define CALM_TO_DNS_A 0 #define CALM_TO_DNS_MX 1 #define CALM_TO_SM_LOOKUP 2 #define CALM_TO_SM_OLOOKUP 3 #define CALM_TO_CONNECT 4 #define CALM_TO_OCONNECT 5 #define CALM_TO_SEND 6 #define CALM_TO_NSEND 7 #define CALM_TO_READ 8 #define CALM_TO_EREAD 9 #define CALM_TO_EOM 10 static time_t Calm_Timeouts[CALM_TO_EOM+1]; static int calm_parse_timeouts(ctx, spec, calm_timeouts) SMFICTX *ctx; const char *spec; time_t *calm_timeouts; { char fcode; int tcode; register char *p, *lspec; if (!spec) return 0; else lspec = p = strdup(spec); if (!p) return 0; VERBOSE_FUNCLINE_MSG(5, ctx, " timeouts %s\n", p); /* now scan through and assign info from the fields */ while (p && *p != '\0') { char *delimptr; while (*p != '\0' && (*p == ';' || (isascii(*p) && isspace(*p)))) p++; /* p now points to field code */ fcode = *p; while (*p != '\0' && *p != ':') p++; if (*p++ != ':') { VERBOSE(2, ctx, "did not find field code in %s\n", spec); free(lspec); return 0; } while (isascii(*p) && isspace(*p)) p++; /* p now points to the field body */ delimptr = strchr(p, ';'); if (delimptr) *(delimptr++) = '\0'; tcode = -1; /* install the field into the filter struct */ switch (fcode) { case 'D': tcode = CALM_TO_DNS_A; break; case 'X': tcode = CALM_TO_DNS_MX; break; case 'L': tcode = CALM_TO_SM_LOOKUP; break; case 'K': tcode = CALM_TO_SM_OLOOKUP; break; case 'C': tcode = CALM_TO_CONNECT; break; case 'O': tcode = CALM_TO_OCONNECT; break; case 'S': tcode = CALM_TO_SEND; break; case 'N': tcode = CALM_TO_NSEND; break; case 'E': tcode = CALM_TO_EREAD; break; case 'R': tcode = CALM_TO_READ; break; case 'M': tcode = CALM_TO_EOM; break; default: VERBOSE(1, ctx, "unknown timeout code %c\n", fcode); free(lspec); return 0; break; } if (tcode >= 0) { calm_timeouts[tcode] = convtime(ctx, p, 's'); VERBOSE(2, ctx, " fcode %c seconds %u\n", fcode, (u_long) calm_timeouts[tcode]); } p = delimptr; } free(lspec); return 1; } static int calm_timedout(SMFICTX *ctx, time_t time_start, int which) { time_t time_end = time(NULL); int time_diff; struct mlfiPriv *priv = NULL; if (ctx) priv = MLFIPRIV; VERBOSE_FUNCLINE(5, ctx); if (which > CALM_TO_EOM) return 0; if (!Calm_Timeouts[which]) return 0; VERBOSE_FUNCLINE_MSG(4, ctx, " time_start %u time_end %u priv->dns_mx_start %u " "priv->olookup_start %u priv->milter_start %u which %d timeout %u\n", time_start, time_end, (priv) ? priv->dns_mx_start : 0, (priv) ? priv->olookup_start : 0, (priv) ? priv->milter_start : 0, which, Calm_Timeouts[which]); if (priv && which == CALM_TO_DNS_MX && priv->dns_mx_start) { if ((time_diff = time_end - priv->dns_mx_start) > Calm_Timeouts[which]) { VERBOSE(2, ctx, "timed out %i seconds DNS MX with timeout %d, %u seconds\n", time_diff, which, Calm_Timeouts[which]); return 1; } } if (priv && which == CALM_TO_SM_LOOKUP && priv->olookup_start) { if ((time_diff = (time_end - priv->olookup_start)) > Calm_Timeouts[CALM_TO_SM_OLOOKUP]) { VERBOSE(2, ctx, "timed out %i seconds sendmail lookups with timeout %d, %u seconds\n", time_diff, CALM_TO_SM_OLOOKUP, Calm_Timeouts[CALM_TO_SM_OLOOKUP]); return 1; } } if ((time_diff = time_end - time_start) > Calm_Timeouts[which]) { VERBOSE(2, ctx, "timed out %i seconds with timeout %d, %u seconds\n", time_diff, which, Calm_Timeouts[which]); return 1; } if (which == CALM_TO_EOM) return 0; if (priv && (time_diff = time_end - priv->milter_start) > Calm_Timeouts[CALM_TO_EOM]) { VERBOSE(2, ctx, "timed out %i seconds with timeout %d, %u seconds\n", time_diff, CALM_TO_EOM, Calm_Timeouts[CALM_TO_EOM]); return 1; } else if ((time_diff = time_end - time_start) > Calm_Timeouts[CALM_TO_EOM]) { VERBOSE(2, ctx, "timed out %i seconds with timeout %d, %u seconds\n", time_diff, CALM_TO_EOM, Calm_Timeouts[CALM_TO_EOM]); return 1; } return 0; } struct calm_hostent *calm_gethostbyname(SMFICTX *ctx, char *host) { struct hostent *hp1, *hp; size_t hstbuflen; char *tmphstbuf; int res = 0; int herr; struct calm_hostent *calm_host; time_t dns_start = time(NULL); VERBOSE_FUNCLINE(5,NULL); hstbuflen = 1024; /* Allocate buffer, remember to free it to avoid memory leakage. */ tmphstbuf = malloc(hstbuflen); if (!tmphstbuf) return NULL; VERBOSE_FUNCLINE(5,ctx); hp1 = malloc(2*sizeof(struct hostent)); /* why 2? dunno, fudge factor*/ if (!hp1) { VERBOSE_FUNCLINE(5,ctx); free(tmphstbuf); return NULL; } VERBOSE_FUNCLINE(5,ctx); while ((res = gethostbyname_r(host, hp1, tmphstbuf, hstbuflen, &hp, &herr)) == ERANGE) { /* Enlarge the buffer. */ char * tbuf = realloc(tmphstbuf, (hstbuflen *= 2)); VERBOSE_FUNCLINE(5,ctx); if (!tbuf) break; else tmphstbuf = tbuf; } /* Check for errors. */ VERBOSE_FUNCLINE(5,ctx); calm_host = malloc(sizeof(struct calm_hostent)); if (res || hp == NULL || !calm_host || calm_timedout(ctx, dns_start, CALM_TO_DNS_A)) { VERBOSE_FUNCLINE(5,ctx); VERBOSE(2, ctx, "res %d, hp %p, calm_host %p, herr %d\n", res, hp, calm_host, herr); free(hp1); free(tmphstbuf); return NULL; } VERBOSE_FUNCLINE(5,ctx); if (hp != hp1) free(hp1); calm_host->host = hp; calm_host->buf = tmphstbuf; VERBOSE_FUNCLINE(5,ctx); return calm_host; } void calm_freehostent(struct calm_hostent *host) { if (!host) return; free(host->host); free(host->buf); free(host); return; } int calm_getfqdn(SMFICTX *ctx, char *buf, int len) { struct calm_hostent *host; if (!buf || !*buf || len < 2) return 0; host = calm_gethostbyname(ctx, buf); if (host) { strncpy(buf,host->host->h_name,len-1); buf[len-1] = '\0'; calm_freehostent(host); return strlen(buf); } return 0; } /* lifted and modified from sendmail/domain.c * props and copyrights to sendmail/org and consortium */ /* ** CALM_GETMXRR -- get MX resource records for a domain ** ** Parameters: ** host -- the name of the host to MX. ** mxs -- a pointer to a return buffer of MX records. ** mxprefs -- a pointer to a return buffer of MX preferences. ** If NULL, don't try to populate. ** buf -- a pointer to space that should be freed when mxhosts is no longer needed. ** buflen -- the length of the buffer ** ** Returns: ** The number of MX records found. ** -1 if there is an internal failure. ** If no MX records are found, mxhosts[0] is set to host ** and 1 is returned. ** ** Side Effects: ** buf and buflen may be altered, realloced, alloced. */ # ifndef MAXPACKET # define MAXPACKET 8192 /* max packet size used internally by BIND */ # endif /* ! MAXPACKET */ # ifndef HFIXEDSZ # define HFIXEDSZ 12 /* sizeof(HEADER) */ # endif /* ! HFIXEDSZ */ #ifndef GETSHORT # define GETSHORT _getshort #endif #ifndef GETLONG # define GETLONG _getlong #endif #define MAXCNAMEDEPTH 10 # if defined(__RES) && (__RES >= 19940415) # define RES_UNC_T char * # else /* defined(__RES) && (__RES >= 19940415) */ # define RES_UNC_T unsigned char * # endif /* defined(__RES) && (__RES >= 19940415) */ union querybuf { HEADER qb1; unsigned char qb2[MAXPACKET]; }; pthread_mutex_t calm_getmxrr_mutex = PTHREAD_MUTEX_INITIALIZER; int calm_getmxrr(SMFICTX *ctx, char *host, char ***mxs, unsigned short **mxprefs, char **buf, int *blen, int depth) { time_t dns_start = time(NULL); register unsigned char *eom = NULL, *cp = NULL; register int i, j, n, bufpos = 0; int nmx = 0; register char *bp = NULL; HEADER *hp; union querybuf answer; int ancount = 0, qdcount, buflen, expnmx; unsigned short pref = 0, type; bool trycanon = false; unsigned short *prefs = NULL; int (*resfunc) __P((const char *, int, int, u_char *, int)); int *weight = NULL; extern int res_query(), res_search(); char **mxhosts = NULL; char *bufp = NULL; char *lhost = NULL; struct mlfiPriv *priv = NULL; if (ctx) priv = MLFIPRIV; if (priv && !priv->dns_mx_start) priv->dns_mx_start = dns_start; VERBOSE_FUNCLINE(5, ctx); if (!host || *host == '\0' || !buf || !blen) return -1; if(calm_timedout(ctx, dns_start, CALM_TO_DNS_MX)) return -1; /* efficiency hack -- numeric or non-MX lookups */ if (host[0] == '[') goto mx_punt; VERBOSE_FUNCLINE(5, ctx); errno = 0; resfunc = res_search; pthread_mutex_lock(&calm_getmxrr_mutex); if ((_res.options & RES_INIT) == 0 && res_init() == -1) return -1; n = (*resfunc)(host, C_IN, T_MX, (unsigned char *) &answer, sizeof(answer)); pthread_mutex_unlock(&calm_getmxrr_mutex); VERBOSE_FUNCLINE(5, ctx); if (n < 0) { VERBOSE(3, ctx, "res_search(%s) failed (errno=%d, h_errno=%d)\n", host == NULL ? "" : host, errno, h_errno); switch (h_errno) { case NO_DATA: trycanon = true; /* FALLTHROUGH */ case NO_RECOVERY: /* no MX data on this host */ goto mx_punt; case HOST_NOT_FOUND: case 0: /* broken resolvers */ /* host doesn't exist in DNS; might be in /etc/hosts */ trycanon = true; goto mx_punt; case TRY_AGAIN: case -1: return -1; break; default: VERBOSE(2, ctx, "res_search (%s) failed with impossible h_errno (%d)\n", host, h_errno); return EX_OSERR; break; } /* irreconcilable differences */ return -1; } VERBOSE_FUNCLINE(5, ctx); if(calm_timedout(ctx, dns_start, CALM_TO_DNS_MX)) return -1; /* avoid problems after truncation in tcp packets */ if (n > sizeof(answer)) n = sizeof(answer); /* find first satisfactory answer */ hp = (HEADER *)&answer; cp = (unsigned char *)&answer + HFIXEDSZ; eom = (unsigned char *)&answer + n; for (qdcount = ntohs((unsigned short) hp->qdcount); qdcount--; cp += n + QFIXEDSZ) { if ((n = dn_skipname(cp, eom)) < 0) goto mx_punt; } VERBOSE_FUNCLINE(5, ctx); ancount = ntohs((unsigned short) hp->ancount); expnmx = ancount+1; mx_storage: VERBOSE_FUNCLINE(5, ctx); if (expnmx && *buf && *blen > 0) { bufp = bp = *buf; buflen = *blen; VERBOSE_FUNCLINE(5, ctx); } else if (expnmx) { VERBOSE_FUNCLINE(5, ctx); bufp = bp = malloc(BUFSIZ); if (!bp) return -1; buflen = BUFSIZ; VERBOSE_FUNCLINE(5, ctx); } if (expnmx) { VERBOSE_FUNCLINE(5, ctx); mxhosts = malloc((expnmx+1) * sizeof(char *)); prefs = malloc((expnmx+1) * sizeof(short)); weight = malloc((expnmx+1) * sizeof(int)); if (host < bp+buflen && host >= bp) { lhost = strdup(host); host = lhost; } if (!mxhosts || !prefs || !weight || !host) goto mx_cleanup_and_leave; mxhosts[expnmx] = NULL; VERBOSE_FUNCLINE(5, ctx); } /* See RFC 1035 for layout of RRs. */ while (--ancount >= 0 && cp < eom) { int ttl; VERBOSE_FUNCLINE(5, ctx); if ((n = dn_expand((unsigned char *)&answer, eom, cp, (RES_UNC_T) bp+bufpos, buflen-bufpos)) < 0) break; cp += n; GETSHORT(type, cp); cp += INT16SZ; /* skip over class */ GETLONG(ttl, cp); GETSHORT(n, cp); /* rdlength */ if (type == T_MX) GETSHORT(pref, cp); /* very crude*/ if (bufpos > buflen/2) { VERBOSE_FUNCLINE(5, ctx); bufp = realloc(bp, (buflen *= 2)); if (!bufp) goto mx_cleanup_and_leave; bp = bufp; } VERBOSE_FUNCLINE(5, ctx); if ((n = dn_expand((unsigned char *)&answer, eom, cp, (RES_UNC_T) bp+bufpos, buflen-bufpos)) < 0) break; cp += n; n = strlen(bp+bufpos); VERBOSE(3, ctx, "got name %s\n", bp+bufpos); VERBOSE_FUNCLINE_MSG(5, ctx, " bp %p bufpos %d n %d bp[bufpos+n-1] %c bp %s bp+bufpos %s\n", bp, bufpos, n, bp[bufpos+n-1], (n || bufpos) ? bp : "", (n) ? bp+bufpos : ""); if (bp[bufpos+n-1] != '.') { struct in_addr ia; #if NETINET6 struct in6_addr ia6; #endif if (!getmx_detect_ipa || ( !inet_pton(AF_INET, bp+bufpos, &ia) && #if NETINET6 !inet_pton(AF_INET6, bp+bufpos, &ia6) && #endif 1)) bp[bufpos+n++] = '.'; } prefs[nmx] = pref; *(bp+bufpos+n++) = '\0'; VERBOSE_FUNCLINE(4, ctx); if (type != T_MX) { VERBOSE_FUNCLINE(5, ctx); if (type == T_CNAME) { VERBOSE_FUNCLINE(5, ctx); if (depth++ >= MAXCNAMEDEPTH) { VERBOSE(3, ctx, "CNAME recursion depth exeeeded\n"); goto mx_cleanup_and_leave; } VERBOSE_FUNCLINE(5, ctx); free(mxhosts); free(prefs); prefs = NULL; mxhosts = NULL; bufp = bp; nmx = calm_getmxrr(ctx, bp+bufpos, &mxhosts, &prefs, &bufp, &buflen, depth); if (nmx && bufp) bp = bufp; VERBOSE_FUNCLINE(5, ctx); goto mx_return_and_leave; } VERBOSE(4, ctx, "unexpected answer type %d, size %d\n", type, n); cp += n; continue; } mxhosts[nmx++] = bp+bufpos; VERBOSE(3, ctx, "found mx host %s\n", mxhosts[nmx-1]); VERBOSE_FUNCLINE(5, ctx); bufpos+=n; } mxhosts[nmx] = NULL; VERBOSE_FUNCLINE(5, ctx); /* sort the records */ for (i = 0; i < nmx; i++) { for (j = i + 1; j < nmx; j++) { if (prefs[i] > prefs[j] || (prefs[i] == prefs[j] && weight[i] > weight[j])) { register int temp; register char *temp1; temp = prefs[i]; prefs[i] = prefs[j]; prefs[j] = temp; temp1 = mxhosts[i]; mxhosts[i] = mxhosts[j]; mxhosts[j] = temp1; temp = weight[i]; weight[i] = weight[j]; weight[j] = temp; } } } VERBOSE_FUNCLINE(5, ctx); /* delete duplicates from list (yes, some bozos have duplicates) */ for (i = 0; i < nmx - 1; ) { if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) i++; else { /* compress out duplicate */ for (j = i + 1; j < nmx; j++) { mxhosts[j] = mxhosts[j + 1]; prefs[j] = prefs[j + 1]; } nmx--; } } VERBOSE_FUNCLINE(5, ctx); if (priv && priv->macro_j) { for (i = 0; i < nmx; i++) { if(strcasecmp(mxhosts[i], priv->macro_j) == 0) { mxhosts[i] = NULL; nmx = i; break; } } } VERBOSE_FUNCLINE(5, ctx); if (nmx == 0) { int hlen; mx_punt: VERBOSE_FUNCLINE(5, ctx); if (!bp || !mxhosts || !prefs || !weight) { expnmx = 1; goto mx_storage; } hlen = strlen(host); if (hlen < (buflen - bufpos - 1)) memmove(bp, host, hlen); else { mx_punt2: VERBOSE_FUNCLINE(5, ctx); bufp = realloc(bp, (buflen*=2)); if (!bufp) goto mx_cleanup_and_leave; bp = bufp; goto mx_punt; } bp[hlen] = '\0'; mxhosts[0] = bp; prefs[0] = 0; nmx = 1; if (bp[0] == '[' && bp[hlen-1] == ']') { VERBOSE_FUNCLINE(5, ctx); (mxhosts[0])++; *(bp+hlen-1) = '\0'; } else if (trycanon && (hlen = calm_getfqdn(ctx, bp, buflen))) { VERBOSE_FUNCLINE(5, ctx); if (hlen >= buflen - 2) goto mx_punt2; if (bp[hlen-1] != '.' && hlen < buflen - 2) { bp[hlen++] = '.'; bp[hlen] = '\0'; } VERBOSE_FUNCLINE(5, ctx); } } VERBOSE_FUNCLINE(5, ctx); if (!nmx) goto mx_cleanup_and_leave; mx_return_and_leave: VERBOSE_FUNCLINE(5, ctx); if (priv) priv->dns_mx_start = 0; if (buf) *buf = bufp; else if (bufp) free(bufp); else if (bp) free(bp); VERBOSE_FUNCLINE(5, ctx); if (blen) *blen = buflen; if (mxs) *mxs = mxhosts; else if (mxhosts) free(mxhosts); VERBOSE_FUNCLINE(5, ctx); if (mxprefs) *mxprefs = prefs; else if (prefs) free(prefs); VERBOSE_FUNCLINE(5, ctx); if (weight) free(weight); if (lhost) free(lhost); VERBOSE_FUNCLINE(5, ctx); return nmx; mx_cleanup_and_leave: VERBOSE_FUNCLINE(5, ctx); if (priv) priv->dns_mx_start = 0; if (prefs) free(prefs); if (bp && (!buf || *buf != bp)) { free(bp); if (*buf) *buf = NULL; } if (mxhosts) free(mxhosts); if (weight) free(weight); if (lhost) free(lhost); return -1; } /* rw == 0 ; select read rw == 1 ; select write rw == 2 ; select write, check connect() rw == 3 ; select nothing (sleep) */ static int calm_select(SMFICTX * ctx, int sock, int rw, int secs, int usecs) { /* select */ fd_set rfds; fd_set wfds; struct timeval tv; int retval; unsigned len = sizeof(retval); struct mlfiPriv *priv = NULL; if (ctx) priv = MLFIPRIV; FD_ZERO(&rfds); FD_ZERO(&wfds); if ((sock < 0 || rw < 0) && rw != 3) return 1; if (rw) FD_SET(sock, &wfds); else FD_SET(sock, &rfds); if (usecs) tv.tv_usec = usecs; else tv.tv_usec = 0; if (secs) tv.tv_sec = secs; else tv.tv_sec = 0; if (!secs && !usecs) { if (rw && rw != 3) tv.tv_sec = 20; else if (rw == 3) tv.tv_sec = 1; else tv.tv_sec = 2 * 60; } if (sock >= FD_SETSIZE) { return 1; } retval = select(sock + 1, (rw) ? NULL : &rfds, (rw && rw != 3) ? &wfds : NULL, NULL, &tv); if (retval < 0) { return -1; } else if (retval) { if (rw == 2) { if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &retval, &len) < 0) { return -1; } else { if (retval) { return -1; } else { return 1; } /* cant happen */ return -1; } /* cant happen */ return -1; } return 1; } else { if (rw == 3) return 1; return -1; } /* cant happen */ return -1; } static int calm_select_read(SMFICTX * ctx, int sock, int secs, int usecs) { return (calm_select(ctx, sock, 0, secs, usecs)); } static int calm_select_write(SMFICTX * ctx, int sock, int secs, int usecs) { return (calm_select(ctx, sock, 1, secs, usecs)); } static int calm_select_connect(SMFICTX * ctx, int sock, int secs, int usecs) { return (calm_select(ctx, sock, 2, secs, usecs)); } static int calm_select_sleep(SMFICTX * ctx, int secs, int usecs) { return (calm_select(ctx, 0, 3, secs, usecs)); } /* all this looping through remotes response charachter by charachter ..... */ #define LAST_SMTPLINE(a,oline) do { \ char * pline, *nline; \ \ oline = pline = a; \ while((nline=strchr(pline+1,'\n'))) \ { \ oline = pline; \ pline = nline; \ } \ if (oline != a) \ oline++; \ } while (0) int read_smtp(SMFICTX *ctx, int consock, char **buf, ssize_t *buflen, ssize_t *bytes_read) { int retval; time_t read_start = time(NULL); VERBOSE_FUNCLINE(5, ctx); if (!buf || !buflen || !bytes_read || consock < 0) return -1; *bytes_read = 0; if (!*buflen) *buflen = BUFSIZ; if (!*buf) *buf = malloc(*buflen); VERBOSE_FUNCLINE(5, ctx); if (!*buf) return -1; VERBOSE_FUNCLINE(5, ctx); retval = calm_select_read(ctx, consock, CALM_TO_READ, 0); while (retval > 0) { char * nline; int k; VERBOSE_FUNCLINE(5, ctx); if (calm_timedout(ctx, read_start, CALM_TO_READ)) goto read_cleanup_and_leave; k = read(consock, (*buf)+ *bytes_read, *buflen - *bytes_read - 1); if (calm_timedout(ctx, read_start, CALM_TO_READ)) goto read_cleanup_and_leave; if (k < 0) { VERBOSE_FUNCLINE(5, ctx); if (errno == EAGAIN || errno == EINTR) { VERBOSE_FUNCLINE(5, ctx); continue; } else { read_cleanup_and_leave: VERBOSE_FUNCLINE(5, ctx); free(*buf); *buf = NULL; *buflen = 0; return -1; } } VERBOSE_FUNCLINE(5, ctx); *bytes_read += k; (*buf)[*bytes_read] = '\0'; if (*bytes_read > (*buflen - 1)) { char * bufp = realloc(*buf, (*buflen *= 2)); VERBOSE_FUNCLINE(5, ctx); if (!bufp) goto read_cleanup_and_leave; *buf = bufp; } VERBOSE_FUNCLINE(5, ctx); if (*((*buf)+*bytes_read-1) == '\n') nline = (*buf)+*bytes_read-1; else nline = strchr(*buf, '\n'); if (nline) { char * lline = *buf; char * lbuf = *buf; VERBOSE_FUNCLINE(5, ctx); LAST_SMTPLINE(lbuf,lline); if ((lline - lbuf) > (*bytes_read - 4)) nline = NULL; else if (lline[3] == '-') nline = NULL; } VERBOSE_FUNCLINE(5, ctx); VERBOSE(4, ctx, "nline %s\n", nline); if (!nline || *bytes_read < 4 || (*buf)[3] == '-') retval = calm_select_read(ctx, consock, nline ? 0 : Calm_Timeouts[CALM_TO_READ], nline ? 50000 : 0); else return 1; VERBOSE_FUNCLINE(5, ctx); if (retval < 0) return 1; } VERBOSE_FUNCLINE(5, ctx); return retval; } int calm_connect(SMFICTX *ctx, char **dsthost) { int retval, i; struct calm_hostent *calm_host = NULL; struct hostent *host = NULL; int consock; char addrbuf[256]; time_t oconnect_start = time(NULL); VERBOSE_FUNCLINE(5, ctx); if (!dsthost) return -1; for(consock = -1 ; dsthost[0] && *dsthost[0] && consock == -1; dsthost++) { long sock_flags; bool lretry_without_dot = retry_without_dot; connect_retry_dotless: VERBOSE_FUNCLINE(5, ctx); calm_host = calm_gethostbyname(ctx, *dsthost); if (calm_timedout(ctx, oconnect_start, CALM_TO_OCONNECT)) break; if (!calm_host) { if (!lretry_without_dot) continue; if (!calm_host && lretry_without_dot) { int hlen = strlen(dsthost[0]); if (dsthost[0][hlen-1] == '.') { dsthost[0][hlen - 1] = '\0'; lretry_without_dot = false; goto connect_retry_dotless; } } } host = calm_host->host; if (host->h_addrtype == AF_INET) for (i = 0; consock == -1 && host->h_addr_list[i]; i++) { struct sockaddr_in sock; time_t connect_start = time(NULL); sock.sin_family = AF_INET; sock.sin_addr = *(struct in_addr *) host->h_addr_list[i]; sock.sin_port = htons(25); VERBOSE_FUNCLINE(5, ctx); consock = socket(PF_INET, SOCK_STREAM, 0); if (consock < 0) break; if ((sock_flags = fcntl(consock, F_GETFL)) < 0) break; if (fcntl(consock, F_SETFL, sock_flags | O_NONBLOCK) < 0) break; VERBOSE_FUNCLINE(5, ctx); VERBOSE(1,ctx, "connecting to %s for host %s\n", inet_ntop(AF_INET, &sock.sin_addr, addrbuf, sizeof(addrbuf)), dsthost[0]); retval = connect(consock, (struct sockaddr *)&sock, sizeof(sock)); if (!retval && !calm_timedout(ctx, connect_start, CALM_TO_CONNECT)) break; if (retval < 0 && errno == EINPROGRESS) { VERBOSE_FUNCLINE(5, ctx); retval = calm_select_connect(ctx, consock, Calm_Timeouts[CALM_TO_CONNECT], 0); if (retval > 0) break; } VERBOSE_FUNCLINE(5, ctx); shutdown(consock, SHUT_RDWR); close(consock); consock = -1; } #if NETINET6 else if (host->h_addrtype == AF_INET6) for (i = 0; consock == -1 && host->h_addr_list[i]; i++) { struct sockaddr_in6 sock; time_t connect_start = time(NULL); sock->sin_family = AF_INET6; sock->sin_addr = (struct in_addr *) hostinfo->h_addr_list[i]; add->sin_port = htonl(25); VERBOSE_FUNCLINE(5, ctx); VERBOSE(1,ctx, "connecting to %s for host %s\n", inet_ntop(AF_INET6, sock.sin_addr, addrbuf, sizeof(addrbuf)), dsthost[0]); consock = socket(PF_INET, SOCK_STREAM, 0); if (consock < 0) break; if (fcntl(consock, F_SETFD, O_NONBLOCK) < 0) break; VERBOSE_FUNCLINE(5, ctx); retval = connect(consock, (struct sockaddr *)&sock, sizeof(sock)); if (!retval && !calm_timedout(ctx, connect_start, CALM_TO_CONNECT)) break; if (retval < 0 && errno == EINPROGRESS) { VERBOSE_FUNCLINE(5, ctx); retval = calm_select_connect(ctx, consock, Calm_Timeouts[CALM_TO_CONNECT], 0); if (retval > 0) break; } VERBOSE_FUNCLINE(5, ctx); shutdown(consock, SHUT_RDWR); close(consock); consock = -1; } #endif VERBOSE_FUNCLINE(5, ctx); calm_freehostent(calm_host); } VERBOSE_FUNCLINE(5, ctx); return consock; } int write_smtp(SMFICTX *ctx, int consock, char *buf, ssize_t buflen, struct iovec *vecs, int num_vecs) { struct iovec lvector[32]; struct iovec *vector = lvector; int num_vectors = (sizeof lvector)/(sizeof lvector[0]); int j,i = 0; ssize_t len = 0; ssize_t bytes_wrote = 0; time_t write_start = time(NULL); VERBOSE_FUNCLINE(5, ctx); if (consock < 0) return -1; if (buf && buflen) { vector[i].iov_base = buf; vector[i++].iov_len = buflen; len = buflen; if (num_vecs + i > num_vectors) { vector = malloc(sizeof(struct iovec) * num_vecs + i); VERBOSE_FUNCLINE(5, ctx); if (!vector) return -1; num_vectors = num_vecs + i; } for (j = 0; i < num_vectors; i++) { vector[i].iov_base = vecs[j].iov_base; vector[i].iov_len = vecs[j].iov_len; len += vector[i].iov_len; } VERBOSE_FUNCLINE(5, ctx); } else { vector = vecs; num_vectors = num_vecs; for (i = 0; i < num_vectors; i++) len += vector[i].iov_len; } VERBOSE_FUNCLINE(5, ctx); i = 0; for (;;) { ssize_t k; VERBOSE_FUNCLINE(5, ctx); if (calm_select_write(ctx, consock, Calm_Timeouts[CALM_TO_SEND], 0) < 0) break; VERBOSE_FUNCLINE(5, ctx); if (calm_timedout(ctx, write_start, CALM_TO_SEND)) break; k = writev(consock, vector, num_vectors); if (calm_timedout(ctx, write_start, CALM_TO_SEND)) break; if (k > 0) { VERBOSE_FUNCLINE(5, ctx); bytes_wrote += k; if (k >= len) break; } else if (errno != EAGAIN && errno != EINTR) break; VERBOSE_FUNCLINE(5, ctx); for (i = 0; i < num_vectors; i++) { VERBOSE_FUNCLINE(5, ctx); if (vector[i].iov_len > (unsigned int) k) { VERBOSE_FUNCLINE(5, ctx); vector[i].iov_base = (char *)vector[i].iov_base + k; vector[i].iov_len -= (unsigned int) k; break; } k -= (int) vector[i].iov_len; vector[i].iov_len = 0; } VERBOSE_FUNCLINE(5, ctx); if (i == num_vectors) /* this shouldnt happen */ break; } VERBOSE_FUNCLINE(5, ctx); if (vector != lvector && vector != vecs) free(vector); if (bytes_wrote == len) return 1; VERBOSE_FUNCLINE(5, ctx); return -1; } #define CHECK_READ_SMTP(a,b,c,d) do {\ if (bytes_read > 2) \ { \ char * line = buf; \ LAST_SMTPLINE(buf,line); \ if (strncmp(line, a, b) != 0) \ { \ if (d) \ { \ d = 0; \ } \ else \ goto c; \ } \ } \ else \ if (d) \ { \ d = 0; \ } \ else \ goto c; \ } while (0) /* return to caller * 1 Verified Good, chek p * 0 Verified Bad, check p. * -1 Error, possible temporary, check p. * -2 Error, possible temporary, check p, close consock. * -3 Error, possible temporary, check p, close consock, open consock, retry. * -4 Error. free p. Close consock. retry with different dsthost only. * * p may contain a dynamic allocated string from smtp server. If p != NULL you must free it. * * rset = 1, try to RSET open connection * rset = 0, new connection with helo * rset = -1, do smtp quit * rset = -2, just do RSET */ int verify_smtprcpt(SMFICTX *ctx, int consock, int *rset, char * envfrom, char * envrcpt, char **p, ssize_t *plen) { char *buf = NULL; ssize_t buflen = BUFSIZ; ssize_t bytes_read = 0; struct iovec vector[10]; int envfrom_len = 0, envrcpt_len = 0, curaddr_len; char *curaddr; char smtp_helo_cmd[] = "helo "; char smtp_ehlo_cmd[] = "ehlo "; char smtp_mail_cmd[] = "mail from:"; char smtp_rcpt_cmd[] = "rcpt to:"; char smtp_rset_cmd[] = "rset"; char smtp_quit_cmd[] = "quit"; char smtp_newline[] = "\r\n"; char smtp_from_null[] = "<>"; char smtp_to_postm[] = ""; char smtp_left_brack[] = "<"; char smtp_right_brack[] = ">"; int retval = -1; char myhname[1024]; int myhname_len = 0; struct mlfiPriv *priv = NULL; int i = 0; if (ctx) priv = MLFIPRIV; VERBOSE_FUNCLINE(5, ctx); VERBOSE(2, ctx, "consock %d rset %p %d envfrom %s envrcpt %s p %p plen %p\n", consock, rset, (rset) ? *rset : 0, envfrom, envrcpt, p, plen); /* XXX: * perhaps there should be dedicated functions for rset, quit */ if (rset && *rset == -1) { retval = 1; VERBOSE_FUNCLINE(5, ctx); goto do_quit; } else if (rset && *rset == -2) goto do_rset; VERBOSE_FUNCLINE(5, ctx); if (envrcpt && envrcpt[0]) envrcpt_len = strlen(envrcpt); else if (calm_envrcpt) { VERBOSE_FUNCLINE(5, ctx); envrcpt = calm_envrcpt; envrcpt_len = calm_envrcpt_len; } else { VERBOSE_FUNCLINE(5, ctx); envrcpt = smtp_to_postm; envrcpt_len = sizeof(smtp_to_postm) - 1; } if (envfrom && envfrom[0]) envfrom_len = strlen(envfrom); else if (!envfrom && priv && priv->sender_address && verify_sender) { VERBOSE_FUNCLINE(5, ctx); envfrom = priv->sender_address; envfrom_len = priv->sender_address_len; } else if (!envfrom && calm_envfrom) { VERBOSE_FUNCLINE(5, ctx); envfrom = calm_envfrom; envfrom_len = calm_envfrom_len; } else if (!envfrom || !envfrom[0]) { VERBOSE_FUNCLINE(5, ctx); envfrom = smtp_from_null; envfrom_len = sizeof(smtp_from_null) - 1; } VERBOSE_FUNCLINE(5, ctx); vector[2].iov_base = smtp_newline; vector[2].iov_len = sizeof(smtp_newline) - 1; if(!rset || !*rset) { VERBOSE_FUNCLINE(5, ctx); bytes_read = 0; if(read_smtp(ctx, consock, &buf, &buflen, &bytes_read) < 0) goto cleanup_and_leave; VERBOSE_FUNCLINE(5, ctx); retval = -1; VERBOSE(4, ctx, "buflen %d bytes_read %d buf %s\n", buflen, bytes_read, buf); CHECK_READ_SMTP("220",3,cleanup_and_leave,retval); if (!retval) { VERBOSE_FUNCLINE(5, ctx); retval = -1; VERBOSE(4, ctx, "buflen %d bytes_read %d buf %s\n", buflen, bytes_read, buf); CHECK_READ_SMTP("4",1,cleanup_and_leave,retval); if (!retval) retval = -1; else retval = -2; do_quit: VERBOSE_FUNCLINE(5, ctx); vector[0].iov_base = smtp_quit_cmd; vector[0].iov_len = sizeof(smtp_quit_cmd) - 1; vector[1].iov_base = smtp_newline; vector[1].iov_len = sizeof(smtp_newline) - 1; write_smtp(ctx, consock, NULL, 0, vector, 2); goto cleanup_and_leave; } VERBOSE_FUNCLINE(5, ctx); if (priv && priv->macro_j && bytes_read > 4+1+priv->macro_j_len && buf[4+priv->macro_j_len] == ' ' && (strncasecmp(buf+4,priv->macro_j, priv->macro_j_len) == 0)) { VERBOSE_FUNCLINE(5, ctx); retval = -4; if (rset) *rset = 0; VERBOSE(2,ctx,"I was talking to my own connected MTA?\n"); goto do_quit; } if (strstr(buf,"ESMTP")) { VERBOSE_FUNCLINE(5, ctx); vector[0].iov_base = smtp_ehlo_cmd; vector[0].iov_len = sizeof(smtp_ehlo_cmd) - 1; } else { VERBOSE_FUNCLINE(5, ctx); vector[0].iov_base = smtp_helo_cmd; vector[0].iov_len = sizeof(smtp_helo_cmd) - 1; } if (priv && priv->myhnamep && !MyHname) { VERBOSE_FUNCLINE(5, ctx); vector[1].iov_base = priv->myhnamep; vector[1].iov_len = priv->myhname_len; } else if (!priv || !MyHname) { get_my_hname: VERBOSE_FUNCLINE(5, ctx); gethostname(myhname, sizeof(myhname)); if (!(myhname_len = calm_getfqdn(ctx, myhname, sizeof(myhname)))) myhname_len = strlen(myhname); vector[1].iov_base = myhname; vector[1].iov_len = myhname_len; } else if (MyHname) { VERBOSE_FUNCLINE(5, ctx); vector[1].iov_base = MyHname; vector[1].iov_len = MyHname_len; } else goto get_my_hname; VERBOSE_FUNCLINE(5, ctx); if(write_smtp(ctx, consock, NULL, 0, vector, 3) < 0) goto cleanup_and_leave; VERBOSE_FUNCLINE(5, ctx); bytes_read = 0; if(read_smtp(ctx, consock, &buf, &buflen, &bytes_read) < 0) goto cleanup_and_leave; VERBOSE(4, ctx, "buflen %d bytes_read %d buf %s\n", buflen, bytes_read, buf); CHECK_READ_SMTP("250",3,cleanup_and_leave,retval); if (!retval) { retval = -4; if (rset) *rset = 0; goto do_quit; } if (rset) *rset = 1; VERBOSE_FUNCLINE(5, ctx); } else if (rset && *rset) { do_rset: VERBOSE_FUNCLINE(5, ctx); vector[0].iov_base = smtp_rset_cmd; vector[0].iov_len = sizeof(smtp_rset_cmd) - 1; vector[1].iov_base = smtp_newline; vector[1].iov_len = 2; VERBOSE_FUNCLINE(5, ctx); if(write_smtp(ctx, consock, NULL, 0, vector, 2) < 0) goto cleanup_and_leave; bytes_read = 0; if(read_smtp(ctx, consock, &buf, &buflen, &bytes_read) < 0) goto cleanup_and_leave; VERBOSE_FUNCLINE(5, ctx); retval = -1; VERBOSE(4, ctx, "buflen %d bytes_read %d buf %s\n", buflen, bytes_read, buf); CHECK_READ_SMTP("250",3,cleanup_and_leave,retval); if (!retval) { *rset = 0; goto do_quit; } if (rset && *rset == -2) { retval = 1; goto cleanup_and_leave; } VERBOSE_FUNCLINE(5, ctx); } VERBOSE_FUNCLINE(5, ctx); vector[i].iov_base = smtp_mail_cmd; vector[i++].iov_len = sizeof(smtp_mail_cmd) - 1; curaddr = envfrom; curaddr_len = envfrom_len; do_mailrcpt: VERBOSE_FUNCLINE(5, ctx); if (*curaddr == '<') { vector[i].iov_base = curaddr; vector[i++].iov_len = curaddr_len; } else { vector[i].iov_base = smtp_left_brack; vector[i++].iov_len = 1; vector[i].iov_base = curaddr; vector[i++].iov_len = curaddr_len; } if (curaddr[curaddr_len-1] != '>') { vector[i].iov_base = smtp_right_brack; vector[i++].iov_len = 1; } if (i > 2) { vector[i].iov_base = smtp_newline; vector[i++].iov_len = sizeof(smtp_newline) - 1; } else i++; VERBOSE_FUNCLINE(5, ctx); if(write_smtp(ctx, consock, NULL, 0, vector, i) < 0) goto cleanup_and_leave; bytes_read = 0; if(read_smtp(ctx, consock, &buf, &buflen, &bytes_read) < 0) goto cleanup_and_leave; retval = -1; VERBOSE_FUNCLINE(5, ctx); VERBOSE(4, ctx, "buflen %d bytes_read %d buf %s\n", buflen, bytes_read, buf); CHECK_READ_SMTP("250",3,cleanup_and_leave,retval); if (!retval) { VERBOSE_FUNCLINE(5, ctx); CHECK_READ_SMTP("421",3,cleanup_and_leave,retval); CHECK_READ_SMTP("5",1,cleanup_and_leave,retval); retval = -1; CHECK_READ_SMTP("4",1,cleanup_and_leave,retval); } VERBOSE_FUNCLINE(5, ctx); if (vector[0].iov_base == smtp_rcpt_cmd) { retval = 1; goto cleanup_and_leave; } i = 0; vector[i].iov_base = smtp_rcpt_cmd; vector[i++].iov_len = sizeof(smtp_rcpt_cmd) - 1; curaddr = envrcpt; curaddr_len = envrcpt_len; VERBOSE_FUNCLINE(5, ctx); goto do_mailrcpt; if (!rset) goto do_quit; cleanup_and_leave: VERBOSE_FUNCLINE(5, ctx); if (buf && p) { VERBOSE_FUNCLINE(5, ctx); *p = buf; if (plen) *plen = bytes_read; } else if (buf) free(buf); return retval; } int calm_smtprcpt(SMFICTX *ctx, char ** dsthost, char * envfrom, char * envrcpt, char **p, ssize_t *plen) { int consock = -1; struct mlfiPriv *priv = NULL; int rset = 0; int retval = -1; VERBOSE_FUNCLINE(5, ctx); VERBOSE(2, ctx, "dsthost %p %s envfrom %s envrcpt %s p %p plen %p\n", dsthost, (dsthost) ? dsthost[0] : NULL, envfrom, envrcpt, p, plen); if (ctx) priv = MLFIPRIV; if (!dsthost || !*dsthost) return -1; if (priv && priv->consock_con) { VERBOSE_FUNCLINE(5, ctx); consock = priv->consock; if(calm_select_connect(ctx, consock, 0, 10000) < 0) { VERBOSE_FUNCLINE(5, ctx); VERBOSE_FUNCLINE(5, ctx); priv->consock_con = 0; priv->consock = consock = -1; } else rset = 1; VERBOSE_FUNCLINE(5, ctx); } if (consock < 0) { bad_consock: VERBOSE_FUNCLINE(5, ctx); if (!*dsthost) return retval; consock = calm_connect(ctx, dsthost); VERBOSE_FUNCLINE(5, ctx); rset = 0; if (priv) { VERBOSE_FUNCLINE(5, ctx); priv->consock_con = (consock > -1); priv->consock = consock; } } if (consock < 0) return retval; VERBOSE_FUNCLINE(5, ctx); retval = verify_smtprcpt(ctx, consock, &rset, envfrom, envrcpt, p, plen); if (retval == -2) { VERBOSE_FUNCLINE(5, ctx); shutdown(consock, SHUT_RDWR); close(consock); if (priv) { priv->consock = -1; priv->consock_con = 0; } } else if (retval == -3) { VERBOSE_FUNCLINE(5, ctx); shutdown(consock, SHUT_RDWR); close(consock); goto bad_consock; } else if (retval == -4) { VERBOSE_FUNCLINE(5, ctx); shutdown(consock, SHUT_RDWR); close(consock); dsthost++; goto bad_consock; } VERBOSE_FUNCLINE(5, ctx); if (!priv) { rset = -1; VERBOSE_FUNCLINE(5, ctx); verify_smtprcpt(ctx, consock, &rset, NULL, NULL, p, plen); shutdown(consock, SHUT_RDWR); close(consock); } VERBOSE_FUNCLINE(5, ctx); return retval; } void calm_smtpshutdown(SMFICTX *ctx, int consock) { struct mlfiPriv *priv = NULL; int rset = -1; int lconsock; VERBOSE_FUNCLINE(5, ctx); if (ctx) priv = MLFIPRIV; else if (consock < 0) return; if (priv && priv->consock > -1 && priv->consock_con) lconsock = priv->consock; else lconsock = consock; if(lconsock > -1 && verify_smtprcpt(ctx, lconsock, &rset, NULL, NULL, NULL, NULL) > -1) { VERBOSE_FUNCLINE(5, ctx); shutdown(lconsock, SHUT_RDWR); close(lconsock); } if (priv && priv->consock > -1 && priv->consock_con) { VERBOSE_FUNCLINE(5, ctx); priv->consock = -1; priv->consock_con = 0; } VERBOSE_FUNCLINE(5, ctx); } /* lifted from sendmail/err.c * props and copyrights to sendmail/org and consortium */ int extenhsc(s, delim, e) const char *s; int delim; char *e; { int l, h; if (s == NULL) return 0; if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) return 0; h = 0; l = 2; e[0] = s[0]; e[1] = '.'; while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) { e[l + h] = s[l + h]; ++h; } if (h == 0 || s[l + h] != '.') return 0; e[l + h] = '.'; l += h + 1; h = 0; while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) { e[l + h] = s[l + h]; ++h; } if (h == 0 || s[l + h] != delim) return 0; e[l + h] = '\0'; return l + h; } /* * mode == 0 envfrom * mode == 1 envrcpt * */ sfsistat mlfi_frrc(ctx, envrcpt, mode) SMFICTX *ctx; char **envrcpt; int mode; { sfsistat retval = smfis_tempfail; #if _FFR_MILTER_REWRITE || _FFR_MILTER_SM_MAP char *ruleset_321 = "3,2,0"; char *ruleset_local = "localaddr"; char *rset = ruleset_321; char *pbuf[2]; time_t lookup_start; #endif /* _FFR_MILTER_REWRITE || _FFR_MILTER_SM_MAP */ char **pp; char *p = NULL; char *p1 = NULL; int plen = 0; int p1len = 0; char *dsthosts[2]; char **dsthost = dsthosts; int rwstat = 0; int i = 0; int local_recipient = 0; char * macro_rcpt_mailer = NULL; struct mlfiPriv *priv = MLFIPRIV; bool ldo_mx_lookups = do_mx_lookups; VERBOSE_FUNCLINE(5, ctx); VERBOSE(1, ctx, "mlfi_frrc(): %s %s\n", mode ? "envrcpt" : "envfrom" , envrcpt[0]); if (!priv) return retval; priv->olookup_start = time(NULL); if (envrcpt && envrcpt[0][0] == '<' && envrcpt[0][1] == '>') return SMFIS_CONTINUE; dsthost[1] = NULL; dsthost[0] = smfi_getsymval(ctx, mode ? "{rcpt_host}" : "{mail_host}"); VERBOSE(2,ctx, "dsthost[0] %s lfallbackdsthost %s fallbackhost %s\n", dsthost[0], lfallbackdsthost, fallbackdsthost); if (lfallbackdsthost) { VERBOSE_FUNCLINE(5, ctx); macro_rcpt_mailer = smfi_getsymval(ctx, mode ? "{rcpt_mailer}" : "{mail_mailer}"); if (macro_rcpt_mailer && strcasecmp(macro_rcpt_mailer, "local") == 0) local_recipient = 1; else VERBOSE_FUNCLINE(5, ctx); VERBOSE(2, ctx, "macro_rcpt_mailer %s local_recipient %d\n", macro_rcpt_mailer, local_recipient); } #if _FFR_MILTER_REWRITE if (local_recipient) rset = ruleset_local; pbuf[0] = envrcpt[0]; pbuf[1] = NULL; pp = pbuf; restart_rewrite: lookup_start = time(NULL); if((((!*dsthost || !*dsthost[0]) && rewrite_maybe) || rewrite_anyways) && (smfi_rewrite(ctx, rset, &pp, &rwstat, SMFRW_MULTIPLE | SMFRW_UNFILTER)) == MI_SUCCESS) { VERBOSE_FUNCLINE(5, ctx); do { if (rwstat < EX_OK) break; VERBOSE_FUNCLINE(5, ctx); if (!pp || !pp[0]) break; VERBOSE_FUNCLINE_MSG(5, ctx, " pp[0] %s\n", pp[0]); if ((pp[0][0] & 0377) != CANONNET) break; VERBOSE_FUNCLINE(5, ctx); while(pp[0][++i]) if ((pp[0][i] & 0377) == CANONHOST) break; VERBOSE_FUNCLINE_MSG(5, ctx, " pp[0][i] %c\n", pp[0][i]); pp[0][i++] = '\0'; if (!local_recipient && strcmp("local", pp[0] + 1) == 0) { VERBOSE_FUNCLINE(5, ctx); local_recipient = 1; for (i = 0; pp && pp[i]; i++) free(pp[i]); free(pp); VERBOSE_FUNCLINE(5, ctx); if (calm_timedout(ctx, lookup_start, CALM_TO_SM_LOOKUP)) goto frrc_cleanup_and_leave; rset = ruleset_local; pp = pbuf; VERBOSE_FUNCLINE(5, ctx); goto restart_rewrite; } VERBOSE_FUNCLINE_MSG(5, ctx, " pp[0][i] %c\n", pp[0][i]); p = pp[0] + i; if (!*p) break; VERBOSE_FUNCLINE(5, ctx); for (i = 0; p[i]; i++) if ((p[i] & 0377) == CANONUSER) break; VERBOSE_FUNCLINE(5, ctx); if (!p[i]) break; VERBOSE_FUNCLINE(5, ctx); p[i] = '\0'; dsthost[0] = strdup(p); for (i = 0; pp && pp[i]; i++) free(pp[i]); pp[0] = dsthost[0]; dsthost = pp; pp = NULL; break; } while (0); VERBOSE_FUNCLINE(5, ctx); VERBOSE(2, ctx, "dsthost %p %s\n", dsthost, (dsthost) ? dsthost[0] : ""); if (pp && pp[0]) { VERBOSE_FUNCLINE(5, ctx); for (i = 0; pp[i]; i++) free(pp[i]); free(pp); } pp = NULL; if (calm_timedout(ctx, lookup_start, CALM_TO_SM_LOOKUP)) goto frrc_cleanup_and_leave; pp = NULL; p = NULL; } VERBOSE_FUNCLINE(5, ctx); #endif /* _FFR_MILTER_REWRITE */ if (!dsthost[0] || !*dsthost[0]) { VERBOSE_FUNCLINE(5, ctx); if (local_recipient && lfallbackdsthost) { VERBOSE_FUNCLINE(5, ctx); dsthost[0] = lfallbackdsthost; } else dsthost[0] = fallbackdsthost; } else if (only_mx_supressed && dsthost[0][0] != '[') { VERBOSE_FUNCLINE(5, ctx); retval = SMFIS_CONTINUE; goto frrc_cleanup_and_leave; } # if _FFR_MILTER_SM_MAP VERBOSE_FUNCLINE_MSG(5, ctx, " pp %p, bestmx_sm_map %s, rwstat %d\n", pp, bestmx_sm_map, rwstat); if((dsthost[0]) && *dsthost[0] && bestmx_sm_map_lookup) { lookup_start = time(NULL); VERBOSE_FUNCLINE(5, ctx); pp = dsthost; /* SMFRW_MULTIPLE to workaround bug in milter-rrres v14 */ /* SMFRW_CONDELSE has a new feature to it in milter-rrres v15 */ if (smfi_sm_map(ctx, bestmx_sm_map, &pp, &rwstat, SMFRW_MULTIPLE | SMFRW_MAPDELIM | SMFRW_CONDELSE) == MI_SUCCESS) { VERBOSE_FUNCLINE(5, ctx); if(pp && pp[0]) { if (dsthost != dsthosts) { for (i = 0; dsthost[i]; i++) free(dsthost[i]); free(dsthost); } dsthost = pp; } VERBOSE_FUNCLINE(5, ctx); if (calm_timedout(ctx, lookup_start, CALM_TO_SM_LOOKUP)) goto frrc_cleanup_and_leave; ldo_mx_lookups = 0; } } # endif /* _FFR_MILTER_SM_MAP */ if (dsthost[0] && *dsthost[0] && ldo_mx_lookups) { VERBOSE_FUNCLINE(5, ctx); if (verbosity > 2) for (i = 0; dsthost[i]; i++) VERBOSE(3, ctx, "dsthost[%d] %s\n", i, dsthost[i]); pp = NULL; if (calm_getmxrr(ctx, dsthost[0], &pp, NULL, &p1, &p1len, 0) > 0) { VERBOSE_FUNCLINE(5, ctx); if (dsthost != dsthosts) { for (i = 0; dsthost[i]; i++) free(dsthost[i]); free(dsthost); } dsthost = pp; } else { VERBOSE_FUNCLINE(5, ctx); free(p1); p1 = NULL; } } VERBOSE_FUNCLINE(5, ctx); if (dsthost[0] && *dsthost[0]) { VERBOSE_FUNCLINE_MSG(5, ctx, " only_mx_supressed %d\n", only_mx_supressed); if (verbosity > 2) for (i = 0; dsthost[i]; i++) VERBOSE(3, ctx, "dsthost[%d] %s\n", i, dsthost[i]); if (verify_sender) { VERBOSE_FUNCLINE(5, ctx); rwstat = calm_smtprcpt(ctx, dsthost, NULL, (prevent_loop) ? NULL : envrcpt[0] , &p, &plen); if (rwstat > 0 && prevent_loop) { VERBOSE_FUNCLINE(5, ctx); rwstat = calm_smtprcpt(ctx, dsthost, "", envrcpt[0] , &p, &plen); } } else { VERBOSE_FUNCLINE(5, ctx); rwstat = calm_smtprcpt(ctx, dsthost, NULL, envrcpt[0] , &p, &plen); } VERBOSE_FUNCLINE(5, ctx); calm_smtpshutdown(ctx, -1); VERBOSE(3,ctx, "rwstat %d plen %d p %s\n", rwstat, plen, (plen) ? p : ""); } else retval = SMFIS_CONTINUE; if (rwstat > 0 || never_stop_anything) retval = SMFIS_CONTINUE; VERBOSE_FUNCLINE(5, ctx); if (!never_stop_anything && rwstat < 1 && p) { char smtp_xcode[15]; char smtp_rcode[4]; int xcode = 0; int fudge = 0; int envrcpt_len = 0; #if !_FFR_MULTILINE_ERRORS char *end_p = NULL; if (p) end_p = strpbrk(p,"\r\n"); if (end_p) end_p[0] = '\0'; #else if (p[plen-2] == '\r') { p[plen-2] = '\0'; plen -= 2; } else if (p[plen-1] == '\n') { p[plen-1] = '\0'; plen -= 1; } #endif /* !_FFR_MULTILINE_ERRORS */ if (plen > 3) xcode = extenhsc(p+4,' ',smtp_xcode); if (plen > 2) strncpy(smtp_rcode, p, 3); else strcpy(smtp_rcode, "550"); smtp_rcode[3] = '\0'; VERBOSE_FUNCLINE(5, ctx); VERBOSE(1,ctx,"Code: %s %s: Line from remote: \"%s\"\n", smtp_rcode, (xcode) ? smtp_xcode : "", (plen) ? p : ""); if (cleanup_reply && plen > 4+xcode+3+(xcode != 0)) { VERBOSE_FUNCLINE(5, ctx); envrcpt_len = strlen(envrcpt[0]); if (envrcpt[0][0] != '<' && *(p+xcode+4+(xcode != 0)) == '<') fudge++; if (strncasecmp(envrcpt[0], p+xcode+4+(xcode != 0)+fudge, envrcpt_len) == 0) { VERBOSE_FUNCLINE(5, ctx); fudge += envrcpt_len; while(4+xcode+(xcode != 0) + fudge < plen && *(p+4+xcode+(xcode != 0)+fudge) == '.') fudge++; if (*(p+4+xcode+(xcode != 0)+fudge) == ' ') fudge++; } VERBOSE_FUNCLINE_MSG(4, ctx, " 4+xcode+3+(xcode != 0) = %d; " "*(p+xcode+4+(xcode != 0)) == %c; " "xcode+4+(xcode != 0)+fudge == %d; " "envrcpt[0] == %s; " "fudge == %d\n", 4+xcode+3+(xcode != 0), *(p+xcode+4+(xcode != 0)), xcode+4+(xcode != 0)+fudge, envrcpt[0], fudge); } if (smfi_setreply(ctx, smtp_rcode, xcode ? smtp_xcode : NULL, (plen > 5) ? p+xcode+4+(xcode != 0)+fudge : "Unknown error") != MI_FAILURE) { VERBOSE(4,ctx, "smfi_setreply() succeeded: smtp_rcode %s smtp_xcode %s string %s\n", smtp_rcode, xcode ? smtp_xcode : "", (plen > 5) ? p+xcode+4+(xcode != 0)+fudge : "Unknown error"); VERBOSE_FUNCLINE(5, ctx); if (smtp_rcode[0] == '5') retval = SMFIS_REJECT; if (smtp_rcode[0] == '4') retval = smfis_remote_tempfail; } else VERBOSE(1,ctx, "smfi_setreply() failed: smtp_rcode %s smtp_xcode %s string %s\n", smtp_rcode, xcode ? smtp_xcode : "", (plen > 5) ? p+xcode+4+(xcode != 0)+fudge : "Unknown error"); } frrc_cleanup_and_leave: VERBOSE_FUNCLINE(5, ctx); if (p1) { VERBOSE_FUNCLINE(5, ctx); free(p1); VERBOSE_FUNCLINE(5, ctx); free(dsthost); VERBOSE_FUNCLINE(5, ctx); } else if (dsthost != dsthosts) { VERBOSE_FUNCLINE(5, ctx); for (i = 0; dsthost && dsthost[i]; i++) { VERBOSE_FUNCLINE(5, ctx); free(dsthost[i]); } VERBOSE_FUNCLINE(5, ctx); free(dsthost); VERBOSE_FUNCLINE(5, ctx); } VERBOSE_FUNCLINE(5, ctx); if(p) { VERBOSE_FUNCLINE(5, ctx); free(p); } VERBOSE_FUNCLINE(5, ctx); return retval; } sfsistat mlfi_envfrom(ctx, envfrom) SMFICTX *ctx; char **envfrom; { struct mlfiPriv *priv; VERBOSE_FUNCLINE(5, ctx); VERBOSE(2, ctx, "envfrom %s\n", envfrom[0]); /* allocate some private memory */ priv = malloc(sizeof *priv); if (!priv) /* can't accept this message right now */ return smfis_tempfail; memset(priv, '\0', sizeof *priv); priv->milter_start = time(NULL); /* save the private data */ smfi_setpriv(ctx, priv); if (verify_sender || callback_sender) { priv->sender_address = strdup(envfrom[0]); VERBOSE_FUNCLINE(5, ctx); if (!priv->sender_address) return smfis_tempfail; priv->sender_address_len = strlen(envfrom[0]); } priv->queue_id = smfi_getsymval(ctx, "i"); if (!priv->queue_id) priv->queue_id = noqueue_id; priv->macro_j = smfi_getsymval(ctx, "j"); if (priv->macro_j) priv->macro_j_len = strlen(priv->macro_j); if (MyHname) { priv->myhnamep = MyHname; priv->myhname_len = MyHname_len; } else if (use_j_macro) { priv->myhnamep = priv->macro_j; priv->myhname_len = priv->macro_j_len; } if (!priv->myhnamep) { gethostname(priv->myhname, sizeof(priv->myhname)); if(!(priv->myhname_len = calm_getfqdn(ctx, priv->myhname, sizeof(priv->myhname)))) priv->myhname_len = strlen(priv->myhname); priv->myhnamep = priv->myhname; } VERBOSE_FUNCLINE(5, ctx); if (callback_sender) { return(mlfi_frrc(ctx, envfrom, 0)); } /* continue processing */ VERBOSE_FUNCLINE(5, ctx); return SMFIS_CONTINUE; } sfsistat mlfi_envrcpt(ctx, envrcpt) SMFICTX *ctx; char **envrcpt; { struct mlfiPriv *priv = MLFIPRIV; VERBOSE_FUNCLINE(5, ctx); if (!priv) return smfis_tempfail; priv->milter_start = time(NULL); return(mlfi_frrc(ctx, envrcpt,1)); } sfsistat mlfi_eom(ctx) SMFICTX *ctx; { struct mlfiPriv *priv = MLFIPRIV; if (priv == NULL) return SMFIS_CONTINUE; return mlfi_cleanup(ctx, true); } sfsistat mlfi_close(ctx) SMFICTX *ctx; { struct mlfiPriv *priv = MLFIPRIV; if (priv == NULL) return SMFIS_ACCEPT; return mlfi_cleanup(ctx, true); } sfsistat mlfi_abort(ctx) SMFICTX *ctx; { struct mlfiPriv *priv = MLFIPRIV; if (priv == NULL) return SMFIS_CONTINUE; return mlfi_cleanup(ctx, false); } sfsistat mlfi_cleanup(ctx, ok) SMFICTX *ctx; bool ok; { VERBOSE_FUNCLINE(5, ctx); sfsistat rstat = SMFIS_CONTINUE; struct mlfiPriv *priv = MLFIPRIV; if (priv == NULL) return rstat; free(priv->sender_address); calm_smtpshutdown(ctx, -1); free(priv); smfi_setpriv(ctx, NULL); VERBOSE_FUNCLINE(5, ctx); /* return status */ return rstat; } struct smfiDesc smfilter = { PROGNAME, /* filter name */ SMFI_VERSION, /* version code -- do not change */ 0, /* flags */ NULL, /* connection info filter */ NULL, /* SMTP HELO command filter */ mlfi_envfrom, /* envelope sender filter */ mlfi_envrcpt, /* envelope recipient filter */ NULL, /* header filter */ NULL, /* end of header */ NULL, /* body block filter */ NULL, /* end of message */ mlfi_abort, /* message aborted */ mlfi_close /* connection cleanup */ }; void usage(int exitval, char **argv) { fprintf(stderr, "Usage: %s [-vh?] [-DaAEmxXTRjsSlNu] [-M bestmxmap] [-b fallbackdsthost] [-B localfallback] [-d debuglvl] [-p sockpath] [-f envfrom] [-r envrcpt [-t testhost] [-H myhostname] [-C cachefile] [-c cachememsize] [-o timeoutspec] [-e expirescpec] [-I cachespec] [-i cachespec] [-g] [-G label] [-n hostnamemap]\n" , argv[0]); fprintf(stderr, "Options :\n" " -a : perform sendmail rewriting to obtain dsthost even if macro {rcpt_host} is set.\n" " -A : perform sendmail rewriting to obtain dsthost only if macro {rcpt_host} isnt set.\n" " -b fallbacksthost : perform verification against this host if it is unknown where else to.\n" " : This generally means that the recipient is considered local to the MTA.\n" " -B localfallback : Use this for dsthost ONLY if the recipient is considered local to the MTA.\n" " -c cachememesize : Turn on memory caching for verification results up to specificed size.\n" " -C cachefile : Turn on persistent verification results caching using cachefile.\n" " -d debuglvl : sets libmilter debug levels.\n" " -D : retry connecting to mxhost without the sendmail style final dot.\n" " -e expirespec : Cache expiration of positive/negative rcpt, pos/neg sender, pos/neg rcpt-sender.\n" " -E : Only use dsthost entries that look like sendmail mx supressed names.\n" " : Typically, using this suggests only verifying against mailertable entries.\n" " -f envfrom address : use this envelope from address while testing verification\n" " : during normal operation, use this envfrom instead of <>\n" " -g : log to syslog.\n" " -G label : log to syslog with this label.\n" " -H myhostname : use this hostname while connecting to smtp servers.\n" " -i cachespec : number/time/rcpts of smtp connection to cache, per thread caching.\n" " OR : -I will take precedence over -i.\n" " -I cachespec : number/time/rcpts of smtp connection to cache, cross thread caching.\n" " -j : use the j macro from sendmail for hostname (-H takes priority)\n" " -l : avoid callback<->callahead loops while verifying sender address.\n" " -L logfile : while being verbose, write output to this file instead of stderr.\n" " -m : perform sendmail map lookup to find mx destinations for dsthost.\n" " -M bestmxmap : perform sendmail map lookups using this map name.\n" " -n hostnamemap : perform sendmail map lookups for hostname in this map.\n" " -N : Never cause any email to be stopped -- this is for testing purposes.\n" " -o timeoutspec : timeouts. Specified as \"D:timecal;X:timeval\". Following are the timeout codes.\n" "\n" " Dns address lookup, dns mX lookup, sendmail Lookup, sendmail overall looKup, Connect,\n" " Overall connect, Send, eNd send, Read, End read, return froM milter callback\n" " Default timeouts:\n" " %s\n" "\n" " -p sockpath : sets libmilter socket path. This is required.\n" " -r envrcpt address : use this envelope rcpt address while testing verification.\n" " -R : do not tempfail email for temporary failurs that are from the smtp server.\n" " -s : verify the sender address on the rcpt server as well.\n" " -S : verify the sender address (callback).\n" " -t dsthost : use this host while testing verification with -f -r values.\n" " -T : do not tempfail email for temporary failurs that are not from the smtp server.\n" " -u : attempt to cleanup the remote reply.\n" " -v : Print program version.\n" " -V : Be verbose. More vebosity for each occurence of -V (compiled with -DDEBUG)\n" " -x : Do MX record dns lookups for dsthost (contrast with -m|-M)\n" " -X : Detect IP addresses as MX names, in violation of RFC's\n" " -h|-? : This message\n\n" , timeouts_spec); exit (exitval); } void print_version(char ** argv) { fprintf(stderr,"%s : callahead-milter version %s\n\n", argv[0], VERSION); usage(0,argv); } int main(argc, argv) int argc; char *argv[]; { bool setconn = false; int c; const char *args = "n:DgG:ui:I:o:Ee:c:C:aAmM:L:VNTRjH:Sslvb:B:d:p:f:r:t:x"; int retval; /* prepare timeouts*/ if (!calm_parse_timeouts(NULL, timeouts_spec, Calm_Timeouts)) { fprintf(stderr,"couldnt parse initial timeouts: %s\n", strerror(errno)); usage(EX_USAGE, argv); } /* Process command line options */ while ((c = getopt(argc, argv, args)) != -1) { switch (c) { case 'u': cleanup_reply = true; break; case 'c': cache_memory_size = atoi(optarg); break; case 'C': cache_disk_file = optarg; break; case 'e': cache_expire_spec = optarg; break; case 'E': only_mx_supressed = true; break; case 'd': smfi_setdbg(atoi(optarg)); break; case 'D': retry_without_dot = true; break; case 'p': if (optarg == NULL || *optarg == '\0') { (void) fprintf(stderr, "Illegal conn: %s\n", optarg); exit(EX_USAGE); } (void) smfi_setconn(optarg); setconn = true; break; case 'o': timeouts_spec = optarg; break; case 'b': fallbackdsthost = optarg; break; case 'B': lfallbackdsthost = optarg; break; case 'f': calm_envfrom = optarg; calm_envfrom_len = strlen(calm_envfrom); break; case 'G': syslog_label = optarg; /*fall-through*/ case 'g': use_syslog = true; openlog(syslog_label, LOG_PID, LOG_MAIL); break; case 'r': calm_envrcpt = optarg; calm_envrcpt_len = strlen(calm_envrcpt); break; case 't': { char * dsthosts[2]; char ** dsthost = dsthosts; char *p = NULL; int plen = 0; char *p1 = NULL; int p1len = 0; dsthost[0] = optarg; dsthost[1] = NULL; if (do_mx_lookups && (calm_getmxrr(NULL, optarg, &dsthost, NULL, &p1, &p1len, 0) < 1)) dsthost = dsthosts; retval = calm_smtprcpt(NULL, dsthost, calm_envfrom, calm_envrcpt, &p, &plen); if (p) free(p); if (p1) free(p1); } break; case 'v': print_version(argv); break; case 's': verify_sender = 1; break; case 'S': callback_sender = 1; break; case 'l': prevent_loop = 1; break; case 'H': MyHname = optarg; MyHname_len = strlen(optarg); break; case 'I': host_connection_caching = optarg; break; case 'i': pt_host_connection_caching = optarg; break; case 'j': use_j_macro = 1; break; case 'T': smfis_tempfail = SMFIS_CONTINUE; break; case 'R': smfis_remote_tempfail = SMFIS_CONTINUE; break; case 'n': hostname_sm_lookup = optarg; break; case 'N': never_stop_anything = 1; break; case 'V': verbosity++; break; case 'L': logfile = fopen(optarg,"a+"); if(!logfile) { fprintf(stderr, "opening logfile \"%s\" errno %d error %s\n", optarg, errno, strerror(errno)); usage(1,argv); } break; case 'a': rewrite_anyways = true; break; case 'A': rewrite_maybe = true; break; #if _FFR_MILTER_SM_MAP case 'M': bestmx_sm_map = optarg; /* fall through */ case 'm': bestmx_sm_map_lookup = true; /* if the below line does not compile, your libmilter is not suitably patched * * look for patch milter-rrres at >= version 14 * * See the milter-rrres patch http://www.jmaimon.com/sendmail */ smfilter.xxfi_flags |= SMFIF_SM_MAP; break; #else /* _FFR_MILTER_SM_MAP */ case 'm': case 'M': break; #endif /* _FFR_MILTER_SM_MAP */ case 'x': do_mx_lookups = true; break; case 'X': getmx_detect_ipa = true; break; default: usage(0,argv); break; } } #if _FFR_MILTER_REWRITE /* if the below line does not compile, your libmilter is not suitably patched * * look for patch milter-rrres at >= version 7 * * See the milter-rrres patch http://www.jmaimon.com/sendmail */ if (rewrite_anyways || rewrite_maybe) smfilter.xxfi_flags |= SMFIF_REWRITE; #endif /* _FFR_MILTER_REWRITE */ if (!calm_parse_timeouts(NULL, timeouts_spec, Calm_Timeouts)) usage(EX_USAGE, argv); if (!setconn) { fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); usage(EX_USAGE, argv); } if (smfi_register(smfilter) == MI_FAILURE) { fprintf(stderr, "smfi_register failed\n"); exit(EX_UNAVAILABLE); } return smfi_main(); }