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 c75461cd 2019-03-29 martijn #include <sys/tree.h>
17 aeda4d4e 2019-03-28 martijn #include <sys/types.h>
18 a248a3c1 2019-03-27 martijn #include <sys/socket.h>
19 a248a3c1 2019-03-27 martijn
20 2e3e5d5f 2019-03-29 martijn #include <arpa/inet.h>
21 a248a3c1 2019-03-27 martijn #include <errno.h>
22 aeda4d4e 2019-03-28 martijn #include <event.h>
23 a287838f 2019-03-31 martijn #include <inttypes.h>
24 a248a3c1 2019-03-27 martijn #include <netdb.h>
25 a248a3c1 2019-03-27 martijn #include <stdlib.h>
26 a248a3c1 2019-03-27 martijn #include <stdio.h>
27 a248a3c1 2019-03-27 martijn #include <string.h>
28 b633fa4c 2019-03-29 martijn #include <syslog.h>
29 a248a3c1 2019-03-27 martijn #include <unistd.h>
30 aeda4d4e 2019-03-28 martijn #include <asr.h>
31 a248a3c1 2019-03-27 martijn
32 b633fa4c 2019-03-29 martijn #include "log.h"
33 a248a3c1 2019-03-27 martijn #include "smtp_proc.h"
34 a248a3c1 2019-03-27 martijn
35 aeda4d4e 2019-03-28 martijn struct dnsbl_session;
36 aeda4d4e 2019-03-28 martijn
37 aeda4d4e 2019-03-28 martijn struct dnsbl_query {
38 aeda4d4e 2019-03-28 martijn struct asr_query *query;
39 aeda4d4e 2019-03-28 martijn struct event_asr *event;
40 aeda4d4e 2019-03-28 martijn int resolved;
41 aeda4d4e 2019-03-28 martijn int blacklist;
42 aeda4d4e 2019-03-28 martijn struct dnsbl_session *session;
43 aeda4d4e 2019-03-28 martijn };
44 aeda4d4e 2019-03-28 martijn
45 aeda4d4e 2019-03-28 martijn struct dnsbl_session {
46 aeda4d4e 2019-03-28 martijn uint64_t reqid;
47 aeda4d4e 2019-03-28 martijn uint64_t token;
48 2e3e5d5f 2019-03-29 martijn char addr[INET6_ADDRSTRLEN];
49 5468729e 2019-03-29 martijn int listed;
50 a287838f 2019-03-31 martijn int set_header;
51 a287838f 2019-03-31 martijn int logged_mark;
52 aeda4d4e 2019-03-28 martijn struct dnsbl_query *query;
53 c75461cd 2019-03-29 martijn RB_ENTRY(dnsbl_session) entry;
54 aeda4d4e 2019-03-28 martijn };
55 aeda4d4e 2019-03-28 martijn
56 c75461cd 2019-03-29 martijn RB_HEAD(dnsbl_sessions, dnsbl_session) dnsbl_sessions = RB_INITIALIZER(NULL);
57 c75461cd 2019-03-29 martijn RB_PROTOTYPE(dnsbl_sessions, dnsbl_session, entry, dnsbl_session_cmp);
58 c75461cd 2019-03-29 martijn
59 a248a3c1 2019-03-27 martijn static char **blacklists = NULL;
60 a248a3c1 2019-03-27 martijn static size_t nblacklists = 0;
61 5468729e 2019-03-29 martijn static int markspam = 0;
62 a248a3c1 2019-03-27 martijn
63 a248a3c1 2019-03-27 martijn void usage(void);
64 42a4c512 2019-03-28 martijn void dnsbl_connect(char *, int, struct timespec *, char *, char *, uint64_t,
65 03a6f52a 2019-03-29 martijn uint64_t, char *, struct inx_addr *);
66 a287838f 2019-03-31 martijn void dnsbl_data(char *, int, struct timespec *, char *, char *, uint64_t,
67 a287838f 2019-03-31 martijn uint64_t);
68 5468729e 2019-03-29 martijn void dnsbl_dataline(char *, int, struct timespec *, char *, char *, uint64_t,
69 5468729e 2019-03-29 martijn uint64_t, char *);
70 c75461cd 2019-03-29 martijn void dnsbl_disconnect(char *, int, struct timespec *, char *, char *, uint64_t);
71 aeda4d4e 2019-03-28 martijn void dnsbl_resolve(struct asr_result *, void *);
72 5468729e 2019-03-29 martijn void dnsbl_session_query_done(struct dnsbl_session *);
73 aeda4d4e 2019-03-28 martijn void dnsbl_session_free(struct dnsbl_session *);
74 c75461cd 2019-03-29 martijn int dnsbl_session_cmp(struct dnsbl_session *, struct dnsbl_session *);
75 a248a3c1 2019-03-27 martijn
76 a248a3c1 2019-03-27 martijn int
77 a248a3c1 2019-03-27 martijn main(int argc, char *argv[])
78 a248a3c1 2019-03-27 martijn {
79 a248a3c1 2019-03-27 martijn int ch;
80 deafb383 2019-03-29 martijn int i;
81 b633fa4c 2019-03-29 martijn int debug = 0;
82 a248a3c1 2019-03-27 martijn
83 5905c22e 2019-03-29 martijn while ((ch = getopt(argc, argv, "dm")) != -1) {
84 a248a3c1 2019-03-27 martijn switch (ch) {
85 b633fa4c 2019-03-29 martijn case 'd':
86 b633fa4c 2019-03-29 martijn debug = 1;
87 b633fa4c 2019-03-29 martijn break;
88 5468729e 2019-03-29 martijn case 'm':
89 5468729e 2019-03-29 martijn markspam = 1;
90 5468729e 2019-03-29 martijn break;
91 a248a3c1 2019-03-27 martijn default:
92 a248a3c1 2019-03-27 martijn usage();
93 a248a3c1 2019-03-27 martijn }
94 a248a3c1 2019-03-27 martijn }
95 a248a3c1 2019-03-27 martijn
96 b633fa4c 2019-03-29 martijn log_init(debug, LOG_MAIL);
97 b633fa4c 2019-03-29 martijn if (pledge("stdio dns", NULL) == -1)
98 b633fa4c 2019-03-29 martijn fatal("pledge");
99 b633fa4c 2019-03-29 martijn
100 96a01110 2019-03-29 martijn if ((nblacklists = argc - optind) == 0)
101 b633fa4c 2019-03-29 martijn fatalx("No blacklist specified");
102 deafb383 2019-03-29 martijn
103 deafb383 2019-03-29 martijn if ((blacklists = calloc(nblacklists, sizeof(*blacklists))) == NULL)
104 b633fa4c 2019-03-29 martijn fatal(NULL);
105 deafb383 2019-03-29 martijn for (i = 0; i < nblacklists; i++)
106 deafb383 2019-03-29 martijn blacklists[i] = argv[optind + i];
107 deafb383 2019-03-29 martijn
108 a248a3c1 2019-03-27 martijn smtp_register_filter_connect(dnsbl_connect);
109 a287838f 2019-03-31 martijn if (markspam) {
110 a287838f 2019-03-31 martijn smtp_register_filter_data(dnsbl_data);
111 deafb383 2019-03-29 martijn smtp_register_filter_dataline(dnsbl_dataline);
112 a287838f 2019-03-31 martijn }
113 c75461cd 2019-03-29 martijn smtp_in_register_report_disconnect(dnsbl_disconnect);
114 b633fa4c 2019-03-29 martijn smtp_run(debug);
115 a248a3c1 2019-03-27 martijn
116 a248a3c1 2019-03-27 martijn return 0;
117 a248a3c1 2019-03-27 martijn }
118 a248a3c1 2019-03-27 martijn
119 aeda4d4e 2019-03-28 martijn void
120 42a4c512 2019-03-28 martijn dnsbl_connect(char *type, int version, struct timespec *tm, char *direction,
121 03a6f52a 2019-03-29 martijn char *phase, uint64_t reqid, uint64_t token, char *hostname,
122 03a6f52a 2019-03-29 martijn struct inx_addr *xaddr)
123 a248a3c1 2019-03-27 martijn {
124 aeda4d4e 2019-03-28 martijn struct dnsbl_session *session;
125 a248a3c1 2019-03-27 martijn char query[255];
126 a248a3c1 2019-03-27 martijn u_char *addr;
127 57cbfccc 2019-03-27 martijn int i, try;
128 a248a3c1 2019-03-27 martijn
129 aeda4d4e 2019-03-28 martijn if ((session = calloc(1, sizeof(*session))) == NULL)
130 b633fa4c 2019-03-29 martijn fatal(NULL);
131 aeda4d4e 2019-03-28 martijn if ((session->query = calloc(nblacklists, sizeof(*(session->query))))
132 aeda4d4e 2019-03-28 martijn == NULL)
133 b633fa4c 2019-03-29 martijn fatal(NULL);
134 aeda4d4e 2019-03-28 martijn session->reqid = reqid;
135 aeda4d4e 2019-03-28 martijn session->token = token;
136 5468729e 2019-03-29 martijn session->listed = -1;
137 a287838f 2019-03-31 martijn session->set_header = 0;
138 a287838f 2019-03-31 martijn session->logged_mark = 0;
139 2e3e5d5f 2019-03-29 martijn if (inet_ntop(xaddr->af, xaddr->af == AF_INET ?
140 2e3e5d5f 2019-03-29 martijn (void *)&(xaddr->addr) : (void *)&(xaddr->addr6), session->addr,
141 2e3e5d5f 2019-03-29 martijn sizeof(session->addr)) == NULL)
142 2e3e5d5f 2019-03-29 martijn fatal("inet_ntop");
143 2e3e5d5f 2019-03-29 martijn
144 c75461cd 2019-03-29 martijn RB_INSERT(dnsbl_sessions, &dnsbl_sessions, session);
145 aeda4d4e 2019-03-28 martijn
146 03a6f52a 2019-03-29 martijn if (xaddr->af == AF_INET)
147 03a6f52a 2019-03-29 martijn addr = (u_char *)&(xaddr->addr);
148 03a6f52a 2019-03-29 martijn else
149 03a6f52a 2019-03-29 martijn addr = (u_char *)&(xaddr->addr6);
150 a248a3c1 2019-03-27 martijn for (i = 0; i < nblacklists; i++) {
151 03a6f52a 2019-03-29 martijn if (xaddr->af == AF_INET) {
152 a248a3c1 2019-03-27 martijn if (snprintf(query, sizeof(query), "%u.%u.%u.%u.%s",
153 a248a3c1 2019-03-27 martijn addr[3], addr[2], addr[1], addr[0],
154 a248a3c1 2019-03-27 martijn blacklists[i]) >= sizeof(query))
155 b633fa4c 2019-03-29 martijn fatalx("Can't create query, domain too long");
156 03a6f52a 2019-03-29 martijn } else if (xaddr->af == AF_INET6) {
157 a248a3c1 2019-03-27 martijn if (snprintf(query, sizeof(query), "%hhx.%hhx.%hhx.%hhx"
158 a248a3c1 2019-03-27 martijn ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx"
159 a248a3c1 2019-03-27 martijn ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx"
160 a248a3c1 2019-03-27 martijn ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%s",
161 a248a3c1 2019-03-27 martijn (u_char) (addr[15] & 0xf), (u_char) (addr[15] >> 4),
162 a248a3c1 2019-03-27 martijn (u_char) (addr[14] & 0xf), (u_char) (addr[14] >> 4),
163 a248a3c1 2019-03-27 martijn (u_char) (addr[13] & 0xf), (u_char) (addr[13] >> 4),
164 a248a3c1 2019-03-27 martijn (u_char) (addr[12] & 0xf), (u_char) (addr[12] >> 4),
165 a248a3c1 2019-03-27 martijn (u_char) (addr[11] & 0xf), (u_char) (addr[11] >> 4),
166 a248a3c1 2019-03-27 martijn (u_char) (addr[10] & 0xf), (u_char) (addr[10] >> 4),
167 a248a3c1 2019-03-27 martijn (u_char) (addr[9] & 0xf), (u_char) (addr[9] >> 4),
168 a248a3c1 2019-03-27 martijn (u_char) (addr[8] & 0xf), (u_char) (addr[8] >> 4),
169 a248a3c1 2019-03-27 martijn (u_char) (addr[7] & 0xf), (u_char) (addr[8] >> 4),
170 a248a3c1 2019-03-27 martijn (u_char) (addr[6] & 0xf), (u_char) (addr[7] >> 4),
171 a248a3c1 2019-03-27 martijn (u_char) (addr[5] & 0xf), (u_char) (addr[5] >> 4),
172 a248a3c1 2019-03-27 martijn (u_char) (addr[4] & 0xf), (u_char) (addr[4] >> 4),
173 a248a3c1 2019-03-27 martijn (u_char) (addr[3] & 0xf), (u_char) (addr[3] >> 4),
174 a248a3c1 2019-03-27 martijn (u_char) (addr[2] & 0xf), (u_char) (addr[2] >> 4),
175 a248a3c1 2019-03-27 martijn (u_char) (addr[1] & 0xf), (u_char) (addr[1] >> 4),
176 a248a3c1 2019-03-27 martijn (u_char) (addr[0] & 0xf), (u_char) (addr[0] >> 4),
177 a248a3c1 2019-03-27 martijn blacklists[i]) >= sizeof(query))
178 b633fa4c 2019-03-29 martijn fatalx( "Can't create query, domain too long");
179 a248a3c1 2019-03-27 martijn } else
180 b633fa4c 2019-03-29 martijn fatalx("Invalid address family received");
181 a248a3c1 2019-03-27 martijn
182 aeda4d4e 2019-03-28 martijn session->query[i].query = gethostbyname_async(query, NULL);
183 aeda4d4e 2019-03-28 martijn session->query[i].event = event_asr_run(session->query[i].query,
184 aeda4d4e 2019-03-28 martijn dnsbl_resolve, &(session->query[i]));
185 aeda4d4e 2019-03-28 martijn session->query[i].blacklist = i;
186 aeda4d4e 2019-03-28 martijn session->query[i].session = session;
187 a248a3c1 2019-03-27 martijn }
188 a248a3c1 2019-03-27 martijn }
189 a248a3c1 2019-03-27 martijn
190 aeda4d4e 2019-03-28 martijn void
191 aeda4d4e 2019-03-28 martijn dnsbl_resolve(struct asr_result *result, void *arg)
192 aeda4d4e 2019-03-28 martijn {
193 aeda4d4e 2019-03-28 martijn struct dnsbl_query *query = arg;
194 aeda4d4e 2019-03-28 martijn struct dnsbl_session *session = query->session;
195 aeda4d4e 2019-03-28 martijn int i, blacklist;
196 aeda4d4e 2019-03-28 martijn
197 aeda4d4e 2019-03-28 martijn query->resolved = 1;
198 aeda4d4e 2019-03-28 martijn query->event = NULL;
199 aeda4d4e 2019-03-28 martijn query->query = NULL;
200 aeda4d4e 2019-03-28 martijn if (result->ar_hostent != NULL) {
201 5468729e 2019-03-29 martijn if (!markspam) {
202 5468729e 2019-03-29 martijn smtp_filter_disconnect(session->reqid, session->token,
203 f15f6435 2019-03-29 martijn "Listed at %s", blacklists[query->blacklist]);
204 a287838f 2019-03-31 martijn log_info("%016"PRIx64" listed at %s: rejected",
205 a287838f 2019-03-31 martijn session->reqid, blacklists[query->blacklist]);
206 5468729e 2019-03-29 martijn dnsbl_session_free(session);
207 5468729e 2019-03-29 martijn } else {
208 5468729e 2019-03-29 martijn dnsbl_session_query_done(session);
209 5468729e 2019-03-29 martijn session->listed = query->blacklist;
210 5468729e 2019-03-29 martijn smtp_filter_proceed(session->reqid, session->token);
211 a287838f 2019-03-31 martijn /* Delay logging until we have a message */
212 5468729e 2019-03-29 martijn }
213 aeda4d4e 2019-03-28 martijn return;
214 aeda4d4e 2019-03-28 martijn }
215 cf6af233 2019-03-28 martijn if (result->ar_h_errno != HOST_NOT_FOUND) {
216 cf6af233 2019-03-28 martijn smtp_filter_disconnect(session->reqid, session->token,
217 76e3b8e2 2019-03-28 martijn "DNS error on %s", blacklists[query->blacklist]);
218 cf6af233 2019-03-28 martijn dnsbl_session_free(session);
219 cf6af233 2019-03-28 martijn return;
220 cf6af233 2019-03-28 martijn }
221 aeda4d4e 2019-03-28 martijn
222 aeda4d4e 2019-03-28 martijn for (i = 0; i < nblacklists; i++) {
223 aeda4d4e 2019-03-28 martijn if (!session->query[i].resolved)
224 aeda4d4e 2019-03-28 martijn return;
225 aeda4d4e 2019-03-28 martijn }
226 aeda4d4e 2019-03-28 martijn smtp_filter_proceed(session->reqid, session->token);
227 a287838f 2019-03-31 martijn log_info("%016"PRIx64" not listed", session->reqid);
228 aeda4d4e 2019-03-28 martijn }
229 aeda4d4e 2019-03-28 martijn
230 aeda4d4e 2019-03-28 martijn void
231 c75461cd 2019-03-29 martijn dnsbl_disconnect(char *type, int version, struct timespec *tm, char *direction,
232 c75461cd 2019-03-29 martijn char *phase, uint64_t reqid)
233 c75461cd 2019-03-29 martijn {
234 c75461cd 2019-03-29 martijn struct dnsbl_session *session, search;
235 c75461cd 2019-03-29 martijn
236 c75461cd 2019-03-29 martijn search.reqid = reqid;
237 c75461cd 2019-03-29 martijn if ((session = RB_FIND(dnsbl_sessions, &dnsbl_sessions, &search)) != NULL)
238 c75461cd 2019-03-29 martijn dnsbl_session_free(session);
239 c75461cd 2019-03-29 martijn }
240 c75461cd 2019-03-29 martijn
241 c75461cd 2019-03-29 martijn void
242 a287838f 2019-03-31 martijn dnsbl_data(char *type, int version, struct timespec *tm, char *direction,
243 a287838f 2019-03-31 martijn char *phase, uint64_t reqid, uint64_t token)
244 a287838f 2019-03-31 martijn {
245 a287838f 2019-03-31 martijn struct dnsbl_session *session, search;
246 a287838f 2019-03-31 martijn
247 a287838f 2019-03-31 martijn search.reqid = reqid;
248 a287838f 2019-03-31 martijn session = RB_FIND(dnsbl_sessions, &dnsbl_sessions, &search);
249 a287838f 2019-03-31 martijn
250 a287838f 2019-03-31 martijn if (session->listed != -1) {
251 a287838f 2019-03-31 martijn if (!session->logged_mark) {
252 a287838f 2019-03-31 martijn log_info("%016"PRIx64" listed at %s: Marking as spam",
253 a287838f 2019-03-31 martijn session->reqid, blacklists[session->listed]);
254 a287838f 2019-03-31 martijn session->logged_mark = 1;
255 a287838f 2019-03-31 martijn }
256 a287838f 2019-03-31 martijn session->set_header = 1;
257 a287838f 2019-03-31 martijn }
258 a287838f 2019-03-31 martijn smtp_filter_proceed(reqid, token);
259 a287838f 2019-03-31 martijn }
260 a287838f 2019-03-31 martijn
261 a287838f 2019-03-31 martijn void
262 5468729e 2019-03-29 martijn dnsbl_dataline(char *type, int version, struct timespec *tm, char *direction,
263 5468729e 2019-03-29 martijn char *phase, uint64_t reqid, uint64_t token, char *line)
264 aeda4d4e 2019-03-28 martijn {
265 5468729e 2019-03-29 martijn struct dnsbl_session *session, search;
266 5468729e 2019-03-29 martijn
267 5468729e 2019-03-29 martijn search.reqid = reqid;
268 5468729e 2019-03-29 martijn session = RB_FIND(dnsbl_sessions, &dnsbl_sessions, &search);
269 5468729e 2019-03-29 martijn
270 a287838f 2019-03-31 martijn if (session->set_header) {
271 5468729e 2019-03-29 martijn smtp_filter_dataline(reqid, token, "X-Spam: yes");
272 5468729e 2019-03-29 martijn smtp_filter_dataline(reqid, token, "X-Spam-DNSBL: Listed at %s",
273 5468729e 2019-03-29 martijn blacklists[session->listed]);
274 a287838f 2019-03-31 martijn session->set_header = 0;
275 a287838f 2019-03-31 martijn
276 5468729e 2019-03-29 martijn }
277 5468729e 2019-03-29 martijn smtp_filter_dataline(reqid, token, "%s", line);
278 5468729e 2019-03-29 martijn }
279 5468729e 2019-03-29 martijn
280 5468729e 2019-03-29 martijn void
281 5468729e 2019-03-29 martijn dnsbl_session_query_done(struct dnsbl_session *session)
282 5468729e 2019-03-29 martijn {
283 aeda4d4e 2019-03-28 martijn int i;
284 aeda4d4e 2019-03-28 martijn
285 aeda4d4e 2019-03-28 martijn for (i = 0; i < nblacklists; i++) {
286 76e3b8e2 2019-03-28 martijn if (!session->query[i].resolved) {
287 aeda4d4e 2019-03-28 martijn event_asr_abort(session->query[i].event);
288 4dce78a6 2019-04-05 martijn session->query[i].resolved = 1;
289 76e3b8e2 2019-03-28 martijn }
290 aeda4d4e 2019-03-28 martijn }
291 5468729e 2019-03-29 martijn }
292 5468729e 2019-03-29 martijn
293 5468729e 2019-03-29 martijn void
294 5468729e 2019-03-29 martijn dnsbl_session_free(struct dnsbl_session *session)
295 5468729e 2019-03-29 martijn {
296 5468729e 2019-03-29 martijn RB_REMOVE(dnsbl_sessions, &dnsbl_sessions, session);
297 5468729e 2019-03-29 martijn dnsbl_session_query_done(session);
298 aeda4d4e 2019-03-28 martijn free(session->query);
299 aeda4d4e 2019-03-28 martijn free(session);
300 aeda4d4e 2019-03-28 martijn }
301 aeda4d4e 2019-03-28 martijn
302 c75461cd 2019-03-29 martijn int
303 c75461cd 2019-03-29 martijn dnsbl_session_cmp(struct dnsbl_session *s1, struct dnsbl_session *s2)
304 c75461cd 2019-03-29 martijn {
305 c75461cd 2019-03-29 martijn return (s1->reqid < s2->reqid ? -1 : s1->reqid > s2->reqid);
306 c75461cd 2019-03-29 martijn }
307 c75461cd 2019-03-29 martijn
308 f8356fc3 2019-03-28 martijn __dead void
309 a248a3c1 2019-03-27 martijn usage(void)
310 a248a3c1 2019-03-27 martijn {
311 deafb383 2019-03-29 martijn fprintf(stderr, "usage: %s [-m] blacklist [...]\n",
312 a248a3c1 2019-03-27 martijn getprogname());
313 a248a3c1 2019-03-27 martijn exit(1);
314 a248a3c1 2019-03-27 martijn }
315 c75461cd 2019-03-29 martijn
316 c75461cd 2019-03-29 martijn RB_GENERATE(dnsbl_sessions, dnsbl_session, entry, dnsbl_session_cmp);