xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/resolve_clnt.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
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