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