1/* $NetBSD$ */ 2 3/*++ 4/* NAME 5/* resolve_clnt 3 6/* SUMMARY 7/* address resolve service client (internal forms) 8/* SYNOPSIS 9/* #include <resolve_clnt.h> 10/* 11/* typedef struct { 12/* .in +4 13/* VSTRING *transport; 14/* VSTRING *nexthop 15/* VSTRING *recipient; 16/* int flags; 17/* .in -4 18/* } RESOLVE_REPLY; 19/* 20/* void resolve_clnt_init(reply) 21/* RESOLVE_REPLY *reply; 22/* 23/* void resolve_clnt_query(address, reply) 24/* const char *address; 25/* RESOLVE_REPLY *reply; 26/* 27/* void resolve_clnt_query_from(sender, address, reply) 28/* const char *sender; 29/* const char *address; 30/* RESOLVE_REPLY *reply; 31/* 32/* void resolve_clnt_verify(address, reply) 33/* const char *address; 34/* RESOLVE_REPLY *reply; 35/* 36/* void resolve_clnt_verify_from(sender, address, reply) 37/* const char *sender; 38/* const char *address; 39/* RESOLVE_REPLY *reply; 40/* 41/* void resolve_clnt_free(reply) 42/* RESOLVE_REPLY *reply; 43/* DESCRIPTION 44/* This module implements a mail address resolver client. 45/* 46/* resolve_clnt_init() initializes a reply data structure for use 47/* by resolve_clnt_query(). The structure is destroyed by passing 48/* it to resolve_clnt_free(). 49/* 50/* resolve_clnt_query() sends an internal-form recipient address 51/* (user@domain) to the resolver daemon and returns the resulting 52/* transport name, next_hop host name, and internal-form recipient 53/* address. In case of communication failure the program keeps trying 54/* until the mail system goes down. 55/* 56/* resolve_clnt_verify() implements an alternative version that can 57/* be used for address verification. 58/* 59/* resolve_clnt_query_from() and resolve_clnt_verify_from() 60/* allow the caller to supply sender context that will be used 61/* for sender-dependent relayhost lookup. 62/* 63/* In the resolver reply, the flags member is the bit-wise OR of 64/* zero or more of the following: 65/* .IP RESOLVE_FLAG_FINAL 66/* The recipient address resolves to a mail transport that performs 67/* final delivery. The destination is local or corresponds to a hosted 68/* domain that is handled by the local machine. This flag is currently 69/* not used. 70/* .IP RESOLVE_FLAG_ROUTED 71/* After address resolution the recipient localpart contains further 72/* routing information, so the resolved next-hop destination is not 73/* the final destination. 74/* .IP RESOLVE_FLAG_ERROR 75/* The address resolved to something that has invalid syntax. 76/* .IP RESOLVE_FLAG_FAIL 77/* The request could not be completed. 78/* .PP 79/* In addition, the address domain class is returned by setting 80/* one of the following flags (this is preliminary code awaiting 81/* more permanent implementation of address domain class handling): 82/* .IP RESOLVE_CLASS_LOCAL 83/* The address domain matches $mydestination, $inet_interfaces 84/* or $proxy_interfaces. 85/* .IP RESOLVE_CLASS_ALIAS 86/* The address domain matches $virtual_alias_domains (virtual 87/* alias domains, where each address is redirected to a real 88/* local or remote address). 89/* .IP RESOLVE_CLASS_VIRTUAL 90/* The address domain matches $virtual_mailbox_domains (true 91/* virtual domains where each address can have its own mailbox). 92/* .IP RESOLVE_CLASS_RELAY 93/* The address domain matches $relay_domains, i.e. this is an 94/* authorized mail relay destination. 95/* .IP RESOLVE_CLASS_DEFAULT 96/* The address matches none of the above. Access to this domain 97/* should be limited to authorized senders only. 98/* .PP 99/* For convenience, the constant RESOLVE_CLASS_FINAL includes all 100/* cases where the local machine is the final destination. 101/* DIAGNOSTICS 102/* Warnings: communication failure. Fatal error: mail system is down. 103/* SEE ALSO 104/* mail_proto(3h) low-level mail component glue. 105/* LICENSE 106/* .ad 107/* .fi 108/* The Secure Mailer license must be distributed with this software. 109/* AUTHOR(S) 110/* Wietse Venema 111/* IBM T.J. Watson Research 112/* P.O. Box 704 113/* Yorktown Heights, NY 10598, USA 114/*--*/ 115 116/* System library. */ 117 118#include <sys_defs.h> 119#include <unistd.h> 120#include <string.h> 121#include <errno.h> 122 123/* Utility library. */ 124 125#include <msg.h> 126#include <vstream.h> 127#include <vstring.h> 128#include <vstring_vstream.h> 129#include <events.h> 130#include <iostuff.h> 131 132/* Global library. */ 133 134#include "mail_proto.h" 135#include "mail_params.h" 136#include "clnt_stream.h" 137#include "resolve_clnt.h" 138 139/* Application-specific. */ 140 141 /* 142 * XXX this is shared with the rewrite client to save a file descriptor. 143 */ 144extern CLNT_STREAM *rewrite_clnt_stream; 145 146static time_t last_expire; 147static VSTRING *last_class; 148static VSTRING *last_sender; 149static VSTRING *last_addr; 150static RESOLVE_REPLY last_reply; 151 152/* resolve_clnt_init - initialize reply */ 153 154void resolve_clnt_init(RESOLVE_REPLY *reply) 155{ 156 reply->transport = vstring_alloc(100); 157 reply->nexthop = vstring_alloc(100); 158 reply->recipient = vstring_alloc(100); 159 reply->flags = 0; 160} 161 162/* resolve_clnt - resolve address to (transport, next hop, recipient) */ 163 164void resolve_clnt(const char *class, const char *sender, 165 const char *addr, RESOLVE_REPLY *reply) 166{ 167 const char *myname = "resolve_clnt"; 168 VSTREAM *stream; 169 int server_flags; 170 int count = 0; 171 172 /* 173 * One-entry cache. 174 */ 175 if (last_addr == 0) { 176 last_class = vstring_alloc(10); 177 last_sender = vstring_alloc(10); 178 last_addr = vstring_alloc(100); 179 resolve_clnt_init(&last_reply); 180 } 181 182 /* 183 * Sanity check. The result must not clobber the input because we may 184 * have to retransmit the request. 185 */ 186#define STR vstring_str 187 188 if (addr == STR(reply->recipient)) 189 msg_panic("%s: result clobbers input", myname); 190 191 /* 192 * Peek at the cache. 193 */ 194#define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "") 195 196 if (time((time_t *) 0) < last_expire 197 && *addr && strcmp(addr, STR(last_addr)) == 0 198 && strcmp(class, STR(last_class)) == 0 199 && strcmp(sender, STR(last_sender)) == 0) { 200 vstring_strcpy(reply->transport, STR(last_reply.transport)); 201 vstring_strcpy(reply->nexthop, STR(last_reply.nexthop)); 202 vstring_strcpy(reply->recipient, STR(last_reply.recipient)); 203 reply->flags = last_reply.flags; 204 if (msg_verbose) 205 msg_info("%s: cached: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s", 206 myname, sender, addr, STR(reply->transport), 207 STR(reply->nexthop), STR(reply->recipient), 208 IFSET(RESOLVE_FLAG_FINAL, "final"), 209 IFSET(RESOLVE_FLAG_ROUTED, "routed"), 210 IFSET(RESOLVE_FLAG_ERROR, "error"), 211 IFSET(RESOLVE_FLAG_FAIL, "fail"), 212 IFSET(RESOLVE_CLASS_LOCAL, "local"), 213 IFSET(RESOLVE_CLASS_ALIAS, "alias"), 214 IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"), 215 IFSET(RESOLVE_CLASS_RELAY, "relay"), 216 IFSET(RESOLVE_CLASS_DEFAULT, "default")); 217 return; 218 } 219 220 /* 221 * Keep trying until we get a complete response. The resolve service is 222 * CPU bound; making the client asynchronous would just complicate the 223 * code. 224 */ 225 if (rewrite_clnt_stream == 0) 226 rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, 227 var_rewrite_service, 228 var_ipc_idle_limit, 229 var_ipc_ttl_limit); 230 231 for (;;) { 232 stream = clnt_stream_access(rewrite_clnt_stream); 233 errno = 0; 234 count += 1; 235 if (attr_print(stream, ATTR_FLAG_NONE, 236 ATTR_TYPE_STR, MAIL_ATTR_REQ, class, 237 ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, 238 ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr, 239 ATTR_TYPE_END) != 0 240 || vstream_fflush(stream) 241 || attr_scan(stream, ATTR_FLAG_STRICT, 242 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &server_flags, 243 ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, reply->transport, 244 ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, reply->nexthop, 245 ATTR_TYPE_STR, MAIL_ATTR_RECIP, reply->recipient, 246 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &reply->flags, 247 ATTR_TYPE_END) != 5) { 248 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) 249 msg_warn("problem talking to service %s: %m", 250 var_rewrite_service); 251 } else { 252 if (msg_verbose) 253 msg_info("%s: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s", 254 myname, sender, addr, STR(reply->transport), 255 STR(reply->nexthop), STR(reply->recipient), 256 IFSET(RESOLVE_FLAG_FINAL, "final"), 257 IFSET(RESOLVE_FLAG_ROUTED, "routed"), 258 IFSET(RESOLVE_FLAG_ERROR, "error"), 259 IFSET(RESOLVE_FLAG_FAIL, "fail"), 260 IFSET(RESOLVE_CLASS_LOCAL, "local"), 261 IFSET(RESOLVE_CLASS_ALIAS, "alias"), 262 IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"), 263 IFSET(RESOLVE_CLASS_RELAY, "relay"), 264 IFSET(RESOLVE_CLASS_DEFAULT, "default")); 265 /* Server-requested disconnect. */ 266 if (server_flags != 0) 267 clnt_stream_recover(rewrite_clnt_stream); 268 if (STR(reply->transport)[0] == 0) 269 msg_warn("%s: null transport result for: <%s>", myname, addr); 270 else if (STR(reply->recipient)[0] == 0 && *addr != 0) 271 msg_warn("%s: null recipient result for: <%s>", myname, addr); 272 else 273 break; 274 } 275 sleep(1); /* XXX make configurable */ 276 clnt_stream_recover(rewrite_clnt_stream); 277 } 278 279 /* 280 * Update the cache. 281 */ 282 vstring_strcpy(last_class, class); 283 vstring_strcpy(last_sender, sender); 284 vstring_strcpy(last_addr, addr); 285 vstring_strcpy(last_reply.transport, STR(reply->transport)); 286 vstring_strcpy(last_reply.nexthop, STR(reply->nexthop)); 287 vstring_strcpy(last_reply.recipient, STR(reply->recipient)); 288 last_reply.flags = reply->flags; 289 last_expire = time((time_t *) 0) + 30; /* XXX make configurable */ 290} 291 292/* resolve_clnt_free - destroy reply */ 293 294void resolve_clnt_free(RESOLVE_REPLY *reply) 295{ 296 reply->transport = vstring_free(reply->transport); 297 reply->nexthop = vstring_free(reply->nexthop); 298 reply->recipient = vstring_free(reply->recipient); 299} 300 301#ifdef TEST 302 303#include <stdlib.h> 304#include <msg_vstream.h> 305#include <vstring_vstream.h> 306#include <split_at.h> 307#include <mail_conf.h> 308 309static NORETURN usage(char *myname) 310{ 311 msg_fatal("usage: %s [-v] [address...]", myname); 312} 313 314static void resolve(char *class, char *addr, RESOLVE_REPLY *reply) 315{ 316 struct RESOLVE_FLAG_TABLE { 317 int flag; 318 const char *name; 319 }; 320 struct RESOLVE_FLAG_TABLE resolve_flag_table[] = { 321 RESOLVE_FLAG_FINAL, "FLAG_FINAL", 322 RESOLVE_FLAG_ROUTED, "FLAG_ROUTED", 323 RESOLVE_FLAG_ERROR, "FLAG_ERROR", 324 RESOLVE_FLAG_FAIL, "FLAG_FAIL", 325 RESOLVE_CLASS_LOCAL, "CLASS_LOCAL", 326 RESOLVE_CLASS_ALIAS, "CLASS_ALIAS", 327 RESOLVE_CLASS_VIRTUAL, "CLASS_VIRTUAL", 328 RESOLVE_CLASS_RELAY, "CLASS_RELAY", 329 RESOLVE_CLASS_DEFAULT, "CLASS_DEFAULT", 330 0, 331 }; 332 struct RESOLVE_FLAG_TABLE *fp; 333 334 resolve_clnt(class, RESOLVE_NULL_FROM, addr, reply); 335 if (reply->flags & RESOLVE_FLAG_FAIL) { 336 vstream_printf("request failed\n"); 337 } else { 338 vstream_printf("%-10s %s\n", "class", class); 339 vstream_printf("%-10s %s\n", "address", addr); 340 vstream_printf("%-10s %s\n", "transport", STR(reply->transport)); 341 vstream_printf("%-10s %s\n", "nexthop", *STR(reply->nexthop) ? 342 STR(reply->nexthop) : "[none]"); 343 vstream_printf("%-10s %s\n", "recipient", STR(reply->recipient)); 344 vstream_printf("%-10s ", "flags"); 345 for (fp = resolve_flag_table; fp->name; fp++) { 346 if (reply->flags & fp->flag) { 347 vstream_printf("%s ", fp->name); 348 reply->flags &= ~fp->flag; 349 } 350 } 351 if (reply->flags != 0) 352 vstream_printf("Unknown flag 0x%x", reply->flags); 353 vstream_printf("\n\n"); 354 vstream_fflush(VSTREAM_OUT); 355 } 356} 357 358int main(int argc, char **argv) 359{ 360 RESOLVE_REPLY reply; 361 char *addr; 362 int ch; 363 364 msg_vstream_init(argv[0], VSTREAM_ERR); 365 366 mail_conf_read(); 367 msg_info("using config files in %s", var_config_dir); 368 if (chdir(var_queue_dir) < 0) 369 msg_fatal("chdir %s: %m", var_queue_dir); 370 371 while ((ch = GETOPT(argc, argv, "v")) > 0) { 372 switch (ch) { 373 case 'v': 374 msg_verbose++; 375 break; 376 default: 377 usage(argv[0]); 378 } 379 } 380 resolve_clnt_init(&reply); 381 382 if (argc > optind) { 383 while (argv[optind] && argv[optind + 1]) { 384 resolve(argv[optind], argv[optind + 1], &reply); 385 optind += 2; 386 } 387 } else { 388 VSTRING *buffer = vstring_alloc(1); 389 390 while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { 391 if ((addr = split_at(STR(buffer), ' ')) == 0 || *STR(buffer) == 0) 392 msg_fatal("need as input: class address"); 393 resolve(STR(buffer), addr, &reply); 394 } 395 vstring_free(buffer); 396 } 397 resolve_clnt_free(&reply); 398 exit(0); 399} 400 401#endif 402