--- sendmail-8.13.7.orig/sendmail/conf.c	2006-03-23 07:49:33.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/conf.c	2006-06-16 00:58:21.846601792 +0900
@@ -6393,6 +6393,11 @@
 /* Peter Philipp */
 	"_FFR_USE_SETLOGIN",
 #endif /* _FFR_USE_SETLOGIN */
+#if _FFR_SPPR_DSN_WITH_SPF
+	/* Suppress DSN with SPF */
+/* Akihiro Sagawa */
+	"_FFR_SPPR_DSN_WITH_SPF",
+#endif
 	NULL
 };
 
--- sendmail-8.13.7.orig/sendmail/deliver.c	2006-05-23 10:32:08.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/deliver.c	2006-06-16 00:58:21.851601032 +0900
@@ -1605,6 +1605,15 @@
 				ovr = false;
 			}
 		}
+		if (rcode == EX_OK && SPFRecieverHostMap != NULL)
+		{
+			/* clear DSN flag if SPF is fail or softfail */
+			if (!bitset(EF_DISCARD, e->e_flags))
+			{
+				rcode = suppress_dsn(to, e);
+				ovr = false;
+			}
+		}
 		if (rcode != EX_OK)
 		{
 			markfailure(e, to, NULL, rcode, ovr);
--- /dev/null	2006-03-13 11:37:01.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/suprdsn.c	2006-06-16 00:58:21.853600728 +0900
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2006 WIDE Antispam Working Group and Akihiro Sagawa.
+ * 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.
+ *
+ */
+
+#ifdef _FFR_DSN_SUPR_WITH_SPF
+#include "sendmail.h"
+
+/*
+ * Basis:
+ *	If SPF result is fail or softfail, it means that sender domain doesn't
+ *	permit to use the domain, the sender address is mostly forged.
+ *	Therefore a Delivery Status Notification (DSN) message (ie. delayed or
+ *	error message) for such an address is *nonsense*.
+ */
+
+static char     *spf_reciever_lookup __P((char *, int *));
+
+/*
+**  SUPPRESS_DSN -- suppress a DSN message by cleaning up its flags
+**
+**	This function checks sid-milter's Authentication-Results header to obtain
+**	SPF result. And verify reciever hostname by using SPFRecieverHostMap map-file.
+**	Please set up SPFRecieverHostMap map-file before using this function. :)
+**
+**	Parameters:
+**		to -- the person being sent to.
+**
+**	Returns:
+**		an exit status
+**
+**	Side Effects:
+**		disable DSN if SPF result is fail or softfail.
+**
+*/
+
+int suppress_dsn
+(to, e)
+	register ADDRESS *to;
+	register ENVELOPE *e;
+{
+	HDR *hdr;
+	if (tTd(70, 1))
+		sm_dprintf("suppress_dsn(to=%s, from=%s)\n",
+			to->q_paddr, e->e_from.q_paddr);
+
+	for (hdr = e->e_header; hdr != NULL; hdr = hdr->h_link)
+	{
+		if (bitset(H_DEFAULT, hdr->h_flags))
+			continue;
+		if (sm_strcasecmp(hdr->h_field,
+				  "Authentication-Results") == 0)
+		{
+			char *h;
+			char **pvp;
+			char pvpbuf[MAXLINE];
+			extern unsigned char MimeTokenTab[256];
+			int status = EX_OK;
+
+			h = hdr->h_value;
+			if (h == NULL ||
+			    (pvp = prescan(h, '\0', pvpbuf, sizeof pvpbuf,
+					   NULL, MimeTokenTab,
+					   false)) == NULL ||
+			    pvp[0] == NULL)
+			{
+				continue;
+			}
+			if (tTd(70, 2))
+				sm_dprintf("hostname = %s\n", pvp[0]);
+
+			sm_dprintf("start lookup\n");
+			if (spf_reciever_lookup(pvp[0], &status) == NULL)
+			{
+				sm_syslog(LOG_NOTICE, e->e_id,
+					  "SPF receiver host is not found "
+					  "(maybe map is not defined), "
+					  "ignore this header", pvp[0]);
+				continue;
+			}
+			
+			if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
+			{
+				sm_syslog(LOG_NOTICE, e->e_id,
+					  "SPF receiver host database is %s, "
+					  "skip this header",
+					  (status == EX_TEMPFAIL ?
+					   "tempfail" : "unavailable"));
+				continue;
+			}
+
+			while (*pvp != NULL)
+			{
+				if (pvp[0] != NULL && pvp[1] != NULL &&
+				    pvp[2] != NULL &&
+				    sm_strcasecmp(pvp[0], "spf") == 0 &&
+				    strcmp(pvp[1], "=") == 0 &&
+				    (sm_strcasecmp(pvp[2], "softfail") == 0 ||
+				     sm_strcasecmp(pvp[2], "fail") == 0))
+				{
+					sm_syslog(LOG_INFO, e->e_id,
+						  "clear DSN flags for "
+						  "suppressing (spf=%s)",
+						  pvp[2]);
+					if (tTd(70, 2))
+					  sm_dprintf("clear %s\n",
+						     to->q_paddr);
+					to->q_flags &= ~Q_PINGFLAGS;
+					to->q_flags |= QHASNOTIFY;
+					break;
+				}
+				pvp++;
+			}
+		}
+	}
+	return EX_OK;
+}
+
+static char *
+spf_reciever_lookup(name, pstat)
+	char *name;
+	int *pstat;
+{
+	static MAP *map = NULL;
+	if (map == NULL)
+	{
+		STAB *s = stab(SPFRecieverHostMap, ST_MAP, ST_FIND);
+
+		if (s == NULL)
+			return NULL;
+		map = &s->s_map;
+	}
+	DYNOPENMAP(map);
+
+	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
+}
+
+#endif /* _FFR_DSN_SUPR_WITH_SPF */
--- sendmail-8.13.7.orig/sendmail/sendmail.h	2006-05-23 10:32:07.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/sendmail.h	2006-06-16 00:58:21.869598296 +0900
@@ -2187,7 +2187,10 @@
 #if _FFR_SOFT_BOUNCE
 EXTERN bool	SoftBounce;	/* replace 5xy by 4xy (for testing) */
 #endif /* _FFR_SOFT_BOUNCE */
-EXTERN bool	volatile StopRequest;	/* stop sending output */
+#ifdef _FFR_DSN_SUPR_WITH_SPF
+EXTERN char     *SPFRecieverHostMap;    /* valid SPF reciever host map name */
+#endif /* _FFR_DSN_SUPR_WITH_SPF */
+EXTERN bool	volatile StopRequest;   /* stop sending output */
 EXTERN bool	SuprErrs;	/* set if we are suppressing errors */
 EXTERN bool	TryNullMXList;	/* if we are the best MX, try host directly */
 EXTERN bool	UseMSP;		/* mail submission: group writable queue ok? */
@@ -2581,6 +2584,9 @@
 extern void	stripbackslash __P((char *));
 extern bool	strreplnonprt __P((char *, int));
 extern bool	strcontainedin __P((bool, char *, char *));
+#if _FFR_DSN_SUPR_WITH_SPF
+extern int      suppress_dsn __P((ADDRESS *, ENVELOPE *));
+#endif /* _FFR_DSN_SUPR_WITH_SPF */
 extern int	switch_map_find __P((char *, char *[], short []));
 extern bool	transienterror __P((int));
 #if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
--- sendmail-8.13.7.orig/sendmail/Makefile.m4	2003-08-09 05:31:17.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/Makefile.m4	2006-06-16 00:58:21.876597232 +0900
@@ -5,7 +5,7 @@
 bldPRODUCT_START(`executable', `sendmail')
 define(`bldBIN_TYPE', `G')
 define(`bldINSTALL_DIR', `')
-define(`bldSOURCES', `main.c alias.c arpadate.c bf.c collect.c conf.c control.c convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c mci.c milter.c mime.c parseaddr.c queue.c ratectrl.c readcf.c recipient.c sasl.c savemail.c sfsasl.c shmticklib.c sm_resolve.c srvrsmtp.c stab.c stats.c sysexits.c timers.c tls.c trace.c udb.c usersmtp.c util.c version.c ')
+define(`bldSOURCES', `main.c alias.c arpadate.c bf.c collect.c conf.c control.c convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c macro.c map.c mci.c milter.c mime.c parseaddr.c queue.c ratectrl.c readcf.c recipient.c sasl.c savemail.c sfsasl.c shmticklib.c sm_resolve.c srvrsmtp.c stab.c stats.c suprdsn.c sysexits.c timers.c tls.c trace.c udb.c usersmtp.c util.c version.c ')
 PREPENDDEF(`confENVDEF', `confMAPDEF')
 bldPUSH_SMLIB(`sm')
 bldPUSH_SMLIB(`smutil')
--- sendmail-8.13.7.orig/sendmail/readcf.c	2006-03-03 04:17:09.000000000 +0900
+++ sm-8.13.7-dsn-supr/sendmail/readcf.c	2006-06-16 00:58:21.905592824 +0900
@@ -2211,7 +2211,10 @@
 # define O_CHK_Q_RUNNERS 0xde
 	{ "CheckQueueRunners",	O_CHK_Q_RUNNERS,	OI_NONE },
 #endif /* _FFR_QUEUE_RUN_PARANOIA */
-
+#if _FFR_DSN_SUPR_WITH_SPF
+#define O_SPF_RECIEVER_HOST_MAP         0xe9
+	{"SPFRecieverHostMap",          O_SPF_RECIEVER_HOST_MAP,        OI_NONE },
+#endif
 	{ NULL,				'\0',		OI_NONE	}
 };
 
@@ -3720,6 +3723,12 @@
 #endif /* REQUIRES_DIR_FSYNC */
 		break;
 
+#ifdef _FFR_DSN_SUPR_WITH_SPF	  
+	  case O_SPF_RECIEVER_HOST_MAP:
+	  SPFRecieverHostMap = newstr(val);
+	  break;
+
+#endif /* _FFR_DSN_SUPR_WITH_SPF */
 	  case O_CONNECTION_RATE_WINDOW_SIZE:
 		ConnectionRateWindowSize = convtime(val, 's');
 		break;
--- /dev/null	2006-03-13 11:37:01.000000000 +0900
+++ sm-8.13.7-dsn-supr/contrib/est_dsn.pl	2006-06-16 00:58:22.559493416 +0900
@@ -0,0 +1,111 @@
+#! /usr/bin/perl
+# -*- cperl -*-
+#
+# Copyright (C) 2006, Akihiro Sagawa and WIDE Antispam Working Group.
+# All rights reserved.
+#
+# This script estimates DSN messages generation, and shows
+# how many error mails are surppressed by SPF.
+#
+# Usage: est_dsn.pl /var/log/maillog
+#
+
+require 5.0006;
+our (%mqueue, %num);
+
+if (!@ARGV) {
+    if (-t STDIN) {
+	print "Usage: est_dsn.pl /var/log/maillog...\n";
+	exit 0;
+    }
+    @ARGV = ("-");
+}
+
+foreach my $file (@ARGV) {
+    process($file);
+}
+
+%desc = (
+	 "in"      => "Total incoming messages",
+	 "out"     => "Total delivered messages",
+	 "est_dsn" => "Estimated DSN messages",
+	 "act_dsn" => "Actual DSN messages",
+	 "supr_dsn"=> "Suppressed DSN messages",
+	 );
+
+foreach $k qw(in out est_dsn act_dsn supr_dsn) {
+    printf("%s: %d\n", $desc{$k}, $num{$k});
+}
+
+sub process {
+    my ($file) = shift;
+
+    if ($file =~ /\.gz$/) {
+	open (LOGF, "gzip -c $file |") or die "$!";
+    }
+    elsif ($file =~ /\.bz2$/) {
+	open (LOGF, "bzip2 -dc $file |") or die "$!";
+    }
+    else {
+	open (LOGF, "< $file");
+    }
+
+    while (<LOGF>) {
+	my ($mon, $day, $time, $host, $proc, $line) = (/^(...) (..) (..:..:..) (\S+) (\S+): (.+)$/);
+	next unless ($proc =~ /^(sm-mta|sendmail)/);
+
+	if ($line =~ s/^(\w+): //) {
+	    $qid = $1;
+	}
+	else {
+	    next;
+	}
+
+	if ($line =~ /^from=([^,]+),.*, daemon=MTA, relay=.*\[(.+)\]/) {
+	    my($sender, $clientip) = ($1, $2);
+	    $mqueue{$qid}{"client"} = $clientip;
+	    $mqueue{$qid}{"sender"} = $sender;
+	    $num{"in"}++;
+	    if ($clientip ne  "127.0.0.1" && $sender ne "<>") {
+		$mqueue{$qid}{"incoming"} = 1;
+	    }
+	}
+	elsif ($line =~ /^Milter insert .+ Authentication-Results: .+ spf=(.+)/
+	      && $mqueue{$qid}) {
+	    $mqueue{$qid}{"spf"} = $1;
+	    $num{"spf"}++;
+	}
+	elsif ($line =~ /^to=.+, dsn=(\d\.\d\.\d)/ && $mqueue{$qid}{"incoming"}) {
+	    my($dsn_status) = ($1);
+
+	    $mqueue{$qid}{"done"} = 1;
+
+	    my($pid) = ($proc =~ /\[(\d+)\]$/);
+	    if ($mqueue{$qid}{"clear_dsnflag:$pid"} && $dsn_status =~ /^5/) {
+		$num{"supr_dsn"}++;
+	    }
+
+	    if ($dsn_status =~ /^5/) {
+		$num{"est_dsn"}++;
+		$mqueue{$qid}{"est_dsn"} = 1;
+#		print "$qid\n";
+	    }
+	    $num{"out"}++ if ($dsn_status =~ /^[25]/);
+	}
+	elsif ($line =~ /^\w+: DSN: / && $mqueue{$qid}{"incoming"}) {
+	    $num{"act_dsn"}++;
+	    warn "can't detect that $qid produces DSN" unless ($mqueue{$qid}{"est_dsn"});
+#	    print "$qid\n";
+	}
+	elsif ($line =~ /^Suppress error response/) {
+	    # my old patch
+	    $num{"supr_dsn"}++;
+#	    print "$qid\n";
+	}
+	elsif ($line =~ /^clear DSN flags/) {
+	    my($pid) = ($proc =~ /\[(\d+)\]/);
+	    $mqueue{$qid}{"clear_dsnflag:$pid"} = 1;
+	}
+    }
+    close(LOGF);
+}
