GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpd/../rfc2822.c Lines: 0 107 0.0 %
Date: 2017-11-13 Branches: 0 78 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rfc2822.c,v 1.10 2017/02/04 19:25:24 guenther Exp $	*/
2
3
/*
4
 * Copyright (c) 2014 Gilles Chehade <gilles@poolp.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 <sys/types.h>
20
#include <sys/queue.h>
21
#include <sys/tree.h>
22
23
#include <ctype.h>
24
#include <errno.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
30
#include "rfc2822.h"
31
32
/* default no-op callbacks */
33
static void hdr_dflt_cb(const struct rfc2822_header *hdr, void *arg) {}
34
static void body_dflt_cb(const char *line, void *arg) {}
35
36
static void
37
header_reset(struct rfc2822_header *hdr)
38
{
39
	struct rfc2822_line	*line;
40
41
	while ((line = TAILQ_FIRST(&hdr->lines))) {
42
		TAILQ_REMOVE(&hdr->lines, line, next);
43
		free(line);
44
	}
45
}
46
47
static void
48
header_callback(struct rfc2822_parser *rp)
49
{
50
	struct rfc2822_hdr_cb		*hdr_cb;
51
	struct rfc2822_hdr_miss_cb	*hdr_miss_cb;
52
53
	if (!rp->in_hdr)
54
		goto end;
55
56
	TAILQ_FOREACH(hdr_cb, &rp->hdr_cb, next)
57
	    if (strcasecmp(hdr_cb->name, rp->header.name) == 0) {
58
		    hdr_cb->func(&rp->header, hdr_cb->arg);
59
		    goto end;
60
	    }
61
	rp->hdr_dflt_cb.func(&rp->header, rp->hdr_dflt_cb.arg);
62
63
end:
64
	TAILQ_FOREACH(hdr_miss_cb, &rp->hdr_miss_cb, next)
65
	    if (strcasecmp(hdr_miss_cb->name, rp->header.name) == 0)
66
		    break;
67
	if (hdr_miss_cb)
68
		TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next);
69
	free(hdr_miss_cb);
70
	header_reset(&rp->header);
71
	rp->in_hdr = 0;
72
	return;
73
}
74
75
static void
76
missing_headers_callback(struct rfc2822_parser *rp)
77
{
78
	struct rfc2822_hdr_miss_cb	*hdr_miss_cb;
79
80
	while ((hdr_miss_cb = TAILQ_FIRST(&rp->hdr_miss_cb))) {
81
		hdr_miss_cb->func(hdr_miss_cb->name, hdr_miss_cb->arg);
82
		TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next);
83
		free(hdr_miss_cb);
84
	}
85
}
86
87
static void
88
body_callback(struct rfc2822_parser *rp, const char *line)
89
{
90
	rp->body_line_cb.func(line, rp->body_line_cb.arg);
91
}
92
93
static int
94
parser_feed_header(struct rfc2822_parser *rp, char *line)
95
{
96
	struct rfc2822_line	*hdrline;
97
	char			*pos;
98
99
	/* new header */
100
	if (!isspace((unsigned char)*line) && *line != '\0') {
101
		rp->in_hdr = 1;
102
		if ((pos = strchr(line, ':')) == NULL)
103
			return 0;
104
		memset(rp->header.name, 0, sizeof rp->header.name);
105
		(void)memcpy(rp->header.name, line, pos - line);
106
		if (isspace((unsigned char)pos[1]))
107
			return parser_feed_header(rp, pos + 1);
108
		else {
109
			*pos = ' ';
110
			return parser_feed_header(rp, pos);
111
		}
112
	}
113
114
	/* continuation */
115
	if (!rp->in_hdr)
116
		return 0;
117
118
	/* append line to header */
119
	if ((hdrline = calloc(1, sizeof *hdrline)) == NULL)
120
		return -1;
121
	(void)strlcpy(hdrline->buffer, line, sizeof hdrline->buffer);
122
	TAILQ_INSERT_TAIL(&rp->header.lines, hdrline, next);
123
	return 1;
124
}
125
126
static int
127
parser_feed_body(struct rfc2822_parser *rp, const char *line)
128
{
129
	/* for now, we only support per-line callbacks */
130
	body_callback(rp, line);
131
	return 1;
132
}
133
134
135
void
136
rfc2822_parser_init(struct rfc2822_parser *rp)
137
{
138
	memset(rp, 0, sizeof *rp);
139
	TAILQ_INIT(&rp->hdr_cb);
140
	TAILQ_INIT(&rp->hdr_miss_cb);
141
	TAILQ_INIT(&rp->header.lines);
142
	rfc2822_header_default_callback(rp, hdr_dflt_cb, NULL);
143
	rfc2822_body_callback(rp, body_dflt_cb, NULL);
144
	rfc2822_parser_reset(rp);
145
}
146
147
void
148
rfc2822_parser_flush(struct rfc2822_parser *rp)
149
{
150
	if (!rp->in_hdrs)
151
		return;
152
153
	header_callback(rp);
154
155
	missing_headers_callback(rp);
156
}
157
158
void
159
rfc2822_parser_reset(struct rfc2822_parser *rp)
160
{
161
	header_reset(&rp->header);
162
	rp->in_hdrs = 1;
163
}
164
165
void
166
rfc2822_parser_release(struct rfc2822_parser *rp)
167
{
168
	struct rfc2822_hdr_cb		*cb;
169
	struct rfc2822_hdr_miss_cb	*mcb;
170
171
	rfc2822_parser_reset(rp);
172
	while ((cb = TAILQ_FIRST(&rp->hdr_cb))) {
173
		TAILQ_REMOVE(&rp->hdr_cb, cb, next);
174
		free(cb);
175
	}
176
	while ((mcb = TAILQ_FIRST(&rp->hdr_miss_cb))) {
177
		TAILQ_REMOVE(&rp->hdr_miss_cb, mcb, next);
178
		free(mcb);
179
	}
180
}
181
182
int
183
rfc2822_parser_feed(struct rfc2822_parser *rp, const char *line)
184
{
185
	char			buffer[RFC2822_MAX_LINE_SIZE+1];
186
187
	/* in header and line is not a continuation, execute callback */
188
	if (rp->in_hdr && (*line == '\0' || !isspace((unsigned char)*line)))
189
		header_callback(rp);
190
191
	/* no longer in headers */
192
	if (*line == '\0') {
193
		if (rp->in_hdrs)
194
			missing_headers_callback(rp);
195
		rp->in_hdrs = 0;
196
	}
197
198
	if (rp->in_hdrs) {
199
		/* line exceeds RFC maximum size requirement */
200
		if (strlcpy(buffer, line, sizeof buffer) >= sizeof buffer)
201
			return 0;
202
		return parser_feed_header(rp, buffer);
203
	}
204
205
	/* don't enforce line max length on content, too many MUA break */
206
	return parser_feed_body(rp, line);
207
}
208
209
int
210
rfc2822_header_callback(struct rfc2822_parser *rp, const char *header,
211
    void (*func)(const struct rfc2822_header *, void *), void *arg)
212
{
213
	struct rfc2822_hdr_cb  *cb;
214
	struct rfc2822_hdr_cb  *cb_tmp;
215
	char			buffer[RFC2822_MAX_LINE_SIZE+1];
216
217
	/* line exceeds RFC maximum size requirement */
218
	if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer)
219
		return 0;
220
221
	TAILQ_FOREACH_SAFE(cb, &rp->hdr_cb, next, cb_tmp) {
222
		if (strcasecmp(cb->name, buffer) == 0) {
223
			TAILQ_REMOVE(&rp->hdr_cb, cb, next);
224
			free(cb);
225
		}
226
	}
227
228
	if ((cb = calloc(1, sizeof *cb)) == NULL)
229
		return -1;
230
	(void)strlcpy(cb->name, buffer, sizeof cb->name);
231
	cb->func = func;
232
	cb->arg  = arg;
233
	TAILQ_INSERT_TAIL(&rp->hdr_cb, cb, next);
234
	return 1;
235
}
236
237
int
238
rfc2822_missing_header_callback(struct rfc2822_parser *rp, const char *header,
239
    void (*func)(const char *, void *), void *arg)
240
{
241
	struct rfc2822_hdr_miss_cb  *cb;
242
	struct rfc2822_hdr_miss_cb  *cb_tmp;
243
	char			buffer[RFC2822_MAX_LINE_SIZE+1];
244
245
	/* line exceeds RFC maximum size requirement */
246
	if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer)
247
		return 0;
248
249
	TAILQ_FOREACH_SAFE(cb, &rp->hdr_miss_cb, next, cb_tmp) {
250
		if (strcasecmp(cb->name, buffer) == 0) {
251
			TAILQ_REMOVE(&rp->hdr_miss_cb, cb, next);
252
			free(cb);
253
		}
254
	}
255
256
	if ((cb = calloc(1, sizeof *cb)) == NULL)
257
		return -1;
258
	(void)strlcpy(cb->name, buffer, sizeof cb->name);
259
	cb->func = func;
260
	cb->arg  = arg;
261
	TAILQ_INSERT_TAIL(&rp->hdr_miss_cb, cb, next);
262
	return 1;
263
}
264
265
void
266
rfc2822_header_default_callback(struct rfc2822_parser *rp,
267
    void (*func)(const struct rfc2822_header *, void *), void *arg)
268
{
269
	struct rfc2822_hdr_cb	*cb;
270
271
	cb = &rp->hdr_dflt_cb;
272
	cb->func = func;
273
	cb->arg  = arg;
274
}
275
276
void
277
rfc2822_body_callback(struct rfc2822_parser *rp,
278
    void (*func)(const char *, void *), void *arg)
279
{
280
	struct rfc2822_line_cb	*cb;
281
282
	cb = &rp->body_line_cb;
283
	cb->func = func;
284
	cb->arg  = arg;
285
}
286