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