1 /* $NetBSD: resolve_clnt.c,v 1.1.1.1 2009/06/23 10:08:47 tron Exp $ */ 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 */ 144 extern CLNT_STREAM *rewrite_clnt_stream; 145 146 static time_t last_expire; 147 static VSTRING *last_class; 148 static VSTRING *last_sender; 149 static VSTRING *last_addr; 150 static RESOLVE_REPLY last_reply; 151 152 /* resolve_clnt_init - initialize reply */ 153 154 void 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 164 void 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 294 void 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 309 static NORETURN usage(char *myname) 310 { 311 msg_fatal("usage: %s [-v] [address...]", myname); 312 } 313 314 static 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 358 int 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