//
//
-// $Id: spamass-milter.cpp,v 1.90 2006/03/23 21:41:36 dnelson Exp $
+// $Id: spamass-milter.cpp,v 1.100 2014/08/15 02:46:50 kovert Exp $
//
// SpamAss-Milter
// - a rather trivial SpamAssassin Sendmail Milter plugin
#include "subst_poll.h"
#endif
#include <errno.h>
+#include <netdb.h>
// C++ includes
#include <cstdio>
// }}}
-static const char Id[] = "$Id: spamass-milter.cpp,v 1.90 2006/03/23 21:41:36 dnelson Exp $";
+static const char Id[] = "$Id: spamass-milter.cpp,v 1.100 2014/08/15 02:46:50 kovert Exp $";
+
+static char FilterName[] = "SpamAssassin";
struct smfiDesc smfilter =
{
- "SpamAssassin", // filter name
+ FilterName, // filter name
SMFI_VERSION, // version code -- leave untouched
SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY, // flags
mlfi_connect, // info filter callback
bool flag_sniffuser = false;
char *defaultuser; /* Username to send to spamc if there are multiple recipients */
char *defaultdomain; /* Domain to append if incoming address has none */
+char *path_to_sendmail = (char *) SENDMAIL;
char *spamdhost;
+char *rejecttext = NULL; /* If we reject a mail, then use this text */
+char *rejectcode = NULL; /* If we reject a mail, then use code */
struct networklist ignorenets;
int spamc_argc;
char **spamc_argv;
char *spambucket;
bool flag_full_email = false; /* pass full email address to spamc */
bool flag_expand = false; /* alias/virtusertable expansion */
-bool ignore_authenticated_senders = false;
bool warnedmacro = false; /* have we logged that we couldn't fetch a macro? */
-
-#if defined(__FreeBSD__) /* popen bug - see PR bin/50770 */
-static pthread_mutex_t popen_mutex = PTHREAD_MUTEX_INITIALIZER;
-#endif
+bool auth = false; /* don't scan authenticated users */
// {{{ main()
main(int argc, char* argv[])
{
int c, err = 0;
- const char *args = "fd:mMp:P:r:u:D:i:Ib:B:e:x";
+ const char *args = "afd:mMp:P:r:u:D:i:b:B:e:xS:R:C:";
char *sock = NULL;
bool dofork = false;
char *pidfilename = NULL;
std::set_terminate (__gnu_cxx::__verbose_terminate_handler);
#endif
- openlog("spamass-milter", LOG_PID, LOG_MAIL);
-
- /* Process command line options */
- while ((c = getopt(argc, argv, args)) != -1) {
- switch (c) {
- case 'f':
- dofork = true;
- break;
- case 'd':
- parse_debuglevel(optarg);
- break;
- case 'D':
- spamdhost = strdup(optarg);
- break;
- case 'e':
- flag_full_email = true;
- defaultdomain = strdup(optarg);
- break;
- case 'i':
- debug(D_MISC, "Parsing ignore list");
- parse_networklist(optarg, &ignorenets);
- break;
- case 'I':
- debug(D_MISC, "Ignore authenticated senders");
- ignore_authenticated_senders = true;
- break;
- case 'm':
- dontmodifyspam = true;
- smfilter.xxfi_flags &= ~SMFIF_CHGBODY;
- break;
- case 'M':
- dontmodify = true;
- dontmodifyspam = true;
- smfilter.xxfi_flags &= ~(SMFIF_CHGBODY|SMFIF_CHGHDRS);
- break;
- case 'p':
- sock = strdup(optarg);
- break;
- case 'P':
- pidfilename = strdup(optarg);
- break;
- case 'r':
- flag_reject = true;
- reject_score = atoi(optarg);
- break;
- case 'u':
- flag_sniffuser = true;
- defaultuser = strdup(optarg);
- break;
- case 'b':
- case 'B':
- if (flag_bucket)
- {
- fprintf(stderr, "Can only have one -b or -B flag\n");
- err = 1;
- break;
- }
- flag_bucket = true;
- if (c == 'b')
- {
- flag_bucket_only = true;
- smfilter.xxfi_flags |= SMFIF_DELRCPT; // May delete recipients
- }
- // we will modify the recipient list; if spamc returns
- // indicating that this mail is spam, the message will be
- // sent to <optarg>@localhost
- smfilter.xxfi_flags |= SMFIF_ADDRCPT; // May add recipients
- // XXX we should probably verify that optarg is vaguely sane
- spambucket = strdup( optarg );
- break;
- case 'x':
- flag_expand = true;
- break;
- case '?':
- err = 1;
- break;
- }
- }
+ openlog("spamass-milter", LOG_PID, LOG_MAIL);
+
+
+ /* Process command line options */
+ while ((c = getopt(argc, argv, args)) != -1) {
+ switch (c) {
+ case 'a':
+ auth = true;
+ break;
+ case 'f':
+ dofork = true;
+ break;
+ case 'd':
+ parse_debuglevel(optarg);
+ break;
+ case 'D':
+ spamdhost = strdup(optarg);
+ break;
+ case 'e':
+ flag_full_email = true;
+ defaultdomain = strdup(optarg);
+ break;
+ case 'i':
+ debug(D_MISC, "Parsing ignore list");
+ parse_networklist(optarg, &ignorenets);
+ break;
+ case 'm':
+ dontmodifyspam = true;
+ smfilter.xxfi_flags &= ~SMFIF_CHGBODY;
+ break;
+ case 'M':
+ dontmodify = true;
+ dontmodifyspam = true;
+ smfilter.xxfi_flags &= ~(SMFIF_CHGBODY|SMFIF_CHGHDRS);
+ break;
+ case 'p':
+ sock = strdup(optarg);
+ break;
+ case 'P':
+ pidfilename = strdup(optarg);
+ break;
+ case 'r':
+ flag_reject = true;
+ reject_score = atoi(optarg);
+ break;
+ case 'S':
+ path_to_sendmail = strdup(optarg);
+ break;
+ case 'C':
+ rejectcode = strdup (optarg);
+ break;
+ case 'R':
+ rejecttext = strdup (optarg);
+ break;
+ case 'u':
+ flag_sniffuser = true;
+ defaultuser = strdup(optarg);
+ break;
+ case 'b':
+ case 'B':
+ if (flag_bucket)
+ {
+ fprintf(stderr, "Can only have one -b or -B flag\n");
+ err = 1;
+ break;
+ }
+ flag_bucket = true;
+ if (c == 'b')
+ {
+ flag_bucket_only = true;
+ smfilter.xxfi_flags |= SMFIF_DELRCPT; // May delete recipients
+ }
+ // we will modify the recipient list; if spamc returns
+ // indicating that this mail is spam, the message will be
+ // sent to <optarg>@localhost
+ smfilter.xxfi_flags |= SMFIF_ADDRCPT; // May add recipients
+ // XXX we should probably verify that optarg is vaguely sane
+ spambucket = strdup( optarg );
+ break;
+ case 'x':
+ flag_expand = true;
+ break;
+ case '?':
+ err = 1;
+ break;
+ }
+ }
if (flag_full_email && !flag_sniffuser)
{
cout << PACKAGE_NAME << " - Version " << PACKAGE_VERSION << endl;
cout << "SpamAssassin Sendmail Milter Plugin" << endl;
cout << "Usage: spamass-milter -p socket [-b|-B bucket] [-d xx[,yy...]] [-D host]" << endl;
- cout << " [-e defaultdomain] [-f] [-i networks] [-I] [-m] [-M]" << endl;
- cout << " [-P pidfile] [-r nn] [-u defaultuser] [-x]" << endl;
+ cout << " [-e defaultdomain] [-f] [-i networks] [-m] [-M]" << endl;
+ cout << " [-P pidfile] [-r nn] [-u defaultuser] [-x] [-a]" << endl;
+ cout << " [-C rejectcode] [ -R rejectmsg ]" << endl;
cout << " [-- spamc args ]" << endl;
cout << " -p socket: path to create socket" << endl;
cout << " -b bucket: redirect spam to this mail address. The orignal" << endl;
cout << " recipient(s) will not receive anything." << endl;
cout << " -B bucket: add this mail address as a BCC recipient of spam." << endl;
+ cout << " -C RejectCode: using this Reject Code." << endl;
cout << " -d xx[,yy ...]: set debug flags. Logs to syslog" << endl;
cout << " -D host: connect to spamd at remote host (deprecated)" << endl;
cout << " -e defaultdomain: pass full email address to spamc instead of just\n"
cout << " -f: fork into background" << endl;
cout << " -i: skip (ignore) checks from these IPs or netblocks" << endl;
cout << " example: -i 192.168.12.5,10.0.0.0/8,172.16.0.0/255.255.0.0" << endl;
- cout << " -I: skip (ignore) checks if sender is authenticated" << endl;
cout << " -m: don't modify body, Content-type: or Subject:" << endl;
cout << " -M: don't modify the message at all" << endl;
cout << " -P pidfile: Put processid in pidfile" << endl;
cout << " -r nn: reject messages with a score >= nn with an SMTP error.\n"
" use -1 to reject any messages tagged by SA." << endl;
+ cout << " -R RejectText: using this Reject Text." << endl;
cout << " -u defaultuser: pass the recipient's username to spamc.\n"
" Uses 'defaultuser' if there are multiple recipients." << endl;
cout << " -x: pass email address through alias and virtusertable expansion." << endl;
+ cout << " -a: don't scan messages over an authenticated connection." << endl;
cout << " -- spamc args: pass the remaining flags to spamc." << endl;
exit(EX_USAGE);
}
- if (pidfilename)
- {
- unlink(pidfilename);
- pidfile = fopen(pidfilename,"w");
- if (!pidfile)
- {
- fprintf(stderr, "Could not open pidfile: %s\n", strerror(errno));
- exit(1);
- }
- /* leave the file open through the fork, since we don't know our pid
- yet
- */
- }
+ /* Set standard reject text */
+ if (rejecttext == NULL) {
+ rejecttext = strdup ("Blocked by SpamAssassin");
+ }
+ if (rejectcode == NULL) {
+ rejectcode = strdup ("5.7.1");
+ }
+
+ if (pidfilename)
+ {
+ unlink(pidfilename);
+ pidfile = fopen(pidfilename,"w");
+ if (!pidfile)
+ {
+ fprintf(stderr, "Could not open pidfile: %s\n", strerror(errno));
+ exit(1);
+ }
+ /* leave the file open through the fork, since we don't know our pid
+ yet
+ */
+ }
- if (dofork == true)
- {
- if (daemon(0, 0) == -1)
- {
+ if (dofork == true)
+ {
+ if (daemon(0, 0) == -1)
+ {
fprintf(stderr, "daemon() failed: %s\n", strerror(errno));
exit(1);
- }
- }
-
- if (pidfile)
- {
- fprintf(pidfile, "%ld\n", (long)getpid());
- fclose(pidfile);
- pidfile = NULL;
- }
-
+ }
+ }
+
+ if (pidfile)
+ {
+ fprintf(pidfile, "%ld\n", (long)getpid());
+ fclose(pidfile);
+ pidfile = NULL;
+ }
+
{
struct stat junk;
if (stat(sock,&junk) == 0) unlink(sock);
// }}}
/* Update a header if SA changes it, or add it if it is new. */
-void update_or_insert(SpamAssassin* assassin, SMFICTX* ctx, string oldstring, t_setter setter, char *header )
+void update_or_insert(SpamAssassin* assassin, SMFICTX* ctx, string oldstring, t_setter setter, const char *header )
{
string::size_type eoh1 = assassin->d().find("\n\n");
string::size_type eoh2 = assassin->d().find("\n\r\n");
if (oldsize > 0)
{
debug(D_UORI, "u_or_i: changing");
- smfi_chgheader(ctx, header, 1, newstring.size() > 0 ?
+ smfi_chgheader(ctx, const_cast<char*>(header), 1, newstring.size() > 0 ?
cstr : NULL );
} else if (newstring.size() > 0)
{
debug(D_UORI, "u_or_i: inserting");
- smfi_addheader(ctx, header, cstr);
+ smfi_addheader(ctx, const_cast<char*>(header), cstr);
}
} else
{
if (do_reject)
{
debug(D_MISC, "Rejecting");
- smfi_setreply(ctx, "550", "5.7.1", "Blocked by SpamAssassin");
+ smfi_setreply(ctx, const_cast<char*>("550"), rejectcode, rejecttext);
if (flag_bucket)
send another copy. The milter API will not let you send the
message AND return a failure code to the sender, so this is
the only way to do it. */
-#if defined(__FreeBSD__)
- int rv;
-#endif
-
-#if defined(HAVE_ASPRINTF)
- char *buf;
-#else
- char buf[1024];
-#endif
- char *fmt="%s \"%s\"";
+ char *popen_argv[3];
FILE *p;
+ pid_t pid;
-#if defined(HAVE_ASPRINTF)
- asprintf(&buf, fmt, SENDMAIL, spambucket);
-#else
-#if defined(HAVE_SNPRINTF)
- snprintf(buf, sizeof(buf)-1, fmt, SENDMAIL, spambucket);
-#else
- /* XXX possible buffer overflow here */
- sprintf(buf, fmt, SENDMAIL, spambucket);
-#endif
-#endif
-
- debug(D_COPY, "calling %s", buf);
-#if defined(__FreeBSD__) /* popen bug - see PR bin/50770 */
- rv = pthread_mutex_lock(&popen_mutex);
- if (rv)
- {
- debug(D_ALWAYS, "Could not lock popen mutex: %s", strerror(rv));
- abort();
- }
-#endif
- p = popen(buf, "w");
+ popen_argv[0] = path_to_sendmail;
+ popen_argv[1] = spambucket;
+ popen_argv[2] = NULL;
+
+ debug(D_COPY, "calling %s %s", path_to_sendmail, spambucket);
+ p = popenv(popen_argv, "w", &pid);
if (!p)
{
- debug(D_COPY, "popen failed(%s). Will not send a copy to spambucket", strerror(errno));
+ debug(D_COPY, "popenv failed(%s). Will not send a copy to spambucket", strerror(errno));
} else
{
// Send message provided by SpamAssassin
fwrite(assassin->d().c_str(), assassin->d().size(), 1, p);
- pclose(p); p = NULL;
+ fclose(p); p = NULL;
+ waitpid(pid, NULL, 0);
}
-#if defined(__FreeBSD__)
- rv = pthread_mutex_unlock(&popen_mutex);
- if (rv)
- {
- debug(D_ALWAYS, "Could not unlock popen mutex: %s", strerror(rv));
- abort();
- }
-#endif
-#if defined(HAVE_ASPRINTF)
- free(buf);
-#endif
}
return SMFIS_REJECT;
}
// time. Note, this may generate multiple X-Spam-Orig-To
// headers, but that's okay.
while( !assassin->recipients.empty()) {
- if ( smfi_addheader( ctx, "X-Spam-Orig-To", (char *)assassin->recipients.front().c_str()) != MI_SUCCESS ) {
+ if ( smfi_addheader( ctx, const_cast<char *>("X-Spam-Orig-To"), (char *)assassin->recipients.front().c_str()) != MI_SUCCESS ) {
throw string( "Failed to save recipient" );
}
sctx = (struct context *)malloc(sizeof(*sctx));
if (!hostaddr)
{
+ static struct sockaddr_in localhost;
+
/* not a socket; probably a local user calling sendmail directly */
/* set to 127.0.0.1 */
- sctx->connect_ip.s_addr = htonl(INADDR_LOOPBACK);
+ strcpy(sctx->connect_ip, "127.0.0.1");
+ localhost.sin_family = AF_INET;
+ localhost.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ hostaddr = (struct sockaddr*) &localhost;
} else
{
- sctx->connect_ip = ((struct sockaddr_in *) hostaddr)->sin_addr;
+ getnameinfo(hostaddr, sizeof(struct sockaddr_in6),
+ sctx->connect_ip, 63, NULL, 0, NI_NUMERICHOST);
+ debug(D_FUNC, "Remote address: %s", sctx->connect_ip);
}
sctx->assassin = NULL;
sctx->helo = NULL;
}
/* debug(D_ALWAYS, "ZZZ set private context to %p", sctx); */
- if (ip_in_networklist(sctx->connect_ip, &ignorenets))
+ //debug(D_FUNC, "sctx->connect_ip: `%d'", sctx->connect_ip.sin_family);
+
+ if (ip_in_networklist(hostaddr, &ignorenets))
{
debug(D_NET, "%s is in our ignore list - accepting message",
- inet_ntoa(sctx->connect_ip));
+ sctx->connect_ip);
debug(D_FUNC, "mlfi_connect: exit ignore");
return SMFIS_ACCEPT;
}
{
SpamAssassin* assassin;
struct context *sctx = (struct context *)smfi_getpriv(ctx);
- char *queueid;
+ const char *queueid;
if (sctx == NULL)
{
}
/* debug(D_ALWAYS, "ZZZ got private context %p", sctx); */
- if (ignore_authenticated_senders)
- {
- char *auth_authen;
-
- auth_authen = smfi_getsymval(ctx, "{auth_authen}");
- debug(D_MISC, "auth_authen=%s", auth_authen ?: "<unauthenticated>");
+ if (auth) {
+ const char *auth_type = smfi_getsymval(ctx,
+ const_cast<char *>("{auth_type}"));
- if (auth_authen)
- {
- debug(D_MISC, "sender authenticated (%s) - accepting message",
- auth_authen);
- debug(D_FUNC, "mlfi_envfrom: exit ignore");
+ if (auth_type) {
+ debug(D_MISC, "auth_type=%s", auth_type);
return SMFIS_ACCEPT;
}
}
return SMFIS_TEMPFAIL;
};
- assassin->set_connectip(string(inet_ntoa(sctx->connect_ip)));
+ assassin->set_connectip(string(sctx->connect_ip));
// Store a pointer to the assassin object in our context struct
sctx->assassin = assassin;
// remember the MAIL FROM address
assassin->set_from(string(envfrom[0]));
- queueid=smfi_getsymval(ctx,"i");
+ queueid=smfi_getsymval(ctx, const_cast<char *>("i"));
if (!queueid)
{
queueid="unknown";
struct context *sctx = (struct context*)smfi_getpriv(ctx);
SpamAssassin* assassin = sctx->assassin;
FILE *p;
-#if defined(__FreeBSD__)
- int rv;
-#endif
debug(D_FUNC, "mlfi_envrcpt: enter");
/* open a pipe to sendmail so we can do address expansion */
char buf[1024];
- char *fmt="%s -bv \"%s\" 2>&1";
-
-#if defined(HAVE_SNPRINTF)
- snprintf(buf, sizeof(buf)-1, fmt, SENDMAIL, envrcpt[0]);
-#else
- /* XXX possible buffer overflow here */
- sprintf(buf, fmt, SENDMAIL, envrcpt[0]);
-#endif
-
- debug(D_RCPT, "calling %s", buf);
+ char *popen_argv[4];
+ pid_t pid;
+
+ popen_argv[0] = path_to_sendmail;
+ popen_argv[1] = (char *)"-bv";
+ popen_argv[2] = envrcpt[0];
+ popen_argv[3] = NULL;
-#if defined(__FreeBSD__) /* popen bug - see PR bin/50770 */
- rv = pthread_mutex_lock(&popen_mutex);
- if (rv)
- {
- debug(D_ALWAYS, "Could not lock popen mutex: %s", strerror(rv));
- abort();
- }
-#endif
+ debug(D_RCPT, "calling %s -bv %s", path_to_sendmail, envrcpt[0]);
- p = popen(buf, "r");
+ p = popenv(popen_argv, "r", &pid);
if (!p)
{
- debug(D_RCPT, "popen failed(%s). Will not expand aliases", strerror(errno));
+ debug(D_RCPT, "popenv failed(%s). Will not expand aliases", strerror(errno));
assassin->expandedrcpt.push_back(envrcpt[0]);
} else
{
assassin->expandedrcpt.push_back(p+7);
}
}
- pclose(p); p = NULL;
+ fclose(p); p = NULL;
+ waitpid(pid, NULL, 0);
}
-#if defined(__FreeBSD__)
- rv = pthread_mutex_unlock(&popen_mutex);
- if (rv)
- {
- debug(D_ALWAYS, "Could not unlock popen mutex: %s", strerror(rv));
- abort();
- }
-#endif
} else
{
assassin->expandedrcpt.push_back(envrcpt[0]);
char date[32];
/* RFC 822 date. */
- macro_b = smfi_getsymval(ctx, "b");
+ macro_b = smfi_getsymval(ctx, const_cast<char *>("b"));
if (!macro_b)
{
time_t tval;
}
/* queue ID */
- macro_i = smfi_getsymval(ctx, "i");
+ macro_i = smfi_getsymval(ctx, const_cast<char *>("i"));
if (!macro_i)
{
macro_i = "unknown";
}
/* FQDN of this site */
- macro_j = smfi_getsymval(ctx, "j");
+ macro_j = smfi_getsymval(ctx, const_cast<char *>("j"));
if (!macro_j)
{
macro_j = "localhost";
}
/* Protocol used to receive the message */
- macro_r = smfi_getsymval(ctx, "r");
+ macro_r = smfi_getsymval(ctx, const_cast<char *>("r"));
if (!macro_r)
{
macro_r = "SMTP";
fixed. Until that day, use the value remembered by
mlfi_helo()
*/
- macro_s = smfi_getsymval(ctx, "s");
+ macro_s = smfi_getsymval(ctx, const_cast<char *>("s"));
if (!macro_s)
macro_s = sctx->helo;
if (!macro_s)
macro_s = "nohelo";
/* Sendmail binary version */
- macro_v = smfi_getsymval(ctx, "v");
+ macro_v = smfi_getsymval(ctx, const_cast<char *>("v"));
if (!macro_v)
{
macro_v = "8.13.0";
}
/* Sendmail .cf version */
- macro_Z = smfi_getsymval(ctx, "Z");
+ macro_Z = smfi_getsymval(ctx, const_cast<char *>("Z"));
if (!macro_Z)
{
macro_Z = "8.13.0";
}
/* Validated sending site's address */
- macro__ = smfi_getsymval(ctx, "_");
+ macro__ = smfi_getsymval(ctx, const_cast<char *>("_"));
if (!macro__)
{
macro__ = "unknown";
assassin->output((string)
"Received: from "+macro_s+" ("+macro__+")\r\n\t"+
- "by "+macro_j+" ("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+"\r\n\t"+
+ "by "+macro_j+"("+macro_v+"/"+macro_Z+") with "+macro_r+" id "+macro_i+";\r\n\t"+
macro_b+"\r\n\t"+
"(envelope-from "+assassin->from()+")\r\n");
// XXX arbitrary 100-argument max
int argc = 0;
char** argv = (char**) malloc(100*sizeof(char*));
- argv[argc++] = SPAMC;
+ argv[argc++] = strdup(SPAMC);
if (flag_sniffuser)
{
- argv[argc++] = "-u";
+ argv[argc++] = strdup("-u");
if ( expandedrcpt.size() != 1 )
{
// More (or less?) than one recipient, so we pass the default
}
if (spamdhost)
{
- argv[argc++] = "-d";
+ argv[argc++] = strdup("-d");
argv[argc++] = spamdhost;
}
if (spamc_argc)
{
char *tnet = strsep(&token, "/");
char *tmask = token;
- struct in_addr net, mask;
+ struct in_addr net;
+ struct in6_addr net6;
if (list->num_nets % 10 == 0)
- list->nets = (struct net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10));
+ list->nets = (union net*)realloc(list->nets, sizeof(*list->nets) * (list->num_nets + 10));
- if (!inet_aton(tnet, &net))
+ if (inet_pton(AF_INET, tnet, &net))
{
- fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet);
- exit(1);
- }
+ struct in_addr mask;
+
+ if (tmask)
+ {
+ if (strchr(tmask, '.') == NULL)
+ {
+ /* CIDR */
+ unsigned int bits;
+ int ret;
+ ret = sscanf(tmask, "%u", &bits);
+ if (ret != 1 || bits > 32)
+ {
+ fprintf(stderr,"%s: bad CIDR value", tmask);
+ exit(1);
+ }
+ mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff);
+ } else if (!inet_pton(AF_INET6, tmask, &mask))
+ {
+ fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask);
+ exit(1);
+ }
+ } else
+ mask.s_addr = 0xffffffff;
+
+ {
+ char *snet = strdup(inet_ntoa(net));
+ debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask));
+ free(snet);
+ }
- if (tmask)
+ net.s_addr = net.s_addr & mask.s_addr;
+ list->nets[list->num_nets].net4.af = AF_INET;
+ list->nets[list->num_nets].net4.network = net;
+ list->nets[list->num_nets].net4.netmask = mask;
+ list->num_nets++;
+ } else if (inet_pton(AF_INET6, tnet, &net6))
{
- if (strchr(tmask, '.') == NULL)
+ int mask;
+
+ if (tmask)
{
- /* CIDR */
- unsigned int bits;
- int ret;
- ret = sscanf(tmask, "%u", &bits);
- if (ret != 1 || bits > 32)
+ if (sscanf(tmask, "%d", &mask) != 1 || mask > 128)
{
fprintf(stderr,"%s: bad CIDR value", tmask);
exit(1);
}
- mask.s_addr = htonl(~((1L << (32 - bits)) - 1) & 0xffffffff);
- } else if (!inet_aton(tmask, &mask))
- {
- fprintf(stderr, "Could not parse \"%s\" as a netmask\n", tmask);
- exit(1);
- }
+ } else
+ mask = 128;
+
+ list->nets[list->num_nets].net6.af = AF_INET6;
+ list->nets[list->num_nets].net6.network = net6;
+ list->nets[list->num_nets].net6.netmask = mask;
+ list->num_nets++;
} else
- mask.s_addr = 0xffffffff;
-
{
- char *snet = strdup(inet_ntoa(net));
- debug(D_MISC, "Adding %s/%s to network list", snet, inet_ntoa(mask));
- free(snet);
+ fprintf(stderr, "Could not parse \"%s\" as a network\n", tnet);
+ exit(1);
}
- net.s_addr = net.s_addr & mask.s_addr;
- list->nets[list->num_nets].network = net;
- list->nets[list->num_nets].netmask = mask;
- list->num_nets++;
}
free(string);
}
-int ip_in_networklist(struct in_addr ip, struct networklist *list)
+int ip_in_networklist(struct sockaddr *addr, struct networklist *list)
{
int i;
if (list->num_nets == 0)
return 0;
-
- debug(D_NET, "Checking %s against:", inet_ntoa(ip));
+
+ //debug(D_NET, "Checking %s against:", inet_ntoa(ip));
for (i = 0; i < list->num_nets; i++)
{
- debug(D_NET, "%s", inet_ntoa(list->nets[i].network));
- debug(D_NET, "/%s", inet_ntoa(list->nets[i].netmask));
- if ((ip.s_addr & list->nets[i].netmask.s_addr) == list->nets[i].network.s_addr)
- {
- debug(D_NET, "Hit!");
- return 1;
+ if (list->nets[i].net.af == AF_INET && addr->sa_family == AF_INET)
+ {
+ struct in_addr ip = ((struct sockaddr_in *)addr)->sin_addr;
+
+ debug(D_NET, "%s", inet_ntoa(list->nets[i].net4.network));
+ debug(D_NET, "/%s", inet_ntoa(list->nets[i].net4.netmask));
+ if ((ip.s_addr & list->nets[i].net4.netmask.s_addr) == list->nets[i].net4.network.s_addr)
+ {
+ debug(D_NET, "Hit!");
+ return 1;
+ }
+ } else if (list->nets[i].net.af == AF_INET6 && addr->sa_family == AF_INET6)
+ {
+ u_int8_t *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr;
+ int mask, j;
+
+ mask = list->nets[i].net6.netmask;
+ for (j = 0; j < 16 && mask > 0; j++, mask -= 8)
+ {
+ unsigned char bytemask;
+
+ bytemask = (mask < 8) ? ~((1L << (8 - mask)) - 1) : 0xff;
+
+ if ((ip[j] & bytemask) != (list->nets[i].net6.network.s6_addr[j] & bytemask))
+ break;
+ }
+
+ if (mask <= 0)
+ {
+ debug(D_NET, "Hit!");
+ return 1;
+ }
}
}
}
/* Log a message about missing milter macros, but only the first time */
-void warnmacro(char *macro, char *scope)
+void warnmacro(const char *macro, const char *scope)
{
if (warnedmacro)
return;
warnedmacro = true;
}
+/*
+ untrusted-argument-safe popen function - only supports "r" and "w" modes
+ for simplicity, and always reads stdout and stderr in "r" mode. Call
+ fclose to close the FILE, and waitpid to reap the child process (pid).
+*/
+FILE *popenv(char *const argv[], const char *type, pid_t *pid)
+{
+ FILE *iop;
+ int pdes[2];
+ int save_errno;
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ {
+ errno = EINVAL;
+ return (NULL);
+ }
+ if (pipe(pdes) < 0)
+ return (NULL);
+ switch (*pid = fork()) {
+
+ case -1: /* Error. */
+ save_errno = errno;
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ errno = save_errno;
+ return (NULL);
+ /* NOTREACHED */
+ case 0: /* Child. */
+ if (*type == 'r') {
+ /*
+ * The dup2() to STDIN_FILENO is repeated to avoid
+ * writing to pdes[1], which might corrupt the
+ * parent's copy. This isn't good enough in
+ * general, since the exit() is no return, so
+ * the compiler is free to corrupt all the local
+ * variables.
+ */
+ (void)close(pdes[0]);
+ (void)dup2(pdes[1], STDOUT_FILENO);
+ (void)dup2(pdes[1], STDERR_FILENO);
+ if (pdes[1] != STDOUT_FILENO && pdes[1] != STDERR_FILENO) {
+ (void)close(pdes[1]);
+ }
+ } else {
+ if (pdes[0] != STDIN_FILENO) {
+ (void)dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ }
+ (void)close(pdes[1]);
+ }
+ execv(argv[0], argv);
+ exit(127);
+ /* NOTREACHED */
+ }
+
+ /* Parent; assume fdopen can't fail. */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+
+ return (iop);
+}
+
// }}}
// vim6:ai:noexpandtab