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