Blame


1 15cacd92 2019-03-29 martijn /*
2 15cacd92 2019-03-29 martijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
3 15cacd92 2019-03-29 martijn *
4 15cacd92 2019-03-29 martijn * Permission to use, copy, modify, and distribute this software for any
5 15cacd92 2019-03-29 martijn * purpose with or without fee is hereby granted, provided that the above
6 15cacd92 2019-03-29 martijn * copyright notice and this permission notice appear in all copies.
7 15cacd92 2019-03-29 martijn *
8 15cacd92 2019-03-29 martijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 15cacd92 2019-03-29 martijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 15cacd92 2019-03-29 martijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 15cacd92 2019-03-29 martijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 15cacd92 2019-03-29 martijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 15cacd92 2019-03-29 martijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 15cacd92 2019-03-29 martijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 15cacd92 2019-03-29 martijn */
16 a248a3c1 2019-03-27 martijn #include <sys/time.h>
17 a248a3c1 2019-03-27 martijn #include <sys/socket.h>
18 a248a3c1 2019-03-27 martijn
19 a248a3c1 2019-03-27 martijn #include <arpa/inet.h>
20 a248a3c1 2019-03-27 martijn #include <errno.h>
21 eaefb262 2019-03-27 martijn #include <event.h>
22 eaefb262 2019-03-27 martijn #include <fcntl.h>
23 a248a3c1 2019-03-27 martijn #include <inttypes.h>
24 57cbfccc 2019-03-27 martijn #include <stdarg.h>
25 a248a3c1 2019-03-27 martijn #include <stdio.h>
26 a248a3c1 2019-03-27 martijn #include <stdlib.h>
27 a248a3c1 2019-03-27 martijn #include <string.h>
28 b633fa4c 2019-03-29 martijn #include <syslog.h>
29 eaefb262 2019-03-27 martijn #include <unistd.h>
30 a248a3c1 2019-03-27 martijn
31 b633fa4c 2019-03-29 martijn #include "log.h"
32 a248a3c1 2019-03-27 martijn #include "smtp_proc.h"
33 a248a3c1 2019-03-27 martijn
34 a248a3c1 2019-03-27 martijn #define NITEMS(x) (sizeof(x) / sizeof(*x))
35 a248a3c1 2019-03-27 martijn
36 a248a3c1 2019-03-27 martijn struct smtp_callback;
37 73ac3ec1 2019-03-27 martijn struct smtp_request;
38 a248a3c1 2019-03-27 martijn
39 03a6f52a 2019-03-29 martijn static int smtp_register(char *, char *, char *, void *);
40 eaefb262 2019-03-27 martijn static void smtp_newline(int, short, void *);
41 42a4c512 2019-03-28 martijn static void smtp_connect(struct smtp_callback *, int, struct timespec *,
42 42a4c512 2019-03-28 martijn uint64_t, uint64_t, char *);
43 02090eee 2019-03-29 martijn static void smtp_dataline(struct smtp_callback *, int, struct timespec *,
44 02090eee 2019-03-29 martijn uint64_t, uint64_t, char *);
45 47628d21 2019-03-29 martijn static void smtp_in_link_disconnect(struct smtp_callback *, int, struct timespec *,
46 47628d21 2019-03-29 martijn uint64_t, char *);
47 a248a3c1 2019-03-27 martijn
48 a248a3c1 2019-03-27 martijn struct smtp_callback {
49 a248a3c1 2019-03-27 martijn char *type;
50 a248a3c1 2019-03-27 martijn char *phase;
51 a248a3c1 2019-03-27 martijn char *direction;
52 47628d21 2019-03-29 martijn union {
53 47628d21 2019-03-29 martijn void (*smtp_filter)(struct smtp_callback *, int,
54 47628d21 2019-03-29 martijn struct timespec *, uint64_t, uint64_t, char *);
55 47628d21 2019-03-29 martijn void (*smtp_report)(struct smtp_callback *, int,
56 47628d21 2019-03-29 martijn struct timespec *, uint64_t, char *);
57 47628d21 2019-03-29 martijn };
58 03a6f52a 2019-03-29 martijn void *cb;
59 a248a3c1 2019-03-27 martijn } smtp_callbacks[] = {
60 47628d21 2019-03-29 martijn {"filter", "connect", "smtp-in", .smtp_filter = smtp_connect, NULL},
61 02090eee 2019-03-29 martijn {"filter", "data-line", "smtp-in", .smtp_filter = smtp_dataline, NULL},
62 47628d21 2019-03-29 martijn {"report", "link-disconnect", "smtp-in",
63 47628d21 2019-03-29 martijn .smtp_report = smtp_in_link_disconnect, NULL}
64 a248a3c1 2019-03-27 martijn };
65 a248a3c1 2019-03-27 martijn
66 a248a3c1 2019-03-27 martijn static int ready = 0;
67 a248a3c1 2019-03-27 martijn
68 a248a3c1 2019-03-27 martijn int
69 42a4c512 2019-03-28 martijn smtp_register_filter_connect(void (*cb)(char *, int, struct timespec *, char *,
70 03a6f52a 2019-03-29 martijn char *, uint64_t, uint64_t, char *, struct inx_addr *))
71 a248a3c1 2019-03-27 martijn {
72 03a6f52a 2019-03-29 martijn return smtp_register("filter", "connect", "smtp-in", (void *)cb);
73 a248a3c1 2019-03-27 martijn }
74 a248a3c1 2019-03-27 martijn
75 47628d21 2019-03-29 martijn int
76 02090eee 2019-03-29 martijn smtp_register_filter_dataline(void (*cb)(char *, int, struct timespec *, char *,
77 02090eee 2019-03-29 martijn char *, uint64_t, uint64_t, char *))
78 02090eee 2019-03-29 martijn {
79 02090eee 2019-03-29 martijn return smtp_register("filter", "data-line", "smtp-in", (void *)cb);
80 02090eee 2019-03-29 martijn }
81 02090eee 2019-03-29 martijn
82 02090eee 2019-03-29 martijn int
83 47628d21 2019-03-29 martijn smtp_in_register_report_disconnect(void (*cb)(char *, int, struct timespec *,
84 47628d21 2019-03-29 martijn char *, char *, uint64_t))
85 47628d21 2019-03-29 martijn {
86 47628d21 2019-03-29 martijn return smtp_register("report", "link-disconnect", "smtp-in", (void *)cb);
87 47628d21 2019-03-29 martijn }
88 47628d21 2019-03-29 martijn
89 a248a3c1 2019-03-27 martijn void
90 b633fa4c 2019-03-29 martijn smtp_run(int debug)
91 a248a3c1 2019-03-27 martijn {
92 eaefb262 2019-03-27 martijn struct event stdinev;
93 eaefb262 2019-03-27 martijn
94 eaefb262 2019-03-27 martijn printf("register|ready\n");
95 6f50562c 2019-03-28 martijn fflush(stdout);
96 eaefb262 2019-03-27 martijn ready = 1;
97 eaefb262 2019-03-27 martijn
98 b633fa4c 2019-03-29 martijn log_init(debug, LOG_MAIL);
99 eaefb262 2019-03-27 martijn event_init();
100 eaefb262 2019-03-27 martijn event_set(&stdinev, STDIN_FILENO, EV_READ | EV_PERSIST, smtp_newline,
101 eaefb262 2019-03-27 martijn &stdinev);
102 eaefb262 2019-03-27 martijn event_add(&stdinev, NULL);
103 eaefb262 2019-03-27 martijn
104 eaefb262 2019-03-27 martijn if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
105 b633fa4c 2019-03-29 martijn fatal("fcntl");
106 eaefb262 2019-03-27 martijn event_dispatch();
107 eaefb262 2019-03-27 martijn }
108 eaefb262 2019-03-27 martijn
109 eaefb262 2019-03-27 martijn static void
110 eaefb262 2019-03-27 martijn smtp_newline(int fd, short event, void *arg)
111 eaefb262 2019-03-27 martijn {
112 eaefb262 2019-03-27 martijn struct event *stdinev = (struct event *)arg;
113 a248a3c1 2019-03-27 martijn char *line = NULL;
114 a248a3c1 2019-03-27 martijn size_t linesize = 0;
115 a248a3c1 2019-03-27 martijn ssize_t linelen;
116 a248a3c1 2019-03-27 martijn char *start, *end, *type, *direction, *phase, *params;
117 a248a3c1 2019-03-27 martijn int version;
118 42a4c512 2019-03-28 martijn struct timespec tm;
119 a248a3c1 2019-03-27 martijn uint64_t reqid, token;
120 a248a3c1 2019-03-27 martijn int i;
121 a248a3c1 2019-03-27 martijn
122 a248a3c1 2019-03-27 martijn while ((linelen = getline(&line, &linesize, stdin)) != -1) {
123 a248a3c1 2019-03-27 martijn if (line[linelen - 1] != '\n')
124 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing newline");
125 a248a3c1 2019-03-27 martijn line[linelen - 1] = '\0';
126 a248a3c1 2019-03-27 martijn type = line;
127 a248a3c1 2019-03-27 martijn if ((start = strchr(type, '|')) == NULL)
128 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing version");
129 a248a3c1 2019-03-27 martijn start++[0] = '\0';
130 a248a3c1 2019-03-27 martijn if ((end = strchr(start, '|')) == NULL)
131 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing time");
132 a248a3c1 2019-03-27 martijn end++[0] = '\0';
133 a248a3c1 2019-03-27 martijn if (strcmp(start, "1") != 0)
134 b633fa4c 2019-03-29 martijn fatalx("Unsupported protocol received: %s", start);
135 a248a3c1 2019-03-27 martijn version = 1;
136 a248a3c1 2019-03-27 martijn start = end;
137 a248a3c1 2019-03-27 martijn if ((direction = strchr(start, '|')) == NULL)
138 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing direction");
139 a248a3c1 2019-03-27 martijn direction++[0] = '\0';
140 42a4c512 2019-03-28 martijn tm.tv_sec = (time_t) strtoull(start, &end, 10);
141 42a4c512 2019-03-28 martijn tm.tv_nsec = 0;
142 42a4c512 2019-03-28 martijn if (start[0] == '\0' || (end[0] != '\0' && end[0] != '.'))
143 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: invalid timestamp");
144 42a4c512 2019-03-28 martijn if (end[0] == '.') {
145 42a4c512 2019-03-28 martijn start = end + 1;
146 42a4c512 2019-03-28 martijn tm.tv_nsec = strtol(start, &end, 10);
147 42a4c512 2019-03-28 martijn if (start[0] == '\0' || end[0] != '\0')
148 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: invalid "
149 42a4c512 2019-03-28 martijn "timestamp");
150 57d45cc8 2019-03-28 martijn for (i = 9 - (end - start); i > 0; i--)
151 42a4c512 2019-03-28 martijn tm.tv_nsec *= 10;
152 42a4c512 2019-03-28 martijn }
153 a248a3c1 2019-03-27 martijn if ((phase = strchr(direction, '|')) == NULL)
154 b633fa4c 2019-03-29 martijn fatalx("Invalid line receieved: missing phase");
155 a248a3c1 2019-03-27 martijn phase++[0] = '\0';
156 a248a3c1 2019-03-27 martijn if ((start = strchr(phase, '|')) == NULL)
157 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing reqid");
158 a248a3c1 2019-03-27 martijn start++[0] = '\0';
159 47628d21 2019-03-29 martijn reqid = strtoull(start, &params, 16);
160 47628d21 2019-03-29 martijn if (start[0] == '|' || (params[0] != '|' & params[0] != '\0'))
161 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: invalid reqid");
162 47628d21 2019-03-29 martijn params++;
163 a248a3c1 2019-03-27 martijn
164 a248a3c1 2019-03-27 martijn for (i = 0; i < NITEMS(smtp_callbacks); i++) {
165 a248a3c1 2019-03-27 martijn if (strcmp(type, smtp_callbacks[i].type) == 0 &&
166 a248a3c1 2019-03-27 martijn strcmp(phase, smtp_callbacks[i].phase) == 0 &&
167 a248a3c1 2019-03-27 martijn strcmp(direction, smtp_callbacks[i].direction) == 0)
168 a248a3c1 2019-03-27 martijn break;
169 a248a3c1 2019-03-27 martijn }
170 a248a3c1 2019-03-27 martijn if (i == NITEMS(smtp_callbacks)) {
171 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: received unregistered "
172 a248a3c1 2019-03-27 martijn "%s: %s", type, phase);
173 a248a3c1 2019-03-27 martijn }
174 47628d21 2019-03-29 martijn if (strcmp(type, "filter") == 0) {
175 47628d21 2019-03-29 martijn start = params;
176 47628d21 2019-03-29 martijn token = strtoull(start, &params, 16);
177 47628d21 2019-03-29 martijn if (start[0] == '|' || params[0] != '|')
178 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: invalid token");
179 47628d21 2019-03-29 martijn params++;
180 47628d21 2019-03-29 martijn smtp_callbacks[i].smtp_filter(&(smtp_callbacks[i]),
181 47628d21 2019-03-29 martijn version, &tm, reqid, token, params);
182 47628d21 2019-03-29 martijn } else
183 47628d21 2019-03-29 martijn smtp_callbacks[i].smtp_report(&(smtp_callbacks[i]),
184 47628d21 2019-03-29 martijn version, &tm, reqid, params);
185 a248a3c1 2019-03-27 martijn }
186 eaefb262 2019-03-27 martijn if (feof(stdin) || (ferror(stdin) && errno != EAGAIN))
187 eaefb262 2019-03-27 martijn event_del(stdinev);
188 a248a3c1 2019-03-27 martijn }
189 a248a3c1 2019-03-27 martijn
190 a248a3c1 2019-03-27 martijn static void
191 42a4c512 2019-03-28 martijn smtp_connect(struct smtp_callback *cb, int version, struct timespec *tm,
192 42a4c512 2019-03-28 martijn uint64_t reqid, uint64_t token, char *params)
193 a248a3c1 2019-03-27 martijn {
194 03a6f52a 2019-03-29 martijn struct inx_addr addrx;
195 03a6f52a 2019-03-29 martijn char *hostname;
196 a248a3c1 2019-03-27 martijn char *address;
197 a248a3c1 2019-03-27 martijn int ret;
198 03a6f52a 2019-03-29 martijn void (*f)(char *, int, struct timespec *,char *, char *, uint64_t,
199 03a6f52a 2019-03-29 martijn uint64_t, char *, struct inx_addr *);
200 a248a3c1 2019-03-27 martijn
201 03a6f52a 2019-03-29 martijn hostname = params;
202 a248a3c1 2019-03-27 martijn if ((address = strchr(params, '|')) == NULL)
203 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: missing address");
204 a248a3c1 2019-03-27 martijn address++[0] = '\0';
205 a248a3c1 2019-03-27 martijn
206 03a6f52a 2019-03-29 martijn addrx.af = AF_INET;
207 a248a3c1 2019-03-27 martijn if (strncasecmp(address, "ipv6:", 5) == 0) {
208 03a6f52a 2019-03-29 martijn addrx.af = AF_INET6;
209 a248a3c1 2019-03-27 martijn address += 5;
210 a248a3c1 2019-03-27 martijn }
211 a248a3c1 2019-03-27 martijn
212 03a6f52a 2019-03-29 martijn ret = inet_pton(addrx.af, address, addrx.af == AF_INET ?
213 03a6f52a 2019-03-29 martijn (void *)&(addrx.addr) : (void *)&(addrx.addr6));
214 a248a3c1 2019-03-27 martijn if (ret == 0)
215 b633fa4c 2019-03-29 martijn fatalx("Invalid line received: Couldn't parse address");
216 a248a3c1 2019-03-27 martijn if (ret == -1)
217 b633fa4c 2019-03-29 martijn fatalx("Couldn't convert address");
218 a248a3c1 2019-03-27 martijn
219 03a6f52a 2019-03-29 martijn f = cb->cb;
220 03a6f52a 2019-03-29 martijn f(cb->type, version, tm, cb->direction, cb->phase, reqid, token,
221 03a6f52a 2019-03-29 martijn hostname, &addrx);
222 a248a3c1 2019-03-27 martijn }
223 953e2ad7 2019-03-29 martijn
224 47628d21 2019-03-29 martijn static void
225 02090eee 2019-03-29 martijn smtp_dataline(struct smtp_callback *cb, int version, struct timespec *tm,
226 02090eee 2019-03-29 martijn uint64_t reqid, uint64_t token, char *line)
227 02090eee 2019-03-29 martijn {
228 02090eee 2019-03-29 martijn void (*f)(char *, int, struct timespec *,char *, char *, uint64_t,
229 02090eee 2019-03-29 martijn uint64_t, char *);
230 02090eee 2019-03-29 martijn
231 02090eee 2019-03-29 martijn f = cb->cb;
232 02090eee 2019-03-29 martijn f(cb->type, version, tm, cb->direction, cb->phase, reqid, token,
233 02090eee 2019-03-29 martijn line);
234 02090eee 2019-03-29 martijn }
235 02090eee 2019-03-29 martijn
236 02090eee 2019-03-29 martijn static void
237 47628d21 2019-03-29 martijn smtp_in_link_disconnect(struct smtp_callback *cb, int version,
238 47628d21 2019-03-29 martijn struct timespec *tm, uint64_t reqid, char *params)
239 47628d21 2019-03-29 martijn {
240 47628d21 2019-03-29 martijn void (*f)(char *, int, struct timespec *, char *, char *, uint64_t);
241 a248a3c1 2019-03-27 martijn
242 47628d21 2019-03-29 martijn f = cb->cb;
243 47628d21 2019-03-29 martijn f(cb->type, version, tm, cb->direction, cb->phase, reqid);
244 47628d21 2019-03-29 martijn }
245 47628d21 2019-03-29 martijn
246 57cbfccc 2019-03-27 martijn void
247 57cbfccc 2019-03-27 martijn smtp_filter_proceed(uint64_t reqid, uint64_t token)
248 57cbfccc 2019-03-27 martijn {
249 42a4c512 2019-03-28 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|proceed\n", token,
250 42a4c512 2019-03-28 martijn reqid);
251 6f50562c 2019-03-28 martijn fflush(stdout);
252 57cbfccc 2019-03-27 martijn }
253 57cbfccc 2019-03-27 martijn
254 57cbfccc 2019-03-27 martijn void
255 57cbfccc 2019-03-27 martijn smtp_filter_reject(uint64_t reqid, uint64_t token, int code,
256 57cbfccc 2019-03-27 martijn const char *reason, ...)
257 57cbfccc 2019-03-27 martijn {
258 57cbfccc 2019-03-27 martijn va_list ap;
259 57cbfccc 2019-03-27 martijn
260 57cbfccc 2019-03-27 martijn if (code < 200 || code > 599)
261 b633fa4c 2019-03-29 martijn fatalx("Invalid reject code");
262 57cbfccc 2019-03-27 martijn
263 57cbfccc 2019-03-27 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|reject|%d ", token,
264 57cbfccc 2019-03-27 martijn reqid, code);
265 57cbfccc 2019-03-27 martijn va_start(ap, reason);
266 57cbfccc 2019-03-27 martijn vprintf(reason, ap);
267 57cbfccc 2019-03-27 martijn va_end(ap);
268 57cbfccc 2019-03-27 martijn putchar('\n');
269 6f50562c 2019-03-28 martijn fflush(stdout);
270 57cbfccc 2019-03-27 martijn }
271 57cbfccc 2019-03-27 martijn
272 57cbfccc 2019-03-27 martijn void
273 57cbfccc 2019-03-27 martijn smtp_filter_disconnect(uint64_t reqid, uint64_t token, const char *reason, ...)
274 57cbfccc 2019-03-27 martijn {
275 57cbfccc 2019-03-27 martijn va_list ap;
276 57cbfccc 2019-03-27 martijn
277 57cbfccc 2019-03-27 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|disconnect|421 ",
278 57cbfccc 2019-03-27 martijn token, reqid);
279 57cbfccc 2019-03-27 martijn va_start(ap, reason);
280 57cbfccc 2019-03-27 martijn vprintf(reason, ap);
281 57cbfccc 2019-03-27 martijn va_end(ap);
282 57cbfccc 2019-03-27 martijn putchar('\n');
283 6f50562c 2019-03-28 martijn fflush(stdout);
284 57cbfccc 2019-03-27 martijn }
285 57cbfccc 2019-03-27 martijn
286 02090eee 2019-03-29 martijn void
287 02090eee 2019-03-29 martijn smtp_filter_dataline(uint64_t reqid, uint64_t token, const char *line, ...)
288 02090eee 2019-03-29 martijn {
289 02090eee 2019-03-29 martijn va_list ap;
290 02090eee 2019-03-29 martijn
291 02090eee 2019-03-29 martijn printf("filter-dataline|%016"PRIx64"|%016"PRIx64"|", token, reqid);
292 02090eee 2019-03-29 martijn va_start(ap, line);
293 02090eee 2019-03-29 martijn vprintf(line, ap);
294 02090eee 2019-03-29 martijn va_end(ap);
295 02090eee 2019-03-29 martijn putchar('\n');
296 02090eee 2019-03-29 martijn fflush(stdout);
297 02090eee 2019-03-29 martijn }
298 02090eee 2019-03-29 martijn
299 a248a3c1 2019-03-27 martijn static int
300 03a6f52a 2019-03-29 martijn smtp_register(char *type, char *phase, char *direction, void *cb)
301 a248a3c1 2019-03-27 martijn {
302 a248a3c1 2019-03-27 martijn int i;
303 a248a3c1 2019-03-27 martijn
304 73ac3ec1 2019-03-27 martijn if (ready)
305 b633fa4c 2019-03-29 martijn fatalx("Can't register when proc is running");
306 73ac3ec1 2019-03-27 martijn
307 a248a3c1 2019-03-27 martijn for (i = 0; i < NITEMS(smtp_callbacks); i++) {
308 a248a3c1 2019-03-27 martijn if (strcmp(type, smtp_callbacks[i].type) == 0 &&
309 a248a3c1 2019-03-27 martijn strcmp(phase, smtp_callbacks[i].phase) == 0 &&
310 a248a3c1 2019-03-27 martijn strcmp(direction, smtp_callbacks[i].direction) == 0) {
311 a248a3c1 2019-03-27 martijn if (smtp_callbacks[i].cb != NULL) {
312 a248a3c1 2019-03-27 martijn errno = EALREADY;
313 a248a3c1 2019-03-27 martijn return -1;
314 a248a3c1 2019-03-27 martijn }
315 a248a3c1 2019-03-27 martijn smtp_callbacks[i].cb = cb;
316 a248a3c1 2019-03-27 martijn printf("register|%s|%s|%s\n", type, direction, phase);
317 a248a3c1 2019-03-27 martijn return 0;
318 a248a3c1 2019-03-27 martijn }
319 a248a3c1 2019-03-27 martijn }
320 a248a3c1 2019-03-27 martijn errno = EINVAL;
321 a248a3c1 2019-03-27 martijn return -1;
322 a248a3c1 2019-03-27 martijn }