xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/rewrite_clnt.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: rewrite_clnt.c,v 1.3 2022/10/08 16:12: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 /*	Wietse Venema
46 /*	Google, Inc.
47 /*	111 8th Avenue
48 /*	New York, NY 10011, USA
49 /*--*/
50 
51 /* System library. */
52 
53 #include <sys_defs.h>
54 #include <unistd.h>
55 #include <errno.h>
56 #include <string.h>
57 
58 /* Utility library. */
59 
60 #include <msg.h>
61 #include <vstring.h>
62 #include <vstream.h>
63 #include <vstring_vstream.h>
64 #include <events.h>
65 #include <iostuff.h>
66 #include <quote_822_local.h>
67 
68 /* Global library. */
69 
70 #include "mail_proto.h"
71 #include "mail_params.h"
72 #include "clnt_stream.h"
73 #include "rewrite_clnt.h"
74 
75 /* Application-specific. */
76 
77  /*
78   * XXX this is shared with the resolver client to save a file descriptor.
79   */
80 CLNT_STREAM *rewrite_clnt_stream = 0;
81 
82 static time_t last_expire;
83 static VSTRING *last_rule;
84 static VSTRING *last_addr;
85 static VSTRING *last_result;
86 
87 /* rewrite_clnt_handshake - receive server protocol announcement */
88 
rewrite_clnt_handshake(VSTREAM * stream)89 static int rewrite_clnt_handshake(VSTREAM *stream)
90 {
91     return (attr_scan(stream, ATTR_FLAG_STRICT,
92 		  RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL),
93 		      ATTR_TYPE_END));
94 }
95 
96 /* rewrite_clnt - rewrite address to (transport, next hop, recipient) */
97 
rewrite_clnt(const char * rule,const char * addr,VSTRING * result)98 VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result)
99 {
100     VSTREAM *stream;
101     int     server_flags;
102     int     count = 0;
103 
104     /*
105      * One-entry cache.
106      */
107     if (last_addr == 0) {
108 	last_rule = vstring_alloc(10);
109 	last_addr = vstring_alloc(100);
110 	last_result = vstring_alloc(100);
111     }
112 
113     /*
114      * Sanity check. An address must be in externalized form. The result must
115      * not clobber the input, because we may have to retransmit the query.
116      */
117 #define STR vstring_str
118 
119     if (*addr == 0)
120 	addr = "";
121     if (addr == STR(result))
122 	msg_panic("rewrite_clnt: result clobbers input");
123 
124     /*
125      * Peek at the cache.
126      */
127     if (time((time_t *) 0) < last_expire
128 	&& strcmp(addr, STR(last_addr)) == 0
129 	&& strcmp(rule, STR(last_rule)) == 0) {
130 	vstring_strcpy(result, STR(last_result));
131 	if (msg_verbose)
132 	    msg_info("rewrite_clnt: cached: %s: %s -> %s",
133 		     rule, addr, vstring_str(result));
134 	return (result);
135     }
136 
137     /*
138      * Keep trying until we get a complete response. The rewrite service is
139      * CPU bound and making the client asynchronous would just complicate the
140      * code.
141      */
142     if (rewrite_clnt_stream == 0)
143 	rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE,
144 						 var_rewrite_service,
145 						 var_ipc_idle_limit,
146 						 var_ipc_ttl_limit,
147 						 rewrite_clnt_handshake);
148 
149     for (;;) {
150 	stream = clnt_stream_access(rewrite_clnt_stream);
151 	errno = 0;
152 	count += 1;
153 	if (stream == 0
154 	    || attr_print(stream, ATTR_FLAG_NONE,
155 			  SEND_ATTR_STR(MAIL_ATTR_REQ, REWRITE_ADDR),
156 			  SEND_ATTR_STR(MAIL_ATTR_RULE, rule),
157 			  SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
158 			  ATTR_TYPE_END) != 0
159 	    || vstream_fflush(stream)
160 	    || attr_scan(stream, ATTR_FLAG_STRICT,
161 			 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &server_flags),
162 			 RECV_ATTR_STR(MAIL_ATTR_ADDR, result),
163 			 ATTR_TYPE_END) != 2) {
164 	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
165 		msg_warn("problem talking to service %s: %m",
166 			 var_rewrite_service);
167 	} else {
168 	    if (msg_verbose)
169 		msg_info("rewrite_clnt: %s: %s -> %s",
170 			 rule, addr, vstring_str(result));
171 	    /* Server-requested disconnect. */
172 	    if (server_flags != 0)
173 		clnt_stream_recover(rewrite_clnt_stream);
174 	    break;
175 	}
176 	sleep(1);				/* XXX make configurable */
177 	clnt_stream_recover(rewrite_clnt_stream);
178     }
179 
180     /*
181      * Update the cache.
182      */
183     vstring_strcpy(last_rule, rule);
184     vstring_strcpy(last_addr, addr);
185     vstring_strcpy(last_result, STR(result));
186     last_expire = time((time_t *) 0) + 30;	/* XXX make configurable */
187 
188     return (result);
189 }
190 
191 /* rewrite_clnt_internal - rewrite from/to internal form */
192 
rewrite_clnt_internal(const char * ruleset,const char * addr,VSTRING * result)193 VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result)
194 {
195     VSTRING *src = vstring_alloc(100);
196     VSTRING *dst = vstring_alloc(100);
197 
198     /*
199      * Convert the address from internal address form to external RFC822
200      * form, then rewrite it. After rewriting, convert to internal form.
201      */
202     quote_822_local(src, addr);
203     rewrite_clnt(ruleset, STR(src), dst);
204     unquote_822_local(result, STR(dst));
205     vstring_free(src);
206     vstring_free(dst);
207     return (result);
208 }
209 
210 #ifdef TEST
211 
212 #include <stdlib.h>
213 #include <string.h>
214 #include <msg_vstream.h>
215 #include <split_at.h>
216 #include <vstring_vstream.h>
217 #include <mail_conf.h>
218 #include <mail_params.h>
219 
usage(char * myname)220 static NORETURN usage(char *myname)
221 {
222     msg_fatal("usage: %s [-v] [rule address...]", myname);
223 }
224 
rewrite(char * rule,char * addr,VSTRING * reply)225 static void rewrite(char *rule, char *addr, VSTRING *reply)
226 {
227     rewrite_clnt(rule, addr, reply);
228     vstream_printf("%-10s %s\n", "rule", rule);
229     vstream_printf("%-10s %s\n", "address", addr);
230     vstream_printf("%-10s %s\n\n", "result", STR(reply));
231     vstream_fflush(VSTREAM_OUT);
232 }
233 
main(int argc,char ** argv)234 int     main(int argc, char **argv)
235 {
236     VSTRING *reply;
237     int     ch;
238     char   *rule;
239     char   *addr;
240 
241     msg_vstream_init(argv[0], VSTREAM_ERR);
242 
243     mail_conf_read();
244     msg_info("using config files in %s", var_config_dir);
245     if (chdir(var_queue_dir) < 0)
246 	msg_fatal("chdir %s: %m", var_queue_dir);
247 
248     while ((ch = GETOPT(argc, argv, "v")) > 0) {
249 	switch (ch) {
250 	case 'v':
251 	    msg_verbose++;
252 	    break;
253 	default:
254 	    usage(argv[0]);
255 	}
256     }
257     reply = vstring_alloc(1);
258 
259     if (argc > optind) {
260 	for (;;) {
261 	    if ((rule = argv[optind++]) == 0)
262 		break;
263 	    if ((addr = argv[optind++]) == 0)
264 		usage(argv[0]);
265 	    rewrite(rule, addr, reply);
266 	}
267     } else {
268 	VSTRING *buffer = vstring_alloc(1);
269 
270 	while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
271 	    if ((addr = split_at(STR(buffer), ' ')) == 0
272 		|| *(rule = STR(buffer)) == 0)
273 		usage(argv[0]);
274 	    rewrite(rule, addr, reply);
275 	}
276 	vstring_free(buffer);
277     }
278     vstring_free(reply);
279     exit(0);
280 }
281 
282 #endif
283