diff -ru milter-greylist-3.0rc6.orig/conf_lex.l milter-greylist-3.0rc6/conf_lex.l
--- milter-greylist-3.0rc6.orig/conf_lex.l	Wed Sep 20 16:38:24 2006
+++ milter-greylist-3.0rc6/conf_lex.l	Sun Oct 29 02:00:06 2006
@@ -11,7 +11,8 @@
 qtext		"\""[!-~]{1,}"\""
 mailbox		{atext}|{qtext}
 domainname	[A-Za-z0-9._-]+
-email		{mailbox}"\@"{domainname}
+domainliteral	"["([0-9.]+|IPv6:[0-9A-Fa-f.:]+)"]"
+email		{mailbox}"\@"({domainname}|{domainliteral})
 comment		#.*$
 blank		[ \t]+
 addr		[Aa][Dd][Dd][Rr]:?
diff -ru milter-greylist-3.0rc6.orig/dump_lex.l milter-greylist-3.0rc6/dump_lex.l
--- milter-greylist-3.0rc6.orig/dump_lex.l	Tue Sep  5 07:05:59 2006
+++ milter-greylist-3.0rc6/dump_lex.l	Sun Oct 29 02:02:12 2006
@@ -10,8 +10,9 @@
 atext		[A-Za-z0-9!#$%&'*+/=?$^_`}{|~.-]{1,}
 qtext		"\""[!-~]{1,}"\""
 mailbox		{atext}|{qtext}
+domainliteral	"["([0-9.]+|IPv6:[0-9A-Fa-f.:]+)"]"
 domain		[A-Za-z0-9._-]{1,}
-email		"<"?{mailbox}"\@"?{domain}?">"?
+email		"<"?{mailbox}"\@"?({domain}|{domainliteral})?">"?
 void_email	"<>"
 comment		#.*$
 blank		[ \t]{1,}
diff -ru milter-greylist-3.0rc6.orig/milter-greylist.c milter-greylist-3.0rc6/milter-greylist.c
--- milter-greylist-3.0rc6.orig/milter-greylist.c	Fri Oct 27 06:01:08 2006
+++ milter-greylist-3.0rc6/milter-greylist.c	Sun Oct 29 02:57:25 2006
@@ -91,7 +91,7 @@
 #endif
 #include "macro.h"
 
-static char *strncpy_rmsp(char *, char *, size_t);
+static void cook_mail_address(char *, const char *, size_t, int);
 static char *gmtoffset(time_t *, char *, size_t);
 static void writepid(char *);
 static void log_and_report_greylisting(SMFICTX *, struct mlfi_priv *, char *);
@@ -178,7 +178,7 @@
 
 #if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
      defined(HAVE_SPF2_10) || defined(HAVE_SPF2)) 
-	strncpy_rmsp(priv->priv_helo, helostr, ADDRLEN);
+	strncpy(priv->priv_helo, helostr, ADDRLEN);
 	priv->priv_helo[ADDRLEN] = '\0';
 #endif
 
@@ -191,8 +191,7 @@
 	SMFICTX *ctx;
 	char **envfrom;
 {
-	char tmpfrom[ADDRLEN + 1];
-	char *idx;
+	char addrstr[IPADDRSTRLEN];
 	struct mlfi_priv *priv;
 	char *auth_authen;
 	char *verify;
@@ -200,28 +199,22 @@
 
 	priv = (struct mlfi_priv *) smfi_getpriv(ctx);
 
+	if (!iptostring(SA(&priv->priv_addr), priv->priv_addrlen, addrstr,
+	    sizeof(addrstr)))
+		return SMFIS_CONTINUE;
+
 	if ((priv->priv_queueid = smfi_getsymval(ctx, "{i}")) == NULL) {
 		mg_log(LOG_DEBUG, "smfi_getsymval failed for {i}");
 		priv->priv_queueid = "(unknown id)";
 	}
 
-	/*
-	 * Strip spaces from the source address
-	 */
-	strncpy_rmsp(tmpfrom, *envfrom, ADDRLEN);
-	tmpfrom[ADDRLEN] = '\0';
-
-	/* 
-	 * Strip anything before the last '=' in the
-	 * source address. This avoid problems with
-	 * mailing lists using a unique sender address
-	 * for each retry.
-	 */
-	if ((idx = rindex(tmpfrom, '=')) == NULL)
-		idx = tmpfrom;
-
-	strncpy(priv->priv_from, idx, ADDRLEN);
-	priv->priv_from[ADDRLEN] = '\0';
+	if (canonize_mail_address(priv->priv_from, *envfrom, ADDRLEN + 1) != 0) {
+		mg_log(LOG_INFO, 
+		    "%s: addr %s[%s] from %s: unintelligible sender address",
+		    priv->priv_queueid, priv->priv_hostname, addrstr, *envfrom);
+		smfi_setreply(ctx, "551", "5.7.1", "unintelligible sender address");
+		return SMFIS_REJECT;
+	}
 
 	/*
 	 * Reload the config file if it has been touched
@@ -269,7 +262,7 @@
 	 */
 	if ((conf.c_nospf == 0) && 
 	    (SPF_CHECK(SA(&priv->priv_addr), priv->priv_addrlen,
-	    priv->priv_helo, *envfrom) != EXF_NONE)) {
+	    priv->priv_helo, priv->priv_from) != EXF_NONE)) {
 		char ipstr[IPADDRSTRLEN];
 
 		if (iptostring(SA(&priv->priv_addr),
@@ -277,7 +270,7 @@
 
 			mg_log(LOG_DEBUG, 
 			    "Sender IP %s and address %s are SPF-compliant, "
-			    "bypassing greylist", ipstr, *envfrom);
+			    "bypassing greylist", ipstr, priv->priv_from);
 		}
 
 		priv->priv_elapsed = 0;
@@ -299,6 +292,8 @@
 	char *greylist;
 	char addrstr[IPADDRSTRLEN];
 	char rcpt[ADDRLEN + 1];
+	char cooked_from[ADDRLEN + 1];
+	char cooked_rcpt[ADDRLEN + 1];
 
 	priv = (struct mlfi_priv *) smfi_getpriv(ctx);
 
@@ -367,11 +362,15 @@
 	 */
 	sync_master_restart();
 
-	/*
-	 * Strip spaces from the recipient address
-	 */
-	strncpy_rmsp(rcpt, *envrcpt, ADDRLEN);
-	rcpt[ADDRLEN] = '\0';
+	if (canonize_mail_address(rcpt, *envrcpt, ADDRLEN + 1) != 0) {
+		mg_log(LOG_INFO, 
+		    "%s: addr %s[%s] from %s to %s: unintelligible recipient address",
+		    priv->priv_queueid, priv->priv_hostname, addrstr, 
+		    priv->priv_from, *envrcpt);
+		smfi_setreply(ctx, "551", "5.7.1", "unintelligible recipient address");
+		return SMFIS_REJECT;
+	}
+
 
 	/*
 	 * Check the ACL
@@ -439,8 +438,10 @@
 	 * is in the greylist and if it ca now be accepted. If it is not
 	 * in the greylist, it will be added.
 	 */
+	cook_mail_address(cooked_from, priv->priv_from, ADDRLEN + 1, 1);
+	cook_mail_address(cooked_rcpt, rcpt, ADDRLEN + 1, 0);
 	if (pending_check(SA(&priv->priv_addr), priv->priv_addrlen,
-	    priv->priv_from, rcpt, &remaining, &priv->priv_elapsed,
+	    cooked_from, cooked_rcpt, &remaining, &priv->priv_elapsed,
 	    priv->priv_queueid, priv->priv_delay, priv->priv_autowhite) != 0)
 		return SMFIS_CONTINUE;
 
@@ -466,7 +467,7 @@
 	/*
 	 * Log temporary failure and report to the client.
 	 */
-	log_and_report_greylisting(ctx, priv, *envrcpt);
+	log_and_report_greylisting(ctx, priv, rcpt);
 	return SMFIS_TEMPFAIL;
 }
 
@@ -1065,25 +1066,238 @@
 	return;
 }
 
+/* Skip RFC 2822 CFWS (comment and white space). If ill-formed, returns NULL. */
 static char *
-strncpy_rmsp(dst, src, len)
-	char *dst;
-	char *src;
-	size_t len;
+skip_rfc2822_cfws(start)
+	const char *start;
 {
-	unsigned int i;
+	int level = 0;
+	const char *p;
 
-	for (i = 0; src[i] && (i < len); i++) {
-		if (isgraph((int)src[i]))
-			dst[i] = src[i];
-		else
-			dst[i] = '_';
+	for (p = start; *p; ++p) {
+		int ch = (int)(unsigned char)*p;
+
+		if (!isascii(ch)) {
+			return NULL;
+		} else if (ch == '(') {
+			++level;
+		} else if (level == 0) {
+			if (strchr(" \t\n\r", ch) == NULL)
+				return (char *)p;
+		} else if (ch == ')') {
+			--level;
+		} else if (ch == '\\') {
+			++p;
+			if (*p == '\0')
+				return NULL;
+		}
 	}
+	return level ? NULL : (char *)p;
+}
 
-	if (i < len)
-		dst[i] = '\0';
+/* 
+ * Removes redundant whitespaces and comments from an e-mail addresss.
+ * Permits RFC 2822 angle-addr and some loose representations.
+ */
+int
+canonize_mail_address(dst, src, len)
+	char *dst;
+	const char *src;
+	size_t len;
+{
+	const char *sp = src;
+	char *dp = dst, *dstend = dst + len - 2; /* for the last ">\0" */
+	int angle = 0;
+
+	if (len <= 3)
+		return -1;
+	*dp++ = '<';
+	if (*sp == '<') {
+		angle = 1;
+		sp = skip_rfc2822_cfws(++sp);
+		if (sp == NULL)
+			return -1;
+		if (*sp == '>') /* otherwise local-part must not be empty */
+			goto ok;
+	}
+	/* Check local-part */
+	if (*sp == '"') {
+		/* local-part is a quoted-text */
+		if (dp == dstend)
+			return -1;
+		*dp++ = *sp++;
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+			if (ch == '\0' || !isascii(ch))
+				return -1;
+			if (ch == '\n' || ch == '\r')
+				continue; /* strip CRLF */
+			if (ch == '\\') {
+				if (sp[1] != '"' && sp[1] != '\\' )
+					continue; /* strip redundant '\\' */
+				if (dp >= dstend - 1)
+					return -1;
+				*dp++ = '\\';
+				*dp++ = sp[1];
+				++sp;
+			} else {
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+				if (ch == '"')
+					break;
+			}
+		}
+		++sp; /* the last '"' */
+	} else {
+		/* local-part is a dot-atom */
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+			if (ch == '\0')
+				break;
+                        else if (!isalnum(ch) && strchr(".!#$%&'*+-/=?^_`{|}~", ch) == NULL)
+				break;
+			if (dp == dstend)
+				return -1;
+			*dp++ = (char)ch;
+		}
+		if (dp == dst + 1)
+			return -1; /* empty local-part */
+	}
+	/* End of local-part */
+	if (angle) {
+		sp = skip_rfc2822_cfws(sp);
+		if (sp == NULL)
+			return -1;
+		if (*sp == '>')
+			goto ok; /* no domain */
+	} else {
+		/* disallow CFWS here */
+		if (*sp == '\0')
+			goto ok; /* no domain */
+	}
+	if (*sp != '@')
+		return -1;
+	++sp;
+	if (dp == dstend)
+		return -1;
+	*dp++ = '@';
+	if (angle) {
+		sp = skip_rfc2822_cfws(sp);
+		if (sp == NULL)
+			return -1;
+	}
+	/* Check domain */
+	if (*sp == '[') {
+		/* domain-literal */
+		/* While RFC 2822 allows various contents, we only allow IPv4 and
+		 * IPv6 addresses */
+		/* TODO: more strict sanity check */
+                if (dp == dstend)
+                        return -1;
+                *dp++ = *sp++; /* the first '[' */
+		if (strncmp(sp, "IPv6:", 5) == 0) {
+			if (dp >= dstend - 5)
+				return -1;
+			strcpy(dp, "IPv6:");
+                        sp += 5;
+			dp += 5;
+			for (;; ++sp) {
+				int ch = (int)(unsigned char)*sp;
+				if (ch == ']')
+					break;
+				else if (!isxdigit(ch) && ch != ':' && ch != '.')
+					return -1;
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+			}
+		} else {
+			for (;; ++sp) {
+				int ch = (int)(unsigned char)*sp;
+				if (ch == ']')
+					break;
+				else if (!isdigit(ch) && ch != '.')
+					return -1;
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+			}
+		}
+                if (dp == dstend)
+                        return -1;
+                *dp++ = *sp++; /* the last ']' */
+	} else {
+		/* Internet domain name */
+		/* While RFC 2822 allows wider range of characters, we allow
+		 * only [0-9A-Za-z._-] */
+		char *dst_domain = dp;
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+			if (ch == '\0')
+				break;
+			else if (!isalnum(ch) && strchr("._-", ch) == NULL) /* permit '_' */
+				break;
+			if (dp == dstend)
+				return -1;
+			*dp++ = (char)ch;
+		}
+		if (dp == dst_domain)
+			return -1; /* empty domain */
+	}
+	/* End of domain */
+	if (angle) {
+		sp = skip_rfc2822_cfws(sp);
+		if (sp == NULL)
+			return -1;
+		if (*sp == '>' && sp[1] == '\0')
+			goto ok;
+	} else {
+		/* disallow CFWS here */
+		if (*sp == '\0')
+			goto ok;
+	}
+	/* trailing garbage */
+	return -1;
+ok:
+	*dp++ = '>';
+	*dp++ = '\0';
+	return 0;
+}
 
-	return dst;
+void
+cook_mail_address(dst, src, len, mlhack)
+	char *dst;
+	const char *src;
+	size_t len;
+	int mlhack;
+{
+	const char *sp;
+	char *dp = dst, *dstend = dst + len - 1;
+
+	if (len <= 3 || strlen(src) <= 1)
+		abort();
+	/* 
+	 * Strip anything before the last '=' in the
+	 * source address. This avoid problems with
+	 * mailing lists using a unique sender address
+	 * for each retry.
+	 *
+	 * In this case they are represented as =a@b.com>.
+	 */
+	if (!mlhack || src[1] == '"' || (sp = strrchr(src + 1, '=')) == NULL)
+		sp = src;
+	for (;; ++sp) {
+		int ch = (int)(unsigned char)*sp;
+		if (ch == '\0')
+			break;
+		if (dp == dstend)
+			break;
+		*dp++ = (ch == ' ' || !isprint(ch)) ? (char)'_' : (char)ch;
+		if (ch == '>')
+			break;
+	}
+	*dp++ = '\0';
 }
 
 int
diff -ru milter-greylist-3.0rc6.orig/milter-greylist.h milter-greylist-3.0rc6/milter-greylist.h
--- milter-greylist-3.0rc6.orig/milter-greylist.h	Fri Oct  6 05:05:17 2006
+++ milter-greylist-3.0rc6/milter-greylist.h	Sun Oct 29 01:13:01 2006
@@ -164,6 +164,8 @@
 void final_dump(void);
 int main(int, char **);
 void mg_log(int, char *, ...);
+int canonize_mail_address(char *, const char *, size_t);
+int is_canon_mail_address(const char *);
 
 #ifdef HAVE_STRLCAT
 /* #include <string.h> */
diff -ru milter-greylist-3.0rc6.orig/sync.c milter-greylist-3.0rc6/sync.c
--- milter-greylist-3.0rc6.orig/sync.c	Wed Oct 11 05:28:15 2006
+++ milter-greylist-3.0rc6/sync.c	Sun Oct 29 02:37:07 2006
@@ -44,6 +44,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
+#include <ctype.h>
 #include <strings.h>
 #include <pthread.h>
 #include <syslog.h>
@@ -896,6 +897,148 @@
 	return;
 }
 
+/* Check if we received a well-cooked address and add angles */
+static int
+mail_address_peer2me(dst, src, len, mlhack)
+	char *dst;
+	const char *src;
+	size_t len;
+	int mlhack;
+{
+	const char *sp = src;
+	char *dp = dst, *dstend = dst + len - 2;
+
+	if (len <= 3)
+		return -1;
+	if (strcmp(sp, "<>") == 0) {
+		strcpy(dp, "<>");
+		return 0;
+	}
+	if ((mlhack && *sp == '=') || *sp == '<')
+		*dp++ = *sp++;
+	else
+		*dp++ = '<';
+	/* Check local-part (a bit different from canonize_mail_address) */
+	if (*sp == '"') {
+		/* local-part is a quoted-text */
+		if (dp == dstend)
+			return -1;
+		*dp++ = *sp++;
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+
+			if (ch == '\0' || !isgraph(ch))
+				return -1;
+			if (ch == '\\') {
+				if (sp[1] != '"' && sp[1] != '\\' )
+					continue; /* strip redundant '\\' */
+				if (dp >= dstend - 1)
+					return -1;
+				*dp++ = '\\';
+				*dp++ = sp[1];
+				++sp;
+			} else {
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+				if (ch == '"')
+					break;
+			}
+		}
+		++sp; /* the last '"' */
+	} else {
+		/* local-part is a dot-atom */
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+			if (ch == '\0')
+				break;
+			else if (mlhack && ch == '=')
+				return -1;
+                        else if (!isalnum(ch) && strchr(".!#$%&'*+-/=?^_`{|}~", ch) == NULL)
+				break;
+			if (dp == dstend)
+				return -1;
+			*dp++ = (char)ch;
+		}
+		if (dp == dst + 1)
+			return -1; /* empty local-part */
+	}
+	/* End of local-part */
+	if (*sp == '\0' || *sp == '>')
+		goto ok; /* no domain */
+	if (*sp != '@')
+		return -1;
+	++sp;
+	if (dp == dstend)
+		return -1;
+	*dp++ = '@';
+	/* Check domain (same as canonize_mail_address) */
+	if (*sp == '[') {
+		/* domain-literal */
+		/* While RFC 2822 allows various contents, we only allow IPv4 and
+		 * IPv6 addresses */
+		/* TODO: more strict sanity check */
+                if (dp == dstend)
+                        return -1;
+                *dp++ = *sp++; /* the first '[' */
+		if (strncmp(sp, "IPv6:", 5) == 0) {
+			if (dp >= dstend - 5)
+				return -1;
+			strcpy(dp, "IPv6:");
+                        sp += 5;
+			dp += 5;
+			for (;; ++sp) {
+				int ch = (int)(unsigned char)*sp;
+				if (ch == ']')
+					break;
+				else if (!isxdigit(ch) && ch != ':' && ch != '.')
+					return -1;
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+			}
+		} else {
+			for (;; ++sp) {
+				int ch = (int)(unsigned char)*sp;
+				if (ch == ']')
+					break;
+				else if (!isdigit(ch) && ch != '.')
+					return -1;
+				if (dp == dstend)
+					return -1;
+				*dp++ = (char)ch;
+			}
+		}
+                if (dp == dstend)
+                        return -1;
+                *dp++ = *sp++; /* the last ']' */
+	} else {
+		/* Internet domain name */
+		/* While RFC 2822 allows wider range of characters, we allow
+		 * only [0-9A-Za-z._-] */
+		char *dst_domain = dp;
+		for (;; ++sp) {
+			int ch = (int)(unsigned char)*sp;
+			if (ch == '\0')
+				break;
+			else if (!isalnum(ch) && strchr("._-", ch) == NULL) /* permit '_' */
+				break;
+			if (dp == dstend)
+				return -1;
+			*dp++ = (char)ch;
+		}
+		if (dp == dst_domain)
+			return -1; /* empty domain */
+	}
+	/* End of domain */
+	if (*sp == '\0' || (*sp == '>' && sp[1] == '\0'))
+		goto ok;
+ok:
+	*dp++ = '>';
+	*dp++ = '\0';
+	return 0;
+}
+
 void
 sync_server(arg) 
 	void *arg;
@@ -907,6 +1050,8 @@
 	char *addrstr;
 	char *from;
 	char *rcpt;
+	char from2[ADDRLEN + 1];
+	char rcpt2[ADDRLEN + 1];
 	char *datestr;
 	char *awstr;
 	char *cookie;
@@ -1026,6 +1171,12 @@
 			continue;
 		}
 
+		if (mail_address_peer2me(from2, from, ADDRLEN + 1, 1) != 0) {
+			fprintf(stream, "103 Bad mail address\n");
+			fflush(stream);
+			continue;
+		}
+
 		/*
 		 * get { "rcpt" email_address }
 		 */
@@ -1048,6 +1199,12 @@
 			continue;
 		}
 
+		if (mail_address_peer2me(rcpt2, rcpt, ADDRLEN + 1, 0) != 0) {
+			fprintf(stream, "103 Bad mail address\n");
+			fflush(stream);
+			continue;
+		}
+
 		/*
 		 * get { "date" valid_date }
 		 */
@@ -1115,13 +1272,13 @@
 		if (action == PS_CREATE) {
 			PENDING_WRLOCK;
 			/* delay = -1 means unused: we supply the date */
-			pending_get(SA(&addr), addrlen, from, rcpt, date);
+			pending_get(SA(&addr), addrlen, from2, rcpt2, date);
 			PENDING_UNLOCK;
 		}
 		if (action == PS_DELETE) {
-			pending_del(SA(&addr), addrlen, from, rcpt, date);
-			autowhite_add(SA(&addr), addrlen, from, 
-			    rcpt, &aw, "(mxsync)");
+			pending_del(SA(&addr), addrlen, from2, rcpt2, date);
+			autowhite_add(SA(&addr), addrlen, from2, 
+			    rcpt2, &aw, "(mxsync)");
 		}
 		if (action == PS_FLUSH) {
 			pending_del_addr(SA(&addr), addrlen, NULL, 0);
