Commit Diff


commit - c45570465634b62926ee7e196568b1c801c1d771
commit + 7e7046817041529117a0aa3b446e48c43a50619f
blob - c7af2ef576381355cc60edf461252b87b32a5771
blob + 33546d36fa9c8e45e9c30423b18e5cb446de5f59
--- main.c
+++ main.c
@@ -46,12 +46,15 @@
 
 /*
  * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
- * since they're more expressive.
+ * since they're more expressive with additional NONE and SOFTFAIL for
+ * RFC7208 (Sender Policy Framework (SPF)).
  */
 enum ar_state {
 	AR_UNKNOWN,
+	AR_NONE,
 	AR_PASS,
 	AR_FAIL,
+	AR_SOFTFAIL,
 	AR_POLICY,
 	AR_NEUTRAL,
 	AR_TEMPERROR,
@@ -104,28 +107,6 @@ struct ar_signature {
 	/* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
 #define AR_LOOKUP_LOOKUP_LIMIT 11
 	int nqueries;
-};
-
-/*
- * Use RFC7601 (Authentication-Results), anyway OpenSMTPD reports only pass or fail
- */
-enum iprev_state {
-	IPREV_NONE,
-	IPREV_PASS,
-	IPREV_FAIL
-};
-
-/*
- * Base on RFC7208
- */
-enum spf_state {
-	SPF_NONE,
-	SPF_NEUTRAL,
-	SPF_PASS,
-	SPF_FAIL,
-	SPF_SOFTFAIL,
-	SPF_TEMPERROR,
-	SPF_PERMERROR
 };
 
 /*
@@ -137,7 +118,7 @@ struct spf_query {
 	struct spf_record *spf;
 	struct event_asr *eva;
 	int type;
-	enum spf_state q;
+	enum ar_state q;
 	int include;
 	int exists;
 	char *domain;
@@ -147,7 +128,7 @@ struct spf_query {
 
 struct spf_record {
 	struct osmtpd_ctx *ctx;
-	enum spf_state state;
+	enum ar_state state;
 	const char *state_reason;
 	char *sender_local;
 	char *sender_domain;
@@ -189,7 +170,7 @@ struct message {
 
 struct session {
 	struct osmtpd_ctx *ctx;
-	enum iprev_state iprev;
+	enum ar_state iprev;
 	struct spf_record *spf_helo;
 	struct spf_record *spf_mailfrom;
 	struct sockaddr_storage src;
@@ -234,11 +215,10 @@ void ar_header_cat(struct osmtpd_ctx *, const char *);
 void ar_body_parse(struct message *, const char *);
 void ar_body_verify(struct ar_signature *);
 void ar_rr_resolve(struct asr_result *, void *);
-const char *iprev_state2str(enum iprev_state);
 char *spf_evaluate_domain(struct spf_record *, const char *);
 void spf_lookup_record(struct spf_record *, const char *, int,
-    enum spf_state, int, int);
-void spf_done(struct spf_record *, enum spf_state, const char *);
+    enum ar_state, int, int);
+void spf_done(struct spf_record *, enum ar_state, const char *);
 void spf_resolve(struct asr_result *, void *);
 void spf_resolve_txt(struct dns_rr *, struct spf_query *);
 void spf_resolve_mx(struct dns_rr *, struct spf_query *);
@@ -249,7 +229,6 @@ char* spf_parse_txt(const char *, size_t);
 int spf_check_cidr(struct spf_record *, struct in_addr *, int );
 int spf_check_cidr6(struct spf_record *, struct in6_addr *, int );
 int spf_execute_txt(struct spf_query *);
-const char *spf_state2str(enum spf_state);
 int spf_ar_cat(const char *, struct spf_record *, char **, size_t *, ssize_t *);
 void auth_message_verify(struct message *);
 void auth_ar_create(struct osmtpd_ctx *);
@@ -316,9 +295,9 @@ auth_connect(struct osmtpd_ctx *ctx, const char *rdns,
 	struct session *ses = ctx->local_session;
 
 	if (fcrdns == OSMTPD_STATUS_OK)
-		ses->iprev = IPREV_PASS;
+		ses->iprev = AR_PASS;
 	else
-		ses->iprev = IPREV_FAIL;
+		ses->iprev = AR_FAIL;
 
 	memcpy(&ses->src, src, sizeof(struct sockaddr_storage));
 
@@ -422,7 +401,7 @@ spf_record_new(struct osmtpd_ctx *ctx, const char *fro
 		osmtpd_err(1, "%s: malloc", __func__);
 
 	spf->ctx = ctx;
-	spf->state = SPF_NONE;
+	spf->state = AR_NONE;
 	spf->state_reason = NULL;
 	spf->nqueries = 0;
 	spf->running = 0;
@@ -454,7 +433,7 @@ spf_record_new(struct osmtpd_ctx *ctx, const char *fro
 		osmtpd_err(1, "%s: malloc", __func__);
 
 	spf_lookup_record(
-		spf, spf->sender_domain, T_TXT, SPF_PASS, 0, 0);
+		spf, spf->sender_domain, T_TXT, AR_PASS, 0, 0);
 
 	return spf;
 
@@ -495,7 +474,7 @@ auth_session_new(struct osmtpd_ctx *ctx)
 		osmtpd_err(1, "%s: malloc", __func__);
 
 	ses->ctx = ctx;
-	ses->iprev = IPREV_NONE;
+	ses->iprev = AR_NONE;
 
 	ses->spf_helo = NULL;
 	ses->spf_mailfrom = NULL;
@@ -1344,6 +1323,7 @@ ar_signature_state(struct ar_signature *sig, enum ar_s
 		break;
 	case AR_PASS:
 	case AR_FAIL:
+	case AR_SOFTFAIL:
 		osmtpd_errx(1, "Unexpected transition");
 	case AR_POLICY:
 		if (state == AR_PASS)
@@ -1377,6 +1357,8 @@ ar_state2str(enum ar_state state)
 		return "pass";
 	case AR_FAIL:
 		return "fail";
+	case AR_SOFTFAIL:
+		return "softfail";
 	case AR_POLICY:
 		return "policy";
 	case AR_NEUTRAL:
@@ -1838,20 +1820,6 @@ ar_body_verify(struct ar_signature *sig)
 
 	if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
 		ar_signature_state(sig, AR_FAIL, "bh mismatch");
-}
-
-const char *
-iprev_state2str(enum iprev_state state)
-{
-	switch (state)
-	{
-	case IPREV_NONE:
-		return "none";
-	case IPREV_PASS:
-		return "pass";
-	case IPREV_FAIL:
-		return "fail";
-	}
 }
 
 char *
@@ -1869,7 +1837,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 	int reverse;
 
 	if (domain == NULL || domain[0] == '\0') {
-		spf_done(spf, SPF_PERMERROR, "Empty domain");
+		spf_done(spf, AR_PERMERROR, "Empty domain");
 		return NULL;
 	}
 
@@ -1879,7 +1847,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 
 		if (domain[0] < 0x21 || domain[0] > 0x7e) {
 			spf_done(
-			    spf, SPF_PERMERROR, "Invalid character in domain-spec");
+			    spf, AR_PERMERROR, "Invalid character in domain-spec");
 			return NULL;
 		}
 
@@ -1899,7 +1867,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 		case '-':
 			if (i + 3 >= sizeof(spec)) {
 				spf_done(
-				    spf, SPF_PERMERROR, "domain-spec too large");
+				    spf, AR_PERMERROR, "domain-spec too large");
 				return NULL;
 			}
 
@@ -1920,64 +1888,204 @@ spf_evaluate_domain(struct spf_record *spf, const char
 				    "%s@%s", spf->sender_local,
 				    spf->sender_domain);
 				break;
-			case 'L':
-			case 'l':
-				mlen = strlcpy(macro,
-				    spf->sender_local, sizeof(macro));
-				break;
-			case 'O':
-			case 'o':
-				mlen = strlcpy(macro,
-				    spf->sender_domain,
-				    sizeof(macro));
-				break;
-			case 'D':
-			case 'd':
-				if (spf->nqueries < 1) {
-					spf_done(spf, SPF_PERMERROR,
-					    "no domain for d macro");
+			case '{':
+				domain++;
+				digits = -1;
+				reverse = 0;
+				delimiters[0] = '\0';
+
+				switch (domain[0]) {
+				case 'S':
+				case 's':
+					mlen = (size_t) snprintf(macro, sizeof(macro),
+					    "%s@%s", spf->sender_local,
+					    spf->sender_domain);
+					break;
+				case 'L':
+				case 'l':
+					mlen = strlcpy(macro,
+					    spf->sender_local, sizeof(macro));
+					break;
+				case 'O':
+				case 'o':
+					mlen = strlcpy(macro,
+					    spf->sender_domain,
+					    sizeof(macro));
+					break;
+				case 'D':
+				case 'd':
+					if (spf->nqueries < 1) {
+						spf_done(spf, AR_PERMERROR,
+						    "no domain for d macro");
+						return NULL;
+					}
+					mlen = strlcpy(macro,
+					    spf->queries[spf->nqueries - 1].domain,
+					    sizeof(macro));
+					break;
+				case 'I':
+				case 'i':
+					if (ses->src.ss_family == AF_INET) {
+						addr = (u_char *)(&((struct sockaddr_in *)
+						    &(ses->src))->sin_addr);
+						mlen = snprintf(macro, sizeof(macro),
+						    "%u.%u.%u.%u",
+						    (addr[0] & 0xff), (addr[1] & 0xff),
+						    (addr[2] & 0xff), (addr[3] & 0xff));
+					} else if (ses->src.ss_family == AF_INET6) {
+						addr = (u_char *)(&((struct sockaddr_in6 *)
+						    &(ses->src))->sin6_addr);
+						mlen = snprintf(macro, sizeof(macro),
+						    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
+						    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
+						    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
+						    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx",
+						    (u_char) ((addr[0] >> 4) & 0x0f), (u_char) (addr[0] & 0x0f),
+						    (u_char) ((addr[1] >> 4) & 0x0f), (u_char) (addr[1] & 0x0f),
+						    (u_char) ((addr[2] >> 4) & 0x0f), (u_char) (addr[2] & 0x0f),
+						    (u_char) ((addr[3] >> 4) & 0x0f), (u_char) (addr[3] & 0x0f),
+						    (u_char) ((addr[4] >> 4) & 0x0f), (u_char) (addr[4] & 0x0f),
+						    (u_char) ((addr[5] >> 4) & 0x0f), (u_char) (addr[5] & 0x0f),
+						    (u_char) ((addr[6] >> 4) & 0x0f), (u_char) (addr[6] & 0x0f),
+						    (u_char) ((addr[7] >> 4) & 0x0f), (u_char) (addr[7] & 0x0f),
+						    (u_char) ((addr[8] >> 4) & 0x0f), (u_char) (addr[8] & 0x0f),
+						    (u_char) ((addr[9] >> 4) & 0x0f), (u_char) (addr[9] & 0x0f),
+						    (u_char) ((addr[10] >> 4) & 0x0f), (u_char) (addr[10] & 0x0f),
+						    (u_char) ((addr[11] >> 4) & 0x0f), (u_char) (addr[11] & 0x0f),
+						    (u_char) ((addr[12] >> 4) & 0x0f), (u_char) (addr[12] & 0x0f),
+						    (u_char) ((addr[13] >> 4) & 0x0f), (u_char) (addr[13] & 0x0f),
+						    (u_char) ((addr[14] >> 4) & 0x0f), (u_char) (addr[14] & 0x0f),
+						    (u_char) ((addr[15] >> 4) & 0x0f), (u_char) (addr[15] & 0x0f));
+					} else {
+						spf_done(spf, AR_PERMERROR,
+						    "unsupported type of address");
+						return NULL;
+					}
+					break;
+				case 'P':
+				case 'p':
+					mlen = strlcpy(macro, ses->rdns, sizeof(macro));
+					break;
+				case 'V':
+				case 'v':
+					if (ses->src.ss_family == AF_INET)
+						mlen = strlcpy(macro, "in-addr",
+						    sizeof(macro));
+					else if (ses->src.ss_family == AF_INET6)
+						mlen = strlcpy(macro, "ip6",
+						    sizeof(macro));
+					else {
+						spf_done(spf, AR_PERMERROR,
+						    "unsupported type of address");
+						return NULL;
+					}
+					break;
+				case 'H':
+				case 'h':
+					mlen = strlcpy(macro, ses->identity,
+					    sizeof(macro));
+					break;
+				default:
+					spf_done(spf, AR_PERMERROR,
+					    "Unexpected macro in domain-spec");
 					return NULL;
 				}
-				mlen = strlcpy(macro,
-				    spf->queries[spf->nqueries - 1].domain,
-				    sizeof(macro));
-				break;
-			case 'I':
-			case 'i':
-				if (ses->src.ss_family == AF_INET) {
-					addr = (u_char *)(&((struct sockaddr_in *)
-					    &(ses->src))->sin_addr);
-					mlen = snprintf(macro, sizeof(macro),
-					    "%u.%u.%u.%u",
-					    (addr[0] & 0xff), (addr[1] & 0xff),
-					    (addr[2] & 0xff), (addr[3] & 0xff));
-				} else if (ses->src.ss_family == AF_INET6) {
-					addr = (u_char *)(&((struct sockaddr_in6 *)
-					    &(ses->src))->sin6_addr);
-					mlen = snprintf(macro, sizeof(macro),
-					    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
-					    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
-					    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
-					    "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx",
-					    (u_char) ((addr[0] >> 4) & 0x0f), (u_char) (addr[0] & 0x0f),
-					    (u_char) ((addr[1] >> 4) & 0x0f), (u_char) (addr[1] & 0x0f),
-					    (u_char) ((addr[2] >> 4) & 0x0f), (u_char) (addr[2] & 0x0f),
-					    (u_char) ((addr[3] >> 4) & 0x0f), (u_char) (addr[3] & 0x0f),
-					    (u_char) ((addr[4] >> 4) & 0x0f), (u_char) (addr[4] & 0x0f),
-					    (u_char) ((addr[5] >> 4) & 0x0f), (u_char) (addr[5] & 0x0f),
-					    (u_char) ((addr[6] >> 4) & 0x0f), (u_char) (addr[6] & 0x0f),
-					    (u_char) ((addr[7] >> 4) & 0x0f), (u_char) (addr[7] & 0x0f),
-					    (u_char) ((addr[8] >> 4) & 0x0f), (u_char) (addr[8] & 0x0f),
-					    (u_char) ((addr[9] >> 4) & 0x0f), (u_char) (addr[9] & 0x0f),
-					    (u_char) ((addr[10] >> 4) & 0x0f), (u_char) (addr[10] & 0x0f),
-					    (u_char) ((addr[11] >> 4) & 0x0f), (u_char) (addr[11] & 0x0f),
-					    (u_char) ((addr[12] >> 4) & 0x0f), (u_char) (addr[12] & 0x0f),
-					    (u_char) ((addr[13] >> 4) & 0x0f), (u_char) (addr[13] & 0x0f),
-					    (u_char) ((addr[14] >> 4) & 0x0f), (u_char) (addr[14] & 0x0f),
-					    (u_char) ((addr[15] >> 4) & 0x0f), (u_char) (addr[15] & 0x0f));
+
+				if (mlen >= sizeof(macro)) {
+					spf_done(spf, AR_PERMERROR,
+					    "Macro expansions too large");
+					return NULL;
+				}
+
+				domain++;
+				if (isdigit(domain[0])) {
+					digits = strtol(domain, &endptr, 10);
+					if (digits < 1) {
+						spf_done(spf, AR_PERMERROR,
+						    "digits in macro can't be 0");
+						return NULL;
+					}
+					domain = endptr;
+				}
+
+				if (domain[0] == 'r') {
+					domain++;
+					reverse = 1;
+				}
+
+				for (; strchr(".-+,/_=", domain[0]) != NULL; domain++) {
+					if (strchr(delimiters, domain[0]) == NULL) {
+						delimiters[strlen(delimiters) + 1] = '\0';
+						delimiters[strlen(delimiters)] = domain[0];
+					}
+				}
+
+				if (delimiters[0] == '\0') {
+					delimiters[0] = '.';
+					delimiters[1] = '\0';
+				}
+
+				if (domain[0] != '}') {
+					spf_done(spf, AR_PERMERROR,
+					    "Mallformed macro, expected end");
+					return NULL;
+				}
+
+				if (reverse) {
+					smacro[0] = '\0';
+					tmp = macro + strlen(macro) - 1;
+					/*
+					 * DIGIT rightmost elements after reversal is DIGIT
+					 * lefmost elements before reversal
+					 */
+					while (1) {
+						while (tmp > macro &&
+						    strchr(delimiters, tmp[0]) == NULL)
+							tmp--;
+						if (tmp == macro)
+							break;
+						if (digits == 0)
+							break;
+						if (digits > 0)
+							digits--;
+
+						tmp[0] = '\0';
+						if (smacro[0] != '\0')
+							strlcat(smacro, ".", sizeof(smacro));
+						strlcat(smacro, tmp + 1, sizeof(smacro));
+						tmp--;
+					}
+					if (digits != 0) {
+						if (smacro[0] != '\0')
+							strlcat(smacro, ".", sizeof(smacro));
+						strlcat(smacro, macro, sizeof(smacro));
+					}
 				} else {
-					spf_done(spf, SPF_PERMERROR,
-					    "unsupported type of address");
+					if (digits != -1) {
+						tmp = macro;
+						endptr = macro + strlen(macro);
+						while (digits > 0) {
+							while (tmp < endptr &&
+							    strchr(delimiters, tmp[0]) == NULL)
+								tmp++;
+							if (tmp == endptr)
+								break;
+							if (digits == 1) {
+								tmp[0] = '\0';
+								break;
+							}
+							digits--;
+							tmp++;
+						}
+					}
+					strlcpy(smacro, macro, sizeof(smacro));
+				}
+
+				spec[i] = '\0';
+				i = strlcat(spec, smacro, sizeof(spec));
+				if (i >= sizeof(spec)) {
+					spf_done(
+					    spf, AR_PERMERROR, "domain-spec too large");
 					return NULL;
 				}
 				break;
@@ -1994,7 +2102,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 					mlen = strlcpy(macro, "ip6",
 					    sizeof(macro));
 				else {
-					spf_done(spf, SPF_PERMERROR,
+					spf_done(spf, AR_PERMERROR,
 					    "unsupported type of address");
 					return NULL;
 				}
@@ -2005,13 +2113,13 @@ spf_evaluate_domain(struct spf_record *spf, const char
 				    sizeof(macro));
 				break;
 			default:
-				spf_done(spf, SPF_PERMERROR,
-				    "Unexpected macro in domain-spec");
+				spf_done(spf, AR_PERMERROR,
+				    "Mallformed macro, unexpected character after %");
 				return NULL;
 			}
 
 			if (mlen >= sizeof(macro)) {
-				spf_done(spf, SPF_PERMERROR,
+				spf_done(spf, AR_PERMERROR,
 				    "Macro expansions too large");
 				return NULL;
 			}
@@ -2020,7 +2128,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 			if (isdigit(domain[0])) {
 				digits = strtol(domain, &endptr, 10);
 				if (digits < 1) {
-					spf_done(spf, SPF_PERMERROR,
+					spf_done(spf, AR_PERMERROR,
 					    "digits in macro can't be 0");
 					return NULL;
 				}
@@ -2045,7 +2153,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 			}
 
 			if (domain[0] != '}') {
-				spf_done(spf, SPF_PERMERROR,
+				spf_done(spf, AR_PERMERROR,
 				    "Mallformed macro, expected end");
 				return NULL;
 			}
@@ -2104,13 +2212,13 @@ spf_evaluate_domain(struct spf_record *spf, const char
 			i = strlcat(spec, smacro, sizeof(spec));
 			if (i >= sizeof(spec)) {
 				spf_done(
-				    spf, SPF_PERMERROR, "domain-spec too large");
+				    spf, AR_PERMERROR, "domain-spec too large");
 				return NULL;
 			}
 			break;
 
 		default:
-			spf_done(spf, SPF_PERMERROR,
+			spf_done(spf, AR_PERMERROR,
 			    "Mallformed macro, unexpected character after %");
 			return NULL;
 		}
@@ -2124,7 +2232,7 @@ spf_evaluate_domain(struct spf_record *spf, const char
 
 void
 spf_lookup_record(struct spf_record *spf, const char *domain, int type,
-    enum spf_state qualifier, int include, int exists)
+    enum ar_state qualifier, int include, int exists)
 {
 	struct asr_query *aq;
 	struct spf_query *query;
@@ -2133,7 +2241,7 @@ spf_lookup_record(struct spf_record *spf, const char *
 		return;
 
 	if (spf->nqueries >= SPF_DNS_LOOKUP_LIMIT) {
-		spf_done(spf, SPF_PERMERROR, "To many DNS queries");
+		spf_done(spf, AR_PERMERROR, "To many DNS queries");
 		return;
 	}
 
@@ -2150,7 +2258,7 @@ spf_lookup_record(struct spf_record *spf, const char *
 		return;
 
 	if (domain == NULL || !strlen(domain)) {
-		spf_done(spf, SPF_PERMERROR, "Empty domain");
+		spf_done(spf, AR_PERMERROR, "Empty domain");
 		return;
 	}
 
@@ -2182,14 +2290,14 @@ spf_resolve(struct asr_result *ar, void *arg)
 
 	if (ar->ar_h_errno == TRY_AGAIN
 	    || ar->ar_h_errno == NO_RECOVERY) {
-		spf_done(query->spf, SPF_TEMPERROR, hstrerror(ar->ar_h_errno));
+		spf_done(query->spf, AR_TEMPERROR, hstrerror(ar->ar_h_errno));
 		goto end;
 	}
 
 	if (ar->ar_h_errno == HOST_NOT_FOUND) {
 		if (query->include && !query->exists)
 			spf_done(query->spf,
-			    SPF_PERMERROR, hstrerror(ar->ar_h_errno));
+			    AR_PERMERROR, hstrerror(ar->ar_h_errno));
 		goto consume;
 	}
 
@@ -2200,7 +2308,7 @@ spf_resolve(struct asr_result *ar, void *arg)
 		    "Mallformed SPF DNS response for domain %s: %s",
 		    print_dname(q.q_dname, buf, sizeof(buf)),
 		    pack.err);
-		spf_done(query->spf, SPF_TEMPERROR, pack.err);
+		spf_done(query->spf, AR_TEMPERROR, pack.err);
 		goto end;
 	}
 
@@ -2239,7 +2347,7 @@ spf_resolve(struct asr_result *ar, void *arg)
 			osmtpd_warn(spf->ctx,
 			    "Unexpected SPF DNS record: %d for domain %s",
 			    rr.rr_type, query->domain);
-			spf_done(query->spf, SPF_TEMPERROR, "Unexpected record");
+			spf_done(query->spf, AR_TEMPERROR, "Unexpected record");
 			break;
 		}
 
@@ -2261,7 +2369,7 @@ spf_resolve(struct asr_result *ar, void *arg)
  end:
 	free(ar->ar_data);
 	if (!spf->done && spf->running == 0)
-		spf_done(spf, SPF_NONE, NULL);
+		spf_done(spf, AR_NONE, NULL);
 }
 
 void
@@ -2281,7 +2389,7 @@ spf_resolve_txt(struct dns_rr *rr, struct spf_query *q
 
 	if (query->txt != NULL) {
 		free(txt);
-		spf_done(query->spf, SPF_PERMERROR, "Duplicated SPF record");
+		spf_done(query->spf, AR_PERMERROR, "Duplicated SPF record");
 		return;
 	}
 
@@ -2437,7 +2545,7 @@ spf_execute_txt(struct spf_query *query)
 	char *end;
 	int bits;
 
-	enum spf_state q = query->q;
+	enum ar_state q = query->q;
 
 	while ((ap = strsep(&in, " ")) != NULL) {
 		if (strcasecmp(ap, "v=spf1") == 0)
@@ -2448,20 +2556,20 @@ spf_execute_txt(struct spf_query *query)
 			*end = '\0';
 
 		if (*ap == '+') {
-			q = SPF_PASS;
+			q = AR_PASS;
 			ap++;
 		} else if (*ap == '-') {
-			q = SPF_FAIL;
+			q = AR_FAIL;
 			ap++;
 		} else if (*ap == '~') {
-			q = SPF_SOFTFAIL;
+			q = AR_SOFTFAIL;
 			ap++;
 		} else if (*ap == '?') {
-			q = SPF_NEUTRAL;
+			q = AR_NEUTRAL;
 			ap++;
 		}
 
-		if (q != SPF_PASS && query->include)
+		if (q != AR_PASS && query->include)
 			continue;
 
 		if (strncasecmp("all", ap, 3) == 0) {
@@ -2539,7 +2647,7 @@ spf_execute_txt(struct spf_query *query)
 }
 
 void
-spf_done(struct spf_record *spf, enum spf_state state, const char *reason)
+spf_done(struct spf_record *spf, enum ar_state state, const char *reason)
 {
 	int i;
 
@@ -2560,28 +2668,6 @@ spf_done(struct spf_record *spf, enum spf_state state,
 	spf->done = 1;
 
 	osmtpd_filter_proceed(spf->ctx);
-}
-
-const char *
-spf_state2str(enum spf_state state)
-{
-	switch (state)
-	{
-	case SPF_NONE:
-		return "none";
-	case SPF_NEUTRAL:
-		return "neutral";
-	case SPF_PASS:
-		return "pass";
-	case SPF_FAIL:
-		return "fail";
-	case SPF_SOFTFAIL:
-		return "softfail";
-	case SPF_TEMPERROR:
-		return "temperror";
-	case SPF_PERMERROR:
-		return "permerror";
-	}
 }
 
 int
@@ -2599,7 +2685,7 @@ spf_ar_cat(const char *type, struct spf_record *spf, c
 
 	if ((*aroff =
 	    auth_ar_cat(line, linelen, *aroff,
-	    "; spf=%s", spf_state2str(spf->state))
+	    "; spf=%s", ar_state2str(spf->state))
 	    ) == -1) {
 		return -1;
 	}
@@ -2707,7 +2793,7 @@ auth_ar_create(struct osmtpd_ctx *ctx)
 	}
 
 	if ((aroff = auth_ar_cat(&line, &linelen, aroff,
-	    "; iprev=%s", iprev_state2str(ses->iprev))) == -1)
+	    "; iprev=%s", ar_state2str(ses->iprev))) == -1)
 		osmtpd_err(1, "%s: malloc", __func__);
 
 	if (spf_ar_cat("smtp.helo", ses->spf_helo,