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