1 /* $NetBSD: resolve_clnt.c,v 1.4 2022/10/08 16:12:45 christos Exp $ */
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_from(sender, address, reply)
24 /* const char *sender;
25 /* const char *address;
26 /* RESOLVE_REPLY *reply;
27 /*
28 /* void resolve_clnt_verify_from(sender, address, reply)
29 /* const char *sender;
30 /* const char *address;
31 /* RESOLVE_REPLY *reply;
32 /*
33 /* void resolve_clnt_free(reply)
34 /* RESOLVE_REPLY *reply;
35 /* DESCRIPTION
36 /* This module implements a mail address resolver client.
37 /*
38 /* resolve_clnt_init() initializes a reply data structure for use
39 /* by resolve_clnt_query(). The structure is destroyed by passing
40 /* it to resolve_clnt_free().
41 /*
42 /* resolve_clnt_query_from() sends an internal-form recipient address
43 /* (user@domain) to the resolver daemon and returns the resulting
44 /* transport name, next_hop host name, and internal-form recipient
45 /* address. In case of communication failure the program keeps trying
46 /* until the mail system goes down. The internal-form sender
47 /* information is used for sender-dependent relayhost lookup.
48 /* Specify RESOLVE_NULL_FROM when the sender is unavailable.
49 /*
50 /* resolve_clnt_verify_from() implements an alternative version that can
51 /* be used for address verification.
52 /*
53 /* In the resolver reply, the flags member is the bit-wise OR of
54 /* zero or more of the following:
55 /* .IP RESOLVE_FLAG_FINAL
56 /* The recipient address resolves to a mail transport that performs
57 /* final delivery. The destination is local or corresponds to a hosted
58 /* domain that is handled by the local machine. This flag is currently
59 /* not used.
60 /* .IP RESOLVE_FLAG_ROUTED
61 /* After address resolution the recipient localpart contains further
62 /* routing information, so the resolved next-hop destination is not
63 /* the final destination.
64 /* .IP RESOLVE_FLAG_ERROR
65 /* The address resolved to something that has invalid syntax.
66 /* .IP RESOLVE_FLAG_FAIL
67 /* The request could not be completed.
68 /* .PP
69 /* In addition, the address domain class is returned by setting
70 /* one of the following flags (this is preliminary code awaiting
71 /* more permanent implementation of address domain class handling):
72 /* .IP RESOLVE_CLASS_LOCAL
73 /* The address domain matches $mydestination, $inet_interfaces
74 /* or $proxy_interfaces.
75 /* .IP RESOLVE_CLASS_ALIAS
76 /* The address domain matches $virtual_alias_domains (virtual
77 /* alias domains, where each address is redirected to a real
78 /* local or remote address).
79 /* .IP RESOLVE_CLASS_VIRTUAL
80 /* The address domain matches $virtual_mailbox_domains (true
81 /* virtual domains where each address can have its own mailbox).
82 /* .IP RESOLVE_CLASS_RELAY
83 /* The address domain matches $relay_domains, i.e. this is an
84 /* authorized mail relay destination.
85 /* .IP RESOLVE_CLASS_DEFAULT
86 /* The address matches none of the above. Access to this domain
87 /* should be limited to authorized senders only.
88 /* .PP
89 /* For convenience, the constant RESOLVE_CLASS_FINAL includes all
90 /* cases where the local machine is the final destination.
91 /* DIAGNOSTICS
92 /* Warnings: communication failure. Fatal error: mail system is down.
93 /* SEE ALSO
94 /* mail_proto(3h) low-level mail component glue.
95 /* LICENSE
96 /* .ad
97 /* .fi
98 /* The Secure Mailer license must be distributed with this software.
99 /* AUTHOR(S)
100 /* Wietse Venema
101 /* IBM T.J. Watson Research
102 /* P.O. Box 704
103 /* Yorktown Heights, NY 10598, USA
104 /*
105 /* Wietse Venema
106 /* Google, Inc.
107 /* 111 8th Avenue
108 /* New York, NY 10011, USA
109 /*--*/
110
111 /* System library. */
112
113 #include <sys_defs.h>
114 #include <unistd.h>
115 #include <string.h>
116 #include <errno.h>
117
118 /* Utility library. */
119
120 #include <msg.h>
121 #include <vstream.h>
122 #include <vstring.h>
123 #include <vstring_vstream.h>
124 #include <events.h>
125 #include <iostuff.h>
126
127 /* Global library. */
128
129 #include "mail_proto.h"
130 #include "mail_params.h"
131 #include "clnt_stream.h"
132 #include "resolve_clnt.h"
133
134 /* Application-specific. */
135
136 /*
137 * XXX this is shared with the rewrite client to save a file descriptor.
138 */
139 extern CLNT_STREAM *rewrite_clnt_stream;
140
141 static time_t last_expire;
142 static VSTRING *last_class;
143 static VSTRING *last_sender;
144 static VSTRING *last_addr;
145 static RESOLVE_REPLY last_reply;
146
147 /* resolve_clnt_init - initialize reply */
148
resolve_clnt_init(RESOLVE_REPLY * reply)149 void resolve_clnt_init(RESOLVE_REPLY *reply)
150 {
151 reply->transport = vstring_alloc(100);
152 reply->nexthop = vstring_alloc(100);
153 reply->recipient = vstring_alloc(100);
154 reply->flags = 0;
155 }
156
157 /* resolve_clnt_handshake - receive server protocol announcement */
158
resolve_clnt_handshake(VSTREAM * stream)159 static int resolve_clnt_handshake(VSTREAM *stream)
160 {
161 return (attr_scan(stream, ATTR_FLAG_STRICT,
162 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL),
163 ATTR_TYPE_END));
164 }
165
166 /* resolve_clnt - resolve address to (transport, next hop, recipient) */
167
resolve_clnt(const char * class,const char * sender,const char * addr,RESOLVE_REPLY * reply)168 void resolve_clnt(const char *class, const char *sender,
169 const char *addr, RESOLVE_REPLY *reply)
170 {
171 const char *myname = "resolve_clnt";
172 VSTREAM *stream;
173 int server_flags;
174 int count = 0;
175
176 /*
177 * One-entry cache.
178 */
179 if (last_addr == 0) {
180 last_class = vstring_alloc(10);
181 last_sender = vstring_alloc(10);
182 last_addr = vstring_alloc(100);
183 resolve_clnt_init(&last_reply);
184 }
185
186 /*
187 * Sanity check. The result must not clobber the input because we may
188 * have to retransmit the request.
189 */
190 #define STR vstring_str
191
192 if (addr == STR(reply->recipient))
193 msg_panic("%s: result clobbers input", myname);
194
195 /*
196 * Peek at the cache.
197 */
198 #define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "")
199
200 if (time((time_t *) 0) < last_expire
201 && *addr && strcmp(addr, STR(last_addr)) == 0
202 && strcmp(class, STR(last_class)) == 0
203 && strcmp(sender, STR(last_sender)) == 0) {
204 vstring_strcpy(reply->transport, STR(last_reply.transport));
205 vstring_strcpy(reply->nexthop, STR(last_reply.nexthop));
206 vstring_strcpy(reply->recipient, STR(last_reply.recipient));
207 reply->flags = last_reply.flags;
208 if (msg_verbose)
209 msg_info("%s: cached: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
210 myname, sender, addr, STR(reply->transport),
211 STR(reply->nexthop), STR(reply->recipient),
212 IFSET(RESOLVE_FLAG_FINAL, "final"),
213 IFSET(RESOLVE_FLAG_ROUTED, "routed"),
214 IFSET(RESOLVE_FLAG_ERROR, "error"),
215 IFSET(RESOLVE_FLAG_FAIL, "fail"),
216 IFSET(RESOLVE_CLASS_LOCAL, "local"),
217 IFSET(RESOLVE_CLASS_ALIAS, "alias"),
218 IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"),
219 IFSET(RESOLVE_CLASS_RELAY, "relay"),
220 IFSET(RESOLVE_CLASS_DEFAULT, "default"));
221 return;
222 }
223
224 /*
225 * Keep trying until we get a complete response. The resolve service is
226 * CPU bound; making the client asynchronous would just complicate the
227 * code.
228 */
229 if (rewrite_clnt_stream == 0)
230 rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE,
231 var_rewrite_service,
232 var_ipc_idle_limit,
233 var_ipc_ttl_limit,
234 resolve_clnt_handshake);
235
236 for (;;) {
237 stream = clnt_stream_access(rewrite_clnt_stream);
238 errno = 0;
239 count += 1;
240 if (stream == 0
241 || attr_print(stream, ATTR_FLAG_NONE,
242 SEND_ATTR_STR(MAIL_ATTR_REQ, class),
243 SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
244 SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
245 ATTR_TYPE_END) != 0
246 || vstream_fflush(stream)
247 || attr_scan(stream, ATTR_FLAG_STRICT,
248 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &server_flags),
249 RECV_ATTR_STR(MAIL_ATTR_TRANSPORT, reply->transport),
250 RECV_ATTR_STR(MAIL_ATTR_NEXTHOP, reply->nexthop),
251 RECV_ATTR_STR(MAIL_ATTR_RECIP, reply->recipient),
252 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &reply->flags),
253 ATTR_TYPE_END) != 5) {
254 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
255 msg_warn("problem talking to service %s: %m",
256 var_rewrite_service);
257 } else {
258 if (msg_verbose)
259 msg_info("%s: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
260 myname, sender, addr, STR(reply->transport),
261 STR(reply->nexthop), STR(reply->recipient),
262 IFSET(RESOLVE_FLAG_FINAL, "final"),
263 IFSET(RESOLVE_FLAG_ROUTED, "routed"),
264 IFSET(RESOLVE_FLAG_ERROR, "error"),
265 IFSET(RESOLVE_FLAG_FAIL, "fail"),
266 IFSET(RESOLVE_CLASS_LOCAL, "local"),
267 IFSET(RESOLVE_CLASS_ALIAS, "alias"),
268 IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"),
269 IFSET(RESOLVE_CLASS_RELAY, "relay"),
270 IFSET(RESOLVE_CLASS_DEFAULT, "default"));
271 /* Server-requested disconnect. */
272 if (server_flags != 0)
273 clnt_stream_recover(rewrite_clnt_stream);
274 if (STR(reply->transport)[0] == 0)
275 msg_warn("%s: null transport result for: <%s>", myname, addr);
276 else if (STR(reply->recipient)[0] == 0 && *addr != 0)
277 msg_warn("%s: null recipient result for: <%s>", myname, addr);
278 else
279 break;
280 }
281 sleep(1); /* XXX make configurable */
282 clnt_stream_recover(rewrite_clnt_stream);
283 }
284
285 /*
286 * Update the cache.
287 */
288 vstring_strcpy(last_class, class);
289 vstring_strcpy(last_sender, sender);
290 vstring_strcpy(last_addr, addr);
291 vstring_strcpy(last_reply.transport, STR(reply->transport));
292 vstring_strcpy(last_reply.nexthop, STR(reply->nexthop));
293 vstring_strcpy(last_reply.recipient, STR(reply->recipient));
294 last_reply.flags = reply->flags;
295 last_expire = time((time_t *) 0) + 30; /* XXX make configurable */
296 }
297
298 /* resolve_clnt_free - destroy reply */
299
resolve_clnt_free(RESOLVE_REPLY * reply)300 void resolve_clnt_free(RESOLVE_REPLY *reply)
301 {
302 reply->transport = vstring_free(reply->transport);
303 reply->nexthop = vstring_free(reply->nexthop);
304 reply->recipient = vstring_free(reply->recipient);
305 }
306
307 #ifdef TEST
308
309 #include <stdlib.h>
310 #include <msg_vstream.h>
311 #include <vstring_vstream.h>
312 #include <split_at.h>
313 #include <mail_conf.h>
314
usage(char * myname)315 static NORETURN usage(char *myname)
316 {
317 msg_fatal("usage: %s [-v] [address...]", myname);
318 }
319
resolve(char * class,char * addr,RESOLVE_REPLY * reply)320 static void resolve(char *class, char *addr, RESOLVE_REPLY *reply)
321 {
322 struct RESOLVE_FLAG_TABLE {
323 int flag;
324 const char *name;
325 };
326 struct RESOLVE_FLAG_TABLE resolve_flag_table[] = {
327 RESOLVE_FLAG_FINAL, "FLAG_FINAL",
328 RESOLVE_FLAG_ROUTED, "FLAG_ROUTED",
329 RESOLVE_FLAG_ERROR, "FLAG_ERROR",
330 RESOLVE_FLAG_FAIL, "FLAG_FAIL",
331 RESOLVE_CLASS_LOCAL, "CLASS_LOCAL",
332 RESOLVE_CLASS_ALIAS, "CLASS_ALIAS",
333 RESOLVE_CLASS_VIRTUAL, "CLASS_VIRTUAL",
334 RESOLVE_CLASS_RELAY, "CLASS_RELAY",
335 RESOLVE_CLASS_DEFAULT, "CLASS_DEFAULT",
336 0,
337 };
338 struct RESOLVE_FLAG_TABLE *fp;
339
340 resolve_clnt(class, RESOLVE_NULL_FROM, addr, reply);
341 if (reply->flags & RESOLVE_FLAG_FAIL) {
342 vstream_printf("request failed\n");
343 } else {
344 vstream_printf("%-10s %s\n", "class", class);
345 vstream_printf("%-10s %s\n", "address", addr);
346 vstream_printf("%-10s %s\n", "transport", STR(reply->transport));
347 vstream_printf("%-10s %s\n", "nexthop", *STR(reply->nexthop) ?
348 STR(reply->nexthop) : "[none]");
349 vstream_printf("%-10s %s\n", "recipient", STR(reply->recipient));
350 vstream_printf("%-10s ", "flags");
351 for (fp = resolve_flag_table; fp->name; fp++) {
352 if (reply->flags & fp->flag) {
353 vstream_printf("%s ", fp->name);
354 reply->flags &= ~fp->flag;
355 }
356 }
357 if (reply->flags != 0)
358 vstream_printf("Unknown flag 0x%x", reply->flags);
359 vstream_printf("\n\n");
360 vstream_fflush(VSTREAM_OUT);
361 }
362 }
363
main(int argc,char ** argv)364 int main(int argc, char **argv)
365 {
366 RESOLVE_REPLY reply;
367 char *addr;
368 int ch;
369
370 msg_vstream_init(argv[0], VSTREAM_ERR);
371
372 mail_conf_read();
373 msg_info("using config files in %s", var_config_dir);
374 if (chdir(var_queue_dir) < 0)
375 msg_fatal("chdir %s: %m", var_queue_dir);
376
377 while ((ch = GETOPT(argc, argv, "v")) > 0) {
378 switch (ch) {
379 case 'v':
380 msg_verbose++;
381 break;
382 default:
383 usage(argv[0]);
384 }
385 }
386 resolve_clnt_init(&reply);
387
388 if (argc > optind) {
389 while (argv[optind] && argv[optind + 1]) {
390 resolve(argv[optind], argv[optind + 1], &reply);
391 optind += 2;
392 }
393 } else {
394 VSTRING *buffer = vstring_alloc(1);
395
396 while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
397 addr = split_at(STR(buffer), ' ');
398 if (*STR(buffer) == 0)
399 msg_fatal("need as input: class [address]");
400 if (addr == 0)
401 addr = "";
402 resolve(STR(buffer), addr, &reply);
403 }
404 vstring_free(buffer);
405 }
406 resolve_clnt_free(&reply);
407 exit(0);
408 }
409
410 #endif
411