Blame


1 a248a3c1 2019-03-27 martijn #include <sys/time.h>
2 73ac3ec1 2019-03-27 martijn #include <sys/tree.h>
3 a248a3c1 2019-03-27 martijn #include <sys/socket.h>
4 a248a3c1 2019-03-27 martijn
5 a248a3c1 2019-03-27 martijn #include <arpa/inet.h>
6 a248a3c1 2019-03-27 martijn #include <err.h>
7 a248a3c1 2019-03-27 martijn #include <errno.h>
8 eaefb262 2019-03-27 martijn #include <event.h>
9 eaefb262 2019-03-27 martijn #include <fcntl.h>
10 a248a3c1 2019-03-27 martijn #include <inttypes.h>
11 57cbfccc 2019-03-27 martijn #include <stdarg.h>
12 a248a3c1 2019-03-27 martijn #include <stdio.h>
13 a248a3c1 2019-03-27 martijn #include <stdlib.h>
14 a248a3c1 2019-03-27 martijn #include <string.h>
15 eaefb262 2019-03-27 martijn #include <unistd.h>
16 a248a3c1 2019-03-27 martijn
17 a248a3c1 2019-03-27 martijn #include "smtp_proc.h"
18 a248a3c1 2019-03-27 martijn
19 a248a3c1 2019-03-27 martijn #define NITEMS(x) (sizeof(x) / sizeof(*x))
20 a248a3c1 2019-03-27 martijn
21 a248a3c1 2019-03-27 martijn typedef int (*smtp_cb)(char *, int, time_t, char *, char *, uint64_t, uint64_t,
22 a248a3c1 2019-03-27 martijn void *);
23 a248a3c1 2019-03-27 martijn
24 a248a3c1 2019-03-27 martijn struct smtp_callback;
25 73ac3ec1 2019-03-27 martijn struct smtp_request;
26 73ac3ec1 2019-03-27 martijn RB_HEAD(smtp_requests, smtp_request) smtp_requests;
27 a248a3c1 2019-03-27 martijn
28 a248a3c1 2019-03-27 martijn static int smtp_register(char *, char *, char *, smtp_cb);
29 eaefb262 2019-03-27 martijn static void smtp_newline(int, short, void *);
30 a248a3c1 2019-03-27 martijn static void smtp_connect(struct smtp_callback *, int, time_t, uint64_t,
31 a248a3c1 2019-03-27 martijn uint64_t, char *);
32 eaefb262 2019-03-27 martijn static void smtp_handle_filter(struct smtp_callback *, int, time_t, uint64_t,
33 eaefb262 2019-03-27 martijn uint64_t, void *);
34 73ac3ec1 2019-03-27 martijn static int smtp_request_cmp(struct smtp_request *, struct smtp_request *);
35 73ac3ec1 2019-03-27 martijn RB_PROTOTYPE_STATIC(smtp_requests, smtp_request, entry, smtp_request_cmp);
36 a248a3c1 2019-03-27 martijn
37 73ac3ec1 2019-03-27 martijn struct smtp_request {
38 73ac3ec1 2019-03-27 martijn uint64_t reqid;
39 73ac3ec1 2019-03-27 martijn uint64_t token;
40 73ac3ec1 2019-03-27 martijn RB_ENTRY(smtp_request) entry;
41 73ac3ec1 2019-03-27 martijn };
42 73ac3ec1 2019-03-27 martijn
43 a248a3c1 2019-03-27 martijn struct smtp_callback {
44 a248a3c1 2019-03-27 martijn char *type;
45 a248a3c1 2019-03-27 martijn char *phase;
46 a248a3c1 2019-03-27 martijn char *direction;
47 a248a3c1 2019-03-27 martijn void (*smtp_parse)(struct smtp_callback *, int, time_t, uint64_t,
48 a248a3c1 2019-03-27 martijn uint64_t, char *);
49 a248a3c1 2019-03-27 martijn smtp_cb cb;
50 a248a3c1 2019-03-27 martijn } smtp_callbacks[] = {
51 9d22f500 2019-03-28 martijn {"filter", "connect", "smtp-in", smtp_connect, NULL}
52 a248a3c1 2019-03-27 martijn };
53 a248a3c1 2019-03-27 martijn
54 a248a3c1 2019-03-27 martijn static int ready = 0;
55 57cbfccc 2019-03-27 martijn static int resolved = 1;
56 a248a3c1 2019-03-27 martijn
57 a248a3c1 2019-03-27 martijn int
58 a248a3c1 2019-03-27 martijn smtp_register_filter_connect(enum filter_decision (*cb)(char *, int, time_t,
59 a248a3c1 2019-03-27 martijn char *, char *, uint64_t, uint64_t, struct smtp_filter_connect *))
60 a248a3c1 2019-03-27 martijn {
61 a248a3c1 2019-03-27 martijn return smtp_register("filter", "connect", "smtp-in", (smtp_cb) cb);
62 a248a3c1 2019-03-27 martijn }
63 a248a3c1 2019-03-27 martijn
64 a248a3c1 2019-03-27 martijn void
65 a248a3c1 2019-03-27 martijn smtp_run(void)
66 a248a3c1 2019-03-27 martijn {
67 eaefb262 2019-03-27 martijn struct event stdinev;
68 eaefb262 2019-03-27 martijn
69 eaefb262 2019-03-27 martijn printf("register|ready\n");
70 eaefb262 2019-03-27 martijn ready = 1;
71 eaefb262 2019-03-27 martijn
72 eaefb262 2019-03-27 martijn event_init();
73 eaefb262 2019-03-27 martijn event_set(&stdinev, STDIN_FILENO, EV_READ | EV_PERSIST, smtp_newline,
74 eaefb262 2019-03-27 martijn &stdinev);
75 eaefb262 2019-03-27 martijn event_add(&stdinev, NULL);
76 eaefb262 2019-03-27 martijn
77 eaefb262 2019-03-27 martijn if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
78 eaefb262 2019-03-27 martijn err(1, "fcntl");
79 eaefb262 2019-03-27 martijn
80 eaefb262 2019-03-27 martijn event_dispatch();
81 eaefb262 2019-03-27 martijn }
82 eaefb262 2019-03-27 martijn
83 eaefb262 2019-03-27 martijn static void
84 eaefb262 2019-03-27 martijn smtp_newline(int fd, short event, void *arg)
85 eaefb262 2019-03-27 martijn {
86 eaefb262 2019-03-27 martijn struct event *stdinev = (struct event *)arg;
87 a248a3c1 2019-03-27 martijn char *line = NULL;
88 a248a3c1 2019-03-27 martijn size_t linesize = 0;
89 a248a3c1 2019-03-27 martijn ssize_t linelen;
90 a248a3c1 2019-03-27 martijn char *start, *end, *type, *direction, *phase, *params;
91 a248a3c1 2019-03-27 martijn int version;
92 a248a3c1 2019-03-27 martijn time_t tm;
93 a248a3c1 2019-03-27 martijn uint64_t reqid, token;
94 a248a3c1 2019-03-27 martijn int i;
95 a248a3c1 2019-03-27 martijn
96 a248a3c1 2019-03-27 martijn while ((linelen = getline(&line, &linesize, stdin)) != -1) {
97 a248a3c1 2019-03-27 martijn if (line[linelen - 1] != '\n')
98 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing newline");
99 a248a3c1 2019-03-27 martijn line[linelen - 1] = '\0';
100 a248a3c1 2019-03-27 martijn type = line;
101 a248a3c1 2019-03-27 martijn if ((start = strchr(type, '|')) == NULL)
102 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing version");
103 a248a3c1 2019-03-27 martijn start++[0] = '\0';
104 a248a3c1 2019-03-27 martijn if ((end = strchr(start, '|')) == NULL)
105 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing time");
106 a248a3c1 2019-03-27 martijn end++[0] = '\0';
107 a248a3c1 2019-03-27 martijn if (strcmp(start, "1") != 0)
108 a248a3c1 2019-03-27 martijn errx(1, "Unsupported protocol received: %s", start);
109 a248a3c1 2019-03-27 martijn version = 1;
110 a248a3c1 2019-03-27 martijn start = end;
111 a248a3c1 2019-03-27 martijn if ((direction = strchr(start, '|')) == NULL)
112 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing direction");
113 a248a3c1 2019-03-27 martijn direction++[0] = '\0';
114 a248a3c1 2019-03-27 martijn tm = (time_t) strtoull(start, &end, 10);
115 a248a3c1 2019-03-27 martijn if (start[0] == '\0' || end[0] != '\0')
116 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: invalid timestamp");
117 a248a3c1 2019-03-27 martijn if ((phase = strchr(direction, '|')) == NULL)
118 a248a3c1 2019-03-27 martijn errx(1, "Invalid line receieved: missing phase");
119 a248a3c1 2019-03-27 martijn phase++[0] = '\0';
120 a248a3c1 2019-03-27 martijn if ((start = strchr(phase, '|')) == NULL)
121 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing reqid");
122 a248a3c1 2019-03-27 martijn start++[0] = '\0';
123 a248a3c1 2019-03-27 martijn reqid = strtoull(start, &end, 16);
124 a248a3c1 2019-03-27 martijn if (start[0] == '|' || end[0] != '|')
125 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: invalid reqid");
126 a248a3c1 2019-03-27 martijn end++[0] = '\0';
127 a248a3c1 2019-03-27 martijn start = end;
128 a248a3c1 2019-03-27 martijn token = strtoull(start, &end, 16);
129 a248a3c1 2019-03-27 martijn if (start[0] == '|' || end[0] != '|')
130 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: invalid token");
131 a248a3c1 2019-03-27 martijn params = end + 1;
132 a248a3c1 2019-03-27 martijn
133 a248a3c1 2019-03-27 martijn for (i = 0; i < NITEMS(smtp_callbacks); i++) {
134 a248a3c1 2019-03-27 martijn if (strcmp(type, smtp_callbacks[i].type) == 0 &&
135 a248a3c1 2019-03-27 martijn strcmp(phase, smtp_callbacks[i].phase) == 0 &&
136 a248a3c1 2019-03-27 martijn strcmp(direction, smtp_callbacks[i].direction) == 0)
137 a248a3c1 2019-03-27 martijn break;
138 a248a3c1 2019-03-27 martijn }
139 a248a3c1 2019-03-27 martijn if (i == NITEMS(smtp_callbacks)) {
140 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: received unregistered "
141 a248a3c1 2019-03-27 martijn "%s: %s", type, phase);
142 a248a3c1 2019-03-27 martijn }
143 a248a3c1 2019-03-27 martijn smtp_callbacks[i].smtp_parse(&(smtp_callbacks[i]), version, tm,
144 a248a3c1 2019-03-27 martijn reqid, token, params);
145 a248a3c1 2019-03-27 martijn }
146 eaefb262 2019-03-27 martijn if (feof(stdin) || (ferror(stdin) && errno != EAGAIN))
147 eaefb262 2019-03-27 martijn event_del(stdinev);
148 a248a3c1 2019-03-27 martijn }
149 a248a3c1 2019-03-27 martijn
150 a248a3c1 2019-03-27 martijn static void
151 a248a3c1 2019-03-27 martijn smtp_connect(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
152 a248a3c1 2019-03-27 martijn uint64_t token, char *params)
153 a248a3c1 2019-03-27 martijn {
154 a248a3c1 2019-03-27 martijn struct smtp_filter_connect sfconnect;
155 a248a3c1 2019-03-27 martijn char *address;
156 a248a3c1 2019-03-27 martijn int ret;
157 a248a3c1 2019-03-27 martijn
158 a248a3c1 2019-03-27 martijn sfconnect.hostname = params;
159 a248a3c1 2019-03-27 martijn if ((address = strchr(params, '|')) == NULL)
160 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: missing address");
161 a248a3c1 2019-03-27 martijn address++[0] = '\0';
162 a248a3c1 2019-03-27 martijn
163 a248a3c1 2019-03-27 martijn sfconnect.af = AF_INET;
164 a248a3c1 2019-03-27 martijn if (strncasecmp(address, "ipv6:", 5) == 0) {
165 a248a3c1 2019-03-27 martijn sfconnect.af = AF_INET6;
166 a248a3c1 2019-03-27 martijn address += 5;
167 a248a3c1 2019-03-27 martijn }
168 a248a3c1 2019-03-27 martijn
169 a248a3c1 2019-03-27 martijn ret = inet_pton(sfconnect.af, address, sfconnect.af == AF_INET ?
170 a248a3c1 2019-03-27 martijn (void *)&(sfconnect.addr) : (void *)&(sfconnect.addr6));
171 a248a3c1 2019-03-27 martijn if (ret == 0)
172 a248a3c1 2019-03-27 martijn errx(1, "Invalid line received: Couldn't parse address");
173 a248a3c1 2019-03-27 martijn if (ret == -1)
174 a248a3c1 2019-03-27 martijn err(1, "Couldn't convert address");
175 a248a3c1 2019-03-27 martijn
176 eaefb262 2019-03-27 martijn smtp_handle_filter(cb, version, tm, reqid, token, (void *)(&sfconnect));
177 a248a3c1 2019-03-27 martijn }
178 a248a3c1 2019-03-27 martijn
179 eaefb262 2019-03-27 martijn static void
180 73ac3ec1 2019-03-27 martijn smtp_handle_filter(struct smtp_callback *cb, int version, time_t tm,
181 73ac3ec1 2019-03-27 martijn uint64_t reqid, uint64_t token, void *params)
182 a248a3c1 2019-03-27 martijn {
183 57cbfccc 2019-03-27 martijn enum filter_decision fdes;
184 a248a3c1 2019-03-27 martijn
185 57cbfccc 2019-03-27 martijn if (!resolved)
186 57cbfccc 2019-03-27 martijn errx(1, "Handling unexpected second request");
187 57cbfccc 2019-03-27 martijn resolved = 0;
188 57cbfccc 2019-03-27 martijn fdes = cb->cb(cb->type, version, tm, cb->direction, cb->phase, reqid,
189 57cbfccc 2019-03-27 martijn token, params);
190 57cbfccc 2019-03-27 martijn if (!resolved) {
191 57cbfccc 2019-03-27 martijn switch (fdes) {
192 57cbfccc 2019-03-27 martijn case FILTER_PROCEED:
193 57cbfccc 2019-03-27 martijn smtp_filter_proceed(reqid, token);
194 57cbfccc 2019-03-27 martijn break;
195 57cbfccc 2019-03-27 martijn case FILTER_REJECT:
196 57cbfccc 2019-03-27 martijn smtp_filter_reject(reqid, token, 451,
197 57cbfccc 2019-03-27 martijn "Rejected by filter");
198 57cbfccc 2019-03-27 martijn break;
199 57cbfccc 2019-03-27 martijn case FILTER_DISCONNECT:
200 57cbfccc 2019-03-27 martijn smtp_filter_disconnect(reqid, token,
201 57cbfccc 2019-03-27 martijn "Rejected by filter");
202 57cbfccc 2019-03-27 martijn break;
203 57cbfccc 2019-03-27 martijn case FILTER_REWRITE:
204 57cbfccc 2019-03-27 martijn errx(1, "Not sure what is intended here yet");
205 57cbfccc 2019-03-27 martijn }
206 a248a3c1 2019-03-27 martijn }
207 a248a3c1 2019-03-27 martijn }
208 a248a3c1 2019-03-27 martijn
209 57cbfccc 2019-03-27 martijn void
210 57cbfccc 2019-03-27 martijn smtp_filter_proceed(uint64_t reqid, uint64_t token)
211 57cbfccc 2019-03-27 martijn {
212 57cbfccc 2019-03-27 martijn if (resolved)
213 57cbfccc 2019-03-27 martijn errx(1, "reqid: %016"PRIx64" token: %016"PRIx64" already "
214 57cbfccc 2019-03-27 martijn "resolved", reqid, token);
215 57cbfccc 2019-03-27 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|proceed\n", token, reqid);
216 57cbfccc 2019-03-27 martijn resolved = 1;
217 57cbfccc 2019-03-27 martijn }
218 57cbfccc 2019-03-27 martijn
219 57cbfccc 2019-03-27 martijn void
220 57cbfccc 2019-03-27 martijn smtp_filter_reject(uint64_t reqid, uint64_t token, int code,
221 57cbfccc 2019-03-27 martijn const char *reason, ...)
222 57cbfccc 2019-03-27 martijn {
223 57cbfccc 2019-03-27 martijn va_list ap;
224 57cbfccc 2019-03-27 martijn
225 57cbfccc 2019-03-27 martijn if (resolved)
226 57cbfccc 2019-03-27 martijn errx(1, "reqid: %016"PRIx64" token: %016"PRIx64" already "
227 57cbfccc 2019-03-27 martijn "resolved", reqid, token);
228 57cbfccc 2019-03-27 martijn if (code < 200 || code > 599)
229 57cbfccc 2019-03-27 martijn errx(1, "Invalid reject code");
230 57cbfccc 2019-03-27 martijn
231 57cbfccc 2019-03-27 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|reject|%d ", token,
232 57cbfccc 2019-03-27 martijn reqid, code);
233 57cbfccc 2019-03-27 martijn va_start(ap, reason);
234 57cbfccc 2019-03-27 martijn vprintf(reason, ap);
235 57cbfccc 2019-03-27 martijn va_end(ap);
236 57cbfccc 2019-03-27 martijn putchar('\n');
237 57cbfccc 2019-03-27 martijn resolved = 1;
238 57cbfccc 2019-03-27 martijn }
239 57cbfccc 2019-03-27 martijn
240 57cbfccc 2019-03-27 martijn void
241 57cbfccc 2019-03-27 martijn smtp_filter_disconnect(uint64_t reqid, uint64_t token, const char *reason, ...)
242 57cbfccc 2019-03-27 martijn {
243 57cbfccc 2019-03-27 martijn va_list ap;
244 57cbfccc 2019-03-27 martijn
245 57cbfccc 2019-03-27 martijn if (resolved)
246 57cbfccc 2019-03-27 martijn errx(1, "reqid: %016"PRIx64" token: %016"PRIx64" already "
247 57cbfccc 2019-03-27 martijn "resolved", reqid, token);
248 57cbfccc 2019-03-27 martijn printf("filter-result|%016"PRIx64"|%016"PRIx64"|disconnect|421 ",
249 57cbfccc 2019-03-27 martijn token, reqid);
250 57cbfccc 2019-03-27 martijn va_start(ap, reason);
251 57cbfccc 2019-03-27 martijn vprintf(reason, ap);
252 57cbfccc 2019-03-27 martijn va_end(ap);
253 57cbfccc 2019-03-27 martijn putchar('\n');
254 57cbfccc 2019-03-27 martijn resolved = 1;
255 57cbfccc 2019-03-27 martijn }
256 57cbfccc 2019-03-27 martijn
257 a248a3c1 2019-03-27 martijn static int
258 a248a3c1 2019-03-27 martijn smtp_register(char *type, char *phase, char *direction, smtp_cb cb)
259 a248a3c1 2019-03-27 martijn {
260 a248a3c1 2019-03-27 martijn int i;
261 a248a3c1 2019-03-27 martijn
262 73ac3ec1 2019-03-27 martijn if (ready)
263 a248a3c1 2019-03-27 martijn errx(1, "Can't register when proc is running");
264 73ac3ec1 2019-03-27 martijn
265 a248a3c1 2019-03-27 martijn for (i = 0; i < NITEMS(smtp_callbacks); i++) {
266 a248a3c1 2019-03-27 martijn if (strcmp(type, smtp_callbacks[i].type) == 0 &&
267 a248a3c1 2019-03-27 martijn strcmp(phase, smtp_callbacks[i].phase) == 0 &&
268 a248a3c1 2019-03-27 martijn strcmp(direction, smtp_callbacks[i].direction) == 0) {
269 a248a3c1 2019-03-27 martijn if (smtp_callbacks[i].cb != NULL) {
270 a248a3c1 2019-03-27 martijn errno = EALREADY;
271 a248a3c1 2019-03-27 martijn return -1;
272 a248a3c1 2019-03-27 martijn }
273 a248a3c1 2019-03-27 martijn smtp_callbacks[i].cb = cb;
274 a248a3c1 2019-03-27 martijn printf("register|%s|%s|%s\n", type, direction, phase);
275 a248a3c1 2019-03-27 martijn return 0;
276 a248a3c1 2019-03-27 martijn }
277 a248a3c1 2019-03-27 martijn }
278 a248a3c1 2019-03-27 martijn errno = EINVAL;
279 a248a3c1 2019-03-27 martijn return -1;
280 a248a3c1 2019-03-27 martijn }
281 73ac3ec1 2019-03-27 martijn
282 73ac3ec1 2019-03-27 martijn static int
283 73ac3ec1 2019-03-27 martijn smtp_request_cmp(struct smtp_request *r1, struct smtp_request *r2)
284 73ac3ec1 2019-03-27 martijn {
285 73ac3ec1 2019-03-27 martijn return r1->reqid - r2->reqid;
286 73ac3ec1 2019-03-27 martijn }
287 73ac3ec1 2019-03-27 martijn
288 73ac3ec1 2019-03-27 martijn RB_GENERATE_STATIC(smtp_requests, smtp_request, entry, smtp_request_cmp);