7 /* address resolve service client (internal forms)
9 /* #include <resolve_clnt.h>
13 /* VSTRING *transport;
15 /* VSTRING *recipient;
20 /* void resolve_clnt_init(reply)
21 /* RESOLVE_REPLY *reply;
23 /* void resolve_clnt_query(address, reply)
24 /* const char *address;
25 /* RESOLVE_REPLY *reply;
27 /* void resolve_clnt_query_from(sender, address, reply)
28 /* const char *sender;
29 /* const char *address;
30 /* RESOLVE_REPLY *reply;
32 /* void resolve_clnt_verify(address, reply)
33 /* const char *address;
34 /* RESOLVE_REPLY *reply;
36 /* void resolve_clnt_verify_from(sender, address, reply)
37 /* const char *sender;
38 /* const char *address;
39 /* RESOLVE_REPLY *reply;
41 /* void resolve_clnt_free(reply)
42 /* RESOLVE_REPLY *reply;
44 /* This module implements a mail address resolver client.
46 /* resolve_clnt_init() initializes a reply data structure for use
47 /* by resolve_clnt_query(). The structure is destroyed by passing
48 /* it to resolve_clnt_free().
50 /* resolve_clnt_query() sends an internal-form recipient address
51 /* (user@domain) to the resolver daemon and returns the resulting
52 /* transport name, next_hop host name, and internal-form recipient
53 /* address. In case of communication failure the program keeps trying
54 /* until the mail system goes down.
56 /* resolve_clnt_verify() implements an alternative version that can
57 /* be used for address verification.
59 /* resolve_clnt_query_from() and resolve_clnt_verify_from()
60 /* allow the caller to supply sender context that will be used
61 /* for sender-dependent relayhost lookup.
63 /* In the resolver reply, the flags member is the bit-wise OR of
64 /* zero or more of the following:
65 /* .IP RESOLVE_FLAG_FINAL
66 /* The recipient address resolves to a mail transport that performs
67 /* final delivery. The destination is local or corresponds to a hosted
68 /* domain that is handled by the local machine. This flag is currently
70 /* .IP RESOLVE_FLAG_ROUTED
71 /* After address resolution the recipient localpart contains further
72 /* routing information, so the resolved next-hop destination is not
73 /* the final destination.
74 /* .IP RESOLVE_FLAG_ERROR
75 /* The address resolved to something that has invalid syntax.
76 /* .IP RESOLVE_FLAG_FAIL
77 /* The request could not be completed.
79 /* In addition, the address domain class is returned by setting
80 /* one of the following flags (this is preliminary code awaiting
81 /* more permanent implementation of address domain class handling):
82 /* .IP RESOLVE_CLASS_LOCAL
83 /* The address domain matches $mydestination, $inet_interfaces
84 /* or $proxy_interfaces.
85 /* .IP RESOLVE_CLASS_ALIAS
86 /* The address domain matches $virtual_alias_domains (virtual
87 /* alias domains, where each address is redirected to a real
88 /* local or remote address).
89 /* .IP RESOLVE_CLASS_VIRTUAL
90 /* The address domain matches $virtual_mailbox_domains (true
91 /* virtual domains where each address can have its own mailbox).
92 /* .IP RESOLVE_CLASS_RELAY
93 /* The address domain matches $relay_domains, i.e. this is an
94 /* authorized mail relay destination.
95 /* .IP RESOLVE_CLASS_DEFAULT
96 /* The address matches none of the above. Access to this domain
97 /* should be limited to authorized senders only.
99 /* For convenience, the constant RESOLVE_CLASS_FINAL includes all
100 /* cases where the local machine is the final destination.
102 /* Warnings: communication failure. Fatal error: mail system is down.
104 /* mail_proto(3h) low-level mail component glue.
108 /* The Secure Mailer license must be distributed with this software.
111 /* IBM T.J. Watson Research
113 /* Yorktown Heights, NY 10598, USA
116 /* System library. */
118 #include <sys_defs.h>
123 /* Utility library. */
128 #include <vstring_vstream.h>
132 /* Global library. */
134 #include "mail_proto.h"
135 #include "mail_params.h"
136 #include "clnt_stream.h"
137 #include "resolve_clnt.h"
139 /* Application-specific. */
142 * XXX this is shared with the rewrite client to save a file descriptor.
144 extern CLNT_STREAM
*rewrite_clnt_stream
;
146 static time_t last_expire
;
147 static VSTRING
*last_class
;
148 static VSTRING
*last_sender
;
149 static VSTRING
*last_addr
;
150 static RESOLVE_REPLY last_reply
;
152 /* resolve_clnt_init - initialize reply */
154 void resolve_clnt_init(RESOLVE_REPLY
*reply
)
156 reply
->transport
= vstring_alloc(100);
157 reply
->nexthop
= vstring_alloc(100);
158 reply
->recipient
= vstring_alloc(100);
162 /* resolve_clnt - resolve address to (transport, next hop, recipient) */
164 void resolve_clnt(const char *class, const char *sender
,
165 const char *addr
, RESOLVE_REPLY
*reply
)
167 const char *myname
= "resolve_clnt";
175 if (last_addr
== 0) {
176 last_class
= vstring_alloc(10);
177 last_sender
= vstring_alloc(10);
178 last_addr
= vstring_alloc(100);
179 resolve_clnt_init(&last_reply
);
183 * Sanity check. The result must not clobber the input because we may
184 * have to retransmit the request.
186 #define STR vstring_str
188 if (addr
== STR(reply
->recipient
))
189 msg_panic("%s: result clobbers input", myname
);
194 #define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "")
196 if (time((time_t *) 0) < last_expire
197 && *addr
&& strcmp(addr
, STR(last_addr
)) == 0
198 && strcmp(class, STR(last_class
)) == 0
199 && strcmp(sender
, STR(last_sender
)) == 0) {
200 vstring_strcpy(reply
->transport
, STR(last_reply
.transport
));
201 vstring_strcpy(reply
->nexthop
, STR(last_reply
.nexthop
));
202 vstring_strcpy(reply
->recipient
, STR(last_reply
.recipient
));
203 reply
->flags
= last_reply
.flags
;
205 msg_info("%s: cached: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
206 myname
, sender
, addr
, STR(reply
->transport
),
207 STR(reply
->nexthop
), STR(reply
->recipient
),
208 IFSET(RESOLVE_FLAG_FINAL
, "final"),
209 IFSET(RESOLVE_FLAG_ROUTED
, "routed"),
210 IFSET(RESOLVE_FLAG_ERROR
, "error"),
211 IFSET(RESOLVE_FLAG_FAIL
, "fail"),
212 IFSET(RESOLVE_CLASS_LOCAL
, "local"),
213 IFSET(RESOLVE_CLASS_ALIAS
, "alias"),
214 IFSET(RESOLVE_CLASS_VIRTUAL
, "virtual"),
215 IFSET(RESOLVE_CLASS_RELAY
, "relay"),
216 IFSET(RESOLVE_CLASS_DEFAULT
, "default"));
221 * Keep trying until we get a complete response. The resolve service is
222 * CPU bound; making the client asynchronous would just complicate the
225 if (rewrite_clnt_stream
== 0)
226 rewrite_clnt_stream
= clnt_stream_create(MAIL_CLASS_PRIVATE
,
232 stream
= clnt_stream_access(rewrite_clnt_stream
);
235 if (attr_print(stream
, ATTR_FLAG_NONE
,
236 ATTR_TYPE_STR
, MAIL_ATTR_REQ
, class,
237 ATTR_TYPE_STR
, MAIL_ATTR_SENDER
, sender
,
238 ATTR_TYPE_STR
, MAIL_ATTR_ADDR
, addr
,
240 || vstream_fflush(stream
)
241 || attr_scan(stream
, ATTR_FLAG_STRICT
,
242 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &server_flags
,
243 ATTR_TYPE_STR
, MAIL_ATTR_TRANSPORT
, reply
->transport
,
244 ATTR_TYPE_STR
, MAIL_ATTR_NEXTHOP
, reply
->nexthop
,
245 ATTR_TYPE_STR
, MAIL_ATTR_RECIP
, reply
->recipient
,
246 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &reply
->flags
,
247 ATTR_TYPE_END
) != 5) {
248 if (msg_verbose
|| count
> 1 || (errno
&& errno
!= EPIPE
&& errno
!= ENOENT
))
249 msg_warn("problem talking to service %s: %m",
250 var_rewrite_service
);
253 msg_info("%s: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
254 myname
, sender
, addr
, STR(reply
->transport
),
255 STR(reply
->nexthop
), STR(reply
->recipient
),
256 IFSET(RESOLVE_FLAG_FINAL
, "final"),
257 IFSET(RESOLVE_FLAG_ROUTED
, "routed"),
258 IFSET(RESOLVE_FLAG_ERROR
, "error"),
259 IFSET(RESOLVE_FLAG_FAIL
, "fail"),
260 IFSET(RESOLVE_CLASS_LOCAL
, "local"),
261 IFSET(RESOLVE_CLASS_ALIAS
, "alias"),
262 IFSET(RESOLVE_CLASS_VIRTUAL
, "virtual"),
263 IFSET(RESOLVE_CLASS_RELAY
, "relay"),
264 IFSET(RESOLVE_CLASS_DEFAULT
, "default"));
265 /* Server-requested disconnect. */
266 if (server_flags
!= 0)
267 clnt_stream_recover(rewrite_clnt_stream
);
268 if (STR(reply
->transport
)[0] == 0)
269 msg_warn("%s: null transport result for: <%s>", myname
, addr
);
270 else if (STR(reply
->recipient
)[0] == 0 && *addr
!= 0)
271 msg_warn("%s: null recipient result for: <%s>", myname
, addr
);
275 sleep(1); /* XXX make configurable */
276 clnt_stream_recover(rewrite_clnt_stream
);
282 vstring_strcpy(last_class
, class);
283 vstring_strcpy(last_sender
, sender
);
284 vstring_strcpy(last_addr
, addr
);
285 vstring_strcpy(last_reply
.transport
, STR(reply
->transport
));
286 vstring_strcpy(last_reply
.nexthop
, STR(reply
->nexthop
));
287 vstring_strcpy(last_reply
.recipient
, STR(reply
->recipient
));
288 last_reply
.flags
= reply
->flags
;
289 last_expire
= time((time_t *) 0) + 30; /* XXX make configurable */
292 /* resolve_clnt_free - destroy reply */
294 void resolve_clnt_free(RESOLVE_REPLY
*reply
)
296 reply
->transport
= vstring_free(reply
->transport
);
297 reply
->nexthop
= vstring_free(reply
->nexthop
);
298 reply
->recipient
= vstring_free(reply
->recipient
);
304 #include <msg_vstream.h>
305 #include <vstring_vstream.h>
306 #include <split_at.h>
307 #include <mail_conf.h>
309 static NORETURN
usage(char *myname
)
311 msg_fatal("usage: %s [-v] [address...]", myname
);
314 static void resolve(char *class, char *addr
, RESOLVE_REPLY
*reply
)
316 struct RESOLVE_FLAG_TABLE
{
320 struct RESOLVE_FLAG_TABLE resolve_flag_table
[] = {
321 RESOLVE_FLAG_FINAL
, "FLAG_FINAL",
322 RESOLVE_FLAG_ROUTED
, "FLAG_ROUTED",
323 RESOLVE_FLAG_ERROR
, "FLAG_ERROR",
324 RESOLVE_FLAG_FAIL
, "FLAG_FAIL",
325 RESOLVE_CLASS_LOCAL
, "CLASS_LOCAL",
326 RESOLVE_CLASS_ALIAS
, "CLASS_ALIAS",
327 RESOLVE_CLASS_VIRTUAL
, "CLASS_VIRTUAL",
328 RESOLVE_CLASS_RELAY
, "CLASS_RELAY",
329 RESOLVE_CLASS_DEFAULT
, "CLASS_DEFAULT",
332 struct RESOLVE_FLAG_TABLE
*fp
;
334 resolve_clnt(class, RESOLVE_NULL_FROM
, addr
, reply
);
335 if (reply
->flags
& RESOLVE_FLAG_FAIL
) {
336 vstream_printf("request failed\n");
338 vstream_printf("%-10s %s\n", "class", class);
339 vstream_printf("%-10s %s\n", "address", addr
);
340 vstream_printf("%-10s %s\n", "transport", STR(reply
->transport
));
341 vstream_printf("%-10s %s\n", "nexthop", *STR(reply
->nexthop
) ?
342 STR(reply
->nexthop
) : "[none]");
343 vstream_printf("%-10s %s\n", "recipient", STR(reply
->recipient
));
344 vstream_printf("%-10s ", "flags");
345 for (fp
= resolve_flag_table
; fp
->name
; fp
++) {
346 if (reply
->flags
& fp
->flag
) {
347 vstream_printf("%s ", fp
->name
);
348 reply
->flags
&= ~fp
->flag
;
351 if (reply
->flags
!= 0)
352 vstream_printf("Unknown flag 0x%x", reply
->flags
);
353 vstream_printf("\n\n");
354 vstream_fflush(VSTREAM_OUT
);
358 int main(int argc
, char **argv
)
364 msg_vstream_init(argv
[0], VSTREAM_ERR
);
367 msg_info("using config files in %s", var_config_dir
);
368 if (chdir(var_queue_dir
) < 0)
369 msg_fatal("chdir %s: %m", var_queue_dir
);
371 while ((ch
= GETOPT(argc
, argv
, "v")) > 0) {
380 resolve_clnt_init(&reply
);
383 while (argv
[optind
] && argv
[optind
+ 1]) {
384 resolve(argv
[optind
], argv
[optind
+ 1], &reply
);
388 VSTRING
*buffer
= vstring_alloc(1);
390 while (vstring_fgets_nonl(buffer
, VSTREAM_IN
)) {
391 if ((addr
= split_at(STR(buffer
), ' ')) == 0 || *STR(buffer
) == 0)
392 msg_fatal("need as input: class address");
393 resolve(STR(buffer
), addr
, &reply
);
395 vstring_free(buffer
);
397 resolve_clnt_free(&reply
);