Commit Diff


commit - 5c6a65504e44766a5cf8d0b9131bd2c2331ee8ca
commit + 4e2dd90f1001dd3bf2b8e342bbd73d01231bb065
blob - 5a6d7c794505e09b9725140560776db03b94882f
blob + ef1d207bcdc89ba8d2ddfbcdb12558593fa667dc
--- Makefile
+++ Makefile
@@ -4,7 +4,7 @@ MAN=	filter-dkimverify.8
 BINDIR=	${LOCALBASE}/libexec/smtpd/
 MANDIR=	${LOCALBASE}/man/man
 
-SRCS+=	main.c mheader.c unpack_dns.c
+SRCS+=	ltok.c main.c unpack_dns.c
 
 CFLAGS+=-I${LOCALBASE}/include
 CFLAGS+=-Wall -I${.CURDIR}
blob - b20aa0b202fd630f75db94bf7facc5cbdcea8b07
blob + 1292720beba141660a45ac4b9c01d2aa1b99b57a
--- main.c
+++ main.c
@@ -39,7 +39,7 @@
 
 #include "opensmtpd.h"
 #include "unpack_dns.h"
-#include "mheader.h"
+#include "ltok.h"
 
 /*
  * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
@@ -60,13 +60,13 @@ struct signature {
 	enum state state;
 	const char *state_reason;
 	int v;
-	char *a;
+	const char *a;
 	size_t asz;
 	int ak;
 	const EVP_MD *ah;
 	char *b;
 	size_t bsz;
-	char *bheader;
+	const char *bheader;
 	/* Make sure padding bits for base64 decoding fit */
 	char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
 	size_t bhsz;
@@ -81,7 +81,7 @@ struct signature {
 #define CANON_DONE		1 << 2
 	char d[HOST_NAME_MAX + 1];
 	char **h;
-	char *i;
+	const char *i;
 	size_t isz;
 	ssize_t l;
 	int q;
@@ -130,20 +130,20 @@ void *dkim_message_new(struct osmtpd_ctx *);
 void dkim_message_free(struct osmtpd_ctx *, void *);
 void dkim_header_add(struct osmtpd_ctx *, const char *);
 void dkim_signature_parse(struct header *);
-void dkim_signature_parse_v(struct signature *, char *, char *);
-void dkim_signature_parse_a(struct signature *, char *, char *);
-void dkim_signature_parse_b(struct signature *, char *, char *);
-void dkim_signature_parse_bh(struct signature *, char *, char *);
-void dkim_signature_parse_c(struct signature *, char *, char *);
-void dkim_signature_parse_d(struct signature *, char *, char *);
-void dkim_signature_parse_h(struct signature *, char *, char *);
-void dkim_signature_parse_i(struct signature *, char *, char *);
-void dkim_signature_parse_l(struct signature *, char *, char *);
-void dkim_signature_parse_q(struct signature *, char *, char *);
-void dkim_signature_parse_s(struct signature *, char *, char *);
-void dkim_signature_parse_t(struct signature *, char *, char *);
-void dkim_signature_parse_x(struct signature *, char *, char *);
-void dkim_signature_parse_z(struct signature *, char *, char *);
+void dkim_signature_parse_v(struct signature *, const char *, const char *);
+void dkim_signature_parse_a(struct signature *, const char *, const char *);
+void dkim_signature_parse_b(struct signature *, const char *, const char *);
+void dkim_signature_parse_bh(struct signature *, const char *, const char *);
+void dkim_signature_parse_c(struct signature *, const char *, const char *);
+void dkim_signature_parse_d(struct signature *, const char *, const char *);
+void dkim_signature_parse_h(struct signature *, const char *, const char *);
+void dkim_signature_parse_i(struct signature *, const char *, const char *);
+void dkim_signature_parse_l(struct signature *, const char *, const char *);
+void dkim_signature_parse_q(struct signature *, const char *, const char *);
+void dkim_signature_parse_s(struct signature *, const char *, const char *);
+void dkim_signature_parse_t(struct signature *, const char *, const char *);
+void dkim_signature_parse_x(struct signature *, const char *, const char *);
+void dkim_signature_parse_z(struct signature *, const char *, const char *);
 void dkim_signature_verify(struct signature *);
 void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
 void dkim_signature_state(struct signature *, enum state, const char *);
@@ -155,8 +155,8 @@ void dkim_rr_resolve(struct asr_result *, void *);
 void dkim_message_verify(struct message *);
 ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
     __attribute__((__format__ (printf, 4, 5)));
-void dkim_ar_print(struct osmtpd_ctx *, char *);
-int dkim_key_text_parse(struct signature *, char *);
+void dkim_ar_print(struct osmtpd_ctx *, const char *);
+int dkim_key_text_parse(struct signature *, const char *);
 
 char *authservid;
 EVP_ENCODE_CTX *ectx = NULL;
@@ -187,7 +187,7 @@ main(int argc, char *argv[])
 void
 dkim_conf(const char *key, const char *value)
 {
-	char *end;
+	const char *end;
 
 	if (key == NULL) {
 		if (authservid == NULL)
@@ -197,10 +197,9 @@ dkim_conf(const char *key, const char *value)
 	if (strcmp(key, "admd") == 0 && authservid == NULL) {
 		if ((authservid = strdup(value)) == NULL)
 			osmtpd_err(1, "malloc");
-		end = osmtpd_mheader_skip_value(authservid, 0);
+		end = osmtpd_ltok_skip_value(authservid, 0);
 		if (authservid + strlen(authservid) != end)
 			osmtpd_errx(1, "Invalid authservid");
-		osmtpd_mheader_quoted_string_normalize(authservid);
 	}
 }
 
@@ -314,7 +313,7 @@ void
 dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
 {
 	struct message *msg = ctx->local_message;
-	char *start, *end, *verify;
+	const char *start, *end, *verify;
 	struct header *headers;
 	size_t i;
 
@@ -323,10 +322,10 @@ dkim_header_add(struct osmtpd_ctx *ctx, const char *li
 		if (line[0] != ' ' && line[0] != '\t') {
 			msg->header[msg->nheaders - 1].readdone = 1;
 			start = msg->header[msg->nheaders - 1].buf;
-			end = osmtpd_mheader_skip_fieldname(start, 0);
+			end = osmtpd_ltok_skip_field_name(start, 0);
 			/* In case someone uses an obs-optional */
 			if (end != NULL)
-				verify = osmtpd_mheader_skip_wsp(end, 1);
+				verify = osmtpd_ltok_skip_wsp(end, 1);
 			if (end != NULL &&
 			    strncasecmp(
 			    start, "DKIM-Signature", end - start) == 0 &&
@@ -385,14 +384,14 @@ dkim_signature_parse(struct header *header)
 {
 	struct signature *sig;
 	struct asr_query *query;
-	char *buf, *i;
-	char *end, tagname[3];
+	const char *buf, *i, *end;
+	char tagname[3];
 	char subdomain[HOST_NAME_MAX + 1];
 	size_t ilen, dlen;
 
 	/* Format checked by dkim_header_add */
-	buf = osmtpd_mheader_skip_fieldname(header->buf, 0);
-	buf = osmtpd_mheader_skip_wsp(buf, 1) + 1;
+	buf = osmtpd_ltok_skip_field_name(header->buf, 0);
+	buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
 
 	if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL) {
 		dkim_err(header->msg, "malloc");
@@ -404,25 +403,25 @@ dkim_signature_parse(struct header *header)
 	sig->t = -1;
 	sig->x = -1;
 
-	end = osmtpd_mheader_skip_dkimsig_taglist(buf, 0);
+	end = osmtpd_ltok_skip_tag_list(buf, 0);
 	if (end == NULL || end[0] != '\0') {
-		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid taglist");
+		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
 		return;
 	}
 
 	while (buf[0] != '\0') {
-		buf = osmtpd_mheader_skip_fws(buf, 1);
-		end = osmtpd_mheader_skip_dkimsig_tagname(buf, 0);
+		buf = osmtpd_ltok_skip_fws(buf, 1);
+		end = osmtpd_ltok_skip_tag_name(buf, 0);
 
 		/* Unknown tag-name */
 		if ((size_t)(end - buf) >= sizeof(tagname))
 			tagname[0] = '\0';
 		else
 			strlcpy(tagname, buf, (end - buf) + 1);
-		buf = osmtpd_mheader_skip_fws(end, 1);
+		buf = osmtpd_ltok_skip_fws(end, 1);
 		/* '=' */
-		buf = osmtpd_mheader_skip_fws(buf + 1, 1);
-		end = osmtpd_mheader_skip_dkimsig_tagvalue(buf, 1);
+		buf = osmtpd_ltok_skip_fws(buf + 1, 1);
+		end = osmtpd_ltok_skip_tag_value(buf, 1);
 		if (strcmp(tagname, "v") == 0)
 			dkim_signature_parse_v(sig, buf, end);
 		else if (strcmp(tagname, "a") == 0)
@@ -452,12 +451,12 @@ dkim_signature_parse(struct header *header)
 		else if (strcmp(tagname, "z") == 0)
 			dkim_signature_parse_z(sig, buf, end);
 
-		buf = osmtpd_mheader_skip_fws(end, 1);
+		buf = osmtpd_ltok_skip_fws(end, 1);
 		if (buf[0] == ';')
 			buf++;
 		else if (buf[0] != '\0') {
 			dkim_signature_state(sig, DKIM_PERMERROR,
-			    "Invalid taglist");
+			    "Invalid tag-list");
 			return;
 		}
 	}
@@ -482,7 +481,7 @@ dkim_signature_parse(struct header *header)
 		return;
 
 	if (sig->i != NULL) {
-		i = osmtpd_mheader_skip_local_part(sig->i, 1) + 1;
+		i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
 		ilen = strlen(i);
 		dlen = strlen(sig->d);
 		if (ilen < dlen) {
@@ -521,7 +520,7 @@ dkim_signature_parse(struct header *header)
 }
 
 void
-dkim_signature_parse_v(struct signature *sig, char *start, char *end)
+dkim_signature_parse_v(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->v != 0) {	/* Duplicate tag */
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
@@ -535,7 +534,7 @@ dkim_signature_parse_v(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_a(struct signature *sig, char *start, char *end)
+dkim_signature_parse_a(struct signature *sig, const char *start, const char *end)
 {
 	char ah[sizeof("sha256")];
 
@@ -544,7 +543,7 @@ dkim_signature_parse_a(struct signature *sig, char *st
 		return;
 	}
 
-	if (osmtpd_mheader_skip_dkimsig_sigatagalg(start, 0) != end) {
+	if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
 		return;
 	}
@@ -578,7 +577,7 @@ dkim_signature_parse_a(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_b(struct signature *sig, char *start, char *end)
+dkim_signature_parse_b(struct signature *sig, const char *start, const char *end)
 {
 	int decodesz;
 
@@ -608,9 +607,9 @@ dkim_signature_parse_b(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_bh(struct signature *sig, char *start, char *end)
+dkim_signature_parse_bh(struct signature *sig, const char *start, const char *end)
 {
-	char *b64;
+	const char *b64;
 	size_t n;
 	int decodesz;
 
@@ -625,15 +624,15 @@ dkim_signature_parse_bh(struct signature *sig, char *s
 	b64 = start;
 	n = 0;
 	while (1) {
-		b64 = osmtpd_mheader_skip_fws(b64, 1);
-		if (osmtpd_mheader_skip_alphadigitps(b64, 0) == NULL)
+		b64 = osmtpd_ltok_skip_fws(b64, 1);
+		if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
 			break;
 		n++;
 		b64++;
 	}
 	if (b64[0] == '=') {
 		n++;
-		b64 = osmtpd_mheader_skip_fws(b64 + 1, 1);
+		b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
 		if (b64[0] == '=') {
 			n++;
 			b64++;
@@ -662,7 +661,7 @@ dkim_signature_parse_bh(struct signature *sig, char *s
 }
 
 void
-dkim_signature_parse_c(struct signature *sig, char *start, char *end)
+dkim_signature_parse_c(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->c != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
@@ -701,13 +700,13 @@ dkim_signature_parse_c(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_d(struct signature *sig, char *start, char *end)
+dkim_signature_parse_d(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->d[0] != '\0') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
 		return;
 	}
-	if (osmtpd_mheader_skip_domain(start, 0) != end ||
+	if (osmtpd_ltok_skip_domain(start, 0) != end ||
 	    (size_t)(end - start) >= sizeof(sig->d)) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
 		return;
@@ -716,9 +715,9 @@ dkim_signature_parse_d(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_h(struct signature *sig, char *start, char *end)
+dkim_signature_parse_h(struct signature *sig, const char *start, const char *end)
 {
-	char *h;
+	const char *h;
 	size_t n = 0;
 
 	if (sig->h != NULL) {
@@ -727,7 +726,7 @@ dkim_signature_parse_h(struct signature *sig, char *st
 	}
 	h = start;
 	while (1) {
-		if ((h = osmtpd_mheader_skip_hdrname(h, 0)) == NULL) {
+		if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
 			dkim_signature_state(sig, DKIM_PERMERROR,
 			    "Invalid h tag");
 			return;
@@ -738,10 +737,10 @@ dkim_signature_parse_h(struct signature *sig, char *st
 			h = end;
 			break;
 		}
-		h = osmtpd_mheader_skip_fws(h, 1);
+		h = osmtpd_ltok_skip_fws(h, 1);
 		if (h[0] != ':')
 			break;
-		h = osmtpd_mheader_skip_fws(h + 1, 1);
+		h = osmtpd_ltok_skip_fws(h + 1, 1);
 	}
 	if (h != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
@@ -754,7 +753,7 @@ dkim_signature_parse_h(struct signature *sig, char *st
 	n = 0;
 	h = start;
 	while (1) {
-		h = osmtpd_mheader_skip_hdrname(start, 0);
+		h = osmtpd_ltok_skip_hdr_name(start, 0);
 		/* ';' is part of hdr-name */
 		if (h > end) {
 			sig->h[n] = strndup(start, end - start);
@@ -764,28 +763,28 @@ dkim_signature_parse_h(struct signature *sig, char *st
 			dkim_err(sig->header->msg, "malloc");
 			return;
 		}
-		start = osmtpd_mheader_skip_fws(h, 1);
+		start = osmtpd_ltok_skip_fws(h, 1);
 		if (start[0] != ':')
 			break;
-		start = osmtpd_mheader_skip_fws(start + 1, 1);
+		start = osmtpd_ltok_skip_fws(start + 1, 1);
 	}
 }
 
 void
-dkim_signature_parse_i(struct signature *sig, char *start, char *end)
+dkim_signature_parse_i(struct signature *sig, const char *start, const char *end)
 {
-	char *i;
+	const char *i;
 
 	if (sig->i != NULL) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
 		return;
 	}
-	i = osmtpd_mheader_skip_local_part(start, 1);
+	i = osmtpd_ltok_skip_local_part(start, 1);
 	if (i[0] != '@') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
 		return;
 	}
-	if (osmtpd_mheader_skip_domain(i + 1, 0) != end) {
+	if (osmtpd_ltok_skip_domain(i + 1, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
 		return;
 	}
@@ -794,7 +793,7 @@ dkim_signature_parse_i(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_l(struct signature *sig, char *start, char *end)
+dkim_signature_parse_l(struct signature *sig, const char *start, const char *end)
 {
 	long long l;
 	char *lend;
@@ -806,7 +805,7 @@ dkim_signature_parse_l(struct signature *sig, char *st
 	errno = 0;
 	l = strtoll(start, &lend, 10);
 	/* > 76 digits in stroll is an overflow */
-	if (osmtpd_mheader_skip_digit(start, 0) == NULL ||
+	if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
 	    lend != end || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
 		return;
@@ -819,9 +818,9 @@ dkim_signature_parse_l(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_q(struct signature *sig, char *start, char *end)
+dkim_signature_parse_q(struct signature *sig, const char *start, const char *end)
 {
-	char *qend;
+	const char *qend;
 
 	if (sig->q != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
@@ -829,15 +828,15 @@ dkim_signature_parse_q(struct signature *sig, char *st
 	}
 
 	while (1) {
-		start = osmtpd_mheader_skip_fws(start, 1);
-		qend = osmtpd_mheader_skip_dkimsig_sigqtagmethod(start, 0);
+		start = osmtpd_ltok_skip_fws(start, 1);
+		qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
 		if (qend == NULL) {
 			dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
 			return;
 		}
 		if (strncmp(start, "dns/txt", qend - start) == 0)
 			sig->q = 1;
-		start = osmtpd_mheader_skip_fws(qend, 1);
+		start = osmtpd_ltok_skip_fws(qend, 1);
 		if (start[0] != ':')
 			break;
 	}
@@ -853,13 +852,13 @@ dkim_signature_parse_q(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_s(struct signature *sig, char *start, char *end)
+dkim_signature_parse_s(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->s[0] != '\0') {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
 		return;
 	}
-	if (osmtpd_mheader_skip_dkimsig_selector(start, 0) != end) {
+	if (osmtpd_ltok_skip_selector(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
 		return;
 	}
@@ -867,7 +866,7 @@ dkim_signature_parse_s(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_t(struct signature *sig, char *start, char *end)
+dkim_signature_parse_t(struct signature *sig, const char *start, const char *end)
 {
 	char *tend;
 
@@ -877,7 +876,7 @@ dkim_signature_parse_t(struct signature *sig, char *st
 	}
 	errno = 0;
 	sig->t = strtoll(start, &tend, 10);
-	if (osmtpd_mheader_skip_digit(start, 0) == NULL || tend != end ||
+	if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
 	    tend - start > 12 || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
 		return;
@@ -885,7 +884,7 @@ dkim_signature_parse_t(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_x(struct signature *sig, char *start, char *end)
+dkim_signature_parse_x(struct signature *sig, const char *start, const char *end)
 {
 	char *xend;
 
@@ -895,7 +894,7 @@ dkim_signature_parse_x(struct signature *sig, char *st
 	}
 	errno = 0;
 	sig->x = strtoll(start, &xend, 10);
-	if (osmtpd_mheader_skip_digit(start, 0) == NULL || xend != end ||
+	if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
 	    xend - start > 12 || errno != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
 		return;
@@ -903,7 +902,7 @@ dkim_signature_parse_x(struct signature *sig, char *st
 }
 
 void
-dkim_signature_parse_z(struct signature *sig, char *start, char *end)
+dkim_signature_parse_z(struct signature *sig, const char *start, const char *end)
 {
 	if (sig->z != 0) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
@@ -911,7 +910,7 @@ dkim_signature_parse_z(struct signature *sig, char *st
 	}
 
 	sig->z = 1;
-	if (osmtpd_mheader_skip_dkimsig_sigztagvalue(start, 0) != end) {
+	if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
 		dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
 		return;
 	}
@@ -922,7 +921,7 @@ dkim_signature_verify(struct signature *sig)
 {
 	struct message *msg = sig->header->msg;
 	static EVP_MD_CTX *bctx = NULL;
-	char *end;
+	const char *end;
 	size_t i, header;
 
 	if (sig->state != DKIM_UNKNOWN)
@@ -951,7 +950,7 @@ dkim_signature_verify(struct signature *sig)
 			    strlen(sig->h[header])) != 0 ||
 			    msg->header[i].sig == sig)
 				continue;
-			end = osmtpd_mheader_skip_fws(
+			end = osmtpd_ltok_skip_fws(
 			    msg->header[i].buf + strlen(sig->h[header]), 1);
 			if (end[0] != ':')
 				continue;
@@ -968,21 +967,22 @@ void
 dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig,
     struct header *header)
 {
-	char c, *ptr = header->buf, *end;
+	char c;
+	const char *ptr = header->buf, *end;
 	int inhdrname = 1;
 	int canon = sig->c & CANON_HEADER;
 
 	for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
 		if (inhdrname) {
 			if (canon == CANON_HEADER_RELAXED) {
-				ptr = osmtpd_mheader_skip_fws(ptr, 1);
+				ptr = osmtpd_ltok_skip_fws(ptr, 1);
 				c = tolower(ptr[0]);
 			} else
 				c = ptr[0];
 			if (c == ':') {
 				inhdrname = 0;
 				if (canon == CANON_HEADER_RELAXED)
-					ptr = osmtpd_mheader_skip_fws(
+					ptr = osmtpd_ltok_skip_fws(
 					    ptr + 1, 1) - 1;
 			}
 			if (EVP_DigestVerifyUpdate(bctx, &c, 1) == 0) {
@@ -992,10 +992,10 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu
 			}
 			continue;
 		}
-		end = osmtpd_mheader_skip_fws(ptr, 1);
+		end = osmtpd_ltok_skip_fws(ptr, 1);
 		if (end == ptr) {
 			if (sig->header == header && ptr == sig->bheader) {
-				ptr = osmtpd_mheader_skip_dkimsig_tagvalue(
+				ptr = osmtpd_ltok_skip_tag_value(
 				    ptr, 0) - 1;
 				continue;
 			}
@@ -1163,58 +1163,59 @@ dkim_rr_resolve(struct asr_result *ar, void *arg)
 }
 
 int
-dkim_key_text_parse(struct signature *sig, char *key)
+dkim_key_text_parse(struct signature *sig, const char *key)
 {
-	char *end, *tagvend, tagname;
-	char tmp, pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
+	char tagname, *hashname;
+	const char *end, *tagvend;
+	char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
 	size_t pkoff, linelen;
 	int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0;
 	BIO *bio;
 
-	key = osmtpd_mheader_skip_fws(key, 1);
+	key = osmtpd_ltok_skip_fws(key, 1);
 	/* Validate syntax early */
-	if ((end = osmtpd_mheader_skip_dkimsig_taglist(key, 0)) == NULL)
+	if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
 		return 0;
 
 	/*
 	 * RFC 6376 section 3.6.1, v=:
 	 * RECOMMENDED...This tag MUST be the first tag in the record.
 	 */
-	if (osmtpd_mheader_skip_dkimsig_tagname(key, 0) - key == 1 &&
+	if (osmtpd_ltok_skip_tag_name(key, 0) - key == 1 &&
 	    key[0] == 'v') {
-		key = osmtpd_mheader_skip_dkimsig_tagname(key, 0);
-		key = osmtpd_mheader_skip_fws(key, 1);
+		key = osmtpd_ltok_skip_tag_name(key, 0);
+		key = osmtpd_ltok_skip_fws(key, 1);
 		key++;	/* = */
-		key = osmtpd_mheader_skip_fws(key, 1);
-		end = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
+		key = osmtpd_ltok_skip_fws(key, 1);
+		end = osmtpd_ltok_skip_tag_value(key, 0);
 		if (end - key != 5 || strncmp(key, "DKIM1", 5) != 0)
 			return 0;
 		key += 5;
-		key = osmtpd_mheader_skip_fws(key, 1);
+		key = osmtpd_ltok_skip_fws(key, 1);
 		if (key[0] == ';')
 			key++;
 	}
 	while (key[0] != '\0') {
-		key = osmtpd_mheader_skip_fws(key, 1);
-		end = osmtpd_mheader_skip_dkimsig_tagname(key, 0);
+		key = osmtpd_ltok_skip_fws(key, 1);
+		end = osmtpd_ltok_skip_tag_name(key, 0);
 
 		if ((size_t)(end - key) != 1) {
-			key = osmtpd_mheader_skip_fws(end, 1);
+			key = osmtpd_ltok_skip_fws(end, 1);
 			/* '=' */
 			key++;
-			key = osmtpd_mheader_skip_fws(key, 1);
-			key = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
-			key = osmtpd_mheader_skip_fws(key, 1);
+			key = osmtpd_ltok_skip_fws(key, 1);
+			key = osmtpd_ltok_skip_tag_value(key, 0);
+			key = osmtpd_ltok_skip_fws(key, 1);
 			if (key[0] == ';')
 				key++;
 			continue;
 		}
 		tagname = key[0];
-		key = osmtpd_mheader_skip_fws(end, 1);
+		key = osmtpd_ltok_skip_fws(end, 1);
 		/* '=' */
 		key++;
-		key = osmtpd_mheader_skip_fws(key, 1);
-		end = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
+		key = osmtpd_ltok_skip_fws(key, 1);
+		end = osmtpd_ltok_skip_tag_value(key, 0);
 		switch (tagname) {
 		case 'v':
 			/* tagname in wrong position */
@@ -1223,26 +1224,29 @@ dkim_key_text_parse(struct signature *sig, char *key)
 			if (h != 0)	/* Duplicate tag */
 				return 0;
 			/* Invalid tag value */
-			if (osmtpd_mheader_skip_dkimsig_keyhtagvalue(
+			if (osmtpd_ltok_skip_key_h_tag_value(
 			    key, 0) != end)
 				return 0;
 			while (1) {
 				if ((tagvend =
-				    osmtpd_mheader_skip_dkimsig_keyhtagvalue(
+				    osmtpd_ltok_skip_key_h_tag_value(
 				    key, 0)) == NULL)
 					break;
-				tmp = tagvend[0];
-				tagvend[0] = '\0';
-				if (EVP_get_digestbyname(key) == sig->ah) {
-					tagvend[0] = tmp;
+				hashname = strndup(key, tagvend - key);
+				if (hashname == NULL) {
+					dkim_err(sig->header->msg, "malloc");
+					return 0;
+				}
+				if (EVP_get_digestbyname(hashname) == sig->ah) {
+					free(hashname);
 					h = 1;
 					break;
 				}
-				tagvend[0] = tmp;
-				key = osmtpd_mheader_skip_fws(tagvend, 1);
+				free(hashname);
+				key = osmtpd_ltok_skip_fws(tagvend, 1);
 				if (key[0] != ':')
 					break;
-				key = osmtpd_mheader_skip_fws(key + 1, 1);
+				key = osmtpd_ltok_skip_fws(key + 1, 1);
 			}
 			if (h != 1)
 				return 0;
@@ -1260,13 +1264,15 @@ dkim_key_text_parse(struct signature *sig, char *key)
 			if (n != 0)	/* Duplicate tag */
 				return 0;
 			n = 1;
+			if (osmtpd_ltok_skip_key_n_tag_value(key, 0) != end)
+				return 0;
 			key = end;
 			break;
 		case 'p':
 			if (p != 0)	/* Duplicate tag */
 				return 0;
 			p = 1;
-			tagvend = osmtpd_mheader_skip_base64string(key, 1);
+			tagvend = osmtpd_ltok_skip_base64string(key, 1);
 			/* Invalid tag value */
 			if (tagvend != end ||
 			    (size_t)(end - key) >= sizeof(pkraw))
@@ -1278,12 +1284,12 @@ dkim_key_text_parse(struct signature *sig, char *key)
 			if (s != 0)	/* Duplicate tag */
 				return 0;
 			/* Invalid tag value */
-			if (osmtpd_mheader_skip_dkimsig_keystagvalue(key, 0) !=
+			if (osmtpd_ltok_skip_key_s_tag_value(key, 0) !=
 			    end)
 				return 0;
 			while (1) {
 				if ((tagvend =
-				    osmtpd_mheader_skip_dkimsig_keystagtype(
+				    osmtpd_ltok_skip_key_s_tag_type(
 				    key, 0)) == NULL)
 					break;
 				if (strncmp(key, "*", tagvend - key) == 0 ||
@@ -1291,10 +1297,10 @@ dkim_key_text_parse(struct signature *sig, char *key)
 					s = 1;
 					break;
 				}
-				key = osmtpd_mheader_skip_fws(tagvend, 1);
+				key = osmtpd_ltok_skip_fws(tagvend, 1);
 				if (key[0] != ':')
 					break;
-				key = osmtpd_mheader_skip_fws(key + 1, 1);
+				key = osmtpd_ltok_skip_fws(key + 1, 1);
 			}
 			if (s != 1)
 				return 0;
@@ -1304,21 +1310,21 @@ dkim_key_text_parse(struct signature *sig, char *key)
 			if (t != 0)	/* Duplicate tag */
 				return 0;
 			t = 1;
-			if (osmtpd_mheader_skip_dkimsig_keystagtype(key, 0) !=
+			if (osmtpd_ltok_skip_key_s_tag_type(key, 0) !=
 			    end)
 				return 0;
 			while (1) {
 				tagvend =
-				    osmtpd_mheader_skip_dkimsig_keyttagflag(
+				    osmtpd_ltok_skip_key_t_tag_flag(
 				    key, 0);
 				if (strncmp(key, "y", tagvend - key) == 0)
 					sig->kt |= KT_Y;
 				else if (strncmp(key, "s", tagvend - key) == 0)
 					sig->kt |= KT_S;
-				key = osmtpd_mheader_skip_fws(tagvend, 1);
+				key = osmtpd_ltok_skip_fws(tagvend, 1);
 				if (key[0] != ':')
 					break;
-				key = osmtpd_mheader_skip_fws(key + 1, 1);
+				key = osmtpd_ltok_skip_fws(key + 1, 1);
 			}
 			break;
 		default:
@@ -1326,7 +1332,7 @@ dkim_key_text_parse(struct signature *sig, char *key)
 			break;
 		}
 
-		key = osmtpd_mheader_skip_fws(key, 1);
+		key = osmtpd_ltok_skip_fws(key, 1);
 		if (key[0] == ';')
 			key++;
 		else if (key[0] != '\0')
@@ -1356,7 +1362,7 @@ dkim_key_text_parse(struct signature *sig, char *key)
 				pkimp[pkoff++] = '\n';
 				linelen = 0;
 			}
-			key = osmtpd_mheader_skip_fws(key + 1, 1);
+			key = osmtpd_ltok_skip_fws(key + 1, 1);
 		}
 		/* Leverage pkoff check in loop */
 		if (linelen != 0)
@@ -1579,14 +1585,14 @@ dkim_message_verify(struct message *msg)
 }
 
 void
-dkim_ar_print(struct osmtpd_ctx *ctx, char *start)
+dkim_ar_print(struct osmtpd_ctx *ctx, const char *start)
 {
-	char *scan, *checkpoint, *ncheckpoint;
+	const char *scan, *checkpoint, *ncheckpoint;
 	size_t arlen = 0;
 	int first = 1, arid = 1;
 
 	checkpoint = start;
-	ncheckpoint = osmtpd_mheader_skip_hdrname(start, 0) + 1;
+	ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
 	for (scan = start; scan[0] != '\0'; scan++) {
 		if (scan[0] == '\t')
 			arlen = (arlen + 8) & ~7;
@@ -1596,33 +1602,33 @@ dkim_ar_print(struct osmtpd_ctx *ctx, char *start)
 			osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
 			    (int)((checkpoint == start ?
 			    ncheckpoint : checkpoint) - start), start);
-			start = osmtpd_mheader_skip_cfws(checkpoint, 1);
+			start = osmtpd_ltok_skip_cfws(checkpoint, 1);
 			scan = start;
 			arlen = 8;
 			first = 0;
 		}
 		if (scan == ncheckpoint) {
 			checkpoint = ncheckpoint;
-			ncheckpoint = osmtpd_mheader_skip_cfws(ncheckpoint, 1);
+			ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
 			/* authserv-id */
 			if (arid) {
-				ncheckpoint = osmtpd_mheader_skip_value(
+				ncheckpoint = osmtpd_ltok_skip_value(
 				    ncheckpoint, 0);
 				arid = 0;
 			/* methodspec */
 			} else if (strncmp(ncheckpoint, "dkim",
 			    sizeof("dkim") - 1) == 0) {
-				ncheckpoint = osmtpd_mheader_skip_keyword(
+				ncheckpoint = osmtpd_ltok_skip_keyword(
 				    ncheckpoint + sizeof("dkim"), 0);
 			/* reasonspec */
 			} else if (strncmp(ncheckpoint, "reason",
 			    sizeof("reason") - 1) == 0) {
-				ncheckpoint = osmtpd_mheader_skip_value(
+				ncheckpoint = osmtpd_ltok_skip_value(
 				    ncheckpoint + sizeof("reason"), 0);
 			/* propspec */
 			} else {
 				ncheckpoint += sizeof("header.x=") - 1;
-				ncheckpoint = osmtpd_mheader_skip_ar_pvalue(
+				ncheckpoint = osmtpd_ltok_skip_ar_pvalue(
 				    ncheckpoint, 0);
 				if (ncheckpoint[0] == ';')
 					ncheckpoint++;
blob - /dev/null
blob + 117b867d4dfc0e4fb5b0439df53f83d56c3f852d (mode 644)
--- /dev/null
+++ ltok.c
@@ -0,0 +1,1882 @@
+/*
+ * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+
+#include "ltok.h"
+
+#include <stdio.h>
+
+/* RFC 5234 - Augmented BNF for Syntax Specifications: ABNF */
+const char *
+osmtpd_ltok_skip_alpha(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) ||
+	    (ptr[0] >= 0x61 && ptr[0] <= 0x7a))
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_bit(const char *ptr, int optional)
+{
+	if (ptr[0] == '0' || ptr[0] == '1')
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_char(const char *ptr, int optional)
+{
+	if (ptr[0] >= 0x01 && ptr[0] <= 0x7f)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_cr(const char *ptr, int optional)
+{
+	if (ptr[0] == 0xd)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_crlf(const char *ptr, int optional)
+{
+	if (ptr[0] == 13 && ptr[1] == 10)
+		return ptr + 2;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_ctl(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 0x00 && ptr[0] <= 0x1f) || ptr[0] == 0x7f)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_digit(const char *ptr, int optional)
+{
+	if (ptr[0] >= 0x30 && ptr[0] <= 0x39)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_dquote(const char *ptr, int optional)
+{
+	if (ptr[0] == 0x22)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_hexdig(const char *ptr, int optional)
+{
+	const char *start = ptr;
+	char l;
+
+	if ((ptr = osmtpd_ltok_skip_digit(ptr, 0)) != NULL)
+		return ptr;
+	l = tolower(ptr[0]);
+	if (l == 'a' || l == 'b' || l == 'c' || l == 'd' ||
+	    l == 'e' || l == 'f')
+		return ptr + 1;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_htab(const char *ptr, int optional)
+{
+	if (ptr[0] == 0x9)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_lf(const char *ptr, int optional)
+{
+	if (ptr[0] == 0xa)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_octet(const char *ptr, int optional)
+{
+	return ptr + 1;
+}
+
+const char *
+osmtpd_ltok_skip_sp(const char *ptr, int optional)
+{
+	if (ptr[0] == 0x20)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_vchar(const char *ptr, int optional)
+{
+	if (ptr[0] >= 0x21 && ptr[0] <= 0x7e)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_wsp(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sp(start, 0)) != NULL ||
+	    (ptr = osmtpd_ltok_skip_htab(start, 0)) != NULL)
+		return ptr;
+	return optional ? start : NULL;
+}
+
+/* RFC 5321 - Simple Mail Transfer Protocol */
+const char *
+osmtpd_ltok_skip_keyword(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_ldh_string(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sub_domain(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_let_dig(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return osmtpd_ltok_skip_ldh_string(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_let_dig(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(start, 0)) == NULL &&
+	    (ptr = osmtpd_ltok_skip_digit(start, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_ldh_string(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+	int letdig = 0;
+
+	while (1) {
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_alpha(prev, 0)) != NULL ||
+		    (ptr = osmtpd_ltok_skip_digit(prev, 0)) != NULL) {
+			letdig = 1;
+			continue;
+		}
+		if (prev[0] == '-') {
+			letdig = 0;
+			ptr = prev + 1;
+			continue;
+		}
+		ptr = prev;
+		break;
+	}
+	if (letdig)
+		return ptr;
+	if (ptr == start)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+/* RFC 5322 - Internet Message Format */
+const char *
+osmtpd_ltok_skip_quoted_pair(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] == '\\' && (
+	    (ptr = osmtpd_ltok_skip_vchar(start + 1, 0)) != NULL ||
+	    (ptr = osmtpd_ltok_skip_wsp(start + 1, 0)) != NULL))
+		return ptr;
+	return osmtpd_ltok_skip_obs_qp(start, optional);
+}
+
+const char *
+osmtpd_ltok_skip_fws(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev = ptr;
+
+	while ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) != NULL)
+		prev = ptr;
+	if ((ptr = osmtpd_ltok_skip_crlf(prev, 1)) == prev)
+		ptr = start;
+	if ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) == NULL)
+		return osmtpd_ltok_skip_obs_fws(start, optional);
+	prev = ptr;
+	while ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) != NULL)
+		prev = ptr;
+	return prev;
+}
+
+const char *
+osmtpd_ltok_skip_ctext(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) ||
+	    (ptr[0] >= 93 && ptr[0] <= 126))
+		return ptr + 1;
+	if ((ptr = osmtpd_ltok_skip_obs_ctext(ptr, 0)) != NULL)
+		return ptr;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_ccontent(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_ctext(ptr, 0)) != NULL)
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_quoted_pair(start, 0)) != NULL)
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_comment(start, 0)) != NULL)
+		return ptr;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_comment(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr++[0] != '(')
+		return optional ? start : NULL;
+	while (1) {
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] == ')')
+			return ptr + 1;
+		if ((ptr = osmtpd_ltok_skip_ccontent(ptr, 0)) == NULL)
+			return optional ? start : NULL;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_cfws(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	while (1) {
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_comment(ptr, 0)) == NULL) {
+			ptr = prev;
+			break;
+		}
+	}
+	return ptr == start && !optional ? NULL : ptr;
+}
+
+const char *
+osmtpd_ltok_skip_atext(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(start, 0)) != NULL ||
+	    (ptr = osmtpd_ltok_skip_digit(start, 0)) != NULL)
+		return ptr;
+	ptr = start;
+	if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' ||
+	    ptr[0] == '&' || ptr[0] == '\'' || ptr[0] == '*' || ptr[0] == '+' ||
+	    ptr[0] == '-' || ptr[0] == '/' || ptr[0] == '=' || ptr[0] == '?' ||
+	    ptr[0] == '^' || ptr[0] == '_' || ptr[0] == '`' || ptr[0] == '{' ||
+	    ptr[0] == '|' || ptr[0] == '}' || ptr[0] == '~')
+		return ptr + 1;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_atom(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_atext(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	do {
+		prev = ptr;
+		ptr = osmtpd_ltok_skip_atext(ptr, 1);
+	} while (prev != ptr);
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_dot_atom_text(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_atext(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	do {
+		prev = ptr;
+		ptr = osmtpd_ltok_skip_atext(ptr, 1);
+	} while (ptr != prev);
+
+	while (ptr[0] == '.') {
+		ptr++;
+		if ((ptr = osmtpd_ltok_skip_atext(ptr, 0)) == NULL)
+			return prev;
+		do {
+			prev = ptr;
+			ptr = osmtpd_ltok_skip_atext(ptr, 1);
+		} while (ptr != prev);
+	}
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_dot_atom(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_dot_atom_text(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_qtext(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) ||
+	    (ptr[0] >= 93 && ptr[0] <= 126))
+		return ptr + 1;
+	if ((ptr = osmtpd_ltok_skip_obs_qtext(ptr, 0)) != NULL)
+		return ptr;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_qcontent(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_qtext(ptr, 0)) != NULL)
+		return ptr;
+	return osmtpd_ltok_skip_quoted_pair(start, optional);
+}
+
+const char *
+osmtpd_ltok_skip_quoted_string(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_dquote(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while (1) {
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if ((ptr = osmtpd_ltok_skip_qcontent(ptr, 0)) == NULL)
+			break;
+		prev = ptr;
+	}
+	if ((ptr = osmtpd_ltok_skip_dquote(prev, 0)) == NULL)
+		return optional ? start : NULL;
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_word(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_atom(ptr, 0)) != NULL)
+		return ptr;
+	return osmtpd_ltok_skip_quoted_string(start, optional);
+}
+
+const char *
+osmtpd_ltok_skip_phrase(const char *ptr, int optional)
+{
+	/* obs-phrase is a superset of phrae */
+	return osmtpd_ltok_skip_obs_phrase(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_name_addr(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_display_name(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_angle_addr(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_angle_addr(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if (ptr++[0] != '<')
+		return osmtpd_ltok_skip_obs_angle_addr(start, optional);
+	if ((ptr = osmtpd_ltok_skip_addr_spec(ptr, 0)) == NULL)
+		return osmtpd_ltok_skip_obs_angle_addr(start, optional);
+	if (ptr++[0] != '>')
+		return osmtpd_ltok_skip_obs_angle_addr(start, optional);
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_display_name(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_phrase(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_addr_spec(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_local_part(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if (ptr++[0] != '@')
+		return optional ? start : NULL;
+	if ((ptr = osmtpd_ltok_skip_domain(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_local_part(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_dot_atom(ptr, 0)) != NULL)
+		return ptr;
+	ptr = start;
+	if ((ptr = osmtpd_ltok_skip_quoted_string(ptr, 0)) != NULL)
+		return ptr;
+	return osmtpd_ltok_skip_obs_local_part(start, optional);
+}
+
+const char *
+osmtpd_ltok_skip_domain(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_dot_atom(start, 0)) != NULL)
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_domain_literal(start, 0)) != NULL)
+		return ptr;
+	return osmtpd_ltok_skip_obs_domain(start, optional);
+}
+
+const char *
+osmtpd_ltok_skip_domain_literal(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if (ptr++[0] != '[')
+		return optional ? start : NULL;
+	while (1) {
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_dtext(ptr, 0)) == NULL) {
+			ptr = prev;
+			break;
+		}
+	}
+	if (ptr[0] != ']')
+		return optional ? start : NULL;
+	ptr++;
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_dtext(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
+		return ptr + 1;
+	return osmtpd_ltok_skip_obs_dtext(ptr, optional);
+
+}
+
+const char *
+osmtpd_ltok_skip_field_name(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_ftext(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_ftext(ptr, 0)) == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_ftext(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 33 && ptr[0] <= 57) ||
+	    (ptr[0] >= 59 && ptr[0] <= 126))
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_obs_no_ws_ctl(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 ||
+	    (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127)
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_obs_ctext(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_obs_no_ws_ctl(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_obs_qtext(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_obs_no_ws_ctl(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_obs_qp(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] == '\\' && (
+	    (ptr = osmtpd_ltok_skip_obs_no_ws_ctl(start + 1, 0)) != NULL ||
+	    (ptr = osmtpd_ltok_skip_lf(start + 1, 0)) != NULL ||
+	    (ptr = osmtpd_ltok_skip_cr(start + 1, 0)) != NULL))
+		return ptr;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_obs_phrase(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_word(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_word(ptr, 0)) != NULL)
+			continue;
+		ptr = prev;
+		if (ptr[0] == '.') {
+			ptr++;
+			continue;
+		}
+		if ((ptr = osmtpd_ltok_skip_cfws(ptr, 0)) != NULL)
+			continue;
+		return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_obs_fws(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) != NULL)
+		prev = ptr;
+
+	ptr = prev;
+	while (1) {
+		if ((ptr = osmtpd_ltok_skip_crlf(ptr, 0)) == NULL)
+			return prev;
+		if ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) == NULL)
+			return prev;
+		prev = ptr;
+		while ((ptr = osmtpd_ltok_skip_wsp(ptr, 0)) != NULL)
+			prev = ptr;
+		ptr = prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_obs_angle_addr(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if (ptr++[0] != '<')
+		return optional ? start : NULL;
+	if ((ptr = osmtpd_ltok_skip_obs_route(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if ((ptr = osmtpd_ltok_skip_addr_spec(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if (ptr++[0] != '>')
+		return optional ? start : NULL;
+	return osmtpd_ltok_skip_cfws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_obs_route(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_obs_domain_list(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if (ptr++[0] != ':')
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_obs_domain_list(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev = ptr;
+
+	while (1) {
+		if (ptr[0] == ',') {
+			ptr++;
+			prev = ptr;
+			continue;
+		} else if ((ptr = osmtpd_ltok_skip_cfws(ptr, 0)) != NULL) {
+			prev = ptr;
+			continue;
+		}
+		break;
+	}
+	ptr = prev;
+
+	if (ptr++[0] != '@')
+		return optional ? start : NULL;
+	if ((ptr = osmtpd_ltok_skip_domain(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		if (ptr[0] != ',')
+			break;
+		ptr++;
+		ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+		if (ptr[0] != '@')
+			continue;
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_domain(ptr + 1, 0)) == NULL) {
+			ptr = prev;
+			break;
+		}
+	}
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_obs_local_part(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_word(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while (ptr[0] == '.') {
+		ptr++;
+		if ((ptr = osmtpd_ltok_skip_word(ptr, 0)) == NULL)
+			return prev;
+		prev = ptr;
+	}
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_obs_domain(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_atom(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while (1) {
+		if (ptr++[0] != '.')
+			return prev;
+		if ((ptr = osmtpd_ltok_skip_atom(ptr, 0)) == NULL)
+			return prev;
+		prev = ptr;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_obs_dtext(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
+		return ptr;
+	return osmtpd_ltok_skip_quoted_pair(start, optional);
+}
+
+/* RFC 2045 - Multipurpose Internet Mail Extensions */
+const char *
+osmtpd_ltok_skip_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_token(start, 0)) != NULL)
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_quoted_string(start, 0)) != NULL)
+		return ptr;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_token(const char *ptr, int optional)
+{
+	const char *start;
+	int first = 1;
+
+	while (1) {
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_char(start, 0)) != NULL &&
+		    osmtpd_ltok_skip_sp(start, 0) == NULL &&
+		    osmtpd_ltok_skip_ctl(start, 0) == NULL &&
+		    osmtpd_ltok_skip_tspecials(start, 0) == NULL) {
+			first = 0;
+			continue;
+		}
+		return optional || !first ? start : NULL;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_tspecials(const char *ptr, int optional)
+{
+	if (ptr[0] == '(' || ptr[0] == ')' || ptr[0] == '<' || ptr[0] == '>' ||
+	    ptr[0] == '@' || ptr[0] == ',' || ptr[0] == ';' || ptr[0] == ':' ||
+	    ptr[0] == '\\' || ptr[0] == '"' || ptr[0] == '/' || ptr[0] == '[' ||
+	    ptr[0] == ']' || ptr[0] == '?' || ptr[0] == '=')
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_qp_section(const char *ptr, int optional)
+{
+	const char *prev, *last = ptr;
+
+	while (1) {
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_ptext(prev, 0)) != NULL)
+			last = ptr;
+		else if ((ptr = osmtpd_ltok_skip_sp(prev, 0)) == NULL &&
+		    (ptr = osmtpd_ltok_skip_htab(prev, 0)) == NULL)
+			return last;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_ptext(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_hex_octet(start, 0)) == NULL &&
+	    (ptr = osmtpd_ltok_skip_safe_char(start, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_safe_char(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 33 && ptr[0] <= 60) || (ptr[0] >= 62 && ptr[0] <= 126))
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_hex_octet(const char *ptr, int optional)
+{
+	const char *start = ptr;
+	char l;
+
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr++;
+	l = tolower(ptr[0]);
+	if (l == 'a' || l == 'b' || l == 'c' || l == 'd' ||
+	    l == 'e' || l == 'f')
+		ptr++;
+	else if ((ptr = osmtpd_ltok_skip_digit(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	l = tolower(ptr[0]);
+	start = ptr;
+	if (l == 'a' || l == 'b' || l == 'c' || l == 'd' ||
+	    l == 'e' || l == 'f')
+		ptr++;
+	else if ((ptr = osmtpd_ltok_skip_digit(ptr, 0)) == NULL)
+		return start;
+	return ptr;
+}
+
+/* RFC 6376 - DomainKeys Identified Mail (DKIM) Signatures */
+const char *
+osmtpd_ltok_skip_hyphenated_word(const char *ptr, int optional)
+{
+	const char *start = ptr, *end, *hyphen;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+
+	end = ptr;
+	while (1) {
+		if (ptr[0] == '-') {
+			hyphen = hyphen == NULL ? ptr - 1 : hyphen;
+			ptr++;
+			continue;
+		}
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_alpha(start, 0)) == NULL &&
+		    (ptr = osmtpd_ltok_skip_digit(start, 0)) == NULL)
+			break;
+		hyphen = NULL;
+		end = ptr;
+		
+	}
+	return hyphen == NULL ? end : hyphen;
+}
+
+const char *
+osmtpd_ltok_skip_alphadigitps(const char *ptr, int optional)
+{
+	const char *end;
+
+	if ((end = osmtpd_ltok_skip_alpha(ptr, 0)) == NULL &&
+	    (end = osmtpd_ltok_skip_digit(ptr, 0)) == NULL &&
+	    ptr[0] != '+' && ptr[0] != '/')
+		return optional ? ptr : NULL;
+	return end == NULL ? ptr + 1 : end;
+}
+
+const char *
+osmtpd_ltok_skip_base64string(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_alphadigitps(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		start = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if ((ptr = osmtpd_ltok_skip_alphadigitps(ptr, 0)) == NULL)
+			break;
+	}
+	ptr = start;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if (ptr[0] == '=') {
+		ptr++;
+		start = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] == '=')
+			ptr++;
+		else
+			ptr = start;
+	} else
+		ptr = start;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_hdr_name(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_field_name(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_qp_hdr_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_dkim_quoted_printable(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_dkim_quoted_printable(const char *ptr, int optional)
+{
+	const char *start;
+
+	while (1) {
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_fws(start, 0)) != NULL)
+			continue;
+		if ((ptr = osmtpd_ltok_skip_hex_octet(start, 0)) != NULL)
+			continue;
+		ptr = osmtpd_ltok_skip_dkim_safe_char(start, 0);
+		if (ptr == NULL)
+			break;
+	}
+	return start;
+}
+
+const char *
+osmtpd_ltok_skip_dkim_safe_char(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 0x21 && ptr[0] <= 0x3a) || ptr[0] == 0x3c ||
+	    (ptr[0] >= 0x3e && ptr[0] <= 0x7e))
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_selector(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sub_domain(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		start = ptr;
+		if (ptr[0] != '.')
+			return start;
+		ptr++;
+		if ((ptr = osmtpd_ltok_skip_sub_domain(ptr, 0)) == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_tag_list(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_tag_spec(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		/* Starting or trailing ';' */
+		if (ptr[0] != ';')
+			return ptr;
+		ptr++;
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_tag_spec(ptr, 0)) == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_tag_spec(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_tag_name(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr++;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return osmtpd_ltok_skip_fws(ptr, 1);
+}
+
+const char *
+osmtpd_ltok_skip_tag_name(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while ((ptr = osmtpd_ltok_skip_alnumpunc(ptr, 0)) != NULL)
+		prev = ptr;
+	return prev;
+}
+
+const char *
+osmtpd_ltok_skip_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_tval(ptr, 0)) == NULL)
+		return start;
+
+	while (1) {
+		start = ptr;
+		/* FWS contains WSP */
+		if ((ptr = osmtpd_ltok_skip_fws(ptr, 0)) == NULL)
+			return start;
+		prev = ptr;
+		while ((ptr = osmtpd_ltok_skip_fws(ptr, 0)) != NULL)
+			prev = ptr;
+		ptr = prev;
+		if ((ptr = osmtpd_ltok_skip_tval(ptr, 0)) == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_tval(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_valchar(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while ((ptr = osmtpd_ltok_skip_valchar(ptr, 0)) != NULL)
+		prev = ptr;
+	return prev;
+}
+
+const char *
+osmtpd_ltok_skip_valchar(const char *ptr, int optional)
+{
+	if ((ptr[0] >= 0x21 && ptr[0] <= 0x3A) ||
+	    (ptr[0] >= 0x3C && ptr[0] <= 0x7E))
+		return ptr + 1;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_alnumpunc(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(start, 0)) != NULL)
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_digit(start, 0)) != NULL)
+		return ptr;
+	if (start[0] == '_')
+		return start + 1;
+	return optional ? start : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_sig_v_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x76)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_v_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_v_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev;
+
+	if ((ptr = osmtpd_ltok_skip_digit(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		prev = ptr;
+		if ((ptr = osmtpd_ltok_skip_digit(ptr, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_sig_a_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x61)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_a_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_a_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_sig_a_tag_alg(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_a_tag_alg(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sig_a_tag_k(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if (ptr[0] != '-')
+		return optional ? start : NULL;
+	ptr++;
+	if ((ptr = osmtpd_ltok_skip_sig_a_tag_h(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_a_tag_k(const char *ptr, int optional)
+{
+	/* sha1 / sha256 covered by x-sig-a-tag-k */
+	return osmtpd_ltok_skip_x_sig_a_tag_k(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_a_tag_h(const char *ptr, int optional)
+{
+	/* rsa / ed25519 covered by x-sig-a-tag-h */
+	return osmtpd_ltok_skip_x_sig_a_tag_h(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_sig_a_tag_k(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev, *end;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while ((end = osmtpd_ltok_skip_alpha(ptr, 0)) != NULL ||
+	    (end = osmtpd_ltok_skip_digit(ptr, 0)) != NULL) {
+		ptr = end;
+		prev = end;
+	}
+	return prev;
+}
+
+const char *
+osmtpd_ltok_skip_x_sig_a_tag_h(const char *ptr, int optional)
+{
+	const char *start = ptr, *prev, *end;
+
+	if ((ptr = osmtpd_ltok_skip_alpha(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	prev = ptr;
+	while ((end = osmtpd_ltok_skip_alpha(ptr, 0)) != NULL ||
+	    (end = osmtpd_ltok_skip_digit(ptr, 0)) != NULL) {
+		ptr = end;
+		prev = end;
+	}
+	return prev;
+}
+
+const char *
+osmtpd_ltok_skip_sig_b_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x62)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_b_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_b_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_sig_b_tag_data(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_b_tag_data(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_base64string(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_bh_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x62 && ptr[0] != 0x68)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 2, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_bh_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_bh_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_sig_bh_tag_data(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_bh_tag_data(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_base64string(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_c_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x63)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_c_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_c_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sig_c_tag_alg(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	if (ptr[0] == '/') {
+		start = ptr;
+		if ((ptr = osmtpd_ltok_skip_sig_c_tag_alg(ptr, 0)) == NULL)
+			return start;
+	}
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_c_tag_alg(const char *ptr, int optional)
+{
+	/* simple / relaxed covered by x-sig-c-tag-alga */
+	return osmtpd_ltok_skip_x_sig_c_tag_alg(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_sig_c_tag_alg(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_d_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x64)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_d_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_d_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_domain_name(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_domain_name(const char *ptr, int optional)
+{
+	const char *prev = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sub_domain(ptr, 0)) == NULL)
+		return optional ? prev : NULL;
+	while (1) {
+		prev = ptr;
+		if (ptr[0] != '.' ||
+		    (ptr = osmtpd_ltok_skip_sub_domain(ptr + 1, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_sig_h_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x68)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_h_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_h_tag_value(const char *ptr, int optional)
+{
+	const char *prev = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_hdr_name(ptr, 0)) == NULL)
+		return optional ? prev : NULL;
+	while (1) {
+		prev = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] != ':')
+			return prev;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		if ((ptr = osmtpd_ltok_skip_hdr_name(ptr, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_sig_i_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x69)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_i_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_i_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	ptr = osmtpd_ltok_skip_local_part(ptr, 1);
+	if (ptr[0] != '@' ||
+	    (ptr = osmtpd_ltok_skip_domain_name(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_l_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x6c)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_l_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_l_tag_value(const char *ptr, int optional)
+{
+	size_t i;
+
+	for (i = 0; i < 76; i++) {
+		if (osmtpd_ltok_skip_digit(ptr + i, 0) == NULL)
+			break;
+	}
+	if (i >= 1 && i <= 76)
+		return ptr + i;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_sig_q_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x71)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_q_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_q_tag_value(const char *ptr, int optional)
+{
+	const char *prev = ptr;
+	if ((ptr = osmtpd_ltok_skip_sig_q_tag_method(ptr, 0)) == NULL)
+		return optional ? prev : NULL;
+	while (1) {
+		prev = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] != ':')
+			return prev;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		if ((ptr = osmtpd_ltok_skip_sig_q_tag_method(ptr, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_sig_q_tag_method(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	/* dns/txt covered by x-sig-q-tag-type ["/" x-sig-q-tag-args] */
+	if ((ptr = osmtpd_ltok_skip_x_sig_q_tag_type(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	start = ptr;
+	if (ptr[0] != '/')
+		return ptr;
+	if ((ptr = osmtpd_ltok_skip_x_sig_q_tag_args(ptr, 0)) == NULL)
+		return start;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_x_sig_q_tag_type(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_sig_q_tag_args(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_qp_hdr_value(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_s_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x73)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_s_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_s_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_selector(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_sig_t_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x74)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_t_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_t_tag_value(const char *ptr, int optional)
+{
+	size_t i;
+
+	for (i = 0; i < 12; i++) {
+		if (osmtpd_ltok_skip_digit(ptr + i, 0) == NULL)
+			break;
+	}
+	if (i >= 1 && i <= 12)
+		return ptr + i;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_sig_x_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x78)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_x_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_x_tag_value(const char *ptr, int optional)
+{
+	size_t i;
+
+	for (i = 0; i < 12; i++) {
+		if (osmtpd_ltok_skip_digit(ptr + i, 0) == NULL)
+			break;
+	}
+	if (i >= 1 && i <= 12)
+		return ptr + i;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_sig_z_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x7a)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_sig_z_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_sig_z_tag_value(const char *ptr, int optional)
+{
+	const char *prev = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_sig_z_tag_copy(ptr, 0)) == NULL)
+		return optional ? ptr : NULL;
+	while (1) {
+		prev = ptr;
+		if (ptr[0] != '|')
+			return prev;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		if ((ptr = osmtpd_ltok_skip_sig_z_tag_copy(ptr, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_sig_z_tag_copy(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_hdr_name(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if (ptr[0] != ':')
+		return optional ? start : NULL;
+	if ((ptr = osmtpd_ltok_skip_qp_hdr_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_v_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x76)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_v_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_v_tag_value(const char *ptr, int optional)
+{
+	if (ptr[0] == 0x44 && ptr[1] == 0x4b && ptr[2] == 0x49 &&
+	    ptr[3] == 0x4d && ptr[4] == 0x31)
+		return ptr + 5;
+	return optional ? ptr : NULL;
+}
+
+const char *
+osmtpd_ltok_skip_key_h_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x68)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_h_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_h_tag_value(const char *ptr, int optional)
+{
+	const char *prev = ptr;
+
+	if ((prev = osmtpd_ltok_skip_key_h_tag_alg(ptr, 0)) == NULL)
+		return optional ? prev : NULL;
+	while (1) {
+		prev = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] != ':')
+			return prev;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		if ((ptr = osmtpd_ltok_skip_key_h_tag_alg(ptr, 0)) == NULL)
+			return prev;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_key_h_tag_alg(const char *ptr, int optional)
+{
+	/* sha1 / sha256 covered by x-key-h-tag-alg */
+	return osmtpd_ltok_skip_x_key_h_tag_alg(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_key_h_tag_alg(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_key_k_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x6b)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_k_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_k_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_key_k_tag_type(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_k_tag_type(const char *ptr, int optional)
+{
+	/* rsa covered by x-key-k-tag-type */
+	return osmtpd_ltok_skip_x_key_k_tag_type(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_key_k_tag_type(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_key_n_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x6e)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_n_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_n_tag_value(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_qp_section(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_key_p_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x70)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_p_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_p_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_base64string(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_s_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x73)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_s_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_s_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_key_s_tag_type(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		start = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] != ':')
+			return start;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		ptr = osmtpd_ltok_skip_key_s_tag_type(ptr, 0);
+		if (ptr == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_key_s_tag_type(const char *ptr, int optional)
+{
+	if (ptr[0] == '*')
+		return ptr + 1;
+	/* email covered by x-key-s-tag-type */
+	return osmtpd_ltok_skip_x_key_s_tag_type(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_key_s_tag_type(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_key_t_tag(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if (ptr[0] != 0x74)
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+	if (ptr[0] != '=')
+		return optional ? start : NULL;
+	ptr = osmtpd_ltok_skip_fws(ptr, 1);
+	if ((ptr = osmtpd_ltok_skip_key_t_tag_value(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
+
+const char *
+osmtpd_ltok_skip_key_t_tag_value(const char *ptr, int optional)
+{
+	const char *start = ptr;
+
+	if ((ptr = osmtpd_ltok_skip_key_t_tag_flag(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	while (1) {
+		start = ptr;
+		ptr = osmtpd_ltok_skip_fws(ptr, 1);
+		if (ptr[0] != ':')
+			return start;
+		ptr = osmtpd_ltok_skip_fws(ptr + 1, 1);
+		ptr = osmtpd_ltok_skip_key_t_tag_flag(ptr, 0);
+		if (ptr == NULL)
+			return start;
+	}
+}
+
+const char *
+osmtpd_ltok_skip_key_t_tag_flag(const char *ptr, int optional)
+{
+	/* y / s covered by x-key-t-tag-flag */
+	return osmtpd_ltok_skip_x_key_t_tag_flag(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_x_key_t_tag_flag(const char *ptr, int optional)
+{
+	return osmtpd_ltok_skip_hyphenated_word(ptr, optional);
+}
+
+const char *
+osmtpd_ltok_skip_ar_pvalue(const char *ptr, int optional)
+{
+	const char *start = ptr, *tmp;
+
+	ptr = osmtpd_ltok_skip_cfws(ptr, 1);
+	if ((tmp = osmtpd_ltok_skip_value(ptr, 0)) != NULL)
+		return tmp;
+	ptr = osmtpd_ltok_skip_local_part(ptr, 1);
+	if (ptr[0] == '@')
+		ptr++;
+	if ((ptr = osmtpd_ltok_skip_domain(ptr, 0)) == NULL)
+		return optional ? start : NULL;
+	return ptr;
+}
blob - e57f15bdb8c8c4014114be0ac5b8eb26391c82e3 (mode 644)
blob + /dev/null
--- mheader.c
+++ /dev/null
@@ -1,1394 +0,0 @@
-/*
- * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <stddef.h>
-#include <string.h>
-#include <strings.h>
-
-#include "mheader.h"
-
-#include <stdio.h>
-
-char *
-osmtpd_mheader_skip_sp(char *ptr, int optional)
-{
-	if (ptr[0] == 0x20)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_htab(char *ptr, int optional)
-{
-	if (ptr[0] == 0x9)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_char(char *ptr, int optional)
-{
-	if (ptr[0] >= 0x01 && ptr[0] <= 0x7f)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_ctl(char *ptr, int optional)
-{
-	if ((ptr[0] >= 0x00 && ptr[0] <= 0x1f) || ptr[0] == 0x7f)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_wsp(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_sp(start, 0)) != NULL ||
-	    (ptr = osmtpd_mheader_skip_htab(start, 0)) != NULL)
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_crlf(char *ptr, int optional)
-{
-	if (ptr[0] == 13 && ptr[1] == 10)
-		return ptr + 2;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_vchar(char *ptr, int optional)
-{
-	if (ptr[0] >= 0x21 && ptr[0] <= 0x7e)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_lf(char *ptr, int optional)
-{
-	if (ptr[0] == 0xa)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_cr(char *ptr, int optional)
-{
-	if (ptr[0] == 0xd)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_alpha(char *ptr, int optional)
-{
-	if ((ptr[0] >= 0x41 && ptr[0] <= 0x5a) ||
-	    (ptr[0] >= 0x61 && ptr[0] <= 0x7a))
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_digit(char *ptr, int optional)
-{
-	if (ptr[0] >= 0x30 && ptr[0] <= 0x39)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_letdig(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) == NULL &&
-	    (ptr = osmtpd_mheader_skip_digit(start, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_ldhstring(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-	int letdig = 0;
-
-	while (1) {
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_alpha(prev, 0)) != NULL ||
-		    (ptr = osmtpd_mheader_skip_digit(prev, 0)) != NULL) {
-			letdig = 1;
-			continue;
-		}
-		if (prev[0] == '-') {
-			letdig = 0;
-			ptr = prev + 1;
-			continue;
-		}
-		ptr = prev;
-		break;
-	}
-	if (letdig)
-		return ptr;
-	if (ptr == start)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_dquote(char *ptr, int optional)
-{
-	if (ptr[0] == 0x22)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_hexoctet(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if (ptr[0] != '=')
-		return optional ? ptr : NULL;
-	ptr++;
-	if (ptr[0] == 'A' || ptr[0] == 'B' || ptr[0] == 'C' || ptr[0] == 'D' ||
-	    ptr[0] == 'E' || ptr[0] == 'F')
-		ptr++;
-	else if ((ptr = osmtpd_mheader_skip_digit(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if (ptr[0] == 'A' || ptr[0] == 'B' || ptr[0] == 'C' || ptr[0] == 'D' ||
-	    ptr[0] == 'E' || ptr[0] == 'F')
-		ptr++;
-	else if ((ptr = osmtpd_mheader_skip_digit(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_obs_fws(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
-		prev = ptr;
-
-	ptr = prev;
-	while (1) {
-		if ((ptr = osmtpd_mheader_skip_crlf(ptr, 0)) == NULL)
-			return prev;
-		if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
-			return prev;
-		prev = ptr;
-		while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
-			prev = ptr;
-		ptr = prev;
-	}
-}
-
-char *
-osmtpd_mheader_skip_fws(char *ptr, int optional)
-{
-	char *start = ptr, *prev = ptr;
-
-	while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
-		prev = ptr;
-	if ((ptr = osmtpd_mheader_skip_crlf(prev, 1)) == prev)
-		ptr = start;
-	if ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) == NULL)
-		return osmtpd_mheader_skip_obs_fws(start, optional);
-	prev = ptr;
-	while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL)
-		prev = ptr;
-	return prev;
-}
-
-char *
-osmtpd_mheader_skip_obs_no_ws_ctl(char *ptr, int optional)
-{
-	if ((ptr[0] >= 1 && ptr[0] <= 8) || ptr[0] == 11 || ptr[0] == 12 ||
-	    (ptr[0] >= 14 && ptr[0] <= 31) || ptr[0] == 127)
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_obs_ctext(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_ctext(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr[0] >= 33 && ptr[0] <= 39) || (ptr[0] >= 42 && ptr[0] <= 91) ||
-	    (ptr[0] >= 93 && ptr[0] <= 126))
-		return ptr + 1;
-	if ((ptr = osmtpd_mheader_skip_obs_ctext(ptr, 0)) != NULL)
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_obs_qp(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if (ptr[0] == '\\' && (
-	    (ptr = osmtpd_mheader_skip_obs_no_ws_ctl(start + 1, 0)) != NULL ||
-	    (ptr = osmtpd_mheader_skip_lf(start + 1, 0)) != NULL ||
-	    (ptr = osmtpd_mheader_skip_cr(start + 1, 0)) != NULL))
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_quoted_pair(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if (ptr[0] == '\\' && (
-	    (ptr = osmtpd_mheader_skip_vchar(start + 1, 0)) != NULL ||
-	    (ptr = osmtpd_mheader_skip_wsp(start + 1, 0)) != NULL))
-		return ptr;
-	return osmtpd_mheader_skip_obs_qp(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_ccontent(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_ctext(ptr, 0)) != NULL)
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_quoted_pair(start, 0)) != NULL)
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_comment(start, 0)) != NULL)
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_comment(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if (ptr++[0] != '(')
-		return optional ? start : NULL;
-	while (1) {
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if (ptr[0] == ')')
-			return ptr + 1;
-		if ((ptr = osmtpd_mheader_skip_ccontent(ptr, 0)) == NULL)
-			return optional ? start : NULL;
-	}
-}
-
-char *
-osmtpd_mheader_skip_cfws(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	while (1) {
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_comment(ptr, 0)) == NULL) {
-			ptr = prev;
-			break;
-		}
-	}
-	return ptr == start && !optional ? NULL : ptr;
-}
-
-char *
-osmtpd_mheader_skip_atext(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL ||
-	    (ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
-		return ptr;
-	ptr = start;
-	if (ptr[0] == '!' || ptr[0] == '#' || ptr[0] == '$' || ptr[0] == '%' ||
-	    ptr[0] == '&' || ptr[0] == '\'' || ptr[0] == '*' || ptr[0] == '+' ||
-	    ptr[0] == '-' || ptr[0] == '/' || ptr[0] == '=' || ptr[0] == '?' ||
-	    ptr[0] == '^' || ptr[0] == '_' || ptr[0] == '`' || ptr[0] == '{' ||
-	    ptr[0] == '|' || ptr[0] == '}' || ptr[0] == '~')
-		return ptr + 1;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_atom(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	do {
-		prev = ptr;
-		ptr = osmtpd_mheader_skip_atext(ptr, 1);
-	} while (prev != ptr);
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_dot_atom_text(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	do {
-		prev = ptr;
-		ptr = osmtpd_mheader_skip_atext(ptr, 1);
-	} while (ptr != prev);
-
-	while (ptr[0] == '.') {
-		ptr++;
-		if ((ptr = osmtpd_mheader_skip_atext(ptr, 0)) == NULL)
-			return prev;
-		do {
-			prev = ptr;
-			ptr = osmtpd_mheader_skip_atext(ptr, 1);
-		} while (ptr != prev);
-	}
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_dot_atom(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_dot_atom_text(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_obs_qtext(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_obs_no_ws_ctl(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_qtext(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if (ptr[0] == 33 || (ptr[0] >= 35 && ptr[0] <= 91) ||
-	    (ptr[0] >= 93 && ptr[0] <= 126))
-		return ptr + 1;
-	if ((ptr = osmtpd_mheader_skip_obs_qtext(ptr, 0)) != NULL)
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_qcontent(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_qtext(ptr, 0)) != NULL)
-		return ptr;
-	return osmtpd_mheader_skip_quoted_pair(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_quoted_string(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_dquote(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while (1) {
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if ((ptr = osmtpd_mheader_skip_qcontent(ptr, 0)) == NULL)
-			break;
-		prev = ptr;
-	}
-	if ((ptr = osmtpd_mheader_skip_dquote(prev, 0)) == NULL)
-		return optional ? start : NULL;
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_keyword(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_ldhstring(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_word(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) != NULL)
-		return ptr;
-	return osmtpd_mheader_skip_quoted_string(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_obs_phrase(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) != NULL)
-			continue;
-		ptr = prev;
-		if (ptr[0] == '.') {
-			ptr++;
-			continue;
-		}
-		if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL)
-			continue;
-		return prev;
-	}
-}
-
-char *
-osmtpd_mheader_skip_phrase(char *ptr, int optional)
-{
-	/* obs-phrase is a superset of phrae */
-	return osmtpd_mheader_skip_obs_phrase(ptr, optional);
-#if 0
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
-			return prev;
-	}
-#endif
-}
-
-char *
-osmtpd_mheader_skip_obs_local_part(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while (ptr[0] == '.') {
-		ptr++;
-		if ((ptr = osmtpd_mheader_skip_word(ptr, 0)) == NULL)
-			return prev;
-		prev = ptr;
-	}
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_local_part(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dot_atom(ptr, 0)) != NULL)
-		return ptr;
-	ptr = start;
-	if ((ptr = osmtpd_mheader_skip_quoted_string(ptr, 0)) != NULL)
-		return ptr;
-	return osmtpd_mheader_skip_obs_local_part(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_subdomain(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_letdig(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return osmtpd_mheader_skip_ldhstring(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_obs_dtext(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_obs_no_ws_ctl(ptr, 0)) != NULL)
-		return ptr;
-	return osmtpd_mheader_skip_quoted_pair(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_dtext(char *ptr, int optional)
-{
-	if ((ptr[0] >= 33 && ptr[0] <= 90) || (ptr[0] >= 94 && ptr[0] <= 126))
-		return ptr + 1;
-	return osmtpd_mheader_skip_obs_dtext(ptr, optional);
-
-}
-
-char *
-osmtpd_mheader_skip_domain_literal(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if (ptr++[0] != '[')
-		return optional ? start : NULL;
-	while (1) {
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_dtext(ptr, 0)) == NULL) {
-			ptr = prev;
-			break;
-		}
-	}
-	if (ptr[0] != ']')
-		return optional ? start : NULL;
-	ptr++;
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_obs_domain(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while (1) {
-		if (ptr++[0] != '.')
-			return prev;
-		if ((ptr = osmtpd_mheader_skip_atom(ptr, 0)) == NULL)
-			return prev;
-		prev = ptr;
-	}
-}
-
-char *
-osmtpd_mheader_skip_domain(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dot_atom(start, 0)) != NULL)
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_domain_literal(start, 0)) != NULL)
-		return ptr;
-	return osmtpd_mheader_skip_obs_domain(start, optional);
-}
-
-char *
-osmtpd_mheader_skip_display_name(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_phrase(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_obs_domain_list(char *ptr, int optional)
-{
-	char *start = ptr, *prev = ptr;
-
-	while (1) {
-		if (ptr[0] == ',') {
-			ptr++;
-			prev = ptr;
-			continue;
-		} else if ((ptr = osmtpd_mheader_skip_cfws(ptr, 0)) != NULL) {
-			prev = ptr;
-			continue;
-		}
-		break;
-	}
-	ptr = prev;
-
-	if (ptr++[0] != '@')
-		return optional ? start : NULL;
-	if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		if (ptr[0] != ',')
-			break;
-		ptr++;
-		ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-		if (ptr[0] != '@')
-			continue;
-		prev = ptr;
-		if ((ptr = osmtpd_mheader_skip_domain(ptr + 1, 0)) == NULL) {
-			ptr = prev;
-			break;
-		}
-	}
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_obs_route(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_obs_domain_list(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if (ptr++[0] != ':')
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_addr_spec(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_local_part(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if (ptr++[0] != '@')
-		return optional ? start : NULL;
-	if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_obs_angle_addr(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if (ptr++[0] != '<')
-		return optional ? start : NULL;
-	if ((ptr = osmtpd_mheader_skip_obs_route(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if (ptr++[0] != '>')
-		return optional ? start : NULL;
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_angle_addr(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if (ptr++[0] != '<')
-		return osmtpd_mheader_skip_obs_angle_addr(start, optional);
-	if ((ptr = osmtpd_mheader_skip_addr_spec(ptr, 0)) == NULL)
-		return osmtpd_mheader_skip_obs_angle_addr(start, optional);
-	if (ptr++[0] != '>')
-		return osmtpd_mheader_skip_obs_angle_addr(start, optional);
-	return osmtpd_mheader_skip_cfws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_name_addr(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	ptr = osmtpd_mheader_skip_display_name(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_angle_addr(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_alphadigitps(char *ptr, int optional)
-{
-	char *end;
-
-	if ((end = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL &&
-	    (end = osmtpd_mheader_skip_digit(ptr, 0)) == NULL &&
-	    ptr[0] != '+' && ptr[0] != '/')
-		return optional ? ptr : NULL;
-	return end == NULL ? ptr + 1 : end;
-}
-
-char *
-osmtpd_mheader_skip_base64string(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_alphadigitps(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if ((ptr = osmtpd_mheader_skip_alphadigitps(ptr, 0)) == NULL)
-			break;
-	}
-	ptr = start;
-	ptr = osmtpd_mheader_skip_fws(ptr, 1);
-	if (ptr[0] == '=') {
-		ptr++;
-		start = ptr;
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if (ptr[0] == '=')
-			ptr++;
-		else
-			ptr = start;
-	} else
-		ptr = start;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_hyphenatedword(char *ptr, int optional)
-{
-	char *start = ptr, *end, *hyphen;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-
-	end = ptr;
-	while (1) {
-		if (ptr[0] == '-') {
-			hyphen = hyphen == NULL ? ptr - 1 : hyphen;
-			ptr++;
-			continue;
-		}
-		start = ptr;
-		if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) == NULL &&
-		    (ptr = osmtpd_mheader_skip_digit(start, 0)) == NULL)
-			break;
-		hyphen = NULL;
-		end = ptr;
-		
-	}
-	return hyphen == NULL ? end : hyphen;
-}
-
-char *
-osmtpd_mheader_skip_ftext(char *ptr, int optional)
-{
-	if ((ptr[0] >= 33 && ptr[0] <= 57) ||
-	    (ptr[0] >= 59 && ptr[0] <= 126))
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_fieldname(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_ftext(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		if ((ptr = osmtpd_mheader_skip_ftext(ptr, 0)) == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_hdrname(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_fieldname(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_tspecials(char *ptr, int optional)
-{
-	if (ptr[0] == '(' || ptr[0] == ')' || ptr[0] == '<' || ptr[0] == '>' ||
-	    ptr[0] == '@' || ptr[0] == ',' || ptr[0] == ';' || ptr[0] == ':' ||
-	    ptr[0] == '\\' || ptr[0] == '"' || ptr[0] == '/' || ptr[0] == '[' ||
-	    ptr[0] == ']' || ptr[0] == '?' || ptr[0] == '=')
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_token(char *ptr, int optional)
-{
-	char *start;
-	int first = 1;
-
-	while (1) {
-		start = ptr;
-		if ((ptr = osmtpd_mheader_skip_char(start, 0)) != NULL &&
-		    osmtpd_mheader_skip_sp(start, 0) == NULL &&
-		    osmtpd_mheader_skip_ctl(start, 0) == NULL &&
-		    osmtpd_mheader_skip_tspecials(start, 0) == NULL) {
-			first = 0;
-			continue;
-		}
-		return optional || !first ? start : NULL;
-	}
-}
-
-char *
-osmtpd_mheader_skip_value(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_token(start, 0)) != NULL)
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_quoted_string(start, 0)) != NULL)
-		return ptr;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_dkim_safe_char(char *ptr, int optional)
-{
-	if ((ptr[0] >= 0x21 && ptr[0] <= 0x3a) || ptr[0] == 0x3c ||
-	    (ptr[0] >= 0x3e && ptr[0] <= 0x7e))
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_dkim_quoted_printable(char *ptr, int optional)
-{
-	char *start;
-
-	while (1) {
-		start = ptr;
-		if ((ptr = osmtpd_mheader_skip_fws(start, 0)) != NULL)
-			continue;
-		if ((ptr = osmtpd_mheader_skip_hexoctet(start, 0)) != NULL)
-			continue;
-		ptr = osmtpd_mheader_skip_dkim_safe_char(start, 0);
-		if (ptr == NULL)
-			break;
-	}
-	return start;
-}
-
-char *
-osmtpd_mheader_skip_dkim_qp_hdr_value(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_dkim_quoted_printable(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_alnumpunc(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(start, 0)) != NULL)
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_digit(start, 0)) != NULL)
-		return ptr;
-	if (start[0] == '_')
-		return start + 1;
-	return optional ? start : NULL;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_valchar(char *ptr, int optional)
-{
-	if ((ptr[0] >= 0x21 && ptr[0] <= 0x3A) ||
-	    (ptr[0] >= 0x3C && ptr[0] <= 0x7E))
-		return ptr + 1;
-	return optional ? ptr : NULL;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_tval(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_valchar(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while ((ptr = osmtpd_mheader_skip_dkimsig_valchar(ptr, 0)) != NULL)
-		prev = ptr;
-	return prev;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_tagvalue(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_tval(ptr, 0)) == NULL)
-		return start;
-
-	while (1) {
-		start = ptr;
-		/* FWS contains WSP */
-		if ((ptr = osmtpd_mheader_skip_fws(ptr, 0)) == NULL)
-			return start;
-		prev = ptr;
-		while ((ptr = osmtpd_mheader_skip_fws(ptr, 0)) != NULL)
-			prev = ptr;
-		ptr = prev;
-		if ((ptr = osmtpd_mheader_skip_dkimsig_tval(ptr, 0)) == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_tagname(char *ptr, int optional)
-{
-	char *start = ptr, *prev;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while ((ptr = osmtpd_mheader_skip_dkimsig_alnumpunc(ptr, 0)) != NULL)
-		prev = ptr;
-	return prev;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_tagspec(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	ptr = osmtpd_mheader_skip_fws(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_dkimsig_tagname(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	ptr = osmtpd_mheader_skip_fws(ptr, 1);
-	if (ptr[0] != '=')
-		return optional ? start : NULL;
-	ptr++;
-	ptr = osmtpd_mheader_skip_fws(ptr, 1);
-	if ((ptr = osmtpd_mheader_skip_dkimsig_tagvalue(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return osmtpd_mheader_skip_fws(ptr, 1);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_taglist(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_tagspec(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		/* Starting or trailing ';' */
-		if (ptr[0] != ';')
-			return ptr;
-		ptr++;
-		start = ptr;
-		if ((ptr = osmtpd_mheader_skip_dkimsig_tagspec(ptr, 0)) == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xsigatagh(char *ptr, int optional)
-{
-	char *start = ptr, *prev, *end;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while ((end = osmtpd_mheader_skip_alpha(ptr, 0)) != NULL ||
-	    (end = osmtpd_mheader_skip_digit(ptr, 0)) != NULL) {
-		ptr = end;
-		prev = end;
-	}
-	return prev;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xsigatagk(char *ptr, int optional)
-{
-	char *start = ptr, *prev, *end;
-
-	if ((ptr = osmtpd_mheader_skip_alpha(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	prev = ptr;
-	while ((end = osmtpd_mheader_skip_alpha(ptr, 0)) != NULL ||
-	    (end = osmtpd_mheader_skip_digit(ptr, 0)) != NULL) {
-		ptr = end;
-		prev = end;
-	}
-	return prev;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigatagh(char *ptr, int optional)
-{
-	/* rsa / ed25519 covered by x-sig-a-tag-h */
-	return osmtpd_mheader_skip_dkimsig_xsigatagh(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigatagk(char *ptr, int optional)
-{
-	/* sha1 / sha256 covered by x-sig-a-tag-k */
-	return osmtpd_mheader_skip_dkimsig_xsigatagk(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigatagalg(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_sigatagk(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	if (ptr[0] != '-')
-		return optional ? start : NULL;
-	ptr++;
-	if ((ptr = osmtpd_mheader_skip_dkimsig_sigatagh(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xsigctagalg(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigctagalg(char *ptr, int optional)
-{
-	/* simple / relaxed covered by x-sig-c-tag-alga */
-	return osmtpd_mheader_skip_dkimsig_xsigctagalg(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xkeyhtagalg(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keyhtagalg(char *ptr, int optional)
-{
-	/* sha1 / sha256 covered by x-key-h-tag-alg */
-	return osmtpd_mheader_skip_dkimsig_xkeyhtagalg(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keyhtagvalue(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_keyhtagalg(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if (ptr[0] != ':')
-			return start;
-		ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
-		ptr = osmtpd_mheader_skip_dkimsig_keyhtagalg(ptr, 0);
-		if (ptr == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xsigqtagargs(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_dkim_qp_hdr_value(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xsigqtagtype(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigqtagmethod(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	/* dns/txt covered by x-sig-q-tag-type ["/" x-sig-q-tag-args] */
-	if ((ptr = osmtpd_mheader_skip_dkimsig_xsigqtagtype(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	start = ptr;
-	if (ptr[0] != '/')
-		return ptr;
-	if ((ptr = osmtpd_mheader_skip_dkimsig_xsigqtagargs(ptr, 0)) == NULL)
-		return start;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigztagcopy(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_hdrname(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	ptr = osmtpd_mheader_skip_fws(ptr, 1);
-	if (ptr[0] != ':')
-		return optional ? start : NULL;
-	if ((ptr = osmtpd_mheader_skip_dkim_qp_hdr_value(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_sigztagvalue(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_sigztagcopy(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		if (ptr[0] != '|')
-			return start;
-		osmtpd_mheader_skip_fws(ptr + 1, 1);
-		ptr = osmtpd_mheader_skip_dkimsig_sigztagcopy(ptr, 0);
-		if (ptr == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xkeystagtype(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keystagtype(char *ptr, int optional)
-{
-	if (ptr[0] == '*')
-		return ptr + 1;
-	/* email covered by x-key-s-tag-type */
-	return osmtpd_mheader_skip_dkimsig_xkeystagtype(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keystagvalue(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_keystagtype(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if (ptr[0] != ':')
-			return start;
-		ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
-		ptr = osmtpd_mheader_skip_dkimsig_keystagtype(ptr, 0);
-		if (ptr == NULL)
-			return start;
-	}
-	
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_xkeyttagflag(char *ptr, int optional)
-{
-	return osmtpd_mheader_skip_hyphenatedword(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keyttagflag(char *ptr, int optional)
-{
-	/* y / s covered by x-key-t-tag-flag */
-	return osmtpd_mheader_skip_dkimsig_xkeyttagflag(ptr, optional);
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_keyttagvalue(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_dkimsig_keyttagflag(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		ptr = osmtpd_mheader_skip_fws(ptr, 1);
-		if (ptr[0] != ':')
-			return start;
-		ptr = osmtpd_mheader_skip_fws(ptr + 1, 1);
-		ptr = osmtpd_mheader_skip_dkimsig_keyttagflag(ptr, 0);
-		if (ptr == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_dkimsig_selector(char *ptr, int optional)
-{
-	char *start = ptr;
-
-	if ((ptr = osmtpd_mheader_skip_subdomain(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	while (1) {
-		start = ptr;
-		if (ptr[0] != '.')
-			return start;
-		ptr++;
-		if ((ptr = osmtpd_mheader_skip_subdomain(ptr, 0)) == NULL)
-			return start;
-	}
-}
-
-char *
-osmtpd_mheader_skip_ar_pvalue(char *ptr, int optional)
-{
-	char *start = ptr, *tmp;
-
-	ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-	if ((tmp = osmtpd_mheader_skip_value(ptr, 0)) != NULL)
-		return tmp;
-	ptr = osmtpd_mheader_skip_local_part(ptr, 1);
-	if (ptr[0] == '@')
-		ptr++;
-	if ((ptr = osmtpd_mheader_skip_domain(ptr, 0)) == NULL)
-		return optional ? start : NULL;
-	return ptr;
-}
-
-char *
-osmtpd_mheader_domain_uncomment(char *ptr)
-{
-	char *domain0, *domain, *tmp, *end;
-
-	if (osmtpd_mheader_skip_dot_atom(ptr, 0) != NULL) {
-		ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-		return strndup(ptr,
-		    osmtpd_mheader_skip_dot_atom_text(ptr, 0) - ptr);
-	}
-	if ((tmp = osmtpd_mheader_skip_domain_literal(ptr, 0)) != NULL) {
-		ptr = osmtpd_mheader_skip_cfws(ptr, 1) + 1;
-		domain0 = domain = strndup(ptr, (size_t)(tmp - ptr));
-		if (domain0 == NULL)
-			return NULL;
-		end = domain0 + (tmp - ptr) + 1;
-		domain++;
-		while (1) {
-			tmp = osmtpd_mheader_skip_fws(domain, 1);
-			if (tmp != domain) {
-				memmove(domain, tmp, end - tmp);
-				end -= (tmp - domain);
-			}
-			tmp = osmtpd_mheader_skip_dtext(domain, 0);
-			if (tmp == NULL)
-				break;
-			domain = tmp;
-		}
-		/* domain[0] ==  ']' */
-		domain[0] = '\0';
-		return domain0;
-	}
-	return strndup(ptr, osmtpd_mheader_skip_obs_domain(ptr, 1) - ptr);
-}
-
-/* Return the domain component of the first mailbox */
-char *
-osmtpd_mheader_from_domain(char *ptr)
-{
-	char *tmp;
-
-	/* from */
-	if (strncasecmp(ptr, "from:", 5) == 0) {
-		ptr += 5;
-	/* obs-from */
-	} else if (strncasecmp(ptr, "from", 4) == 0) {
-		ptr += 4;
-		do {
-			tmp = ptr;
-		} while ((ptr = osmtpd_mheader_skip_wsp(ptr, 0)) != NULL);
-		ptr = tmp;
-		if (ptr++[0] != ':')
-			return NULL;
-	} else {
-		errno = EINVAL;
-		return NULL;
-	}
-
-	/* Both from and obs-from use Mailbox-list CRLF */
-	/* obs-mbox-list has just a prefix compared to mailbox-list */
-	while (1) {
-		tmp = ptr;
-		ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-		if (ptr++[0] != ',') {
-			ptr = tmp;
-			break;
-		}
-	}
-	/* We're only interested in the first mailbox */
-	if (osmtpd_mheader_skip_name_addr(ptr, 0) != NULL) {
-		ptr = osmtpd_mheader_skip_display_name(ptr, 1);
-		ptr = osmtpd_mheader_skip_cfws(ptr, 1);
-		/* < */
-		ptr++;
-		/* addr-spec */
-		ptr = osmtpd_mheader_skip_local_part(ptr, 0);
-		/* @ */
-		ptr++;
-		return osmtpd_mheader_domain_uncomment(ptr);
-	}
-	if (osmtpd_mheader_skip_addr_spec(ptr, 0) != NULL) {
-		ptr = osmtpd_mheader_skip_local_part(ptr, 0);
-		/* @ */
-		ptr++;
-		return osmtpd_mheader_domain_uncomment(ptr);
-	}
-	errno = EINVAL;
-	return NULL;
-}
-
-char *
-osmtpd_mheader_quoted_string_normalize(char *ptr)
-{
-	char *end;
-	size_t d = 0, s;
-
-	end = osmtpd_mheader_skip_cfws(ptr, 1);
-	s = end - ptr;
-	if (osmtpd_mheader_skip_dquote(end, 0) == NULL)
-		return NULL;
-	ptr[d++] = ptr[s++];
-	while (ptr[s] != '\0') {
-		if (osmtpd_mheader_skip_quoted_pair(ptr + s, 0) != NULL) {
-			end = osmtpd_mheader_skip_qtext(ptr + s + 1, 0);
-			if (end != NULL)
-				s++;
-			else
-				ptr[d++] = ptr[s++];
-			ptr[d++] = ptr[s++];
-			continue;
-		} else if (osmtpd_mheader_skip_qtext(ptr + s, 0) != NULL) {
-			ptr[d++] = ptr[s++];
-		} else if ((end = osmtpd_mheader_skip_fws(
-		    ptr + s, 0)) != NULL) {
-			ptr[d++] = ' ';
-			s = end - ptr;
-		} else
-			return NULL;
-	}
-	if (osmtpd_mheader_skip_dquote(end, 0) == NULL)
-		return NULL;
-	ptr[d++] = ptr[s++];
-	end = osmtpd_mheader_skip_cfws(ptr + s, 1);
-	if (end[0] != '\0')
-		return NULL;
-	ptr[d] = '\0';
-	return ptr;
-}
blob - /dev/null
blob + 53f593f7ff572b8a02bdb6d3b2822dad66daccc0 (mode 644)
--- /dev/null
+++ ltok.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* RFC 5234 - Augmented BNF for Syntax Specifications: ABNF */
+const char *osmtpd_ltok_skip_alpha(const char *, int);
+const char *osmtpd_ltok_skip_bit(const char *, int);
+const char *osmtpd_ltok_skip_char(const char *, int);
+const char *osmtpd_ltok_skip_cr(const char *, int);
+const char *osmtpd_ltok_skip_crlf(const char *, int);
+const char *osmtpd_ltok_skip_ctl(const char *, int);
+const char *osmtpd_ltok_skip_digit(const char *, int);
+const char *osmtpd_ltok_skip_dquote(const char *, int);
+const char *osmtpd_ltok_skip_hexdig(const char *, int);
+const char *osmtpd_ltok_skip_htab(const char *, int);
+const char *osmtpd_ltok_skip_lf(const char *, int);
+const char *osmtpd_ltok_skip_octet(const char *, int);
+const char *osmtpd_ltok_skip_sp(const char *, int);
+const char *osmtpd_ltok_skip_vchar(const char *, int);
+const char *osmtpd_ltok_skip_wsp(const char *, int);
+
+/* RFC 5321 - Simple Mail Transfer Protocol */
+const char *osmtpd_ltok_skip_keyword(const char *, int);
+const char *osmtpd_ltok_skip_sub_domain(const char *, int);
+const char *osmtpd_ltok_skip_let_dig(const char *, int);
+const char *osmtpd_ltok_skip_ldh_string(const char *, int);
+
+/* RFC 5322 - Internet Message Format */
+const char *osmtpd_ltok_skip_quoted_pair(const char *, int);
+const char *osmtpd_ltok_skip_fws(const char *, int);
+const char *osmtpd_ltok_skip_ctext(const char *, int);
+const char *osmtpd_ltok_skip_ccontent(const char *, int);
+const char *osmtpd_ltok_skip_comment(const char *, int);
+const char *osmtpd_ltok_skip_cfws(const char *, int);
+const char *osmtpd_ltok_skip_atext(const char *, int);
+const char *osmtpd_ltok_skip_atom(const char *, int);
+const char *osmtpd_ltok_skip_dot_atom_text(const char *, int);
+const char *osmtpd_ltok_skip_dot_atom(const char *, int);
+const char *osmtpd_ltok_skip_qtext(const char *, int);
+const char *osmtpd_ltok_skip_qcontent(const char *, int);
+const char *osmtpd_ltok_skip_quoted_string(const char *, int);
+const char *osmtpd_ltok_skip_word(const char *, int);
+const char *osmtpd_ltok_skip_phrase(const char *, int);
+const char *osmtpd_ltok_skip_name_addr(const char *, int);
+const char *osmtpd_ltok_skip_angle_addr(const char *, int);
+const char *osmtpd_ltok_skip_display_name(const char *, int);
+const char *osmtpd_ltok_skip_addr_spec(const char *, int);
+const char *osmtpd_ltok_skip_local_part(const char *, int);
+const char *osmtpd_ltok_skip_domain(const char *, int);
+const char *osmtpd_ltok_skip_domain_literal(const char *, int);
+const char *osmtpd_ltok_skip_dtext(const char *, int);
+const char *osmtpd_ltok_skip_field_name(const char *, int);
+const char *osmtpd_ltok_skip_ftext(const char *, int);
+const char *osmtpd_ltok_skip_obs_no_ws_ctl(const char *, int);
+const char *osmtpd_ltok_skip_obs_ctext(const char *, int);
+const char *osmtpd_ltok_skip_obs_qtext(const char *, int);
+const char *osmtpd_ltok_skip_obs_qp(const char *, int);
+const char *osmtpd_ltok_skip_obs_phrase(const char *, int);
+const char *osmtpd_ltok_skip_obs_fws(const char *, int);
+const char *osmtpd_ltok_skip_obs_angle_addr(const char *, int);
+const char *osmtpd_ltok_skip_obs_route(const char *, int);
+const char *osmtpd_ltok_skip_obs_domain_list(const char *, int);
+const char *osmtpd_ltok_skip_obs_local_part(const char *, int);
+const char *osmtpd_ltok_skip_obs_domain(const char *, int);
+const char *osmtpd_ltok_skip_obs_dtext(const char *, int);
+
+/* RFC 2045 - Multipurpose Internet Mail Extensions */
+const char *osmtpd_ltok_skip_value(const char *, int);
+const char *osmtpd_ltok_skip_token(const char *, int);
+const char *osmtpd_ltok_skip_tspecials(const char *, int);
+const char *osmtpd_ltok_skip_qp_section(const char *, int);
+const char *osmtpd_ltok_skip_ptext(const char *, int);
+const char *osmtpd_ltok_skip_safe_char(const char *, int);
+const char *osmtpd_ltok_skip_hex_octet(const char *, int);
+
+/* RFC 6376 - DomainKeys Identified Mail (DKIM) Signatures */
+const char *osmtpd_ltok_skip_hyphenated_word(const char *, int);
+const char *osmtpd_ltok_skip_alphadigitps(const char *, int);
+const char *osmtpd_ltok_skip_base64string(const char *, int);
+const char *osmtpd_ltok_skip_hdr_name(const char *, int);
+const char *osmtpd_ltok_skip_qp_hdr_value(const char *, int);
+const char *osmtpd_ltok_skip_dkim_quoted_printable(const char *, int);
+const char *osmtpd_ltok_skip_dkim_safe_char(const char *, int);
+const char *osmtpd_ltok_skip_selector(const char *, int);
+const char *osmtpd_ltok_skip_tag_list(const char *, int);
+const char *osmtpd_ltok_skip_tag_spec(const char *, int);
+const char *osmtpd_ltok_skip_tag_name(const char *, int);
+const char *osmtpd_ltok_skip_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_tval(const char *, int);
+const char *osmtpd_ltok_skip_valchar(const char *, int);
+const char *osmtpd_ltok_skip_alnumpunc(const char *, int);
+const char *osmtpd_ltok_skip_sig_v_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_v_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_a_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_a_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_a_tag_alg(const char *, int);
+const char *osmtpd_ltok_skip_sig_a_tag_k(const char *, int);
+const char *osmtpd_ltok_skip_sig_a_tag_h(const char *, int);
+const char *osmtpd_ltok_skip_x_sig_a_tag_k(const char *, int);
+const char *osmtpd_ltok_skip_x_sig_a_tag_h(const char *, int);
+const char *osmtpd_ltok_skip_sig_b_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_b_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_b_tag_data(const char *, int);
+const char *osmtpd_ltok_skip_sig_bh_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_bh_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_bh_tag_data(const char *, int);
+const char *osmtpd_ltok_skip_sig_c_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_c_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_c_tag_alg(const char *, int);
+const char *osmtpd_ltok_skip_x_sig_c_tag_alg(const char *, int);
+const char *osmtpd_ltok_skip_sig_d_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_d_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_domain_name(const char *, int);
+const char *osmtpd_ltok_skip_sig_h_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_h_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_i_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_i_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_l_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_l_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_q_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_q_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_q_tag_method(const char *, int);
+const char *osmtpd_ltok_skip_x_sig_q_tag_type(const char *, int);
+const char *osmtpd_ltok_skip_x_sig_q_tag_args(const char *, int);
+const char *osmtpd_ltok_skip_sig_s_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_s_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_t_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_t_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_x_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_x_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_z_tag(const char *, int);
+const char *osmtpd_ltok_skip_sig_z_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_sig_z_tag_copy(const char *, int);
+const char *osmtpd_ltok_skip_key_v_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_v_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_h_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_h_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_h_tag_alg(const char *, int);
+const char *osmtpd_ltok_skip_x_key_h_tag_alg(const char *, int);
+const char *osmtpd_ltok_skip_key_k_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_k_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_k_tag_type(const char *, int);
+const char *osmtpd_ltok_skip_x_key_k_tag_type(const char *, int);
+const char *osmtpd_ltok_skip_key_n_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_n_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_p_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_p_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_s_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_s_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_s_tag_type(const char *, int);
+const char *osmtpd_ltok_skip_x_key_s_tag_type(const char *, int);
+const char *osmtpd_ltok_skip_key_t_tag(const char *, int);
+const char *osmtpd_ltok_skip_key_t_tag_value(const char *, int);
+const char *osmtpd_ltok_skip_key_t_tag_flag(const char *, int);
+const char *osmtpd_ltok_skip_x_key_t_tag_flag(const char *, int);
+
+/* Authentication-Results */
+const char *osmtpd_ltok_skip_ar_pvalue(const char *, int);
+
+const char *osmtpd_ltok_domain_uncomment(const char *);
+const char *osmtpd_ltok_from_domain(const char *);
+
+const char *osmtpd_ltok_quoted_string_normalize(const char *);
blob - 7a68b1f66b7e086387bc60bc77076b4a36d5f149 (mode 644)
blob + /dev/null
--- mheader.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-char *osmtpd_mheader_skip_sp(char *, int);
-char *osmtpd_mheader_skip_htab(char *, int);
-char *osmtpd_mheader_skip_char(char *, int);
-char *osmtpd_mheader_skip_ctl(char *, int);
-char *osmtpd_mheader_skip_wsp(char *, int);
-char *osmtpd_mheader_skip_crlf(char *, int);
-char *osmtpd_mheader_skip_vchar(char *, int);
-char *osmtpd_mheader_skip_lf(char *, int);
-char *osmtpd_mheader_skip_cr(char *, int);
-char *osmtpd_mheader_skip_alpha(char *, int);
-char *osmtpd_mheader_skip_digit(char *, int);
-char *osmtpd_mheader_skip_letdig(char *, int);
-char *osmtpd_mheader_skip_ldhstring(char *, int);
-char *osmtpd_mheader_skip_dquote(char *, int);
-char *osmtpd_mheader_skip_hexoctet(char *, int);
-char *osmtpd_mheader_skip_obs_fws(char *, int);
-char *osmtpd_mheader_skip_fws(char *, int);
-char *osmtpd_mheader_skip_obs_no_ws_ctl(char *, int);
-char *osmtpd_mheader_skip_obs_ctext(char *, int);
-char *osmtpd_mheader_skip_obs_qp(char *, int);
-char *osmtpd_mheader_skip_quoted_pair(char *, int);
-char *osmtpd_mheader_skip_ctext(char *, int);
-char *osmtpd_mheader_skip_ccontent(char *, int);
-char *osmtpd_mheader_skip_comment(char *, int);
-char *osmtpd_mheader_skip_cfws(char *, int);
-char *osmtpd_mheader_skip_atext(char *, int);
-char *osmtpd_mheader_skip_atom(char *, int);
-char *osmtpd_mheader_skip_dot_atom_text(char *, int);
-char *osmtpd_mheader_skip_dot_atom(char *, int);
-char *osmtpd_mheader_skip_obs_qtext(char *, int);
-char *osmtpd_mheader_skip_qtext(char *, int);
-char *osmtpd_mheader_skip_qcontent(char *, int);
-char *osmtpd_mheader_skip_quoted_string(char *, int);
-char *osmtpd_mheader_skip_keyword(char *, int);
-char *osmtpd_mheader_skip_word(char *, int);
-char *osmtpd_mheader_skip_obs_phrase(char *, int);
-char *osmtpd_mheader_skip_phrase(char *, int);
-char *osmtpd_mheader_skip_obs_local_part(char *, int);
-char *osmtpd_mheader_skip_local_part(char *, int);
-char *osmtpd_mheader_skip_subdomain(char *, int);
-char *osmtpd_mheader_skip_obs_dtext(char *, int);
-char *osmtpd_mheader_skip_dtext(char *, int);
-char *osmtpd_mheader_skip_domain_literal(char *, int);
-char *osmtpd_mheader_skip_obs_domain(char *, int);
-char *osmtpd_mheader_skip_domain(char *, int);
-char *osmtpd_mheader_skip_display_name(char *, int);
-char *osmtpd_mheader_skip_obs_domain_list(char *, int);
-char *osmtpd_mheader_skip_obs_route(char *, int);
-char *osmtpd_mheader_skip_addr_spec(char *, int);
-char *osmtpd_mheader_skip_obs_angle_addr(char *, int);
-char *osmtpd_mheader_skip_angle_addr(char *, int);
-char *osmtpd_mheader_skip_name_addr(char *, int);
-char *osmtpd_mheader_skip_alphadigitps(char *, int);
-char *osmtpd_mheader_skip_base64string(char *, int);
-char *osmtpd_mheader_skip_hyphenatedword(char *, int);
-char *osmtpd_mheader_skip_ftext(char *, int);
-char *osmtpd_mheader_skip_fieldname(char *, int);
-char *osmtpd_mheader_skip_hdrname(char *, int);
-char *osmtpd_mheader_skip_tspecials(char *, int);
-char *osmtpd_mheader_skip_token(char *, int);
-char *osmtpd_mheader_skip_value(char *, int);
-
-/* DKIM-Signature */
-char *osmtpd_mheader_skip_dkim_safe_char(char *, int);
-char *osmtpd_mheader_skip_dkim_quoted_printable(char *, int);
-char *osmtpd_mheader_skip_dkim_qp_hdr_value(char *, int);
-char *osmtpd_mheader_skip_dkimsig_alnumpunc(char *, int);
-char *osmtpd_mheader_skip_dkimsig_valchar(char *, int);
-char *osmtpd_mheader_skip_dkimsig_tval(char *, int);
-char *osmtpd_mheader_skip_dkimsig_tagvalue(char *, int);
-char *osmtpd_mheader_skip_dkimsig_tagname(char *, int);
-char *osmtpd_mheader_skip_dkimsig_tagspec(char *, int);
-char *osmtpd_mheader_skip_dkimsig_taglist(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xsigatagh(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xsigatagk(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigatagh(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigatagk(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigatagalg(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xsigctagalg(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigctagalg(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xkeyhtagalg(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xsigqtagargs(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xsigqtagtype(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigqtagmethod(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigztagcopy(char *, int);
-char *osmtpd_mheader_skip_dkimsig_sigztagvalue(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keyhtagalg(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keyhtagvalue(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xkeystagtype(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keystagtype(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keystagvalue(char *, int);
-char *osmtpd_mheader_skip_dkimsig_xkeyttagflag(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keyttagflag(char *, int);
-char *osmtpd_mheader_skip_dkimsig_keyttagvalue(char *, int);
-char *osmtpd_mheader_skip_dkimsig_selector(char *, int);
-
-/* Authentication-Results */
-char *osmtpd_mheader_skip_ar_pvalue(char *, int);
-
-char *osmtpd_mheader_domain_uncomment(char *);
-char *osmtpd_mheader_from_domain(char *);
-
-char *osmtpd_mheader_quoted_string_normalize(char *);