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