2 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/socket.h>
19 #include <arpa/inet.h>
32 #include "smtp_proc.h"
34 #define NITEMS(x) (sizeof(x) / sizeof(*x))
39 static int smtp_register(char *, char *, char *, void *);
40 static void smtp_newline(int, short, void *);
41 static void smtp_connect(struct smtp_callback *, int, struct timespec *,
42 uint64_t, uint64_t, char *);
43 static void smtp_dataline(struct smtp_callback *, int, struct timespec *,
44 uint64_t, uint64_t, char *);
45 static void smtp_in_link_disconnect(struct smtp_callback *, int, struct timespec *,
48 struct smtp_callback {
53 void (*smtp_filter)(struct smtp_callback *, int,
54 struct timespec *, uint64_t, uint64_t, char *);
55 void (*smtp_report)(struct smtp_callback *, int,
56 struct timespec *, uint64_t, char *);
59 } smtp_callbacks[] = {
60 {"filter", "connect", "smtp-in", .smtp_filter = smtp_connect, NULL},
61 {"filter", "data-line", "smtp-in", .smtp_filter = smtp_dataline, NULL},
62 {"report", "link-disconnect", "smtp-in",
63 .smtp_report = smtp_in_link_disconnect, NULL}
69 smtp_register_filter_connect(void (*cb)(char *, int, struct timespec *, char *,
70 char *, uint64_t, uint64_t, char *, struct inx_addr *))
72 return smtp_register("filter", "connect", "smtp-in", (void *)cb);
76 smtp_register_filter_dataline(void (*cb)(char *, int, struct timespec *, char *,
77 char *, uint64_t, uint64_t, char *))
79 return smtp_register("filter", "data-line", "smtp-in", (void *)cb);
83 smtp_in_register_report_disconnect(void (*cb)(char *, int, struct timespec *,
84 char *, char *, uint64_t))
86 return smtp_register("report", "link-disconnect", "smtp-in", (void *)cb);
94 printf("register|ready\n");
98 log_init(debug, LOG_MAIL);
100 event_set(&stdinev, STDIN_FILENO, EV_READ | EV_PERSIST, smtp_newline,
102 event_add(&stdinev, NULL);
104 if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1)
110 smtp_newline(int fd, short event, void *arg)
112 struct event *stdinev = (struct event *)arg;
116 char *start, *end, *type, *direction, *phase, *params;
119 uint64_t reqid, token;
122 while ((linelen = getline(&line, &linesize, stdin)) != -1) {
123 if (line[linelen - 1] != '\n')
124 fatalx("Invalid line received: missing newline");
125 line[linelen - 1] = '\0';
127 if ((start = strchr(type, '|')) == NULL)
128 fatalx("Invalid line received: missing version");
130 if ((end = strchr(start, '|')) == NULL)
131 fatalx("Invalid line received: missing time");
133 if (strcmp(start, "1") != 0)
134 fatalx("Unsupported protocol received: %s", start);
137 if ((direction = strchr(start, '|')) == NULL)
138 fatalx("Invalid line received: missing direction");
139 direction++[0] = '\0';
140 tm.tv_sec = (time_t) strtoull(start, &end, 10);
142 if (start[0] == '\0' || (end[0] != '\0' && end[0] != '.'))
143 fatalx("Invalid line received: invalid timestamp");
146 tm.tv_nsec = strtol(start, &end, 10);
147 if (start[0] == '\0' || end[0] != '\0')
148 fatalx("Invalid line received: invalid "
150 for (i = 9 - (end - start); i > 0; i--)
153 if ((phase = strchr(direction, '|')) == NULL)
154 fatalx("Invalid line receieved: missing phase");
156 if ((start = strchr(phase, '|')) == NULL)
157 fatalx("Invalid line received: missing reqid");
159 reqid = strtoull(start, ¶ms, 16);
160 if (start[0] == '|' || (params[0] != '|' & params[0] != '\0'))
161 fatalx("Invalid line received: invalid reqid");
164 for (i = 0; i < NITEMS(smtp_callbacks); i++) {
165 if (strcmp(type, smtp_callbacks[i].type) == 0 &&
166 strcmp(phase, smtp_callbacks[i].phase) == 0 &&
167 strcmp(direction, smtp_callbacks[i].direction) == 0)
170 if (i == NITEMS(smtp_callbacks)) {
171 fatalx("Invalid line received: received unregistered "
172 "%s: %s", type, phase);
174 if (strcmp(type, "filter") == 0) {
176 token = strtoull(start, ¶ms, 16);
177 if (start[0] == '|' || params[0] != '|')
178 fatalx("Invalid line received: invalid token");
180 smtp_callbacks[i].smtp_filter(&(smtp_callbacks[i]),
181 version, &tm, reqid, token, params);
183 smtp_callbacks[i].smtp_report(&(smtp_callbacks[i]),
184 version, &tm, reqid, params);
186 if (feof(stdin) || (ferror(stdin) && errno != EAGAIN))
191 smtp_connect(struct smtp_callback *cb, int version, struct timespec *tm,
192 uint64_t reqid, uint64_t token, char *params)
194 struct inx_addr addrx;
198 void (*f)(char *, int, struct timespec *,char *, char *, uint64_t,
199 uint64_t, char *, struct inx_addr *);
202 if ((address = strchr(params, '|')) == NULL)
203 fatalx("Invalid line received: missing address");
207 if (strncasecmp(address, "ipv6:", 5) == 0) {
212 ret = inet_pton(addrx.af, address, addrx.af == AF_INET ?
213 (void *)&(addrx.addr) : (void *)&(addrx.addr6));
215 fatalx("Invalid line received: Couldn't parse address");
217 fatalx("Couldn't convert address");
220 f(cb->type, version, tm, cb->direction, cb->phase, reqid, token,
225 smtp_dataline(struct smtp_callback *cb, int version, struct timespec *tm,
226 uint64_t reqid, uint64_t token, char *line)
228 void (*f)(char *, int, struct timespec *,char *, char *, uint64_t,
232 f(cb->type, version, tm, cb->direction, cb->phase, reqid, token,
237 smtp_in_link_disconnect(struct smtp_callback *cb, int version,
238 struct timespec *tm, uint64_t reqid, char *params)
240 void (*f)(char *, int, struct timespec *, char *, char *, uint64_t);
243 f(cb->type, version, tm, cb->direction, cb->phase, reqid);
247 smtp_filter_proceed(uint64_t reqid, uint64_t token)
249 printf("filter-result|%016"PRIx64"|%016"PRIx64"|proceed\n", token,
255 smtp_filter_reject(uint64_t reqid, uint64_t token, int code,
256 const char *reason, ...)
260 if (code < 200 || code > 599)
261 fatalx("Invalid reject code");
263 printf("filter-result|%016"PRIx64"|%016"PRIx64"|reject|%d ", token,
265 va_start(ap, reason);
273 smtp_filter_disconnect(uint64_t reqid, uint64_t token, const char *reason, ...)
277 printf("filter-result|%016"PRIx64"|%016"PRIx64"|disconnect|421 ",
279 va_start(ap, reason);
287 smtp_filter_dataline(uint64_t reqid, uint64_t token, const char *line, ...)
291 printf("filter-dataline|%016"PRIx64"|%016"PRIx64"|", token, reqid);
300 smtp_register(char *type, char *phase, char *direction, void *cb)
305 fatalx("Can't register when proc is running");
307 for (i = 0; i < NITEMS(smtp_callbacks); i++) {
308 if (strcmp(type, smtp_callbacks[i].type) == 0 &&
309 strcmp(phase, smtp_callbacks[i].phase) == 0 &&
310 strcmp(direction, smtp_callbacks[i].direction) == 0) {
311 if (smtp_callbacks[i].cb != NULL) {
315 smtp_callbacks[i].cb = cb;
316 printf("register|%s|%s|%s\n", type, direction, phase);