1 /*        $NetBSD: deliver_pass.c,v 1.4 2025/02/25 19:15:45 christos Exp $      */
2 
3 /*++
4 /* NAME
5 /*        deliver_pass 3
6 /* SUMMARY
7 /*        deliver request pass_through
8 /* SYNOPSIS
9 /*        #include <deliver_request.h>
10 /*
11 /*        int       deliver_pass(class, service, request, recipient)
12 /*        const char *class;
13 /*        const char *service;
14 /*        DELIVER_REQUEST *request;
15 /*        RECIPIENT *recipient;
16 /*
17 /*        int       deliver_pass_all(class, service, request)
18 /*        const char *class;
19 /*        const char *service;
20 /*        DELIVER_REQUEST *request;
21 /* DESCRIPTION
22 /*        This module implements the client side of the `queue manager
23 /*        to delivery agent' protocol, passing one recipient on from
24 /*        one delivery agent to another.
25 /*
26 /*        deliver_pass() delegates delivery of the named recipient.
27 /*
28 /*        deliver_pass_all() delegates an entire delivery request.
29 /*
30 /*        Arguments:
31 /* .IP class
32 /*        Destination delivery agent service class
33 /* .IP service
34 /*        String of the form \fItransport\fR:\fInexthop\fR. Either transport
35 /*        or nexthop are optional. For details see the transport map manual page.
36 /* .IP request
37 /*        Delivery request with queue file information.
38 /* .IP recipient
39 /*        Recipient information. See recipient_list(3).
40 /* DIAGNOSTICS
41 /* LICENSE
42 /* .ad
43 /* .fi
44 /*        The Secure Mailer license must be distributed with this software.
45 /* BUGS
46 /*        One recipient at a time; this is OK for mailbox deliveries.
47 /*
48 /*        Hop status information cannot be passed back.
49 /* AUTHOR(S)
50 /*        Wietse Venema
51 /*        IBM T.J. Watson Research
52 /*        P.O. Box 704
53 /*        Yorktown Heights, NY 10598, USA
54 /*
55 /*        Wietse Venema
56 /*        Google, Inc.
57 /*        111 8th Avenue
58 /*        New York, NY 10011, USA
59 /*
60 /*        Wietse Venema
61 /*        porcupine.org
62 /*--*/
63 
64 /* System library. */
65 
66 #include <sys_defs.h>
67 
68 /* Utility library. */
69 
70 #include <msg.h>
71 #include <vstring.h>
72 #include <vstream.h>
73 #include <split_at.h>
74 #include <mymalloc.h>
75 
76 /* Global library. */
77 
78 #include <mail_params.h>
79 #include <deliver_pass.h>
80 #include <dsb_scan.h>
81 #include <defer.h>
82 #include <rcpt_print.h>
83 #include <info_log_addr_form.h>
84 
85 #define DELIVER_PASS_DEFER    1
86 #define DELIVER_PASS_UNKNOWN  2
87 
88 /* deliver_pass_initial_reply - retrieve initial delivery process response */
89 
deliver_pass_initial_reply(VSTREAM * stream)90 static int deliver_pass_initial_reply(VSTREAM *stream)
91 {
92     if (attr_scan(stream, ATTR_FLAG_STRICT,
93                       RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_DELIVER),
94                       ATTR_TYPE_END) != 0) {
95           msg_warn("%s: malformed response", VSTREAM_PATH(stream));
96           return (-1);
97     }
98     return (0);
99 }
100 
101 /* deliver_pass_send_request - send delivery request to delivery process */
102 
deliver_pass_send_request(VSTREAM * stream,DELIVER_REQUEST * request,const char * nexthop,RECIPIENT * rcpt)103 static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
104                                                      const char *nexthop,
105                                                      RECIPIENT *rcpt)
106 {
107     int     stat;
108 
109     attr_print(stream, ATTR_FLAG_NONE,
110                  SEND_ATTR_INT(MAIL_ATTR_FLAGS, request->flags),
111                  SEND_ATTR_STR(MAIL_ATTR_QUEUE, request->queue_name),
112                  SEND_ATTR_STR(MAIL_ATTR_QUEUEID, request->queue_id),
113                  SEND_ATTR_LONG(MAIL_ATTR_OFFSET, request->data_offset),
114                  SEND_ATTR_LONG(MAIL_ATTR_SIZE, request->data_size),
115                  SEND_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop),
116                  SEND_ATTR_STR(MAIL_ATTR_ENCODING, request->encoding),
117                  SEND_ATTR_INT(MAIL_ATTR_SENDOPTS, request->sendopts),
118                  SEND_ATTR_STR(MAIL_ATTR_SENDER, request->sender),
119                  SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, request->dsn_envid),
120                  SEND_ATTR_INT(MAIL_ATTR_DSN_RET, request->dsn_ret),
121           SEND_ATTR_FUNC(msg_stats_print, (const void *) &request->msg_stats),
122     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
123                SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, request->client_name),
124                SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr),
125                SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, request->client_port),
126                SEND_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, request->client_proto),
127                  SEND_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, request->client_helo),
128     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
129                  SEND_ATTR_STR(MAIL_ATTR_SASL_METHOD, request->sasl_method),
130                SEND_ATTR_STR(MAIL_ATTR_SASL_USERNAME, request->sasl_username),
131                  SEND_ATTR_STR(MAIL_ATTR_SASL_SENDER, request->sasl_sender),
132     /* XXX Ditto if we want to pass TLS certificate info. */
133                  SEND_ATTR_STR(MAIL_ATTR_LOG_IDENT, request->log_ident),
134                SEND_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, request->rewrite_context),
135                  SEND_ATTR_INT(MAIL_ATTR_RCPT_COUNT, 1),
136                  ATTR_TYPE_END);
137     attr_print(stream, ATTR_FLAG_NONE,
138                  SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
139                  ATTR_TYPE_END);
140 
141     if (vstream_fflush(stream)) {
142           msg_warn("%s: bad write: %m", VSTREAM_PATH(stream));
143           stat = -1;
144     } else {
145           stat = 0;
146     }
147     return (stat);
148 }
149 
150 /* deliver_pass_final_reply - retrieve final delivery status response */
151 
deliver_pass_final_reply(VSTREAM * stream,DSN_BUF * dsb)152 static int deliver_pass_final_reply(VSTREAM *stream, DSN_BUF *dsb)
153 {
154     int     stat;
155 
156     if (attr_scan(stream, ATTR_FLAG_STRICT,
157                       RECV_ATTR_FUNC(dsb_scan, (void *) dsb),
158                       RECV_ATTR_INT(MAIL_ATTR_STATUS, &stat),
159                       ATTR_TYPE_END) != 2) {
160           msg_warn("%s: malformed response", VSTREAM_PATH(stream));
161           return (DELIVER_PASS_UNKNOWN);
162     } else {
163           return (stat ? DELIVER_PASS_DEFER : 0);
164     }
165 }
166 
167 /* deliver_pass - deliver one per-site queue entry */
168 
deliver_pass(const char * class,const char * service,DELIVER_REQUEST * request,RECIPIENT * rcpt)169 int     deliver_pass(const char *class, const char *service,
170                                  DELIVER_REQUEST *request,
171                                  RECIPIENT *rcpt)
172 {
173     VSTREAM *stream;
174     DSN_BUF *dsb;
175     DSN     dsn;
176     int     status;
177     char   *saved_service;
178     char   *transport;
179     char   *nexthop;
180 
181     /*
182      * Parse service into transport:nexthop form, and allow for omission of
183      * optional fields
184      */
185     transport = saved_service = mystrdup(service);
186     if ((nexthop = split_at(saved_service, ':')) == 0 || *nexthop == 0)
187           nexthop = request->nexthop;
188     if (*transport == 0)
189           msg_fatal("missing transport name in \"%s\"", service);
190 
191     /*
192      * Initialize.
193      */
194     msg_info("%s: passing <%s> to transport=%s",
195                request->queue_id, info_log_addr_form_recipient(rcpt->address),
196                transport);
197     stream = mail_connect_wait(class, transport);
198     dsb = dsb_create();
199 
200     /*
201      * Get the delivery process initial response. Send the queue file info
202      * and recipient info to the delivery process. Retrieve the delivery
203      * agent status report. The numerical status code indicates if delivery
204      * should be tried again. The reason text is sent only when a destination
205      * should be avoided for a while, so that the queue manager can log why
206      * it does not even try to schedule delivery to the affected recipients.
207      * XXX Can't pass back hop status info because the problem is with a
208      * different transport.
209      */
210     if (deliver_pass_initial_reply(stream) != 0
211           || deliver_pass_send_request(stream, request, nexthop, rcpt) != 0) {
212           (void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable");
213           status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
214                                     request->queue_id, &request->msg_stats,
215                                     rcpt, "none", &dsn);
216     } else if ((status = deliver_pass_final_reply(stream, dsb))
217                  == DELIVER_PASS_UNKNOWN) {
218           (void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error");
219           status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
220                                     request->queue_id, &request->msg_stats,
221                                     rcpt, "none", &dsn);
222     }
223 
224     /*
225      * Clean up.
226      */
227     vstream_fclose(stream);
228     dsb_free(dsb);
229     myfree(saved_service);
230 
231     return (status);
232 }
233 
234 /* deliver_pass_all - pass entire delivery request */
235 
deliver_pass_all(const char * class,const char * service,DELIVER_REQUEST * request)236 int     deliver_pass_all(const char *class, const char *service,
237                                        DELIVER_REQUEST *request)
238 {
239     RECIPIENT_LIST *list;
240     RECIPIENT *rcpt;
241     int     status = 0;
242 
243     /*
244      * XXX We should find out if the target transport can handle
245      * multi-recipient requests. Unfortunately such code is hard to test,
246      * rarely used, and therefore will be buggy.
247      */
248     list = &request->rcpt_list;
249     for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
250           status |= deliver_pass(class, service, request, rcpt);
251     return (status);
252 }
253