xref: /openbsd-src/usr.sbin/smtpd/rfc5322.c (revision a5bc80fb5b01fd00243844063b729cef2049cee2)
1 /*	$OpenBSD: rfc5322.c,v 1.4 2023/12/05 13:38:25 op 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 *
rfc5322_parser_new(void)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
rfc5322_free(struct rfc5322_parser * parser)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
rfc5322_clear(struct rfc5322_parser * parser)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
rfc5322_push(struct rfc5322_parser * parser,const char * line)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
rfc5322_unfold_header(struct rfc5322_parser * parser)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
_rfc5322_next(struct rfc5322_parser * parser,struct rfc5322_result * res)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 && line[0] != ' ' && line[0] != '\t' &&
153 		    (pos = strchr(line, ':'))) {
154 			len = pos - line;
155 			if (buf_grow(&parser->hdr, len + 1) == -1)
156 				return -1;
157 			(void)memcpy(parser->hdr.buf, line, len);
158 			parser->hdr.buf[len] = '\0';
159 			parser->hdr.buflen = len + 1;
160 			parser->line = NULL;
161 			parser->next = 1;
162 			parser->currhdr = pos + 1;
163 			res->hdr = parser->hdr.buf;
164 			res->value = pos + 1;
165 			return RFC5322_HEADER_START;
166 		}
167 
168 		return RFC5322_END_OF_HEADERS;
169 
170 	case RFC5322_END_OF_HEADERS:
171 		if (line == NULL)
172 			return RFC5322_END_OF_MESSAGE;
173 
174 		if (line[0] == '\0') {
175 			parser->line = NULL;
176 			parser->next = 1;
177 			res->value = line;
178 			return RFC5322_BODY_START;
179 		}
180 
181 		errno = EINVAL;
182 		return -1;
183 
184 	case RFC5322_BODY_START:
185 	case RFC5322_BODY:
186 		if (line == NULL)
187 			return RFC5322_END_OF_MESSAGE;
188 
189 		parser->line = NULL;
190 		parser->next = 1;
191 		res->value = line;
192 		return RFC5322_BODY;
193 
194 	case RFC5322_END_OF_MESSAGE:
195 		errno = ENOMSG;
196 		return -1;
197 
198 	default:
199 		errno = EINVAL;
200 		return -1;
201 	}
202 }
203 
204 int
rfc5322_next(struct rfc5322_parser * parser,struct rfc5322_result * res)205 rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res)
206 {
207 	memset(res, 0, sizeof(*res));
208 
209 	if (parser->next)
210 		return RFC5322_NONE;
211 
212 	return (parser->state = _rfc5322_next(parser, res));
213 }
214 
215 static int
buf_alloc(struct buf * b,size_t need)216 buf_alloc(struct buf *b, size_t need)
217 {
218 	char *buf;
219 	size_t alloc;
220 
221 	if (b->buf && b->bufsz >= need)
222 		return 0;
223 
224 	if (need >= b->bufmax) {
225 		errno = ERANGE;
226 		return -1;
227 	}
228 
229 #define N 256
230 	alloc = N * (need / N) + ((need % N) ? N : 0);
231 #undef N
232 	buf = reallocarray(b->buf, alloc, 1);
233 	if (buf == NULL)
234 		return -1;
235 
236 	b->buf = buf;
237 	b->bufsz = alloc;
238 
239 	return 0;
240 }
241 
242 static int
buf_grow(struct buf * b,size_t sz)243 buf_grow(struct buf *b, size_t sz)
244 {
245 	if (SIZE_T_MAX - b->buflen <= sz) {
246 		errno = ERANGE;
247 		return -1;
248 	}
249 
250 	return buf_alloc(b, b->buflen + sz);
251 }
252 
253 static int
buf_cat(struct buf * b,const char * s)254 buf_cat(struct buf *b, const char *s)
255 {
256 	size_t len = strlen(s);
257 
258 	if (buf_grow(b, len + 1) == -1)
259 		return -1;
260 
261 	(void)memmove(b->buf + b->buflen, s, len + 1);
262 	b->buflen += len;
263 	return 0;
264 }
265