1/*++
2/* NAME
3/*	rewrite_clnt 3
4/* SUMMARY
5/*	address rewrite service client
6/* SYNOPSIS
7/*	#include <vstring.h>
8/*	#include <rewrite_clnt.h>
9/*
10/*	VSTRING	*rewrite_clnt(ruleset, address, result)
11/*	const char *ruleset;
12/*	const char *address;
13/*
14/*	VSTRING	*rewrite_clnt_internal(ruleset, address, result)
15/*	const char *ruleset;
16/*	const char *address;
17/*	VSTRING	*result;
18/* DESCRIPTION
19/*	This module implements a mail address rewriting client.
20/*
21/*	rewrite_clnt() sends a rule set name and external-form address to the
22/*	rewriting service and returns the resulting external-form address.
23/*	In case of communication failure the program keeps trying until the
24/*	mail system shuts down.
25/*
26/*	rewrite_clnt_internal() performs the same functionality but takes
27/*	input in internal (unquoted) form, and produces output in internal
28/*	(unquoted) form.
29/* DIAGNOSTICS
30/*	Warnings: communication failure. Fatal error: mail system is down.
31/* SEE ALSO
32/*	mail_proto(3h) low-level mail component glue.
33/* LICENSE
34/* .ad
35/* .fi
36/*	The Secure Mailer license must be distributed with this software.
37/* AUTHOR(S)
38/*	Wietse Venema
39/*	IBM T.J. Watson Research
40/*	P.O. Box 704
41/*	Yorktown Heights, NY 10598, USA
42/*--*/
43
44/* System library. */
45
46#include <sys_defs.h>
47#include <unistd.h>
48#include <errno.h>
49#include <string.h>
50
51/* Utility library. */
52
53#include <msg.h>
54#include <vstring.h>
55#include <vstream.h>
56#include <vstring_vstream.h>
57#include <events.h>
58#include <iostuff.h>
59#include <quote_822_local.h>
60
61/* Global library. */
62
63#include "mail_proto.h"
64#include "mail_params.h"
65#include "clnt_stream.h"
66#include "rewrite_clnt.h"
67
68/* Application-specific. */
69
70 /*
71  * XXX this is shared with the resolver client to save a file descriptor.
72  */
73CLNT_STREAM *rewrite_clnt_stream = 0;
74
75static time_t last_expire;
76static VSTRING *last_rule;
77static VSTRING *last_addr;
78static VSTRING *last_result;
79
80/* rewrite_clnt - rewrite address to (transport, next hop, recipient) */
81
82VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
83{
84    VSTREAM *stream;
85    int     server_flags;
86    int     count = 0;
87
88    /*
89     * One-entry cache.
90     */
91    if (last_addr == 0) {
92	last_rule = vstring_alloc(10);
93	last_addr = vstring_alloc(100);
94	last_result = vstring_alloc(100);
95    }
96
97    /*
98     * Sanity check. An address must be in externalized form. The result must
99     * not clobber the input, because we may have to retransmit the query.
100     */
101#define STR vstring_str
102
103    if (*addr == 0)
104	addr = "";
105    if (addr == STR(result))
106	msg_panic("rewrite_clnt: result clobbers input");
107
108    /*
109     * Peek at the cache.
110     */
111    if (time((time_t *) 0) < last_expire
112	&& strcmp(addr, STR(last_addr)) == 0
113	&& strcmp(rule, STR(last_rule)) == 0) {
114	vstring_strcpy(result, STR(last_result));
115	if (msg_verbose)
116	    msg_info("rewrite_clnt: cached: %s: %s -> %s",
117		     rule, addr, vstring_str(result));
118	return (result);
119    }
120
121    /*
122     * Keep trying until we get a complete response. The rewrite service is
123     * CPU bound and making the client asynchronous would just complicate the
124     * code.
125     */
126    if (rewrite_clnt_stream == 0)
127	rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE,
128						 var_rewrite_service,
129						 var_ipc_idle_limit,
130						 var_ipc_ttl_limit);
131
132    for (;;) {
133	stream = clnt_stream_access(rewrite_clnt_stream);
134	errno = 0;
135	count += 1;
136	if (attr_print(stream, ATTR_FLAG_NONE,
137		       ATTR_TYPE_STR, MAIL_ATTR_REQ, REWRITE_ADDR,
138		       ATTR_TYPE_STR, MAIL_ATTR_RULE, rule,
139		       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
140		       ATTR_TYPE_END) != 0
141	    || vstream_fflush(stream)
142	    || attr_scan(stream, ATTR_FLAG_STRICT,
143			 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &server_flags,
144			 ATTR_TYPE_STR, MAIL_ATTR_ADDR, result,
145			 ATTR_TYPE_END) != 2) {
146	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
147		msg_warn("problem talking to service %s: %m",
148			 var_rewrite_service);
149	} else {
150	    if (msg_verbose)
151		msg_info("rewrite_clnt: %s: %s -> %s",
152			 rule, addr, vstring_str(result));
153	    /* Server-requested disconnect. */
154	    if (server_flags != 0)
155		clnt_stream_recover(rewrite_clnt_stream);
156	    break;
157	}
158	sleep(1);				/* XXX make configurable */
159	clnt_stream_recover(rewrite_clnt_stream);
160    }
161
162    /*
163     * Update the cache.
164     */
165    vstring_strcpy(last_rule, rule);
166    vstring_strcpy(last_addr, addr);
167    vstring_strcpy(last_result, STR(result));
168    last_expire = time((time_t *) 0) + 30;	/* XXX make configurable */
169
170    return (result);
171}
172
173/* rewrite_clnt_internal - rewrite from/to internal form */
174
175VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result)
176{
177    VSTRING *src = vstring_alloc(100);
178    VSTRING *dst = vstring_alloc(100);
179
180    /*
181     * Convert the address from internal address form to external RFC822
182     * form, then rewrite it. After rewriting, convert to internal form.
183     */
184    quote_822_local(src, addr);
185    rewrite_clnt(ruleset, STR(src), dst);
186    unquote_822_local(result, STR(dst));
187    vstring_free(src);
188    vstring_free(dst);
189    return (result);
190}
191
192#ifdef TEST
193
194#include <stdlib.h>
195#include <string.h>
196#include <msg_vstream.h>
197#include <split_at.h>
198#include <vstring_vstream.h>
199#include <mail_conf.h>
200#include <mail_params.h>
201
202static NORETURN usage(char *myname)
203{
204    msg_fatal("usage: %s [-v] [rule address...]", myname);
205}
206
207static void rewrite(char *rule, char *addr, VSTRING *reply)
208{
209    rewrite_clnt(rule, addr, reply);
210    vstream_printf("%-10s %s\n", "rule", rule);
211    vstream_printf("%-10s %s\n", "address", addr);
212    vstream_printf("%-10s %s\n\n", "result", STR(reply));
213    vstream_fflush(VSTREAM_OUT);
214}
215
216int     main(int argc, char **argv)
217{
218    VSTRING *reply;
219    int     ch;
220    char   *rule;
221    char   *addr;
222
223    msg_vstream_init(argv[0], VSTREAM_ERR);
224
225    mail_conf_read();
226    msg_info("using config files in %s", var_config_dir);
227    if (chdir(var_queue_dir) < 0)
228	msg_fatal("chdir %s: %m", var_queue_dir);
229
230    while ((ch = GETOPT(argc, argv, "v")) > 0) {
231	switch (ch) {
232	case 'v':
233	    msg_verbose++;
234	    break;
235	default:
236	    usage(argv[0]);
237	}
238    }
239    reply = vstring_alloc(1);
240
241    if (argc > optind) {
242	for (;;) {
243	    if ((rule = argv[optind++]) == 0)
244		break;
245	    if ((addr = argv[optind++]) == 0)
246		usage(argv[0]);
247	    rewrite(rule, addr, reply);
248	}
249    } else {
250	VSTRING *buffer = vstring_alloc(1);
251
252	while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
253	    if ((addr = split_at(STR(buffer), ' ')) == 0
254		|| *(rule = STR(buffer)) == 0)
255		usage(argv[0]);
256	    rewrite(rule, addr, reply);
257	}
258	vstring_free(buffer);
259    }
260    vstring_free(reply);
261    exit(0);
262}
263
264#endif
265