aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/rfc5322.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/rfc5322.c')
-rw-r--r--smtpd/rfc5322.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/smtpd/rfc5322.c b/smtpd/rfc5322.c
new file mode 100644
index 00000000..0af66772
--- /dev/null
+++ b/smtpd/rfc5322.c
@@ -0,0 +1,266 @@
+/* $OpenBSD: rfc5322.c,v 1.2 2018/10/24 18:59:29 gilles Exp $ */
+
+/*
+ * Copyright (c) 2018 Eric Faurot <eric@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 "includes.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rfc5322.h"
+
+struct buf {
+ char *buf;
+ size_t bufsz;
+ size_t buflen;
+ size_t bufmax;
+};
+
+static int buf_alloc(struct buf *, size_t);
+static int buf_grow(struct buf *, size_t);
+static int buf_cat(struct buf *, const char *);
+
+struct rfc5322_parser {
+ const char *line;
+ int state; /* last parser state */
+ int next; /* parser needs data */
+ int unfold;
+ const char *currhdr;
+ struct buf hdr;
+ struct buf val;
+};
+
+struct rfc5322_parser *
+rfc5322_parser_new(void)
+{
+ struct rfc5322_parser *parser;
+
+ parser = calloc(1, sizeof(*parser));
+ if (parser == NULL)
+ return NULL;
+
+ rfc5322_clear(parser);
+ parser->hdr.bufmax = 1024;
+ parser->val.bufmax = 65536;
+
+ return parser;
+}
+
+void
+rfc5322_free(struct rfc5322_parser *parser)
+{
+ free(parser->hdr.buf);
+ free(parser->val.buf);
+ free(parser);
+}
+
+void
+rfc5322_clear(struct rfc5322_parser *parser)
+{
+ parser->line = NULL;
+ parser->state = RFC5322_NONE;
+ parser->next = 0;
+ parser->hdr.buflen = 0;
+ parser->val.buflen = 0;
+}
+
+int
+rfc5322_push(struct rfc5322_parser *parser, const char *line)
+{
+ if (parser->line) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ parser->line = line;
+ parser->next = 0;
+
+ return 0;
+}
+
+int
+rfc5322_unfold_header(struct rfc5322_parser *parser)
+{
+ if (parser->unfold) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ if (parser->currhdr == NULL) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ if (buf_cat(&parser->val, parser->currhdr) == -1)
+ return -1;
+
+ parser->currhdr = NULL;
+ parser->unfold = 1;
+
+ return 0;
+}
+
+static int
+_rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
+{
+ size_t len;
+ const char *pos, *line;
+
+ line = parser->line;
+
+ switch(parser->state) {
+
+ case RFC5322_HEADER_START:
+ case RFC5322_HEADER_CONT:
+ res->hdr = parser->hdr.buf;
+
+ if (line && (line[0] == ' ' || line[0] == '\t')) {
+ parser->line = NULL;
+ parser->next = 1;
+ if (parser->unfold) {
+ if (buf_cat(&parser->val, "\n") == -1 ||
+ buf_cat(&parser->val, line) == -1)
+ return -1;
+ }
+ res->value = line;
+ return RFC5322_HEADER_CONT;
+ }
+
+ if (parser->unfold) {
+ parser->val.buflen = 0;
+ parser->unfold = 0;
+ res->value = parser->val.buf;
+ }
+ return RFC5322_HEADER_END;
+
+ case RFC5322_NONE:
+ case RFC5322_HEADER_END:
+ if (line && (pos = strchr(line, ':'))) {
+ len = pos - line;
+ if (buf_grow(&parser->hdr, len + 1) == -1)
+ return -1;
+ (void)memcpy(parser->hdr.buf, line, len);
+ parser->hdr.buf[len] = '\0';
+ parser->hdr.buflen = len + 1;
+ parser->line = NULL;
+ parser->next = 1;
+ parser->currhdr = pos + 1;
+ res->hdr = parser->hdr.buf;
+ res->value = pos + 1;
+ return RFC5322_HEADER_START;
+ }
+
+ return RFC5322_END_OF_HEADERS;
+
+ case RFC5322_END_OF_HEADERS:
+ if (line == NULL)
+ return RFC5322_END_OF_MESSAGE;
+
+ if (line[0] == '\0') {
+ parser->line = NULL;
+ parser->next = 1;
+ res->value = line;
+ return RFC5322_BODY_START;
+ }
+
+ errno = EINVAL;
+ return -1;
+
+ case RFC5322_BODY_START:
+ case RFC5322_BODY:
+ if (line == NULL)
+ return RFC5322_END_OF_MESSAGE;
+
+ parser->line = NULL;
+ parser->next = 1;
+ res->value = line;
+ return RFC5322_BODY;
+
+ case RFC5322_END_OF_MESSAGE:
+ errno = ENOMSG;
+ return -1;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+int
+rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
+{
+ memset(res, 0, sizeof(*res));
+
+ if (parser->next)
+ return RFC5322_NONE;
+
+ return (parser->state = _rfc5322_next(parser, res));
+}
+
+static int
+buf_alloc(struct buf *b, size_t need)
+{
+ char *buf;
+ size_t alloc;
+
+ if (b->buf && b->bufsz >= need)
+ return 0;
+
+ if (need >= b->bufmax) {
+ errno = ERANGE;
+ return -1;
+ }
+
+#define N 256
+ alloc = N * (need / N) + ((need % N) ? N : 0);
+#undef N
+ buf = reallocarray(b->buf, alloc, 1);
+ if (buf == NULL)
+ return -1;
+
+ b->buf = buf;
+ b->bufsz = alloc;
+
+ return 0;
+}
+
+static int
+buf_grow(struct buf *b, size_t sz)
+{
+ if (SIZE_T_MAX - b->buflen <= sz) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ return buf_alloc(b, b->buflen + sz);
+}
+
+static int
+buf_cat(struct buf *b, const char *s)
+{
+ size_t len = strlen(s);
+
+ if (buf_grow(b, len + 1) == -1)
+ return -1;
+
+ (void)memmove(b->buf + b->buflen, s, len + 1);
+ b->buflen += len;
+ return 0;
+}