Blob


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