xref: /netbsd-src/external/ibm-public/postfix/dist/src/dnsblog/dnsblog.c (revision 49d8c9ecf4abd21261269266ef64939f71b3cd09)
1 /*	$NetBSD: dnsblog.c,v 1.1.1.3 2012/06/09 11:27:09 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dnsblog 8
6 /* SUMMARY
7 /*	Postfix DNS white/blacklist logger
8 /* SYNOPSIS
9 /*	\fBdnsblog\fR [generic Postfix daemon options]
10 /* DESCRIPTION
11 /*	The \fBdnsblog\fR(8) server implements an ad-hoc DNS
12 /*	white/blacklist lookup service. This may eventually be
13 /*	replaced by an UDP client that is built directly into the
14 /*	\fBpostscreen\fR(8) server.
15 /* PROTOCOL
16 /* .ad
17 /* .fi
18 /*	With each connection, the \fBdnsblog\fR(8) server receives
19 /*	a DNS white/blacklist domain name, IP address, and an ID.
20 /*	If the address is listed under the DNS white/blacklist, the
21 /*	\fBdnsblog\fR(8) server logs the match and replies with the
22 /*	query arguments plus an address list with the resulting IP
23 /*	addresses separated by whitespace.  Otherwise it replies
24 /*	with the query arguments plus an empty address list.  Finally,
25 /*	The \fBdnsblog\fR(8) server closes the connection.
26 /* DIAGNOSTICS
27 /*	Problems and transactions are logged to \fBsyslogd\fR(8).
28 /* CONFIGURATION PARAMETERS
29 /* .ad
30 /* .fi
31 /*	Changes to \fBmain.cf\fR are picked up automatically, as
32 /*	\fBdnsblog\fR(8) processes run for only a limited amount
33 /*	of time. Use the command "\fBpostfix reload\fR" to speed
34 /*	up a change.
35 /*
36 /*	The text below provides only a parameter summary. See
37 /*	\fBpostconf\fR(5) for more details including examples.
38 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
39 /*	The default location of the Postfix main.cf and master.cf
40 /*	configuration files.
41 /* .IP "\fBdaemon_timeout (18000s)\fR"
42 /*	How much time a Postfix daemon process may take to handle a
43 /*	request before it is terminated by a built-in watchdog timer.
44 /* .IP "\fBpostscreen_dnsbl_sites (empty)\fR"
45 /*	Optional list of DNS white/blacklist domains, filters and weight
46 /*	factors.
47 /* .IP "\fBipc_timeout (3600s)\fR"
48 /*	The time limit for sending or receiving information over an internal
49 /*	communication channel.
50 /* .IP "\fBprocess_id (read-only)\fR"
51 /*	The process ID of a Postfix command or daemon process.
52 /* .IP "\fBprocess_name (read-only)\fR"
53 /*	The process name of a Postfix command or daemon process.
54 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
55 /*	The location of the Postfix top-level queue directory.
56 /* .IP "\fBsyslog_facility (mail)\fR"
57 /*	The syslog facility of Postfix logging.
58 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
59 /*	The mail system name that is prepended to the process name in syslog
60 /*	records, so that "smtpd" becomes, for example, "postfix/smtpd".
61 /* SEE ALSO
62 /*	smtpd(8), Postfix SMTP server
63 /*	postconf(5), configuration parameters
64 /*	syslogd(5), system logging
65 /* LICENSE
66 /* .ad
67 /* .fi
68 /*	The Secure Mailer license must be distributed with this software.
69 /* HISTORY
70 /* .ad
71 /* .fi
72 /*	This service was introduced with Postfix version 2.8.
73 /* AUTHOR(S)
74 /*	Wietse Venema
75 /*	IBM T.J. Watson Research
76 /*	P.O. Box 704
77 /*	Yorktown Heights, NY 10598, USA
78 /*--*/
79 
80 /* System library. */
81 
82 #include <sys_defs.h>
83 
84 /* Utility library. */
85 
86 #include <msg.h>
87 #include <vstream.h>
88 #include <vstring.h>
89 #include <argv.h>
90 #include <myaddrinfo.h>
91 #include <valid_hostname.h>
92 #include <sock_addr.h>
93 
94 /* Global library. */
95 
96 #include <mail_conf.h>
97 #include <mail_version.h>
98 #include <mail_proto.h>
99 #include <mail_params.h>
100 
101 /* DNS library. */
102 
103 #include <dns.h>
104 
105 /* Server skeleton. */
106 
107 #include <mail_server.h>
108 
109 /* Application-specific. */
110 
111  /*
112   * Tunable parameters.
113   */
114 int     var_dnsblog_delay;
115 
116  /*
117   * Static so we don't allocate and free on every request.
118   */
119 static VSTRING *rbl_domain;
120 static VSTRING *addr;
121 static VSTRING *query;
122 static VSTRING *why;
123 static VSTRING *result;
124 
125  /*
126   * Silly little macros.
127   */
128 #define STR(x)			vstring_str(x)
129 #define LEN(x)			VSTRING_LEN(x)
130 
131 /* static void dnsblog_query - query DNSBL for client address */
132 
133 static VSTRING *dnsblog_query(VSTRING *result, const char *dnsbl_domain,
134 			              const char *addr)
135 {
136     const char *myname = "dnsblog_query";
137     ARGV   *octets;
138     int     i;
139     struct addrinfo *res;
140     unsigned char *ipv6_addr;
141     int     dns_status;
142     DNS_RR *addr_list;
143     DNS_RR *rr;
144     MAI_HOSTADDR_STR hostaddr;
145 
146     if (msg_verbose)
147 	msg_info("%s: addr %s dnsbl_domain %s",
148 		 myname, addr, dnsbl_domain);
149 
150     VSTRING_RESET(query);
151 
152     /*
153      * Reverse the client IPV6 address, represented as 32 hexadecimal
154      * nibbles. We use the binary address to avoid tricky code. Asking for an
155      * AAAA record makes no sense here. Just like with IPv4 we use the lookup
156      * result as a bit mask, not as an IP address.
157      */
158 #ifdef HAS_IPV6
159     if (valid_ipv6_hostaddr(addr, DONT_GRIPE)) {
160 	if (hostaddr_to_sockaddr(addr, (char *) 0, 0, &res) != 0
161 	    || res->ai_family != PF_INET6)
162 	    msg_fatal("%s: unable to convert address %s", myname, addr);
163 	ipv6_addr = (unsigned char *) &SOCK_ADDR_IN6_ADDR(res->ai_addr);
164 	for (i = sizeof(SOCK_ADDR_IN6_ADDR(res->ai_addr)) - 1; i >= 0; i--)
165 	    vstring_sprintf_append(query, "%x.%x.",
166 				   ipv6_addr[i] & 0xf, ipv6_addr[i] >> 4);
167 	freeaddrinfo(res);
168     } else
169 #endif
170 
171 	/*
172 	 * Reverse the client IPV4 address, represented as four decimal octet
173 	 * values. We use the textual address for convenience.
174 	 */
175     {
176 	octets = argv_split(addr, ".");
177 	for (i = octets->argc - 1; i >= 0; i--) {
178 	    vstring_strcat(query, octets->argv[i]);
179 	    vstring_strcat(query, ".");
180 	}
181 	argv_free(octets);
182     }
183 
184     /*
185      * Tack on the RBL domain name and query the DNS for an A record.
186      */
187     vstring_strcat(query, dnsbl_domain);
188     dns_status = dns_lookup(STR(query), T_A, 0, &addr_list, (VSTRING *) 0, why);
189     VSTRING_RESET(result);
190     if (dns_status == DNS_OK) {
191 	for (rr = addr_list; rr != 0; rr = rr->next) {
192 	    if (dns_rr_to_pa(rr, &hostaddr) == 0) {
193 		msg_warn("%s: skipping reply record type %s for query %s: %m",
194 			 myname, dns_strtype(rr->type), STR(query));
195 	    } else {
196 		msg_info("addr %s listed by domain %s as %s",
197 			 addr, dnsbl_domain, hostaddr.buf);
198 		if (LEN(result) > 0)
199 		    vstring_strcat(result, " ");
200 		vstring_strcat(result, hostaddr.buf);
201 	    }
202 	}
203 	dns_rr_free(addr_list);
204     } else if (dns_status == DNS_NOTFOUND) {
205 	if (msg_verbose)
206 	    msg_info("%s: addr %s not listed by domain %s",
207 		     myname, addr, dnsbl_domain);
208     } else {
209 	msg_warn("%s: lookup error for DNS query %s: %s",
210 		 myname, STR(query), STR(why));
211     }
212     VSTRING_TERMINATE(result);
213     return (result);
214 }
215 
216 /* dnsblog_service - perform service for client */
217 
218 static void dnsblog_service(VSTREAM *client_stream, char *unused_service,
219 			            char **argv)
220 {
221     int     request_id;
222 
223     /*
224      * Sanity check. This service takes no command-line arguments.
225      */
226     if (argv[0])
227 	msg_fatal("unexpected command-line argument: %s", argv[0]);
228 
229     /*
230      * This routine runs whenever a client connects to the socket dedicated
231      * to the dnsblog service. All connection-management stuff is handled by
232      * the common code in single_server.c.
233      */
234     if (attr_scan(client_stream,
235 		  ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
236 		  ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, rbl_domain,
237 		  ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, addr,
238 		  ATTR_TYPE_INT, MAIL_ATTR_LABEL, &request_id,
239 		  ATTR_TYPE_END) == 3) {
240 	(void) dnsblog_query(result, STR(rbl_domain), STR(addr));
241 	if (var_dnsblog_delay > 0)
242 	    sleep(var_dnsblog_delay);
243 	attr_print(client_stream, ATTR_FLAG_NONE,
244 		   ATTR_TYPE_STR, MAIL_ATTR_RBL_DOMAIN, STR(rbl_domain),
245 		   ATTR_TYPE_STR, MAIL_ATTR_ACT_CLIENT_ADDR, STR(addr),
246 		   ATTR_TYPE_INT, MAIL_ATTR_LABEL, request_id,
247 		   ATTR_TYPE_STR, MAIL_ATTR_RBL_ADDR, STR(result),
248 		   ATTR_TYPE_END);
249 	vstream_fflush(client_stream);
250     }
251 }
252 
253 /* post_jail_init - post-jail initialization */
254 
255 static void post_jail_init(char *unused_name, char **unused_argv)
256 {
257     rbl_domain = vstring_alloc(100);
258     addr = vstring_alloc(100);
259     query = vstring_alloc(100);
260     why = vstring_alloc(100);
261     result = vstring_alloc(100);
262     var_use_limit = 0;
263 }
264 
265 MAIL_VERSION_STAMP_DECLARE;
266 
267 /* main - pass control to the multi-threaded skeleton */
268 
269 int     main(int argc, char **argv)
270 {
271     static const CONFIG_TIME_TABLE time_table[] = {
272 	VAR_DNSBLOG_DELAY, DEF_DNSBLOG_DELAY, &var_dnsblog_delay, 0, 0,
273 	0,
274     };
275 
276     /*
277      * Fingerprint executables and core dumps.
278      */
279     MAIL_VERSION_STAMP_ALLOCATE;
280 
281     single_server_main(argc, argv, dnsblog_service,
282 		       MAIL_SERVER_TIME_TABLE, time_table,
283 		       MAIL_SERVER_POST_INIT, post_jail_init,
284 		       MAIL_SERVER_UNLIMITED,
285 		       0);
286 }
287