Mercurial > dnsbl
annotate src/dnsbl.cpp @ 49:4f9e3dc55d38 stable-3-3
properly drop root privs
author | carl |
---|---|
date | Fri, 09 Jul 2004 14:09:02 -0700 |
parents | 5ef10dc14457 |
children | c2371bb6cf84 |
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 | |
34 | 26 3) Add option to only allow one recipient if the return path is empty. |
27 | |
0 | 28 */ |
29 | |
30 | |
31 // from sendmail sample | |
32 #include <sys/types.h> | |
33 #include <sys/stat.h> | |
34 #include <errno.h> | |
35 #include <sysexits.h> | |
36 #include <unistd.h> | |
37 | |
38 // needed for socket io | |
39 #include <sys/ioctl.h> | |
40 #include <net/if.h> | |
41 #include <arpa/inet.h> | |
42 #include <netinet/in.h> | |
43 #include <netinet/tcp.h> | |
44 #include <netdb.h> | |
45 #include <sys/socket.h> | |
42 | 46 #include <sys/un.h> |
0 | 47 |
48 // needed for thread | |
49 #include <pthread.h> | |
50 | |
51 // needed for std c++ collections | |
52 #include <set> | |
53 #include <map> | |
54 #include <list> | |
55 | |
56 // for the dns resolver | |
57 #include <netinet/in.h> | |
58 #include <arpa/nameser.h> | |
59 #include <resolv.h> | |
60 | |
61 // misc stuff needed here | |
62 #include <ctype.h> | |
63 #include <fstream> | |
64 #include <syslog.h> | |
42 | 65 #include <pwd.h> |
0 | 66 |
8 | 67 static char* dnsbl_version="$Id$"; |
0 | 68 |
8 | 69 #define DEFAULT "default" |
70 #define WHITE "white" | |
71 #define BLACK "black" | |
72 #define OK "ok" | |
73 #define MANY "many" | |
74 | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
75 enum status {oksofar, // not rejected yet |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
76 white, // whitelisted by envelope from |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
77 black, // blacklisted by envelope from or to |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
78 reject, // rejected by a dns list |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
79 reject_tag, // too many bad html tags |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
80 reject_host}; // too many hosts/urls in body |
1 | 81 |
0 | 82 using namespace std; |
83 | |
84 extern "C" { | |
85 #include "libmilter/mfapi.h" | |
86 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); | |
87 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv); | |
88 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv); | |
8 | 89 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len); |
90 sfsistat mlfi_eom(SMFICTX *ctx); | |
91 sfsistat mlfi_abort(SMFICTX *ctx); | |
0 | 92 sfsistat mlfi_close(SMFICTX *ctx); |
93 } | |
94 | |
95 struct ltstr { | |
96 bool operator()(char* s1, char* s2) const { | |
97 return strcmp(s1, s2) < 0; | |
98 } | |
99 }; | |
100 | |
101 struct DNSBL { | |
102 char *suffix; // blacklist suffix like blackholes.five-ten-sg.com | |
103 char *message; // error message with one or two %s operators for the ip address replacement | |
104 DNSBL(char *s, char *m); | |
105 }; | |
106 DNSBL::DNSBL(char *s, char *m) { | |
107 suffix = s; | |
108 message = m; | |
109 } | |
110 | |
111 typedef DNSBL * DNSBLP; | |
112 typedef list<DNSBLP> DNSBLL; | |
113 typedef DNSBLL * DNSBLLP; | |
114 typedef map<char *, char *, ltstr> string_map; | |
115 typedef map<char *, string_map *, ltstr> from_map; | |
116 typedef map<char *, DNSBLP, ltstr> dnsblp_map; | |
117 typedef map<char *, DNSBLLP, ltstr> dnsbllp_map; | |
118 typedef set<char *, ltstr> string_set; | |
44 | 119 typedef set<int> int_set; |
0 | 120 typedef list<char *> string_list; |
34 | 121 typedef map<char *, int, ltstr> ns_map; |
0 | 122 |
123 struct CONFIG { | |
124 // the only mutable stuff once it has been loaded from the config file | |
125 int reference_count; // protected by the global config_mutex | |
126 // all the rest is constant after loading from the config file | |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
127 int generation; |
0 | 128 time_t load_time; |
129 string_list config_files; | |
130 dnsblp_map dnsbls; | |
131 dnsbllp_map dnsblls; | |
132 from_map env_from; | |
133 string_map env_to_dnsbll; // map recipient to a named dnsbll | |
134 string_map env_to_chkfrom; // map recipient to a named from map | |
8 | 135 char * content_suffix; // for sbl url body filtering |
9 | 136 char * content_message; // "" |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
137 char * host_limit_message; // error message for excessive host names |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
138 int host_limit; // limit on host names |
44 | 139 bool host_random; // pick a random selection of host names rather than error for excessive hosts |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
140 char * tag_limit_message; // error message for excessive bad html tags |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
141 int tag_limit; // limit on bad html tags |
24 | 142 string_set html_tags; // set of valid html tags |
28 | 143 string_set tlds; // set of valid tld components |
0 | 144 CONFIG(); |
145 ~CONFIG(); | |
146 }; | |
147 CONFIG::CONFIG() { | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
148 reference_count = 0; |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
149 generation = 0; |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
150 load_time = 0; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
151 content_suffix = NULL; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
152 content_message = NULL; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
153 host_limit_message = NULL; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
154 host_limit = 0; |
44 | 155 host_random = false; |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
156 tag_limit_message = NULL; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
157 tag_limit = 0; |
0 | 158 } |
159 CONFIG::~CONFIG() { | |
160 for (dnsblp_map::iterator i=dnsbls.begin(); i!=dnsbls.end(); i++) { | |
161 DNSBLP d = (*i).second; | |
24 | 162 // delete the underlying DNSBL objects. |
0 | 163 delete d; |
164 } | |
165 for (dnsbllp_map::iterator i=dnsblls.begin(); i!=dnsblls.end(); i++) { | |
166 DNSBLLP d = (*i).second; | |
24 | 167 // *d is a list of pointers to DNSBL objects, but |
168 // the underlying objects have already been deleted above. | |
0 | 169 delete d; |
170 } | |
171 for (from_map::iterator i=env_from.begin(); i!=env_from.end(); i++) { | |
172 string_map *d = (*i).second; | |
173 delete d; | |
174 } | |
175 } | |
176 | |
16 | 177 static bool debug_syslog = false; |
18 | 178 static bool loader_run = true; // used to stop the config loader thread |
0 | 179 static string_set all_strings; // owns all the strings, only modified by the config loader thread |
180 static CONFIG * config = NULL; // protected by the config_mutex | |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
181 static int generation = 0; // protected by the config_mutex |
0 | 182 |
183 static pthread_mutex_t config_mutex; | |
184 static pthread_mutex_t syslog_mutex; | |
185 static pthread_mutex_t resolve_mutex; | |
186 | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
187 struct mlfiPriv; |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
188 |
0 | 189 |
190 //////////////////////////////////////////////// | |
34 | 191 // helper to discard the strings and objects held by an ns_map |
192 // | |
193 static void discard(ns_map &s); | |
194 static void discard(ns_map &s) { | |
195 for (ns_map::iterator i=s.begin(); i!=s.end(); i++) { | |
196 char *x = (*i).first; | |
197 free(x); | |
198 } | |
199 s.clear(); | |
200 } | |
201 | |
202 //////////////////////////////////////////////// | |
203 // helper to register a string in an ns_map | |
204 // | |
205 static void register_string(ns_map &s, char *name); | |
206 static void register_string(ns_map &s, char *name) { | |
207 ns_map::iterator i = s.find(name); | |
208 if (i != s.end()) return; | |
209 char *x = strdup(name); | |
210 s[x] = 0; | |
211 } | |
212 | |
213 //////////////////////////////////////////////// | |
8 | 214 // helper to discard the strings held by a string_set |
0 | 215 // |
9 | 216 static void discard(string_set &s); |
217 static void discard(string_set &s) { | |
8 | 218 for (string_set::iterator i=s.begin(); i!=s.end(); i++) { |
219 free(*i); | |
220 } | |
9 | 221 s.clear(); |
8 | 222 } |
0 | 223 |
12 | 224 //////////////////////////////////////////////// |
225 // helper to register a string in a string set | |
226 // | |
227 static char* register_string(string_set &s, char *name); | |
228 static char* register_string(string_set &s, char *name) { | |
229 string_set::iterator i = s.find(name); | |
230 if (i != s.end()) return *i; | |
231 char *x = strdup(name); | |
232 s.insert(x); | |
233 return x; | |
234 } | |
235 | |
16 | 236 //////////////////////////////////////////////// |
237 // syslog a message | |
238 // | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
239 static void my_syslog(mlfiPriv *priv, char *text); |
16 | 240 |
241 | |
12 | 242 // include the content scanner |
243 #include "scanner.cpp" | |
244 | |
245 | |
0 | 246 //////////////////////////////////////////////// |
247 // mail filter private data, held for us by sendmail | |
248 // | |
249 struct mlfiPriv | |
250 { | |
8 | 251 // connection specific data |
252 CONFIG *pc; // global context with our maps | |
253 int ip; // ip4 address of the smtp client | |
254 map<DNSBLP, status> checked; // status from those lists | |
255 // message specific data | |
0 | 256 char *mailaddr; // envelope from value |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
257 char *queueid; // sendmail queue id |
0 | 258 bool authenticated; // client authenticated? if so, suppress all dnsbl checks |
8 | 259 bool have_whites; // have at least one whitelisted recipient? need to accept content and remove all non-whitelisted recipients if it fails |
260 bool only_whites; // every recipient is whitelisted? | |
24 | 261 string_set non_whites; // remember the non-whitelisted recipients so we can remove them if need be |
262 recorder *memory; // memory for the content scanner | |
8 | 263 url_scanner *scanner; // object to handle body scanning |
0 | 264 mlfiPriv(); |
265 ~mlfiPriv(); | |
8 | 266 void reset(bool final = false); // for a new message |
0 | 267 }; |
268 mlfiPriv::mlfiPriv() { | |
269 pthread_mutex_lock(&config_mutex); | |
270 pc = config; | |
271 pc->reference_count++; | |
272 pthread_mutex_unlock(&config_mutex); | |
8 | 273 ip = 0; |
274 mailaddr = NULL; | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
275 queueid = NULL; |
8 | 276 authenticated = false; |
277 have_whites = false; | |
278 only_whites = true; | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
279 memory = new recorder(this, &pc->html_tags, &pc->tlds); |
24 | 280 scanner = new url_scanner(memory); |
0 | 281 } |
282 mlfiPriv::~mlfiPriv() { | |
283 pthread_mutex_lock(&config_mutex); | |
284 pc->reference_count--; | |
285 pthread_mutex_unlock(&config_mutex); | |
8 | 286 reset(true); |
287 } | |
288 void mlfiPriv::reset(bool final) { | |
0 | 289 if (mailaddr) free(mailaddr); |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
290 if (queueid) free(queueid); |
24 | 291 discard(non_whites); |
292 delete memory; | |
8 | 293 delete scanner; |
294 if (!final) { | |
295 mailaddr = NULL; | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
296 queueid = NULL; |
8 | 297 authenticated = false; |
298 have_whites = false; | |
299 only_whites = true; | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
300 memory = new recorder(this, &pc->html_tags, &pc->tlds); |
24 | 301 scanner = new url_scanner(memory); |
8 | 302 } |
0 | 303 } |
304 | |
305 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) | |
306 | |
307 | |
308 //////////////////////////////////////////////// | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
309 // syslog a message |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
310 // |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
311 static void my_syslog(mlfiPriv *priv, char *text) { |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
312 char buf[1000]; |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
313 if (priv) { |
42 | 314 snprintf(buf, sizeof(buf), "%s: %s", priv->queueid, text); |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
315 text = buf; |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
316 } |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
317 pthread_mutex_lock(&syslog_mutex); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
318 openlog("dnsbl", LOG_PID, LOG_MAIL); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
319 syslog(LOG_NOTICE, "%s", text); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
320 closelog(); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
321 pthread_mutex_unlock(&syslog_mutex); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
322 } |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
323 |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
324 static void my_syslog(char *text); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
325 static void my_syslog(char *text) { |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
326 my_syslog(NULL, text); |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
327 } |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
328 |
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
329 //////////////////////////////////////////////// |
0 | 330 // register a global string |
331 // | |
332 static char* register_string(char *name); | |
333 static char* register_string(char *name) { | |
12 | 334 return register_string(all_strings, name); |
0 | 335 } |
336 | |
337 | |
338 static char* next_token(char *delim); | |
339 static char* next_token(char *delim) { | |
340 char *name = strtok(NULL, delim); | |
341 if (!name) return name; | |
342 return register_string(name); | |
343 } | |
344 | |
345 | |
346 //////////////////////////////////////////////// | |
347 // lookup an email address in the env_from or env_to maps | |
348 // | |
349 static char* lookup1(char *email, string_map map); | |
350 static char* lookup1(char *email, string_map map) { | |
351 string_map::iterator i = map.find(email); | |
352 if (i != map.end()) return (*i).second; | |
353 char *x = strchr(email, '@'); | |
354 if (!x) return DEFAULT; | |
355 x++; | |
356 i = map.find(x); | |
357 if (i != map.end()) return (*i).second; | |
358 return DEFAULT; | |
359 } | |
360 | |
361 | |
362 //////////////////////////////////////////////// | |
363 // lookup an email address in the env_from or env_to maps | |
364 // this email address is passed in from sendmail, and will | |
365 // always be enclosed in <>. It may have mixed case, just | |
366 // as the mail client sent it. | |
367 // | |
368 static char* lookup(char* email, string_map map); | |
369 static char* lookup(char* email, string_map map) { | |
370 int n = strlen(email)-2; | |
371 if (n < 1) return DEFAULT; // malformed | |
372 char *key = strdup(email+1); | |
373 key[n] = '\0'; | |
374 for (int i=0; i<n; i++) key[i] = tolower(key[i]); | |
375 char *rc = lookup1(key, map); | |
376 free(key); | |
377 return rc; | |
378 } | |
379 | |
380 | |
381 //////////////////////////////////////////////// | |
382 // find the dnsbl with a specific name | |
383 // | |
384 static DNSBLP find_dnsbl(CONFIG &dc, char *name); | |
385 static DNSBLP find_dnsbl(CONFIG &dc, char *name) { | |
386 dnsblp_map::iterator i = dc.dnsbls.find(name); | |
387 if (i == dc.dnsbls.end()) return NULL; | |
388 return (*i).second; | |
389 } | |
390 | |
391 | |
392 //////////////////////////////////////////////// | |
393 // find the dnsbll with a specific name | |
394 // | |
395 static DNSBLLP find_dnsbll(CONFIG &dc, char *name); | |
396 static DNSBLLP find_dnsbll(CONFIG &dc, char *name) { | |
397 dnsbllp_map::iterator i = dc.dnsblls.find(name); | |
398 if (i == dc.dnsblls.end()) return NULL; | |
399 return (*i).second; | |
400 } | |
401 | |
402 | |
403 //////////////////////////////////////////////// | |
404 // find the envfrom map with a specific name | |
405 // | |
406 static string_map* find_from_map(CONFIG &dc, char *name); | |
407 static string_map* find_from_map(CONFIG &dc, char *name) { | |
408 from_map::iterator i = dc.env_from.find(name); | |
409 if (i == dc.env_from.end()) return NULL; | |
410 return (*i).second; | |
411 } | |
412 | |
413 | |
414 static string_map& really_find_from_map(CONFIG &dc, char *name); | |
415 static string_map& really_find_from_map(CONFIG &dc, char *name) { | |
416 string_map *sm = find_from_map(dc, name); | |
417 if (!sm) { | |
418 sm = new string_map; | |
419 dc.env_from[name] = sm; | |
420 } | |
421 return *sm; | |
422 } | |
423 | |
424 | |
425 //////////////////////////////////////////////// | |
8 | 426 // |
427 // ask a dns question and get an A record answer - we don't try | |
428 // very hard, just using the default resolver retry settings. | |
429 // If we cannot get an answer, we just accept the mail. The | |
430 // caller must ensure thread safety. | |
431 // | |
0 | 432 // |
34 | 433 static int dns_interface(char *question, bool maybe_ip, ns_map *nameservers); |
434 static int dns_interface(char *question, bool maybe_ip, ns_map *nameservers) { | |
16 | 435 #ifdef NS_PACKETSZ |
8 | 436 u_char answer[NS_PACKETSZ]; |
437 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
|
438 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
|
439 // parse the answer |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
440 ns_msg handle; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
441 ns_rr rr; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
442 if (ns_initparse(answer, length, &handle) == 0) { |
34 | 443 // look for ns names |
444 if (nameservers) { | |
445 ns_map &ns = *nameservers; | |
446 int rrnum = 0; | |
447 while (ns_parserr(&handle, ns_s_ns, rrnum++, &rr) == 0) { | |
448 if (ns_rr_type(rr) == ns_t_ns) { | |
449 char nam[NS_MAXDNAME+1]; | |
450 char *n = nam; | |
451 const u_char *p = ns_rr_rdata(rr); | |
452 while (((n-nam) < NS_MAXDNAME) && ((p-answer) < length) && *p) { | |
453 size_t s = *(p++); | |
454 if (s > 191) { | |
455 // compression pointer | |
456 s = (s-192)*256 + *(p++); | |
457 if (s >= length) break; // pointer outside bounds of answer | |
458 p = answer + s; | |
459 s = *(p++); | |
460 } | |
461 if (s > 0) { | |
462 if ((n-nam) >= (NS_MAXDNAME-s)) break; // destination would overflow name buffer | |
463 if ((p-answer) >= (length-s)) break; // source outside bounds of answer | |
464 memcpy(n, p, s); | |
465 n += s; | |
466 p += s; | |
467 *(n++) = '.'; | |
468 } | |
469 } | |
36 | 470 if (n-nam) n--; // remove trailing . |
471 *n = '\0'; // null terminate it | |
34 | 472 register_string(ns, nam); // ns host to lookup later |
473 } | |
474 } | |
475 rrnum = 0; | |
476 while (ns_parserr(&handle, ns_s_ar, rrnum++, &rr) == 0) { | |
477 if (ns_rr_type(rr) == ns_t_a) { | |
478 char* nam = (char*)ns_rr_name(rr); | |
479 ns_map::iterator i = ns.find(nam); | |
480 if (i != ns.end()) { | |
481 // we want this ip address | |
482 int address; | |
483 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); | |
484 ns[nam] = address; | |
485 } | |
486 } | |
487 } | |
488 } | |
23
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
489 int rrnum = 0; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
490 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
|
491 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
|
492 int address; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
493 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
|
494 return address; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
495 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
496 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
497 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
498 } |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
499 if (maybe_ip) { |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
500 // 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
|
501 in_addr ip; |
06de5ab6a232
add url decoding stage, allow http:/ single / in yahoo redirector, allow ip address hostnames
carl
parents:
22
diff
changeset
|
502 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
|
503 return ip.s_addr; |
8 | 504 } |
505 } | |
506 return 0; | |
16 | 507 #else |
508 struct hostent *host = gethostbyname(question); | |
509 if (!host) return 0; | |
510 if (host->h_addrtype != AF_INET) return 0; | |
511 int address; | |
512 memcpy(&address, host->h_addr, sizeof(address)); | |
513 return address; | |
514 #endif | |
8 | 515 } |
516 | |
34 | 517 static int protected_dns_interface(char *question, bool maybe_ip, ns_map *nameservers); |
518 static int protected_dns_interface(char *question, bool maybe_ip, ns_map *nameservers) { | |
8 | 519 int ans; |
520 pthread_mutex_lock(&resolve_mutex); | |
34 | 521 ans = dns_interface(question, maybe_ip, nameservers); |
8 | 522 pthread_mutex_unlock(&resolve_mutex); |
523 return ans; | |
524 | |
525 } | |
526 | |
527 //////////////////////////////////////////////// | |
528 // check a single dnsbl | |
529 // | |
530 static status check_single(int ip, char *suffix); | |
531 static status check_single(int ip, char *suffix) { | |
0 | 532 // make a dns question |
533 const u_char *src = (const u_char *)&ip; | |
534 if (src[0] == 127) return oksofar; // don't do dns lookups on localhost | |
16 | 535 #ifdef NS_MAXDNAME |
0 | 536 char question[NS_MAXDNAME]; |
16 | 537 #else |
538 char question[1000]; | |
539 #endif | |
8 | 540 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); |
541 // ask the question, if we get an A record it implies a blacklisted ip address | |
34 | 542 return (protected_dns_interface(question, false, NULL)) ? reject : oksofar; |
8 | 543 } |
544 | |
545 | |
546 //////////////////////////////////////////////// | |
547 // check a single dnsbl | |
548 // | |
549 static status check_single(int ip, DNSBL &bl); | |
550 static status check_single(int ip, DNSBL &bl) { | |
551 return check_single(ip, bl.suffix); | |
0 | 552 } |
553 | |
554 | |
555 //////////////////////////////////////////////// | |
556 // check the dnsbls specified for this recipient | |
557 // | |
558 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist); | |
559 static status check_dnsbl(mlfiPriv &priv, DNSBLLP dnsbllp, DNSBLP &rejectlist) { | |
560 if (priv.authenticated) return oksofar; | |
561 if (!dnsbllp) return oksofar; | |
562 DNSBLL &dnsbll = *dnsbllp; | |
563 for (DNSBLL::iterator i=dnsbll.begin(); i!=dnsbll.end(); i++) { | |
564 DNSBLP dp = *i; // non null by construction | |
565 status st; | |
566 map<DNSBLP, status>::iterator f = priv.checked.find(dp); | |
567 if (f == priv.checked.end()) { | |
568 // have not checked this list yet | |
8 | 569 st = check_single(priv.ip, *dp); |
0 | 570 rejectlist = dp; |
571 priv.checked[dp] = st; | |
572 } | |
573 else { | |
574 st = (*f).second; | |
575 rejectlist = (*f).first; | |
576 } | |
577 if (st == reject) return st; | |
578 } | |
579 return oksofar; | |
580 } | |
581 | |
582 | |
583 //////////////////////////////////////////////// | |
8 | 584 // check the dnsbls specified for this recipient |
585 // | |
16 | 586 static status check_hosts(mlfiPriv &priv, char *&host, int &ip); |
587 static status check_hosts(mlfiPriv &priv, char *&host, int &ip) { | |
8 | 588 CONFIG &dc = *priv.pc; |
589 if (!dc.content_suffix) return oksofar; | |
590 int count = 0; | |
34 | 591 ns_map nameservers; |
44 | 592 bool ran = priv.pc->host_random; |
593 int lim = priv.pc->host_limit; // we should not look at more than this many hosts | |
594 int cnt = priv.memory->hosts.size(); // number of hosts we could look at | |
595 int_set ips; // remove duplicate ip addresses | |
24 | 596 for (string_set::iterator i=priv.memory->hosts.begin(); i!=priv.memory->hosts.end(); i++) { |
44 | 597 host = *i; // a reference into priv.memory->hosts, which will live until this smtp transaction is closed |
598 if ((cnt > lim) && (lim > 0) && ran) { | |
599 // try to only look at lim/cnt fraction of the available cnt host names | |
600 int r = rand() % cnt; | |
601 if (r >= lim) { | |
602 char buf[1000]; | |
603 snprintf(buf, sizeof(buf), "host %s skipped", host); | |
604 my_syslog(&priv, buf); | |
605 continue; | |
606 } | |
607 } | |
8 | 608 count++; |
44 | 609 if ((count > lim) && (lim > 0) && (!ran)) { |
34 | 610 discard(nameservers); |
611 return reject_host; | |
612 } | |
44 | 613 ip = protected_dns_interface(host, true, &nameservers); |
16 | 614 if (debug_syslog) { |
44 | 615 char buf[1000]; |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
616 if (ip) { |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
617 char adr[sizeof "255.255.255.255"]; |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
618 adr[0] = '\0'; |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
619 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
620 snprintf(buf, sizeof(buf), "host %s found at %s", host, adr); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
621 } |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
622 else { |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
623 snprintf(buf, sizeof(buf), "host %s not found", host); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
624 } |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
625 my_syslog(&priv, buf); |
16 | 626 } |
8 | 627 if (ip) { |
44 | 628 int_set::iterator i = ips.find(ip); |
629 if (i == ips.end()) { | |
630 ips.insert(ip); | |
631 status st = check_single(ip, dc.content_suffix); | |
632 if (st == reject) { | |
633 discard(nameservers); | |
634 return st; | |
635 } | |
34 | 636 } |
8 | 637 } |
638 } | |
34 | 639 lim *= 4; // allow average of 3 ns per host name |
640 for (ns_map::iterator i=nameservers.begin(); i!=nameservers.end(); i++) { | |
641 count++; | |
642 if ((count > lim) && (lim > 0)) { | |
44 | 643 if (ran) continue; // don't complain |
34 | 644 discard(nameservers); |
645 return reject_host; | |
646 } | |
36 | 647 host = (*i).first; // a transient reference that needs to be replaced before we return it |
34 | 648 ip = (*i).second; |
649 if (!ip) ip = protected_dns_interface(host, false, NULL); | |
650 if (debug_syslog) { | |
651 char buf[200]; | |
652 if (ip) { | |
653 char adr[sizeof "255.255.255.255"]; | |
654 adr[0] = '\0'; | |
655 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); | |
656 snprintf(buf, sizeof(buf), "ns %s found at %s", host, adr); | |
657 } | |
658 else { | |
659 snprintf(buf, sizeof(buf), "ns %s not found", host); | |
660 } | |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
661 my_syslog(&priv, buf); |
34 | 662 } |
663 if (ip) { | |
44 | 664 int_set::iterator i = ips.find(ip); |
665 if (i == ips.end()) { | |
666 ips.insert(ip); | |
667 status st = check_single(ip, dc.content_suffix); | |
668 if (st == reject) { | |
669 host = register_string(priv.memory->hosts, host); // put a copy into priv.memory->hosts, and return that reference | |
670 discard(nameservers); | |
671 return st; | |
672 } | |
34 | 673 } |
674 } | |
675 } | |
676 discard(nameservers); | |
24 | 677 host = NULL; |
26 | 678 int bin = priv.memory->binary_tags; |
24 | 679 int bad = priv.memory->bad_html_tags; |
34 | 680 lim = priv.pc->tag_limit; |
41
d95af8129dfa
updates for 3.2, changing file layout, add queueid to messages
carl
parents:
40
diff
changeset
|
681 if (3*bin > bad) return oksofar; // probably .zip or .tar.gz with random content |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
682 if ((bad > lim) && (lim > 0)) return reject_tag; |
9 | 683 return oksofar; |
8 | 684 } |
685 | |
686 | |
687 //////////////////////////////////////////////// | |
0 | 688 // start of sendmail milter interfaces |
689 // | |
690 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) | |
691 { | |
692 // allocate some private memory | |
693 mlfiPriv *priv = new mlfiPriv; | |
694 if (hostaddr->sa_family == AF_INET) { | |
695 priv->ip = ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr; | |
696 } | |
697 | |
698 // save the private data | |
699 smfi_setpriv(ctx, (void*)priv); | |
700 | |
701 // continue processing | |
702 return SMFIS_CONTINUE; | |
703 } | |
704 | |
705 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from) | |
706 { | |
707 mlfiPriv &priv = *MLFIPRIV; | |
708 priv.mailaddr = strdup(from[0]); | |
709 priv.authenticated = (smfi_getsymval(ctx, "{auth_authen}") != NULL); | |
710 return SMFIS_CONTINUE; | |
711 } | |
712 | |
713 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) | |
714 { | |
715 DNSBLP rejectlist = NULL; // list that caused the reject | |
716 status st = oksofar; | |
717 mlfiPriv &priv = *MLFIPRIV; | |
718 CONFIG &dc = *priv.pc; | |
42 | 719 if (!priv.queueid) priv.queueid = strdup(smfi_getsymval(ctx, "i")); |
0 | 720 char *rcptaddr = rcpt[0]; |
721 char *dnsname = lookup(rcptaddr, dc.env_to_dnsbll); | |
722 char *fromname = lookup(rcptaddr, dc.env_to_chkfrom); | |
723 if ((strcmp(dnsname, BLACK) == 0) || | |
724 (strcmp(fromname, BLACK) == 0)) { | |
725 st = black; // two options to blacklist this recipient | |
726 } | |
727 else if (strcmp(fromname, WHITE) == 0) { | |
728 st = white; | |
729 } | |
730 else { | |
731 // check an env_from map | |
732 string_map *sm = find_from_map(dc, fromname); | |
733 if (sm != NULL) { | |
734 fromname = lookup(priv.mailaddr, *sm); // returns default if name not in map | |
735 if (strcmp(fromname, BLACK) == 0) { | |
736 st = black; // blacklist this envelope from value | |
737 } | |
738 if (strcmp(fromname, WHITE) == 0) { | |
739 st = white; // blacklist this envelope from value | |
740 } | |
741 } | |
742 } | |
743 if ((st == oksofar) && (strcmp(dnsname, WHITE) != 0)) { | |
744 // check dns lists | |
745 st = check_dnsbl(priv, find_dnsbll(dc, dnsname), rejectlist); | |
746 } | |
747 | |
748 if (st == reject) { | |
749 // reject the recipient based on some dnsbl | |
750 char adr[sizeof "255.255.255.255"]; | |
751 adr[0] = '\0'; | |
8 | 752 inet_ntop(AF_INET, (const u_char *)&priv.ip, adr, sizeof(adr)); |
0 | 753 char buf[2000]; |
754 snprintf(buf, sizeof(buf), rejectlist->message, adr, adr); | |
755 smfi_setreply(ctx, "550", "5.7.1", buf); | |
756 return SMFIS_REJECT; | |
757 } | |
758 else if (st == black) { | |
759 // reject the recipient based on blacklisting either from or to | |
760 smfi_setreply(ctx, "550", "5.7.1", "no such user"); | |
761 return SMFIS_REJECT; | |
762 } | |
763 else { | |
764 // accept the recipient | |
8 | 765 if (st == oksofar) { |
766 // but remember the non-whites | |
12 | 767 register_string(priv.non_whites, rcptaddr); |
8 | 768 priv.only_whites = false; |
769 } | |
770 if (st == white) { | |
771 priv.have_whites = true; | |
772 } | |
0 | 773 return SMFIS_CONTINUE; |
774 } | |
775 } | |
776 | |
8 | 777 sfsistat mlfi_body(SMFICTX *ctx, u_char *data, size_t len) |
0 | 778 { |
779 mlfiPriv &priv = *MLFIPRIV; | |
8 | 780 if (priv.authenticated) return SMFIS_CONTINUE; |
781 if (priv.only_whites) return SMFIS_CONTINUE; | |
782 priv.scanner->scan(data, len); | |
11 | 783 return SMFIS_CONTINUE; |
8 | 784 } |
785 | |
786 sfsistat mlfi_eom(SMFICTX *ctx) | |
787 { | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
788 sfsistat rc; |
8 | 789 mlfiPriv &priv = *MLFIPRIV; |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
790 char *host = NULL; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
791 int ip; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
792 status st; |
8 | 793 // process end of message |
794 if (priv.authenticated || | |
795 priv.only_whites || | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
796 ((st=check_hosts(priv, host, ip)) == oksofar)) rc = SMFIS_CONTINUE; |
8 | 797 else { |
798 if (!priv.have_whites) { | |
799 // can reject the entire message | |
800 char buf[2000]; | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
801 if (st == reject_tag) { |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
802 // rejected due to excessive bad html tags |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
803 snprintf(buf, sizeof(buf), priv.pc->tag_limit_message); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
804 } |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
805 else if (st == reject_host) { |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
806 // rejected due to excessive unique host/urls |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
807 snprintf(buf, sizeof(buf), priv.pc->host_limit_message); |
24 | 808 } |
809 else { | |
810 char adr[sizeof "255.255.255.255"]; | |
811 adr[0] = '\0'; | |
812 inet_ntop(AF_INET, (const u_char *)&ip, adr, sizeof(adr)); | |
813 snprintf(buf, sizeof(buf), priv.pc->content_message, host, adr); | |
814 } | |
8 | 815 smfi_setreply(ctx, "550", "5.7.1", buf); |
816 rc = SMFIS_REJECT; | |
817 } | |
818 else { | |
819 // need to accept it but remove the recipients that don't want it | |
820 for (string_set::iterator i=priv.non_whites.begin(); i!=priv.non_whites.end(); i++) { | |
821 char *rcpt = *i; | |
822 smfi_delrcpt(ctx, rcpt); | |
823 } | |
824 rc = SMFIS_CONTINUE; | |
825 } | |
0 | 826 } |
8 | 827 // reset for a new message on the same connection |
828 mlfi_abort(ctx); | |
829 return rc; | |
830 } | |
831 | |
832 sfsistat mlfi_abort(SMFICTX *ctx) | |
833 { | |
834 mlfiPriv &priv = *MLFIPRIV; | |
835 priv.reset(); | |
0 | 836 return SMFIS_CONTINUE; |
837 } | |
838 | |
839 sfsistat mlfi_close(SMFICTX *ctx) | |
840 { | |
841 mlfiPriv *priv = MLFIPRIV; | |
842 if (!priv) return SMFIS_CONTINUE; | |
843 delete priv; | |
844 smfi_setpriv(ctx, NULL); | |
845 return SMFIS_CONTINUE; | |
846 } | |
847 | |
848 struct smfiDesc smfilter = | |
849 { | |
850 "DNSBL", // filter name | |
851 SMFI_VERSION, // version code -- do not change | |
852 SMFIF_DELRCPT, // flags | |
853 mlfi_connect, // connection info filter | |
854 NULL, // SMTP HELO command filter | |
855 mlfi_envfrom, // envelope sender filter | |
856 mlfi_envrcpt, // envelope recipient filter | |
857 NULL, // header filter | |
858 NULL, // end of header | |
8 | 859 mlfi_body, // body block filter |
860 mlfi_eom, // end of message | |
861 mlfi_abort, // message aborted | |
0 | 862 mlfi_close, // connection cleanup |
863 }; | |
864 | |
865 | |
866 static void dumpit(char *name, string_map map); | |
867 static void dumpit(char *name, string_map map) { | |
9 | 868 fprintf(stdout, "\n"); |
0 | 869 for (string_map::iterator i=map.begin(); i!=map.end(); i++) { |
9 | 870 fprintf(stdout, "%s %s->%s\n", name, (*i).first, (*i).second); |
0 | 871 } |
872 } | |
873 | |
874 | |
875 static void dumpit(from_map map); | |
876 static void dumpit(from_map map) { | |
877 for (from_map::iterator i=map.begin(); i!=map.end(); i++) { | |
3 | 878 char buf[2000]; |
879 snprintf(buf, sizeof(buf), "envelope from map for %s", (*i).first); | |
0 | 880 string_map *sm = (*i).second; |
3 | 881 dumpit(buf, *sm); |
0 | 882 } |
883 } | |
884 | |
885 | |
3 | 886 static void dumpit(CONFIG &dc); |
887 static void dumpit(CONFIG &dc) { | |
5 | 888 dumpit(dc.env_from); |
889 dumpit("envelope to (dnsbl list)", dc.env_to_dnsbll); | |
890 dumpit("envelope to (from map)", dc.env_to_chkfrom); | |
9 | 891 fprintf(stdout, "\ndnsbls\n"); |
0 | 892 for (dnsblp_map::iterator i=dc.dnsbls.begin(); i!=dc.dnsbls.end(); i++) { |
9 | 893 fprintf(stdout, "%s %s %s\n", (*i).first, (*i).second->suffix, (*i).second->message); |
0 | 894 } |
9 | 895 fprintf(stdout, "\ndnsbl_lists\n"); |
0 | 896 for (dnsbllp_map::iterator i=dc.dnsblls.begin(); i!=dc.dnsblls.end(); i++) { |
897 char *name = (*i).first; | |
898 DNSBLL &dl = *((*i).second); | |
9 | 899 fprintf(stdout, "%s", name); |
0 | 900 for (DNSBLL::iterator j=dl.begin(); j!=dl.end(); j++) { |
901 DNSBL &d = **j; | |
9 | 902 fprintf(stdout, " %s", d.suffix); |
0 | 903 } |
9 | 904 fprintf(stdout, "\n"); |
0 | 905 } |
9 | 906 if (dc.content_suffix) { |
907 fprintf(stdout, "\ncontent filtering enabled with %s %s\n", dc.content_suffix, dc.content_message); | |
908 } | |
46 | 909 if (dc.host_limit && !dc.host_random) { |
910 fprintf(stdout, "\ncontent filtering for host names hard limit %d %s\n", dc.host_limit, dc.host_limit_message); | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
911 } |
46 | 912 if (dc.host_limit && dc.host_random) { |
913 fprintf(stdout, "\ncontent filtering for host names soft limit %d\n", dc.host_limit); | |
44 | 914 } |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
915 if (dc.tag_limit) { |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
916 fprintf(stdout, "\ncontent filtering for excessive html tags enabled with limit %d %s\n", dc.tag_limit, dc.tag_limit_message); |
24 | 917 } |
9 | 918 fprintf(stdout, "\nfiles\n"); |
3 | 919 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { |
920 char *f = *i; | |
9 | 921 fprintf(stdout, "config includes %s\n", f); |
3 | 922 } |
923 } | |
924 | |
925 | |
926 //////////////////////////////////////////////// | |
927 // check for redundant or recursive include files | |
928 // | |
929 static bool ok_to_include(CONFIG &dc, char *fn); | |
930 static bool ok_to_include(CONFIG &dc, char *fn) { | |
931 if (!fn) return false; | |
932 bool ok = true; | |
933 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | |
934 char *f = *i; | |
935 if (strcmp(f, fn) == 0) { | |
936 my_syslog("redundant or recursive include file detected"); | |
937 ok = false; | |
938 break; | |
939 } | |
940 } | |
941 return ok; | |
0 | 942 } |
943 | |
944 | |
945 //////////////////////////////////////////////// | |
946 // load a single config file | |
947 // | |
3 | 948 static void load_conf_dcc(CONFIG &dc, char *name, char *fn); |
949 static void load_conf_dcc(CONFIG &dc, char *name, char *fn) { | |
950 ifstream is(fn); | |
40 | 951 if (is.fail()) { |
952 char buf[1000]; | |
953 snprintf(buf, sizeof(buf), "include file %s not found", fn); | |
954 my_syslog(buf); | |
955 return; | |
956 } | |
39 | 957 dc.config_files.push_back(fn); |
958 const int LINE_SIZE = 2000; | |
3 | 959 char line[LINE_SIZE]; |
39 | 960 char *list = BLACK; |
3 | 961 char *delim = " \t"; |
962 int curline = 0; | |
963 while (!is.eof()) { | |
964 is.getline(line, LINE_SIZE); | |
965 curline++; | |
966 int n = strlen(line); | |
967 if (!n) continue; | |
968 for (int i=0; i<n; i++) line[i] = tolower(line[i]); | |
969 if (line[0] == '#') continue; | |
970 char *head = line; | |
971 if (strspn(line, delim) == 0) { | |
972 // have a leading ok/many tag to fetch | |
973 char *cmd = strtok(line, delim); | |
974 if (strcmp(cmd, MANY) == 0) list = BLACK; | |
975 else if (strcmp(cmd, OK) == 0) list = WHITE; | |
976 head = cmd + strlen(cmd) + 1; | |
977 } | |
978 char *cmd = strtok(head, delim); | |
979 if (!cmd) continue; | |
980 if (strcmp(cmd, "env_from") == 0) { | |
981 char *from = next_token(delim); | |
982 if (from) { | |
983 string_map &fm = really_find_from_map(dc, name); | |
984 fm[from] = list; | |
985 } | |
986 } | |
987 else if (strcmp(cmd, "env_to") == 0) { | |
988 char *to = next_token(delim); | |
989 if (to) { | |
990 dc.env_to_dnsbll[to] = list; | |
991 dc.env_to_chkfrom[to] = list; | |
992 } | |
993 } | |
994 else if (strcmp(cmd, "substitute") == 0) { | |
995 char *tag = next_token(delim); | |
996 if (tag && (strcmp(tag, "mail_host") == 0)) { | |
997 char *from = next_token(delim); | |
998 if (from) { | |
999 string_map &fm = really_find_from_map(dc, name); | |
1000 fm[from] = list; | |
1001 } | |
1002 } | |
1003 } | |
1004 else if (strcmp(cmd, "include") == 0) { | |
1005 char *fn = next_token(delim); | |
1006 if (ok_to_include(dc, fn)) { | |
1007 load_conf_dcc(dc, name, fn); | |
1008 } | |
1009 } | |
1010 | |
1011 } | |
1012 is.close(); | |
1013 } | |
1014 | |
1015 | |
0 | 1016 static void load_conf(CONFIG &dc, char *fn); |
1017 static void load_conf(CONFIG &dc, char *fn) { | |
39 | 1018 ifstream is(fn); |
40 | 1019 if (is.fail()) { |
1020 char buf[1000]; | |
1021 snprintf(buf, sizeof(buf), "include file %s not found", fn); | |
1022 my_syslog(buf); | |
1023 return; | |
1024 } | |
0 | 1025 dc.config_files.push_back(fn); |
1026 map<char*, int, ltstr> commands; | |
46 | 1027 enum {dummy, tld, content, hostlimit, hostslimit, htmllimit, htmltag, dnsbl, dnsbll, envfrom, envto, include, includedcc}; |
1028 commands["tld" ] = tld; | |
1029 commands["content" ] = content; | |
1030 commands["host_limit" ] = hostlimit; | |
1031 commands["host_soft_limit"] = hostslimit; | |
1032 commands["html_limit" ] = htmllimit; | |
1033 commands["html_tag" ] = htmltag; | |
1034 commands["dnsbl" ] = dnsbl; | |
1035 commands["dnsbl_list" ] = dnsbll; | |
1036 commands["env_from" ] = envfrom; | |
1037 commands["env_to" ] = envto; | |
1038 commands["include" ] = include; | |
1039 commands["include_dcc" ] = includedcc; | |
0 | 1040 const int LINE_SIZE = 2000; |
1041 char line[LINE_SIZE]; | |
1042 char orig[LINE_SIZE]; | |
1043 char *delim = " \t"; | |
1044 int curline = 0; | |
1045 while (!is.eof()) { | |
1046 is.getline(line, LINE_SIZE); | |
1047 snprintf(orig, sizeof(orig), "%s", line); | |
1048 curline++; | |
1049 int n = strlen(line); | |
1050 for (int i=0; i<n; i++) line[i] = tolower(line[i]); | |
1051 char *cmd = strtok(line, delim); | |
1052 if (cmd && (cmd[0] != '#') && (cmd[0] != '\0')) { | |
1053 // have a decent command | |
1054 bool processed = false; | |
1055 switch (commands[cmd]) { | |
28 | 1056 case tld: { |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1057 char *tld = next_token(delim); |
28 | 1058 if (!tld) break; // no tld value |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1059 dc.tlds.insert(tld); |
28 | 1060 processed = true; |
1061 } break; | |
1062 | |
8 | 1063 case content: { |
1064 char *suff = strtok(NULL, delim); | |
24 | 1065 if (!suff) break; // no dns suffix |
8 | 1066 char *msg = suff + strlen(suff); |
1067 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix | |
1068 msg = strchr(msg+1, '\''); | |
1069 if (!msg) break; // no reply message template | |
1070 msg++; // move over the leading ' | |
1071 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote | |
1072 char *last = strchr(msg, '\''); | |
1073 if (!last) break; // no trailing quote | |
1074 *last = '\0'; // make it a null terminator | |
1075 dc.content_suffix = register_string(suff); | |
1076 dc.content_message = register_string(msg); | |
1077 processed = true; | |
1078 } break; | |
1079 | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1080 case hostlimit: { |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1081 char *limit = strtok(NULL, delim); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1082 if (!limit) break; // no integer limit |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1083 char *msg = limit + strlen(limit); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1084 if ((msg - line) >= strlen(orig)) break; // line ended with the limit |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1085 msg = strchr(msg+1, '\''); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1086 if (!msg) break; // no reply message template |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1087 msg++; // move over the leading ' |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1088 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1089 char *last = strchr(msg, '\''); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1090 if (!last) break; // no trailing quote |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1091 *last = '\0'; // make it a null terminator |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1092 dc.host_limit = atoi(limit); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1093 dc.host_limit_message = register_string(msg); |
46 | 1094 dc.host_random = false; |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1095 processed = true; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1096 } break; |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1097 |
46 | 1098 case hostslimit: { |
1099 char *limit = next_token(delim); | |
1100 if (!limit) break; // no integer limit | |
1101 dc.host_limit = atoi(limit); | |
44 | 1102 dc.host_random = true; |
1103 processed = true; | |
1104 } break; | |
1105 | |
24 | 1106 case htmllimit: { |
1107 char *limit = strtok(NULL, delim); | |
1108 if (!limit) break; // no integer limit | |
1109 char *msg = limit + strlen(limit); | |
1110 if ((msg - line) >= strlen(orig)) break; // line ended with the limit | |
1111 msg = strchr(msg+1, '\''); | |
1112 if (!msg) break; // no reply message template | |
1113 msg++; // move over the leading ' | |
1114 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote | |
1115 char *last = strchr(msg, '\''); | |
1116 if (!last) break; // no trailing quote | |
1117 *last = '\0'; // make it a null terminator | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1118 dc.tag_limit = atoi(limit); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1119 dc.tag_limit_message = register_string(msg); |
24 | 1120 processed = true; |
1121 } break; | |
1122 | |
1123 case htmltag: { | |
1124 char *tag = next_token(delim); | |
1125 if (!tag) break; // no html tag value | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1126 dc.html_tags.insert(tag); // base version |
26 | 1127 char buf[200]; |
1128 snprintf(buf, sizeof(buf), "/%s", tag); | |
27
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1129 dc.html_tags.insert(register_string(buf)); // leading / |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1130 snprintf(buf, sizeof(buf), "%s/", tag); |
43a4f6b3e668
add configurable host name limit and bad html tag limits.
carl
parents:
26
diff
changeset
|
1131 dc.html_tags.insert(register_string(buf)); // trailing / |
24 | 1132 processed = true; |
1133 } break; | |
1134 | |
0 | 1135 case dnsbl: { |
1136 // have a new dnsbl to use | |
1137 char *name = next_token(delim); | |
1138 if (!name) break; // no name name | |
1139 if (find_dnsbl(dc, name)) break; // duplicate entry | |
1140 char *suff = strtok(NULL, delim); | |
1141 if (!suff) break; // no dns suffic | |
1142 char *msg = suff + strlen(suff); | |
1143 if ((msg - line) >= strlen(orig)) break; // line ended with the dns suffix | |
1144 msg = strchr(msg+1, '\''); | |
1145 if (!msg) break; // no reply message template | |
1146 msg++; // move over the leading ' | |
1147 if ((msg - line) >= strlen(orig)) break; // line ended with the leading quote | |
1148 char *last = strchr(msg, '\''); | |
1149 if (!last) break; // no trailing quote | |
1150 *last = '\0'; // make it a null terminator | |
1151 dc.dnsbls[name] = new DNSBL(register_string(suff), register_string(msg)); | |
1152 processed = true; | |
1153 } break; | |
1154 | |
1155 case dnsbll: { | |
1156 // define a new combination of dnsbls | |
1157 char *name = next_token(delim); | |
1158 if (!name) break; | |
1159 if (find_dnsbll(dc, name)) break; // duplicate entry | |
1160 char *list = next_token(delim); | |
1161 if (!list || (*list == '\0') || (*list == '#')) break; | |
1162 DNSBLLP d = new DNSBLL; | |
1163 DNSBLP p = find_dnsbl(dc, list); | |
1164 if (p) d->push_back(p); | |
1165 while (true) { | |
1166 list = next_token(delim); | |
1167 if (!list || (*list == '\0') || (*list == '#')) break; | |
1168 DNSBLP p = find_dnsbl(dc, list); | |
1169 if (p) d->push_back(p); | |
1170 } | |
1171 dc.dnsblls[name] = d; | |
1172 processed = true; | |
1173 } break; | |
1174 | |
1175 case envfrom: { | |
1176 // add an entry into the named string_map | |
1177 char *name = next_token(delim); | |
1178 if (!name) break; | |
1179 char *from = next_token(delim); | |
1180 if (!from) break; | |
1181 char *list = next_token(delim); | |
1182 if (!list) break; | |
1183 if ((strcmp(list, WHITE) == 0) || | |
1184 (strcmp(list, BLACK) == 0)) { | |
1185 string_map &fm = really_find_from_map(dc, name); | |
1186 fm[from] = list; | |
1187 processed = true; | |
1188 } | |
1189 else { | |
1190 // list may be the name of a previously defined from_map | |
1191 string_map *m = find_from_map(dc, list); | |
1192 if (m && (strcmp(list,name) != 0)) { | |
1193 string_map &pm = *m; | |
1194 string_map &fm = really_find_from_map(dc, name); | |
1195 fm.insert(pm.begin(), pm.end()); | |
1196 processed = true; | |
1197 } | |
1198 } | |
1199 } break; | |
1200 | |
1201 case envto: { | |
1202 // define the dnsbl_list and env_from maps to use for this recipient | |
1203 char *to = next_token(delim); | |
1204 if (!to) break; | |
1205 char *list = next_token(delim); | |
1206 if (!list) break; | |
1207 char *from = next_token(delim); | |
1208 if (!from) break; | |
1209 dc.env_to_dnsbll[to] = list; | |
1210 dc.env_to_chkfrom[to] = from; | |
1211 processed = true; | |
1212 } break; | |
1213 | |
1214 case include: { | |
1215 char *fn = next_token(delim); | |
3 | 1216 if (ok_to_include(dc, fn)) { |
1217 load_conf(dc, fn); | |
1218 processed = true; | |
1219 } | |
1220 } break; | |
1221 | |
1222 case includedcc: { | |
1223 char *name = next_token(delim); | |
1224 if (!name) break; | |
1225 char *fn = next_token(delim); | |
1226 if (ok_to_include(dc, fn)) { | |
1227 load_conf_dcc(dc, name, fn); | |
1228 processed = true; | |
0 | 1229 } |
1230 } break; | |
1231 | |
1232 default: { | |
1233 } break; | |
1234 } | |
1235 if (!processed) { | |
1236 pthread_mutex_lock(&syslog_mutex); | |
1237 openlog("dnsbl", LOG_PID, LOG_MAIL); | |
1238 syslog(LOG_ERR, "ignoring file %s line %d : %s\n", fn, curline, orig); | |
1239 closelog(); | |
1240 pthread_mutex_unlock(&syslog_mutex); | |
1241 } | |
1242 } | |
1243 } | |
1244 is.close(); | |
1245 } | |
1246 | |
1247 | |
1248 //////////////////////////////////////////////// | |
1249 // reload the config | |
1250 // | |
1251 static CONFIG* new_conf(); | |
1252 static CONFIG* new_conf() { | |
1253 CONFIG *newc = new CONFIG; | |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1254 pthread_mutex_lock(&config_mutex); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1255 newc->generation = generation++; |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1256 pthread_mutex_unlock(&config_mutex); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1257 char buf[200]; |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1258 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1259 my_syslog(buf); |
0 | 1260 load_conf(*newc, "dnsbl.conf"); |
1261 newc->load_time = time(NULL); | |
1262 return newc; | |
1263 } | |
1264 | |
1265 | |
1266 //////////////////////////////////////////////// | |
1267 // thread to watch the old config files for changes | |
1268 // and reload when needed. we also cleanup old | |
1269 // configs whose reference count has gone to zero. | |
1270 // | |
1271 static void* config_loader(void *arg); | |
1272 static void* config_loader(void *arg) { | |
1273 typedef set<CONFIG *> configp_set; | |
1274 configp_set old_configs; | |
18 | 1275 while (loader_run) { |
0 | 1276 sleep(180); // look for modifications every 3 minutes |
18 | 1277 if (!loader_run) break; |
0 | 1278 CONFIG &dc = *config; |
1279 time_t then = dc.load_time; | |
1280 struct stat st; | |
1281 bool reload = false; | |
1282 for (string_list::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | |
1283 char *fn = *i; | |
1284 if (stat(fn, &st)) reload = true; // file disappeared | |
1285 else if (st.st_mtime > then) reload = true; // file modified | |
1286 if (reload) break; | |
1287 } | |
1288 if (reload) { | |
1289 CONFIG *newc = new_conf(); | |
1290 // replace the global config pointer | |
1291 pthread_mutex_lock(&config_mutex); | |
1292 CONFIG *old = config; | |
1293 config = newc; | |
1294 pthread_mutex_unlock(&config_mutex); | |
1295 if (old) old_configs.insert(old); | |
1296 } | |
1297 // now look for old configs with zero ref counts | |
1298 for (configp_set::iterator i=old_configs.begin(); i!=old_configs.end(); ) { | |
1299 CONFIG *old = *i; | |
1300 if (!old->reference_count) { | |
29
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1301 char buf[200]; |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1302 snprintf(buf, sizeof(buf), "freeing memory for old configuration generation %d", old->generation); |
4dfdf33f1db0
add syslog msg freeing memory, use bare tld names without leading period
carl
parents:
28
diff
changeset
|
1303 my_syslog(buf); |
0 | 1304 delete old; // destructor does all the work |
1305 old_configs.erase(i++); | |
1306 } | |
1307 else i++; | |
1308 } | |
1309 } | |
18 | 1310 return NULL; |
0 | 1311 } |
1312 | |
1313 | |
1314 static void usage(char *prog); | |
1315 static void usage(char *prog) | |
1316 { | |
16 | 1317 fprintf(stderr, "Usage: %s [-d] [-c] -p socket-addr [-t timeout]\n", prog); |
0 | 1318 fprintf(stderr, "where socket-addr is for the connection to sendmail and should be one of\n"); |
1319 fprintf(stderr, " inet:port@local-ip-address\n"); | |
1320 fprintf(stderr, " local:local-domain-socket-file-name\n"); | |
9 | 1321 fprintf(stderr, "-c will load and dump the config to stdout\n"); |
16 | 1322 fprintf(stderr, "-d will add some syslog debug messages\n"); |
0 | 1323 } |
1324 | |
1325 | |
42 | 1326 |
1327 static void setup_socket(char *sock); | |
1328 static void setup_socket(char *sock) { | |
1329 unlink(sock); | |
43 | 1330 // sockaddr_un addr; |
1331 // memset(&addr, '\0', sizeof addr); | |
1332 // addr.sun_family = AF_UNIX; | |
1333 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); | |
1334 // int s = socket(AF_UNIX, SOCK_STREAM, 0); | |
1335 // bind(s, (sockaddr*)&addr, sizeof(addr)); | |
1336 // close(s); | |
42 | 1337 } |
1338 | |
1339 | |
0 | 1340 int main(int argc, char**argv) |
1341 { | |
3 | 1342 bool check = false; |
1343 bool setconn = false; | |
0 | 1344 int c; |
16 | 1345 const char *args = "p:t:hcd"; |
0 | 1346 extern char *optarg; |
1347 | |
1348 // Process command line options | |
1349 while ((c = getopt(argc, argv, args)) != -1) { | |
1350 switch (c) { | |
1351 case 'p': | |
1352 if (optarg == NULL || *optarg == '\0') { | |
1353 fprintf(stderr, "Illegal conn: %s\n", optarg); | |
1354 exit(EX_USAGE); | |
1355 } | |
1356 if (smfi_setconn(optarg) == MI_FAILURE) { | |
1357 fprintf(stderr, "smfi_setconn failed\n"); | |
1358 exit(EX_SOFTWARE); | |
1359 } | |
1360 | |
42 | 1361 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5); |
1362 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6); | |
3 | 1363 setconn = true; |
0 | 1364 break; |
1365 | |
1366 case 't': | |
1367 if (optarg == NULL || *optarg == '\0') { | |
1368 fprintf(stderr, "Illegal timeout: %s\n", optarg); | |
1369 exit(EX_USAGE); | |
1370 } | |
1371 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) { | |
1372 fprintf(stderr, "smfi_settimeout failed\n"); | |
1373 exit(EX_SOFTWARE); | |
1374 } | |
1375 break; | |
1376 | |
3 | 1377 case 'c': |
1378 check = true; | |
1379 break; | |
1380 | |
16 | 1381 case 'd': |
1382 debug_syslog = true; | |
1383 break; | |
1384 | |
0 | 1385 case 'h': |
1386 default: | |
1387 usage(argv[0]); | |
1388 exit(EX_USAGE); | |
1389 } | |
1390 } | |
5 | 1391 |
1392 if (check) { | |
1393 CONFIG &dc = *new_conf(); | |
1394 dumpit(dc); | |
1395 return 0; | |
1396 } | |
1397 | |
0 | 1398 if (!setconn) { |
1399 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); | |
1400 usage(argv[0]); | |
1401 exit(EX_USAGE); | |
1402 } | |
5 | 1403 |
0 | 1404 if (smfi_register(smfilter) == MI_FAILURE) { |
1405 fprintf(stderr, "smfi_register failed\n"); | |
1406 exit(EX_UNAVAILABLE); | |
1407 } | |
1408 | |
1409 // switch to background mode | |
1410 if (daemon(1,0) < 0) { | |
1411 fprintf(stderr, "daemon() call failed\n"); | |
1412 exit(EX_UNAVAILABLE); | |
1413 } | |
1414 | |
1415 // write the pid | |
1416 const char *pidpath = "/var/run/dnsbl.pid"; | |
1417 unlink(pidpath); | |
1418 FILE *f = fopen(pidpath, "w"); | |
1419 if (f) { | |
22 | 1420 #ifdef linux |
1421 // from a comment in the DCC source code: | |
1422 // Linux threads are broken. Signals given the | |
1423 // original process are delivered to only the | |
1424 // thread that happens to have that PID. The | |
1425 // sendmail libmilter thread that needs to hear | |
1426 // SIGINT and other signals does not, and that breaks | |
1427 // scripts that need to stop milters. | |
1428 // However, signaling the process group works. | |
0 | 1429 fprintf(f, "-%d\n", (u_int)getpgrp()); |
22 | 1430 #else |
1431 fprintf(f, "%d\n", (u_int)getpid()); | |
1432 #endif | |
0 | 1433 fclose(f); |
1434 } | |
1435 | |
42 | 1436 // drop root privs |
1437 struct passwd *pw = getpwnam("dnsbl"); | |
1438 if (pw) { | |
48 | 1439 if (setgid(pw->pw_gid) == -1) { |
1440 my_syslog("failed to switch to group dnsbl"); | |
1441 } | |
42 | 1442 if (setuid(pw->pw_uid) == -1) { |
1443 my_syslog("failed to switch to user dnsbl"); | |
1444 } | |
1445 } | |
1446 | |
48 | 1447 // initialize the thread sync objects |
1448 pthread_mutex_init(&config_mutex, 0); | |
1449 pthread_mutex_init(&syslog_mutex, 0); | |
1450 pthread_mutex_init(&resolve_mutex, 0); | |
1451 | |
1452 // load the initial config | |
1453 config = new_conf(); | |
1454 | |
1455 // only create threads after the fork() in daemon | |
1456 pthread_t tid; | |
1457 if (pthread_create(&tid, 0, config_loader, 0)) | |
1458 my_syslog("failed to create config loader thread"); | |
1459 if (pthread_detach(tid)) | |
1460 my_syslog("failed to detach config loader thread"); | |
1461 | |
18 | 1462 time_t starting = time(NULL); |
1463 int rc = smfi_main(); | |
22 | 1464 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) { |
18 | 1465 my_syslog("trying to restart after smfi_main()"); |
1466 loader_run = false; // eventually the config loader thread will terminate | |
1467 execvp(argv[0], argv); | |
1468 } | |
1469 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE); | |
0 | 1470 } |
8 | 1471 |