xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/resolve_clnt.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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