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