xref: /openbsd/usr.sbin/smtpd/rfc5322.c (revision d415bd75)
1 /*	$OpenBSD: rfc5322.c,v 1.3 2021/06/14 17:58:16 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Eric Faurot <eric@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "rfc5322.h"
25 
26 struct buf {
27 	char	*buf;
28 	size_t	 bufsz;
29 	size_t	 buflen;
30 	size_t	 bufmax;
31 };
32 
33 static int buf_alloc(struct buf *, size_t);
34 static int buf_grow(struct buf *, size_t);
35 static int buf_cat(struct buf *, const char *);
36 
37 struct rfc5322_parser {
38 	const char	*line;
39 	int		 state;		/* last parser state */
40 	int		 next;		/* parser needs data */
41 	int		 unfold;
42 	const char	*currhdr;
43 	struct buf	 hdr;
44 	struct buf	 val;
45 };
46 
47 struct rfc5322_parser *
48 rfc5322_parser_new(void)
49 {
50 	struct rfc5322_parser *parser;
51 
52 	parser = calloc(1, sizeof(*parser));
53 	if (parser == NULL)
54 		return NULL;
55 
56 	rfc5322_clear(parser);
57 	parser->hdr.bufmax = 1024;
58 	parser->val.bufmax = 65536;
59 
60 	return parser;
61 }
62 
63 void
64 rfc5322_free(struct rfc5322_parser *parser)
65 {
66 	free(parser->hdr.buf);
67 	free(parser->val.buf);
68 	free(parser);
69 }
70 
71 void
72 rfc5322_clear(struct rfc5322_parser *parser)
73 {
74 	parser->line = NULL;
75 	parser->state = RFC5322_NONE;
76 	parser->next = 0;
77 	parser->hdr.buflen = 0;
78 	parser->val.buflen = 0;
79 }
80 
81 int
82 rfc5322_push(struct rfc5322_parser *parser, const char *line)
83 {
84 	if (parser->line) {
85 		errno = EALREADY;
86 		return -1;
87 	}
88 
89 	parser->line = line;
90 	parser->next = 0;
91 
92 	return 0;
93 }
94 
95 int
96 rfc5322_unfold_header(struct rfc5322_parser *parser)
97 {
98 	if (parser->unfold) {
99 		errno = EALREADY;
100 		return -1;
101 	}
102 
103 	if (parser->currhdr == NULL) {
104 		errno = EOPNOTSUPP;
105 		return -1;
106 	}
107 
108 	if (buf_cat(&parser->val, parser->currhdr) == -1)
109 		return -1;
110 
111 	parser->currhdr = NULL;
112 	parser->unfold = 1;
113 
114 	return 0;
115 }
116 
117 static int
118 _rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
119 {
120 	size_t len;
121 	const char *pos, *line;
122 
123 	line = parser->line;
124 
125 	switch(parser->state) {
126 
127 	case RFC5322_HEADER_START:
128 	case RFC5322_HEADER_CONT:
129 		res->hdr = parser->hdr.buf;
130 
131 		if (line && (line[0] == ' ' || line[0] == '\t')) {
132 			parser->line = NULL;
133 			parser->next = 1;
134 			if (parser->unfold) {
135 				if (buf_cat(&parser->val, "\n") == -1 ||
136 				    buf_cat(&parser->val, line) == -1)
137 					return -1;
138 			}
139 			res->value = line;
140 			return RFC5322_HEADER_CONT;
141 		}
142 
143 		if (parser->unfold) {
144 			parser->val.buflen = 0;
145 			parser->unfold = 0;
146 			res->value = parser->val.buf;
147 		}
148 		return RFC5322_HEADER_END;
149 
150 	case RFC5322_NONE:
151 	case RFC5322_HEADER_END:
152 		if (line && (pos = strchr(line, ':'))) {
153 			len = pos - line;
154 			if (buf_grow(&parser->hdr, len + 1) == -1)
155 				return -1;
156 			(void)memcpy(parser->hdr.buf, line, len);
157 			parser->hdr.buf[len] = '\0';
158 			parser->hdr.buflen = len + 1;
159 			parser->line = NULL;
160 			parser->next = 1;
161 			parser->currhdr = pos + 1;
162 			res->hdr = parser->hdr.buf;
163 			res->value = pos + 1;
164 			return RFC5322_HEADER_START;
165 		}
166 
167 		return RFC5322_END_OF_HEADERS;
168 
169 	case RFC5322_END_OF_HEADERS:
170 		if (line == NULL)
171 			return RFC5322_END_OF_MESSAGE;
172 
173 		if (line[0] == '\0') {
174 			parser->line = NULL;
175 			parser->next = 1;
176 			res->value = line;
177 			return RFC5322_BODY_START;
178 		}
179 
180 		errno = EINVAL;
181 		return -1;
182 
183 	case RFC5322_BODY_START:
184 	case RFC5322_BODY:
185 		if (line == NULL)
186 			return RFC5322_END_OF_MESSAGE;
187 
188 		parser->line = NULL;
189 		parser->next = 1;
190 		res->value = line;
191 		return RFC5322_BODY;
192 
193 	case RFC5322_END_OF_MESSAGE:
194 		errno = ENOMSG;
195 		return -1;
196 
197 	default:
198 		errno = EINVAL;
199 		return -1;
200 	}
201 }
202 
203 int
204 rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
205 {
206 	memset(res, 0, sizeof(*res));
207 
208 	if (parser->next)
209 		return RFC5322_NONE;
210 
211 	return (parser->state = _rfc5322_next(parser, res));
212 }
213 
214 static int
215 buf_alloc(struct buf *b, size_t need)
216 {
217 	char *buf;
218 	size_t alloc;
219 
220 	if (b->buf && b->bufsz >= need)
221 		return 0;
222 
223 	if (need >= b->bufmax) {
224 		errno = ERANGE;
225 		return -1;
226 	}
227 
228 #define N 256
229 	alloc = N * (need / N) + ((need % N) ? N : 0);
230 #undef N
231 	buf = reallocarray(b->buf, alloc, 1);
232 	if (buf == NULL)
233 		return -1;
234 
235 	b->buf = buf;
236 	b->bufsz = alloc;
237 
238 	return 0;
239 }
240 
241 static int
242 buf_grow(struct buf *b, size_t sz)
243 {
244 	if (SIZE_T_MAX - b->buflen <= sz) {
245 		errno = ERANGE;
246 		return -1;
247 	}
248 
249 	return buf_alloc(b, b->buflen + sz);
250 }
251 
252 static int
253 buf_cat(struct buf *b, const char *s)
254 {
255 	size_t len = strlen(s);
256 
257 	if (buf_grow(b, len + 1) == -1)
258 		return -1;
259 
260 	(void)memmove(b->buf + b->buflen, s, len + 1);
261 	b->buflen += len;
262 	return 0;
263 }
264