Mercurial > dnsbl
annotate src/dnsbl.cpp @ 23:06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
author | carl |
---|---|
date | Wed, 12 May 2004 13:23:22 -0700 |
parents | c21b888cc175 |
children | 2e23b7184d2b |
rev | line source |
---|---|
0 | 1 /* |
2 | |
3 Copyright (c) 2004 Carl Byington - 510 Software Group, released under | |
4 the GPL version 2 or any later version at your choice available at | |
5 http://www.fsf.org/licenses/gpl.txt | |
6 | |
7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its | |
8 suppliers. Inspired by the DCC by Rhyolite Software | |
9 | |
10 -p port The port through which the MTA will connect to this milter. | |
11 -t sec The timeout value. | |
9 | 12 -c Check the config, and print a copy to stdout. Don't start the |
4 | 13 milter or do anything with the socket. |
16 | 14 -d Add debug syslog entries |
15 | |
0 | 16 |
13 | 17 TODO: |
18 1) Add config for max_recipients for each mail domain. Recipients in | |
19 excess of that limit will be rejected, and the entire data will be | |
20 rejected if it is sent. | |
21 | |
22 2) Add config for poison addresses. If any recipient is poison, all | |
23 recipients are rejected even if they would be whitelisted, and the | |
24 data is rejected if sent. | |
25 | |
0 | 26 */ |
27 | |
28 | |
29 // from sendmail sample | |
30 #include <sys/types.h> | |
31 #include <sys/stat.h> | |
32 #include <errno.h> | |
33 #include <sysexits.h> | |
34 #include <unistd.h> | |
35 | |
36 // needed for socket io | |
37 #include <sys/ioctl.h> | |
38 #include <net/if.h> | |
39 #include <arpa/inet.h> | |
40 #include <netinet/in.h> | |
41 #include <netinet/tcp.h> | |
42 #include <netdb.h> | |
43 #include <sys/socket.h> | |
44 | |
45 // needed for thread | |
46 #include <pthread.h> | |
47 | |
48 // needed for std c++ collections | |
49 #include <set> | |
50 #include <map> | |
51 #include <list> | |
52 | |
53 // for the dns resolver | |
54 #include <netinet/in.h> | |
55 #include <arpa/nameser.h> | |
56 #include <resolv.h> | |
57 | |
58 // misc stuff needed here | |
59 #include <ctype.h> | |
60 #include <fstream> | |
61 #include <syslog.h> | |
62 | |
8 | 63 static char* dnsbl_version="$Id$"; |
0 | 64 |
8 | 65 #define DEFAULT "default" |
66 #define WHITE "white" | |
67 #define BLACK "black" | |
68 #define OK "ok" | |
69 #define MANY "many" | |
70 | |
71 enum status {oksofar, // not rejected yet | |
72 white, // whitelisted by envelope from | |
73 black, // blacklisted by envelope from or to | |
74 reject}; // rejected by a dns list | |
1 | 75 |
0 | 76 using namespace std; |
77 | |
78 extern "C" { | |
79 #include "libmilter/mfapi.h" | |
80 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); | |
81 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv); | |
82 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv); | |
8 | 83 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len); |
84 sfsistat mlfi_eom(SMFICTX *ctx); | |
85 sfsistat mlfi_abort(SMFICTX *ctx); | |
0 | 86 sfsistat mlfi_close(SMFICTX *ctx); |
87 } | |
88 | |
89 struct ltstr { | |
90 bool operator()(char* s1, char* s2) const { | |
91 return strcmp(s1, s2) < 0; | |
92 } | |
93 }; | |
94 | |
95 struct DNSBL { | |
96 char *suffix; // blacklist suffix like blackholes.five-ten-sg.com | |
97 char *message; // error message with one or two %s operators for the ip address replacement | |
98 DNSBL(char *s, char *m); | |
99 }; | |
100 DNSBL::DNSBL(char *s, char *m) { | |
101 suffix = s; | |
102 message = m; | |
103 } | |
104 | |
105 typedef DNSBL * DNSBLP; | |
106 typedef list<DNSBLP> DNSBLL; | |
107 typedef DNSBLL * DNSBLLP; | |
108 typedef map<char *, char *, ltstr> string_map; | |
109 typedef map<char *, string_map *, ltstr> from_map; | |
110 typedef map<char *, DNSBLP, ltstr> dnsblp_map; | |
111 typedef map<char *, DNSBLLP, ltstr> dnsbllp_map; | |
112 typedef set<char *, ltstr> string_set; | |
113 typedef list<char *> string_list; | |
114 | |
115 struct CONFIG { | |
116 // the only mutable stuff once it has been loaded from the config file | |
117 int reference_count; // protected by the global config_mutex | |
118 // all the rest is constant after loading from the config file | |
119 time_t load_time; | |
120 string_list config_files; | |
121 dnsblp_map dnsbls; | |
122 dnsbllp_map dnsblls; | |
123 from_map env_from; | |
124 string_map env_to_dnsbll; // map recipient to a named dnsbll | |
125 string_map env_to_chkfrom; // map recipient to a named from map | |
8 | 126 char * content_suffix; // for sbl url body filtering |
9 | 127 char * content_message; // "" |
0 | 128 CONFIG(); |
129 ~CONFIG(); | |
130 }; | |
131 CONFIG::CONFIG() { | |
132 reference_count = 0; | |
133 load_time = 0; | |
8 | 134 content_suffix = NULL; |
135 content_message = NULL; | |
0 | 136 } |
137 CONFIG::~CONFIG() { | |
138 for (dnsblp_map::iterator i=dnsbls.begin(); i!=dnsbls.end(); i++) { | |
139 DNSBLP d = (*i).second; | |
140 delete d; | |
141 } | |
142 for (dnsbllp_map::iterator i=dnsblls.begin(); i!=dnsblls.end(); i++) { | |
143 DNSBLLP d = (*i).second; | |
144 delete d; | |
145 } | |
146 for (from_map::iterator i=env_from.begin(); i!=env_from.end(); i++) { | |
147 string_map *d = (*i).second; | |
148 delete d; | |
149 } | |
150 } | |
151 | |
16 | 152 static bool debug_syslog = false; |
18 | 153 static bool loader_run = true; // used to stop the config loader thread |
0 | 154 static string_set all_strings; // owns all the strings, only modified by the config loader thread |
155 static CONFIG * config = NULL; // protected by the config_mutex | |
156 | |
157 static pthread_mutex_t config_mutex; | |
158 static pthread_mutex_t syslog_mutex; | |
159 static pthread_mutex_t resolve_mutex; | |
160 | |
161 | |
162 //////////////////////////////////////////////// | |
8 | 163 // helper to discard the strings held by a string_set |
0 | 164 // |
9 | 165 static void discard(string_set &s); |
166 static void discard(string_set &s) { | |
8 | 167 for (string_set::iterator i=s.begin(); i!=s.end(); i++) { |
168 free(*i); | |
169 } | |
9 | 170 s.clear(); |
8 | 171 } |
0 | 172 |
12 | 173 //////////////////////////////////////////////// |
174 // helper to register a string in a string set | |
175 // | |
176 static char* register_string(string_set &s, char *name); | |
177 static char* register_string(string_set &s, char *name) { | |
178 string_set::iterator i = s.find(name); | |
179 if (i != s.end()) return *i; | |
180 char *x = strdup(name); | |
181 s.insert(x); | |
182 return x; | |
183 } | |
184 | |
16 | 185 //////////////////////////////////////////////// |
186 // syslog a message | |
187 // | |
188 static void my_syslog(char *text); | |
189 static void my_syslog(char *text) { | |
190 pthread_mutex_lock(&syslog_mutex); | |
191 openlog("dnsbl", LOG_PID, LOG_MAIL); | |
192 syslog(LOG_NOTICE, "%s", text); | |
193 closelog(); | |
194 pthread_mutex_unlock(&syslog_mutex); | |
195 } | |
196 | |
197 | |
12 | 198 // include the content scanner |
199 #include "scanner.cpp" | |
200 | |
201 | |
0 | 202 //////////////////////////////////////////////// |
203 // mail filter private data, held for us by sendmail | |
204 // | |
205 struct mlfiPriv | |
206 { | |
8 | 207 // connection specific data |
208 CONFIG *pc; // global context with our maps | |
209 int ip; // ip4 address of the smtp client | |
210 map<DNSBLP, status> checked; // status from those lists | |
211 // message specific data | |
0 | 212 char *mailaddr; // envelope from value |
213 bool authenticated; // client authenticated? if so, suppress all dnsbl checks | |
8 | 214 bool have_whites; // have at least one whitelisted recipient? need to accept content and remove all non-whitelisted recipients if it fails |
215 bool only_whites; // every recipient is whitelisted? | |
216 url_scanner *scanner; // object to handle body scanning | |
217 string_set non_whites; // remember the non-whitelisted recipients so we can remove them if need be | |
12 | 218 string_set hosts; // remember the hosts that we have checked |
0 | 219 mlfiPriv(); |
220 ~mlfiPriv(); | |
8 | 221 void reset(bool final = false); // for a new message |
0 | 222 }; |
223 mlfiPriv::mlfiPriv() { | |
224 pthread_mutex_lock(&config_mutex); | |
225 pc = config; | |
226 pc->reference_count++; | |
227 pthread_mutex_unlock(&config_mutex); | |
8 | 228 ip = 0; |
229 mailaddr = NULL; | |
230 authenticated = false; | |
231 have_whites = false; | |
232 only_whites = true; | |
12 | 233 scanner = new url_scanner(&hosts); |
0 | 234 } |
235 mlfiPriv::~mlfiPriv() { | |
236 pthread_mutex_lock(&config_mutex); | |
237 pc->reference_count--; | |
238 pthread_mutex_unlock(&config_mutex); | |
8 | 239 reset(true); |
240 } | |
241 void mlfiPriv::reset(bool final) { | |
0 | 242 if (mailaddr) free(mailaddr); |
8 | 243 delete scanner; |
244 discard(non_whites); | |
12 | 245 discard(hosts); |
8 | 246 if (!final) { |
247 mailaddr = NULL; | |
248 authenticated = false; | |
249 have_whites = false; | |
250 only_whites = true; | |
12 | 251 scanner = new url_scanner(&hosts); |
8 | 252 } |
0 | 253 } |
254 | |
255 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) | |
256 | |
257 | |
258 //////////////////////////////////////////////// | |
259 // register a global string | |
260 // | |
261 static char* register_string(char *name); | |
262 static char* register_string(char *name) { | |
12 | 263 return register_string(all_strings, name); |
0 | 264 } |
265 | |
266 | |
267 static char* next_token(char *delim); | |
268 static char* next_token(char *delim) { | |
269 char *name = strtok(NULL, delim); | |
270 if (!name) return name; | |
271 return register_string(name); | |
272 } | |
273 | |
274 | |
275 //////////////////////////////////////////////// | |
276 // lookup an email address in the env_from or env_to maps | |
277 // | |
278 static char* lookup1(char *email, string_map map); | |
279 static char* lookup1(char *email, string_map map) { | |
280 string_map::iterator i = map.find(email); | |
281 if (i != map.end()) return (*i).second; | |
282 char *x = strchr(email, '@'); | |
283 if (!x) return DEFAULT; | |
284 x++; | |
285 i = map.find(x); | |
286 if (i != map.end()) return (*i).second; | |
287 return DEFAULT; | |
288 } | |
289 | |
290 | |
291 //////////////////////////////////////////////// | |
292 // lookup an email address in the env_from or env_to maps | |
293 // this email address is passed in from sendmail, and will | |
294 // always be enclosed in <>. It may have mixed case, just | |
295 // as the mail client sent it. | |
296 // | |
297 static char* lookup(char* email, string_map map); | |
298 static char* lookup(char* email, string_map map) { | |
299 int n = strlen(email)-2; | |
300 if (n < 1) return DEFAULT; // malformed | |
301 char *key = strdup(email+1); | |
302 key[n] = '\0'; | |
303 for (int i=0; i<n; i++) key[i] = tolower(key[i]); | |
304 char *rc = lookup1(key, map); | |
305 free(key); | |
306 return rc; | |
307 } | |
308 | |
309 | |
310 //////////////////////////////////////////////// | |
311 // find the dnsbl with a specific name | |
312 // | |
313 static DNSBLP find_dnsbl(CONFIG &dc, char *name); | |
314 static DNSBLP find_dnsbl(CONFIG &dc, char *name) { | |
315 dnsblp_map::iterator i = dc.dnsbls.find(name); | |
316 if (i == dc.dnsbls.end()) return NULL; | |
317 return (*i).second; | |
318 } | |
319 | |
320 | |
321 //////////////////////////////////////////////// | |
322 // find the dnsbll with a specific name | |
323 // | |
324 static DNSBLLP find_dnsbll(CONFIG &dc, char *name); | |
325 static DNSBLLP find_dnsbll(CONFIG &dc, char *name) { | |
326 dnsbllp_map::iterator i = dc.dnsblls.find(name); | |
327 if (i == dc.dnsblls.end()) return NULL; | |
328 return (*i).second; | |
329 } | |
330 | |
331 | |
332 //////////////////////////////////////////////// | |
333 // find the envfrom map with a specific name | |
334 // | |
335 static string_map* find_from_map(CONFIG &dc, char *name); | |
336 static string_map* find_from_map(CONFIG &dc, char *name) { | |
337 from_map::iterator i = dc.env_from.find(name); | |
338 if (i == dc.env_from.end()) return NULL; | |
339 return (*i).second; | |
340 } | |
341 | |
342 | |
343 static string_map& really_find_from_map(CONFIG &dc, char *name); | |
344 static string_map& really_find_from_map(CONFIG &dc, char *name) { | |
345 string_map *sm = find_from_map(dc, name); | |
346 if (!sm) { | |
347 sm = new string_map; | |
348 dc.env_from[name] = sm; | |
349 } | |
350 return *sm; | |
351 } | |
352 | |
353 | |
354 //////////////////////////////////////////////// | |
8 | 355 // |
356 // ask a dns question and get an A record answer - we don't try | |
357 // very hard, just using the default resolver retry settings. | |
358 // If we cannot get an answer, we just accept the mail. The | |
359 // caller must ensure thread safety. | |
360 // | |
0 | 361 // |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
362 static int dns_interface(char *question, bool maybe_ip); |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
363 static int dns_interface(char *question, bool maybe_ip) { |
16 | 364 #ifdef NS_PACKETSZ |
8 | 365 u_char answer[NS_PACKETSZ]; |
366 int length = res_search(question, ns_c_in, ns_t_a, answer, sizeof(answer)); | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
367 if (length >= 0) { // no error yet |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
368 // parse the answer |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
369 ns_msg handle; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
370 ns_rr rr; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
371 if (ns_initparse(answer, length, &handle) == 0) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
372 int rrnum = 0; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
373 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
374 if (ns_rr_type(rr) == ns_t_a) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
375 int address; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
376 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
377 return address; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
378 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
379 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
380 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
381 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
382 if (maybe_ip) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
383 // might be a bare ip address |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
384 in_addr ip; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
385 if (inet_aton(question, &ip)) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
386 return ip.s_addr; |
8 | 387 } |
388 } | |
389 return 0; | |
16 | 390 #else |
391 struct hostent *host = gethostbyname(question); | |
392 if (!host) return 0; | |
393 if (host->h_addrtype != AF_INET) return 0; | |
394 int address; | |
395 memcpy(&address, host->h_addr, sizeof(address)); | |
396 return address; | |
397 #endif | |
8 | 398 } |
399 | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
400 static int protected_dns_interface(char *question, bool maybe_ip); |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
401 static int protected_dns_interface(char *question, bool maybe_ip) { |
8 | 402 int ans; |
403 pthread_mutex_lock(&resolve_mutex); | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
404 ans = dns_interface(question, maybe_ip); |
8 | 405 pthread_mutex_unlock(&resolve_mutex); |
406 return ans; | |
407 | |
408 } | |
409 | |
410 //////////////////////////////////////////////// | |
411 // check a single dnsbl | |
412 // | |
413 static status check_single(int ip, char *suffix); | |
414 static status check_single(int ip, char *suffix) { | |
0 | 415 // make a dns question |
416 const u_char *src = (const u_char *)&ip; | |
417 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost | |
16 | 418 #ifdef NS_MAXDNAME |
0 | 419 char question[NS_MAXDNAME]; |
16 | 420 #else |
421 char question[1000]; | |
422 #endif | |
8 | 423 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); |
424 // ask the question, if we get an A record it implies a blacklisted ip address | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
425 return (protected_dns_interface(question, false)) ? reject : oksofar; |
8 | 426 } |
427 | |
428 | |
429 //////////////////////////////////////////////// | |
430 // check a single dnsbl | |
431 // | |
432 static status check_single(int ip, DNSBL &bl); | |
433 static status check_single(int ip, DNSBL &bl) { | |
434 return check_single(ip, bl.suffix); | |
0 | 435 } |
436 | |
437 | |
438 //////////////////////////////////////////////// | |
439 // check the dnsbls specified for this recipient | |
440 // | |
441 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist); | |
442 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist) { | |
443 if (priv.authenticated) return oksofar; | |
444 if (!dnsbllp) return oksofar; | |
445 DNSBLL &dnsbll = *dnsbllp; | |
446 for (DNSBLL::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { | |
447 DNSBLP dp = *i; // non null by construction | |
448 status st; | |
449 map<DNSBLP, status>::iterator f = priv.checked.find(dp); | |
450 if (f == priv.checked.end()) { | |
451 // have not checked this list yet | |
8 | 452 st = check_single(priv.ip, *dp); |
0 | 453 rejectlist = dp; |
454 priv.checked[dp] = st; | |
455 } | |
456 else { | |
457 st = (*f).second; | |
458 rejectlist = (*f).first; | |
459 } | |
460 if (st == reject) return st; | |
461 } | |
462 return oksofar; | |
463 } | |
464 | |
465 | |
466 //////////////////////////////////////////////// | |
8 | 467 // check the dnsbls specified for this recipient |
468 // | |
16 | 469 static status check_hosts(mlfiPriv &priv, char *&host, int &ip); |
470 static status check_hosts(mlfiPriv &priv, char *&host, int &ip) { | |
8 | 471 CONFIG &dc = *priv.pc; |
472 if (!dc.content_suffix) return oksofar; | |
473 int count = 0; | |
12 | 474 for (string_set::iterator i=priv.hosts.begin(); i!=priv.hosts.end(); i++) { |
8 | 475 count++; |
12 | 476 if (count > 20) return oksofar; // silly to check too many hosts |
16 | 477 host = *i; |
478 if (debug_syslog) { | |
479 char buf[200]; | |
480 snprintf(buf, sizeof(buf), "looking for host %s", host); | |
481 my_syslog(buf); | |
482 } | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
483 ip = protected_dns_interface(host, true); |
8 | 484 if (ip) { |
16 | 485 if (debug_syslog) { |
486 char adr[sizeof "255.255.255.255"]; | |
487 adr[0] = '\0'; | |
488 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); | |
489 char buf[200]; | |
490 snprintf(buf, sizeof(buf), "found host %s at %s", host, adr); | |
491 my_syslog(buf); | |
492 } | |
8 | 493 status st = check_single(ip, dc.content_suffix); |
494 if (st == reject) return st; | |
495 } | |
496 } | |
9 | 497 return oksofar; |
8 | 498 } |
499 | |
500 | |
501 //////////////////////////////////////////////// | |
0 | 502 // start of sendmail milter interfaces |
503 // | |
504 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) | |
505 { | |
506 // allocate some private memory | |
507 mlfiPriv *priv = new mlfiPriv; | |
508 if (hostaddr->sa_family == AF_INET) { | |
509 priv->ip = ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr; | |
510 } | |
511 | |
512 // save the private data | |
513 smfi_setpriv(ctx, (void*)priv); | |
514 | |
515 // continue processing | |
516 return SMFIS_CONTINUE; | |
517 } | |
518 | |
519 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from) | |
520 { | |
521 mlfiPriv &priv = *MLFIPRIV; | |
522 priv.mailaddr = strdup(from[0]); | |
523 priv.authenticated = (smfi_getsymval(ctx, "{auth_authen}") != NULL); | |
524 return SMFIS_CONTINUE; | |
525 } | |
526 | |
527 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) | |
528 { | |
529 DNSBLP rejectlist = NULL; // list that caused the reject | |
530 status st = oksofar; | |
531 mlfiPriv &priv = *MLFIPRIV; | |
532 CONFIG &dc = *priv.pc; | |
533 char *rcptaddr = rcpt[0]; | |
534 char *dnsname = lookup(rcptaddr, dc.env_to_dnsbll); | |
535 char *fromname = lookup(rcptaddr, dc.env_to_chkfrom); | |
536 if ((strcmp(dnsname, BLACK) == 0) || | |
537 (strcmp(fromname, BLACK) == 0)) { | |
538 st = black; // two options to blacklist this recipient | |
539 } | |
540 else if (strcmp(fromname, WHITE) == 0) { | |
541 st = white; | |
542 } | |
543 else { | |
544 // check an env_from map | |
545 string_map *sm = find_from_map(dc, fromname); | |
546 if (sm != NULL) { | |
547 fromname = lookup(priv.mailaddr, *sm); // returns default if name not in map | |
548 if (strcmp(fromname, BLACK) == 0) { | |
549 st = black; // blacklist this envelope from value | |
550 } | |
551 if (strcmp(fromname, WHITE) == 0) { | |
552 st = white; // blacklist this envelope from value | |
553 } | |
554 } | |
555 } | |
556 if ((st == oksofar) && (strcmp(dnsname, WHITE) != 0)) { | |
557 // check dns lists | |
558 st = check_dnsbl(priv, find_dnsbll(dc, dnsname), rejectlist); | |
559 } | |
560 | |
561 if (st == reject) { | |
562 // reject the recipient based on some dnsbl | |
563 char adr[sizeof "255.255.255.255"]; | |
564 adr[0] = '\0'; | |
8 | 565 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr)); |
0 | 566 char buf[2000]; |
567 snprintf(buf, sizeof(buf), rejectlist->message, adr, adr); | |
568 smfi_setreply(ctx, "550", "5.7.1", buf); | |
569 return SMFIS_REJECT; | |
570 } | |
571 else if (st == black) { | |
572 // reject the recipient based on blacklisting either from or to | |
573 smfi_setreply(ctx, "550", "5.7.1", "no such user"); | |
574 return SMFIS_REJECT; | |
575 } | |
576 else { | |
577 // accept the recipient | |
8 | 578 if (st == oksofar) { |
579 // but remember the non-whites | |
12 | 580 register_string(priv.non_whites, rcptaddr); |
8 | 581 priv.only_whites = false; |
582 } | |
583 if (st == white) { | |
584 priv.have_whites = true; | |
585 } | |
0 | 586 return SMFIS_CONTINUE; |
587 } | |
588 } | |
589 | |
8 | 590 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len) |
0 | 591 { |
592 mlfiPriv &priv = *MLFIPRIV; | |
8 | 593 if (priv.authenticated) return SMFIS_CONTINUE; |
594 if (priv.only_whites) return SMFIS_CONTINUE; | |
595 priv.scanner->scan(data, len); | |
11 | 596 return SMFIS_CONTINUE; |
8 | 597 } |
598 | |
599 sfsistat mlfi_eom(SMFICTX *ctx) | |
600 { | |
601 sfsistat rc; | |
602 mlfiPriv &priv = *MLFIPRIV; | |
16 | 603 char *host = NULL; |
8 | 604 int ip; |
605 // process end of message | |
606 if (priv.authenticated || | |
607 priv.only_whites || | |
16 | 608 (check_hosts(priv, host, ip) == oksofar)) rc = SMFIS_CONTINUE; |
8 | 609 else { |
610 if (!priv.have_whites) { | |
611 // can reject the entire message | |
612 char adr[sizeof "255.255.255.255"]; | |
613 adr[0] = '\0'; | |
614 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); | |
615 char buf[2000]; | |
16 | 616 snprintf(buf, sizeof(buf), priv.pc->content_message, host, adr); |
8 | 617 smfi_setreply(ctx, "550", "5.7.1", buf); |
618 rc = SMFIS_REJECT; | |
619 } | |
620 else { | |
621 // need to accept it but remove the recipients that don't want it | |
622 for (string_set::iterator i=priv.non_whites.begin(); i!=priv.non_whites.end(); i++) { | |
623 char *rcpt = *i; | |
624 smfi_delrcpt(ctx, rcpt); | |
625 } | |
626 rc = SMFIS_CONTINUE; | |
627 } | |
0 | 628 } |
8 | 629 // reset for a new message on the same connection |
630 mlfi_abort(ctx); | |
631 return rc; | |
632 } | |
633 | |
634 sfsistat mlfi_abort(SMFICTX *ctx) | |
635 { | |
636 mlfiPriv &priv = *MLFIPRIV; | |
637 priv.reset(); | |
0 | 638 return SMFIS_CONTINUE; |
639 } | |
640 | |
641 sfsistat mlfi_close(SMFICTX *ctx) | |
642 { | |
643 mlfiPriv *priv = MLFIPRIV; | |
644 if (!priv) return SMFIS_CONTINUE; | |
645 delete priv; | |
646 smfi_setpriv(ctx, NULL); | |
647 return SMFIS_CONTINUE; | |
648 } | |
649 | |
650 struct smfiDesc smfilter = | |
651 { | |
652 "DNSBL", // filter name | |
653 SMFI_VERSION, // version code -- do not change | |
654 SMFIF_DELRCPT, // flags | |
655 mlfi_connect, // connection info filter | |
656 NULL, // SMTP HELO command filter | |
657 mlfi_envfrom, // envelope sender filter | |
658 mlfi_envrcpt, // envelope recipient filter | |
659 NULL, // header filter | |
660 NULL, // end of header | |
8 | 661 mlfi_body, // body block filter |
662 mlfi_eom, // end of message | |
663 mlfi_abort, // message aborted | |
0 | 664 mlfi_close, // connection cleanup |
665 }; | |
666 | |
667 | |
668 static void dumpit(char *name, string_map map); | |
669 static void dumpit(char *name, string_map map) { | |
9 | 670 fprintf(stdout, "\n"); |
0 | 671 for (string_map::iterator i=map.begin(); i!=map.end(); i++) { |
9 | 672 fprintf(stdout, "%s %s->%s\n", name, (*i).first, (*i).second); |
0 | 673 } |
674 } | |
675 | |
676 | |
677 static void dumpit(from_map map); | |
678 static void dumpit(from_map map) { | |
679 for (from_map::iterator i=map.begin(); i!=map.end(); i++) { | |
3 | 680 char buf[2000]; |
681 snprintf(buf, sizeof(buf), "envelope from map for %s", (*i).first); | |
0 | 682 string_map *sm = (*i).second; |
3 | 683 dumpit(buf, *sm); |
0 | 684 } |
685 } | |
686 | |
687 | |
3 | 688 static void dumpit(CONFIG &dc); |
689 static void dumpit(CONFIG &dc) { | |
5 | 690 dumpit(dc.env_from); |
691 dumpit("envelope to (dnsbl list)", dc.env_to_dnsbll); | |
692 dumpit("envelope to (from map)", dc.env_to_chkfrom); | |
9 | 693 fprintf(stdout, "\ndnsbls\n"); |
0 | 694 for (dnsblp_map::iterator i=dc.dnsbls.begin(); i!=dc.dnsbls.end(); i++) { |
9 | 695 fprintf(stdout, "%s %s %s\n", (*i).first, (*i).second->suffix, (*i).second->message); |
0 | 696 } |
9 | 697 fprintf(stdout, "\ndnsbl_lists\n"); |
0 | 698 for (dnsbllp_map::iterator i=dc.dnsblls.begin(); i!=dc.dnsblls.end(); i++) { |
699 char *name = (*i).first; | |
700 DNSBLL &dl = *((*i).second); | |
9 | 701 fprintf(stdout, "%s", name); |
0 | 702 for (DNSBLL::iterator j=dl.begin(); j!=dl.end(); j++) { |
703 DNSBL &d = **j; | |
9 | 704 fprintf(stdout, " %s", d.suffix); |
0 | 705 } |
9 | 706 fprintf(stdout, "\n"); |
0 | 707 } |
9 | 708 if (dc.content_suffix) { |
709 fprintf(stdout, "\ncontent filtering enabled with %s %s\n", dc.content_suffix, dc.content_message); | |
710 } | |
711 fprintf(stdout, "\nfiles\n"); | |
3 | 712 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { |
713 char *f = *i; | |
9 | 714 fprintf(stdout, "config includes %s\n", f); |
3 | 715 } |
716 } | |
717 | |
718 | |
719 //////////////////////////////////////////////// | |
720 // check for redundant or recursive include files | |
721 // | |
722 static bool ok_to_include(CONFIG &dc, char *fn); | |
723 static bool ok_to_include(CONFIG &dc, char *fn) { | |
724 if (!fn) return false; | |
725 bool ok = true; | |
726 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | |
727 char *f = *i; | |
728 if (strcmp(f, fn) == 0) { | |
729 my_syslog("redundant or recursive include file detected"); | |
730 ok = false; | |
731 break; | |
732 } | |
733 } | |
734 return ok; | |
0 | 735 } |
736 | |
737 | |
738 //////////////////////////////////////////////// | |
739 // load a single config file | |
740 // | |
3 | 741 static void load_conf_dcc(CONFIG &dc, char *name, char *fn); |
742 static void load_conf_dcc(CONFIG &dc, char *name, char *fn) { | |
743 dc.config_files.push_back(fn); | |
744 char *list = BLACK; | |
745 const int LINE_SIZE = 2000; | |
746 ifstream is(fn); | |
747 if (is.fail()) return; | |
748 char line[LINE_SIZE]; | |
749 char *delim = " \t"; | |
750 int curline = 0; | |
751 while (!is.eof()) { | |
752 is.getline(line, LINE_SIZE); | |
753 curline++; | |
754 int n = strlen(line); | |
755 if (!n) continue; | |
756 for (int i=0; i<n; i++) line[i] = tolower(line[i]); | |
757 if (line[0] == '#') continue; | |
758 char *head = line; | |
759 if (strspn(line, delim) == 0) { | |
760 // have a leading ok/many tag to fetch | |
761 char *cmd = strtok(line, delim); | |
762 if (strcmp(cmd, MANY) == 0) list = BLACK; | |
763 else if (strcmp(cmd, OK) == 0) list = WHITE; | |
764 head = cmd + strlen(cmd) + 1; | |
765 } | |
766 char *cmd = strtok(head, delim); | |
767 if (!cmd) continue; | |
768 if (strcmp(cmd, "env_from") == 0) { | |
769 char *from = next_token(delim); | |
770 if (from) { | |
771 string_map &fm = really_find_from_map(dc, name); | |
772 fm[from] = list; | |
773 } | |
774 } | |
775 else if (strcmp(cmd, "env_to") == 0) { | |
776 char *to = next_token(delim); | |
777 if (to) { | |
778 dc.env_to_dnsbll[to] = list; | |
779 dc.env_to_chkfrom[to] = list; | |
780 } | |
781 } | |
782 else if (strcmp(cmd, "substitute") == 0) { | |
783 char *tag = next_token(delim); | |
784 if (tag && (strcmp(tag, "mail_host") == 0)) { | |
785 char *from = next_token(delim); | |
786 if (from) { | |
787 string_map &fm = really_find_from_map(dc, name); | |
788 fm[from] = list; | |
789 } | |
790 } | |
791 } | |
792 else if (strcmp(cmd, "include") == 0) { | |
793 char *fn = next_token(delim); | |
794 if (ok_to_include(dc, fn)) { | |
795 load_conf_dcc(dc, name, fn); | |
796 } | |
797 } | |
798 | |
799 } | |
800 is.close(); | |
801 } | |
802 | |
803 | |
0 | 804 static void load_conf(CONFIG &dc, char *fn); |
805 static void load_conf(CONFIG &dc, char *fn) { | |
806 dc.config_files.push_back(fn); | |
807 map<char*, int, ltstr> commands; | |
8 | 808 enum {dummy, content, dnsbl, dnsbll, envfrom, envto, include, includedcc}; |
809 commands["content" ] = content; | |
3 | 810 commands["dnsbl" ] = dnsbl; |
811 commands["dnsbl_list" ] = dnsbll; | |
812 commands["env_from" ] = envfrom; | |
813 commands["env_to" ] = envto; | |
814 commands["include" ] = include; | |
815 commands["include_dcc"] = includedcc; | |
0 | 816 const int LINE_SIZE = 2000; |
817 ifstream is(fn); | |
818 if (is.fail()) return; | |
819 char line[LINE_SIZE]; | |
820 char orig[LINE_SIZE]; | |
821 char *delim = " \t"; | |
822 int curline = 0; | |
823 while (!is.eof()) { | |
824 is.getline(line, LINE_SIZE); | |
825 snprintf(orig, sizeof(orig), "%s", line); | |
826 curline++; | |
827 int n = strlen(line); | |
828 for (int i=0; i<n; i++) line[i] = tolower(line[i]); | |
829 char *cmd = strtok(line, delim); | |
830 if (cmd && (cmd[0] != '#') && (cmd[0] != '\0')) { | |
831 // have a decent command | |
832 bool processed = false; | |
833 switch (commands[cmd]) { | |
8 | 834 case content: { |
835 char *suff = strtok(NULL, delim); | |
836 if (!suff) break; // no dns suffic | |
837 char *msg = suff + strlen(suff); | |
838 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix | |
839 msg = strchr(msg+1, '\''); | |
840 if (!msg) break; // no reply message template | |
841 msg++; // move over the leading ' | |
842 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote | |
843 char *last = strchr(msg, '\''); | |
844 if (!last) break; // no trailing quote | |
845 *last = '\0'; // make it a null terminator | |
846 dc.content_suffix = register_string(suff); | |
847 dc.content_message = register_string(msg); | |
848 processed = true; | |
849 } break; | |
850 | |
0 | 851 case dnsbl: { |
852 // have a new dnsbl to use | |
853 char *name = next_token(delim); | |
854 if (!name) break; // no name name | |
855 if (find_dnsbl(dc, name)) break; // duplicate entry | |
856 char *suff = strtok(NULL, delim); | |
857 if (!suff) break; // no dns suffic | |
858 char *msg = suff + strlen(suff); | |
859 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix | |
860 msg = strchr(msg+1, '\''); | |
861 if (!msg) break; // no reply message template | |
862 msg++; // move over the leading ' | |
863 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote | |
864 char *last = strchr(msg, '\''); | |
865 if (!last) break; // no trailing quote | |
866 *last = '\0'; // make it a null terminator | |
867 dc.dnsbls[name] = new DNSBL(register_string(suff), register_string(msg)); | |
868 processed = true; | |
869 } break; | |
870 | |
871 case dnsbll: { | |
872 // define a new combination of dnsbls | |
873 char *name = next_token(delim); | |
874 if (!name) break; | |
875 if (find_dnsbll(dc, name)) break; // duplicate entry | |
876 char *list = next_token(delim); | |
877 if (!list || (*list == '\0') || (*list == '#')) break; | |
878 DNSBLLP d = new DNSBLL; | |
879 DNSBLP p = find_dnsbl(dc, list); | |
880 if (p) d->push_back(p); | |
881 while (true) { | |
882 list = next_token(delim); | |
883 if (!list || (*list == '\0') || (*list == '#')) break; | |
884 DNSBLP p = find_dnsbl(dc, list); | |
885 if (p) d->push_back(p); | |
886 } | |
887 dc.dnsblls[name] = d; | |
888 processed = true; | |
889 } break; | |
890 | |
891 case envfrom: { | |
892 // add an entry into the named string_map | |
893 char *name = next_token(delim); | |
894 if (!name) break; | |
895 char *from = next_token(delim); | |
896 if (!from) break; | |
897 char *list = next_token(delim); | |
898 if (!list) break; | |
899 if ((strcmp(list, WHITE) == 0) || | |
900 (strcmp(list, BLACK) == 0)) { | |
901 string_map &fm = really_find_from_map(dc, name); | |
902 fm[from] = list; | |
903 processed = true; | |
904 } | |
905 else { | |
906 // list may be the name of a previously defined from_map | |
907 string_map *m = find_from_map(dc, list); | |
908 if (m && (strcmp(list,name) != 0)) { | |
909 string_map &pm = *m; | |
910 string_map &fm = really_find_from_map(dc, name); | |
911 fm.insert(pm.begin(), pm.end()); | |
912 processed = true; | |
913 } | |
914 } | |
915 } break; | |
916 | |
917 case envto: { | |
918 // define the dnsbl_list and env_from maps to use for this recipient | |
919 char *to = next_token(delim); | |
920 if (!to) break; | |
921 char *list = next_token(delim); | |
922 if (!list) break; | |
923 char *from = next_token(delim); | |
924 if (!from) break; | |
925 dc.env_to_dnsbll[to] = list; | |
926 dc.env_to_chkfrom[to] = from; | |
927 processed = true; | |
928 } break; | |
929 | |
930 case include: { | |
931 char *fn = next_token(delim); | |
3 | 932 if (ok_to_include(dc, fn)) { |
933 load_conf(dc, fn); | |
934 processed = true; | |
935 } | |
936 } break; | |
937 | |
938 case includedcc: { | |
939 char *name = next_token(delim); | |
940 if (!name) break; | |
941 char *fn = next_token(delim); | |
942 if (ok_to_include(dc, fn)) { | |
943 load_conf_dcc(dc, name, fn); | |
944 processed = true; | |
0 | 945 } |
946 } break; | |
947 | |
948 default: { | |
949 } break; | |
950 } | |
951 if (!processed) { | |
952 pthread_mutex_lock(&syslog_mutex); | |
953 openlog("dnsbl", LOG_PID, LOG_MAIL); | |
954 syslog(LOG_ERR, "ignoring file %s line %d : %s\n", fn, curline, orig); | |
955 closelog(); | |
956 pthread_mutex_unlock(&syslog_mutex); | |
957 } | |
958 } | |
959 } | |
960 is.close(); | |
961 } | |
962 | |
963 | |
964 //////////////////////////////////////////////// | |
965 // reload the config | |
966 // | |
967 static CONFIG* new_conf(); | |
968 static CONFIG* new_conf() { | |
969 my_syslog("loading new configuration"); | |
970 CONFIG *newc = new CONFIG; | |
971 load_conf(*newc, "dnsbl.conf"); | |
972 newc->load_time = time(NULL); | |
973 return newc; | |
974 } | |
975 | |
976 | |
977 //////////////////////////////////////////////// | |
978 // thread to watch the old config files for changes | |
979 // and reload when needed. we also cleanup old | |
980 // configs whose reference count has gone to zero. | |
981 // | |
982 static void* config_loader(void *arg); | |
983 static void* config_loader(void *arg) { | |
984 typedef set<CONFIG *> configp_set; | |
985 configp_set old_configs; | |
18 | 986 while (loader_run) { |
0 | 987 sleep(180); // look for modifications every 3 minutes |
18 | 988 if (!loader_run) break; |
0 | 989 CONFIG &dc = *config; |
990 time_t then = dc.load_time; | |
991 struct stat st; | |
992 bool reload = false; | |
993 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | |
994 char *fn = *i; | |
995 if (stat(fn, &st)) reload = true; // file disappeared | |
996 else if (st.st_mtime > then) reload = true; // file modified | |
997 if (reload) break; | |
998 } | |
999 if (reload) { | |
1000 CONFIG *newc = new_conf(); | |
1001 // replace the global config pointer | |
1002 pthread_mutex_lock(&config_mutex); | |
1003 CONFIG *old = config; | |
1004 config = newc; | |
1005 pthread_mutex_unlock(&config_mutex); | |
1006 if (old) old_configs.insert(old); | |
1007 } | |
1008 // now look for old configs with zero ref counts | |
1009 for (configp_set::iterator i=old_configs.begin(); i!=old_configs.end(); ) { | |
1010 CONFIG *old = *i; | |
1011 if (!old->reference_count) { | |
1012 delete old; // destructor does all the work | |
1013 old_configs.erase(i++); | |
1014 } | |
1015 else i++; | |
1016 } | |
1017 } | |
18 | 1018 return NULL; |
0 | 1019 } |
1020 | |
1021 | |
1022 static void usage(char *prog); | |
1023 static void usage(char *prog) | |
1024 { | |
16 | 1025 fprintf(stderr, "Usage: %s [-d] [-c] -p socket-addr [-t timeout]\n", prog); |
0 | 1026 fprintf(stderr, "where socket-addr is for the connection to sendmail and should be one of\n"); |
1027 fprintf(stderr, " inet:port@local-ip-address\n"); | |
1028 fprintf(stderr, " local:local-domain-socket-file-name\n"); | |
9 | 1029 fprintf(stderr, "-c will load and dump the config to stdout\n"); |
16 | 1030 fprintf(stderr, "-d will add some syslog debug messages\n"); |
0 | 1031 } |
1032 | |
1033 | |
1034 int main(int argc, char**argv) | |
1035 { | |
3 | 1036 bool check = false; |
1037 bool setconn = false; | |
0 | 1038 int c; |
16 | 1039 const char *args = "p:t:hcd"; |
0 | 1040 extern char *optarg; |
1041 | |
1042 // Process command line options | |
1043 while ((c = getopt(argc, argv, args)) != -1) { | |
1044 switch (c) { | |
1045 case 'p': | |
1046 if (optarg == NULL || *optarg == '\0') { | |
1047 fprintf(stderr, "Illegal conn: %s\n", optarg); | |
1048 exit(EX_USAGE); | |
1049 } | |
1050 if (smfi_setconn(optarg) == MI_FAILURE) { | |
1051 fprintf(stderr, "smfi_setconn failed\n"); | |
1052 exit(EX_SOFTWARE); | |
1053 } | |
1054 | |
1055 if (strncasecmp(optarg, "unix:", 5) == 0) unlink(optarg + 5); | |
1056 else if (strncasecmp(optarg, "local:", 6) == 0) unlink(optarg + 6); | |
3 | 1057 setconn = true; |
0 | 1058 break; |
1059 | |
1060 case 't': | |
1061 if (optarg == NULL || *optarg == '\0') { | |
1062 fprintf(stderr, "Illegal timeout: %s\n", optarg); | |
1063 exit(EX_USAGE); | |
1064 } | |
1065 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) { | |
1066 fprintf(stderr, "smfi_settimeout failed\n"); | |
1067 exit(EX_SOFTWARE); | |
1068 } | |
1069 break; | |
1070 | |
3 | 1071 case 'c': |
1072 check = true; | |
1073 break; | |
1074 | |
16 | 1075 case 'd': |
1076 debug_syslog = true; | |
1077 break; | |
1078 | |
0 | 1079 case 'h': |
1080 default: | |
1081 usage(argv[0]); | |
1082 exit(EX_USAGE); | |
1083 } | |
1084 } | |
5 | 1085 |
1086 if (check) { | |
1087 CONFIG &dc = *new_conf(); | |
1088 dumpit(dc); | |
1089 return 0; | |
1090 } | |
1091 | |
0 | 1092 if (!setconn) { |
1093 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); | |
1094 usage(argv[0]); | |
1095 exit(EX_USAGE); | |
1096 } | |
5 | 1097 |
0 | 1098 if (smfi_register(smfilter) == MI_FAILURE) { |
1099 fprintf(stderr, "smfi_register failed\n"); | |
1100 exit(EX_UNAVAILABLE); | |
1101 } | |
1102 | |
1103 // switch to background mode | |
1104 if (daemon(1,0) < 0) { | |
1105 fprintf(stderr, "daemon() call failed\n"); | |
1106 exit(EX_UNAVAILABLE); | |
1107 } | |
1108 | |
1109 // initialize the thread sync objects | |
1110 pthread_mutex_init(&config_mutex, 0); | |
1111 pthread_mutex_init(&syslog_mutex, 0); | |
1112 pthread_mutex_init(&resolve_mutex, 0); | |
1113 | |
1114 // load the initial config | |
1115 config = new_conf(); | |
1116 | |
1117 // only create threads after the fork() in daemon | |
1118 pthread_t tid; | |
1119 if (pthread_create(&tid, 0, config_loader, 0)) | |
1120 my_syslog("failed to create config loader thread"); | |
1121 if (pthread_detach(tid)) | |
1122 my_syslog("failed to detach config loader thread"); | |
1123 | |
1124 // write the pid | |
1125 const char *pidpath = "/var/run/dnsbl.pid"; | |
1126 unlink(pidpath); | |
1127 FILE *f = fopen(pidpath, "w"); | |
1128 if (f) { | |
22 | 1129 #ifdef linux |
1130 // from a comment in the DCC source code: | |
1131 // Linux threads are broken. Signals given the | |
1132 // original process are delivered to only the | |
1133 // thread that happens to have that PID. The | |
1134 // sendmail libmilter thread that needs to hear | |
1135 // SIGINT and other signals does not, and that breaks | |
1136 // scripts that need to stop milters. | |
1137 // However, signaling the process group works. | |
0 | 1138 fprintf(f, "-%d\n", (u_int)getpgrp()); |
22 | 1139 #else |
1140 fprintf(f, "%d\n", (u_int)getpid()); | |
1141 #endif | |
0 | 1142 fclose(f); |
1143 } | |
1144 | |
18 | 1145 time_t starting = time(NULL); |
1146 int rc = smfi_main(); | |
22 | 1147 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) { |
18 | 1148 my_syslog("trying to restart after smfi_main()"); |
1149 loader_run = false; // eventually the config loader thread will terminate | |
1150 execvp(argv[0], argv); | |
1151 } | |
1152 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE); | |
0 | 1153 } |
8 | 1154 |