Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / resolve_clnt.c
blobb55dc2b23641bd24e974f2660c76a5aefe4c233f
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* resolve_clnt 3
6 /* SUMMARY
7 /* address resolve service client (internal forms)
8 /* SYNOPSIS
9 /* #include <resolve_clnt.h>
11 /* typedef struct {
12 /* .in +4
13 /* VSTRING *transport;
14 /* VSTRING *nexthop
15 /* VSTRING *recipient;
16 /* int flags;
17 /* .in -4
18 /* } RESOLVE_REPLY;
20 /* void resolve_clnt_init(reply)
21 /* RESOLVE_REPLY *reply;
23 /* void resolve_clnt_query(address, reply)
24 /* const char *address;
25 /* RESOLVE_REPLY *reply;
27 /* void resolve_clnt_query_from(sender, address, reply)
28 /* const char *sender;
29 /* const char *address;
30 /* RESOLVE_REPLY *reply;
32 /* void resolve_clnt_verify(address, reply)
33 /* const char *address;
34 /* RESOLVE_REPLY *reply;
36 /* void resolve_clnt_verify_from(sender, address, reply)
37 /* const char *sender;
38 /* const char *address;
39 /* RESOLVE_REPLY *reply;
41 /* void resolve_clnt_free(reply)
42 /* RESOLVE_REPLY *reply;
43 /* DESCRIPTION
44 /* This module implements a mail address resolver client.
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().
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.
56 /* resolve_clnt_verify() implements an alternative version that can
57 /* be used for address verification.
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.
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 /*--*/
116 /* System library. */
118 #include <sys_defs.h>
119 #include <unistd.h>
120 #include <string.h>
121 #include <errno.h>
123 /* Utility library. */
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>
132 /* Global library. */
134 #include "mail_proto.h"
135 #include "mail_params.h"
136 #include "clnt_stream.h"
137 #include "resolve_clnt.h"
139 /* Application-specific. */
142 * XXX this is shared with the rewrite client to save a file descriptor.
144 extern CLNT_STREAM *rewrite_clnt_stream;
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;
152 /* resolve_clnt_init - initialize reply */
154 void resolve_clnt_init(RESOLVE_REPLY *reply)
156 reply->transport = vstring_alloc(100);
157 reply->nexthop = vstring_alloc(100);
158 reply->recipient = vstring_alloc(100);
159 reply->flags = 0;
162 /* resolve_clnt - resolve address to (transport, next hop, recipient) */
164 void resolve_clnt(const char *class, const char *sender,
165 const char *addr, RESOLVE_REPLY *reply)
167 const char *myname = "resolve_clnt";
168 VSTREAM *stream;
169 int server_flags;
170 int count = 0;
173 * One-entry cache.
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);
183 * Sanity check. The result must not clobber the input because we may
184 * have to retransmit the request.
186 #define STR vstring_str
188 if (addr == STR(reply->recipient))
189 msg_panic("%s: result clobbers input", myname);
192 * Peek at the cache.
194 #define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "")
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;
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.
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);
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;
275 sleep(1); /* XXX make configurable */
276 clnt_stream_recover(rewrite_clnt_stream);
280 * Update the cache.
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 */
292 /* resolve_clnt_free - destroy reply */
294 void resolve_clnt_free(RESOLVE_REPLY *reply)
296 reply->transport = vstring_free(reply->transport);
297 reply->nexthop = vstring_free(reply->nexthop);
298 reply->recipient = vstring_free(reply->recipient);
301 #ifdef TEST
303 #include <stdlib.h>
304 #include <msg_vstream.h>
305 #include <vstring_vstream.h>
306 #include <split_at.h>
307 #include <mail_conf.h>
309 static NORETURN usage(char *myname)
311 msg_fatal("usage: %s [-v] [address...]", myname);
314 static void resolve(char *class, char *addr, RESOLVE_REPLY *reply)
316 struct RESOLVE_FLAG_TABLE {
317 int flag;
318 const char *name;
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",
332 struct RESOLVE_FLAG_TABLE *fp;
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;
351 if (reply->flags != 0)
352 vstream_printf("Unknown flag 0x%x", reply->flags);
353 vstream_printf("\n\n");
354 vstream_fflush(VSTREAM_OUT);
358 int main(int argc, char **argv)
360 RESOLVE_REPLY reply;
361 char *addr;
362 int ch;
364 msg_vstream_init(argv[0], VSTREAM_ERR);
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);
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]);
380 resolve_clnt_init(&reply);
382 if (argc > optind) {
383 while (argv[optind] && argv[optind + 1]) {
384 resolve(argv[optind], argv[optind + 1], &reply);
385 optind += 2;
387 } else {
388 VSTRING *buffer = vstring_alloc(1);
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);
395 vstring_free(buffer);
397 resolve_clnt_free(&reply);
398 exit(0);
401 #endif