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