1 /*        $NetBSD: deliver_request.c,v 1.4 2025/02/25 19:15:45 christos Exp $   */
2 
3 /*++
4 /* NAME
5 /*        deliver_request 3
6 /* SUMMARY
7 /*        mail delivery request protocol, server side
8 /* SYNOPSIS
9 /*        #include <deliver_request.h>
10 /*
11 /*        typedef struct DELIVER_REQUEST {
12 /* .in +5
13 /*                  VSTREAM   *fp;
14 /*                  int       flags;
15 /*                  char      *queue_name;
16 /*                  char      *queue_id;
17 /*                  long      data_offset;
18 /*                  long      data_size;
19 /*                  char      *nexthop;
20 /*                  char      *encoding;
21 /*                  int       sendopts;
22 /*                  char      *sender;
23 /*                  MSG_STATS msg_stats;
24 /*                  RECIPIENT_LIST rcpt_list;
25 /*                  DSN       *hop_status;
26 /*                  char      *client_name;
27 /*                  char      *client_addr;
28 /*                  char      *client_port;
29 /*                  char      *client_proto;
30 /*                  char      *client_helo;
31 /*                  char      *sasl_method;
32 /*                  char      *sasl_username;
33 /*                  char      *sasl_sender;
34 /*                  char      *log_ident;
35 /*                  char      *rewrite_context;
36 /*                  char      *dsn_envid;
37 /*                  int       dsn_ret;
38 /* .in -5
39 /*        } DELIVER_REQUEST;
40 /*
41 /*        DELIVER_REQUEST *deliver_request_read(stream)
42 /*        VSTREAM *stream;
43 /*
44 /*        void      deliver_request_done(stream, request, status)
45 /*        VSTREAM *stream;
46 /*        DELIVER_REQUEST *request;
47 /*        int       status;
48 /* DESCRIPTION
49 /*        This module implements the delivery agent side of the `queue manager
50 /*        to delivery agent' protocol. In this game, the queue manager is
51 /*        the client, while the delivery agent is the server.
52 /*
53 /*        deliver_request_read() reads a client message delivery request,
54 /*        opens the queue file, and acquires a shared lock.
55 /*        A null result means that the client sent bad information or that
56 /*        it went away unexpectedly.
57 /*
58 /*        The \fBflags\fR structure member is the bit-wise OR of zero or more
59 /*        of the following:
60 /* .IP \fBDEL_REQ_FLAG_SUCCESS\fR
61 /*        Delete successful recipients from the queue file.
62 /*
63 /*        Note: currently, this also controls whether bounced recipients
64 /*        are deleted.
65 /*
66 /*        Note: the deliver_completed() function ignores this request
67 /*        when the recipient queue file offset is -1.
68 /* .IP \fBDEL_REQ_FLAG_BOUNCE\fR
69 /*        Delete bounced recipients from the queue file. Currently,
70 /*        this flag is non-functional.
71 /* .PP
72 /*        The \fBDEL_REQ_FLAG_DEFLT\fR constant provides a convenient shorthand
73 /*        for the most common case: delete successful and bounced recipients.
74 /*
75 /*        The \fIhop_status\fR member must be updated by the caller
76 /*        when all delivery to the destination in \fInexthop\fR should
77 /*        be deferred. This member is passed to dsn_free().
78 /*
79 /*        deliver_request_done() reports the delivery status back to the
80 /*        client, including the optional \fIhop_status\fR etc. information,
81 /*        closes the queue file,
82 /*        and destroys the DELIVER_REQUEST structure. The result is
83 /*        non-zero when the status could not be reported to the client.
84 /* DIAGNOSTICS
85 /*        Warnings: bad data sent by the client. Fatal errors: out of
86 /*        memory, queue file open errors.
87 /* SEE ALSO
88 /*        attr_scan(3) low-level intra-mail input routines
89 /* LICENSE
90 /* .ad
91 /* .fi
92 /*        The Secure Mailer license must be distributed with this software.
93 /* AUTHOR(S)
94 /*        Wietse Venema
95 /*        IBM T.J. Watson Research
96 /*        P.O. Box 704
97 /*        Yorktown Heights, NY 10598, USA
98 /*
99 /*        Wietse Venema
100 /*        Google, Inc.
101 /*        111 8th Avenue
102 /*        New York, NY 10011, USA
103 /*
104 /*        Wietse Venema
105 /*        porcupine.org
106 /*--*/
107 
108 /* System library. */
109 
110 #include <sys_defs.h>
111 #include <sys/stat.h>
112 #include <string.h>
113 #include <unistd.h>
114 #include <errno.h>
115 
116 /* Utility library. */
117 
118 #include <msg.h>
119 #include <vstream.h>
120 #include <vstring.h>
121 #include <mymalloc.h>
122 #include <iostuff.h>
123 #include <myflock.h>
124 
125 /* Global library. */
126 
127 #include "mail_queue.h"
128 #include "mail_proto.h"
129 #include "mail_open_ok.h"
130 #include "recipient_list.h"
131 #include "dsn.h"
132 #include "dsn_print.h"
133 #include "deliver_request.h"
134 #include "rcpt_buf.h"
135 
136 /* deliver_request_initial - send initial status code */
137 
deliver_request_initial(VSTREAM * stream)138 static int deliver_request_initial(VSTREAM *stream)
139 {
140     int     err;
141 
142     /*
143      * The master processes runs a finite number of delivery agent processes
144      * to handle service requests. Thus, a delivery agent process must send
145      * something to inform the queue manager that it is ready to receive a
146      * delivery request; otherwise the queue manager could block in write().
147      */
148     if (msg_verbose)
149           msg_info("deliver_request_initial: send initial response");
150     attr_print(stream, ATTR_FLAG_NONE,
151                  SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_DELIVER),
152                  ATTR_TYPE_END);
153     if ((err = vstream_fflush(stream)) != 0)
154           if (msg_verbose)
155               msg_warn("send initial response: %m");
156     return (err);
157 }
158 
159 /* deliver_request_final - send final delivery request status */
160 
deliver_request_final(VSTREAM * stream,DELIVER_REQUEST * request,int status)161 static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
162                                                  int status)
163 {
164     DSN    *hop_status;
165     int     err;
166 
167     /* XXX This DSN structure initialization bypasses integrity checks. */
168     static DSN dummy_dsn = {"", "", "", "", "", "", ""};
169 
170     /*
171      * Send the status and the optional reason.
172      */
173     if ((hop_status = request->hop_status) == 0)
174           hop_status = &dummy_dsn;
175     if (msg_verbose)
176           msg_info("deliver_request_final: send: \"%s\" %d",
177                      hop_status->reason, status);
178     attr_print(stream, ATTR_FLAG_NONE,
179                  SEND_ATTR_FUNC(dsn_print, (const void *) hop_status),
180                  SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
181                  ATTR_TYPE_END);
182     if ((err = vstream_fflush(stream)) != 0)
183           if (msg_verbose)
184               msg_warn("send final status: %m");
185 
186     /*
187      * With some UNIX systems, stream sockets lose data when you close them
188      * immediately after writing to them. That is not how sockets are
189      * supposed to behave! The workaround is to wait until the receiver
190      * closes the connection. Calling VSTREAM_GETC() has the benefit of using
191      * whatever timeout is specified in the ipc_timeout parameter.
192      */
193     (void) VSTREAM_GETC(stream);
194     return (err);
195 }
196 
197 /* deliver_request_get - receive message delivery request */
198 
deliver_request_get(VSTREAM * stream,DELIVER_REQUEST * request)199 static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
200 {
201     const char *myname = "deliver_request_get";
202     const char *path;
203     struct stat st;
204     static VSTRING *queue_name;
205     static VSTRING *queue_id;
206     static VSTRING *nexthop;
207     static VSTRING *encoding;
208     static VSTRING *address;
209     static VSTRING *client_name;
210     static VSTRING *client_addr;
211     static VSTRING *client_port;
212     static VSTRING *client_proto;
213     static VSTRING *client_helo;
214     static VSTRING *sasl_method;
215     static VSTRING *sasl_username;
216     static VSTRING *sasl_sender;
217     static VSTRING *log_ident;
218     static VSTRING *rewrite_context;
219     static VSTRING *dsn_envid;
220     static RCPT_BUF *rcpt_buf;
221     int     rcpt_count;
222     int     sendopts;
223     int     dsn_ret;
224 
225     /*
226      * Initialize. For some reason I wanted to allow for multiple instances
227      * of a deliver_request structure, thus the hoopla with string
228      * initialization and copying.
229      */
230     if (queue_name == 0) {
231           queue_name = vstring_alloc(10);
232           queue_id = vstring_alloc(10);
233           nexthop = vstring_alloc(10);
234           encoding = vstring_alloc(10);
235           address = vstring_alloc(10);
236           client_name = vstring_alloc(10);
237           client_addr = vstring_alloc(10);
238           client_port = vstring_alloc(10);
239           client_proto = vstring_alloc(10);
240           client_helo = vstring_alloc(10);
241           sasl_method = vstring_alloc(10);
242           sasl_username = vstring_alloc(10);
243           sasl_sender = vstring_alloc(10);
244           log_ident = vstring_alloc(10);
245           rewrite_context = vstring_alloc(10);
246           dsn_envid = vstring_alloc(10);
247           rcpt_buf = rcpb_create();
248     }
249 
250     /*
251      * Extract the queue file name, data offset, and sender address. Abort
252      * the conversation when they send bad information.
253      */
254     if (attr_scan(stream, ATTR_FLAG_STRICT,
255                       RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request->flags),
256                       RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
257                       RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
258                       RECV_ATTR_LONG(MAIL_ATTR_OFFSET, &request->data_offset),
259                       RECV_ATTR_LONG(MAIL_ATTR_SIZE, &request->data_size),
260                       RECV_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop),
261                       RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
262                       RECV_ATTR_INT(MAIL_ATTR_SENDOPTS, &sendopts),
263                       RECV_ATTR_STR(MAIL_ATTR_SENDER, address),
264                       RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
265                       RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
266                  RECV_ATTR_FUNC(msg_stats_scan, (void *) &request->msg_stats),
267     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
268                       RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, client_name),
269                       RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, client_addr),
270                       RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, client_port),
271                       RECV_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, client_proto),
272                       RECV_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, client_helo),
273     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
274                       RECV_ATTR_STR(MAIL_ATTR_SASL_METHOD, sasl_method),
275                       RECV_ATTR_STR(MAIL_ATTR_SASL_USERNAME, sasl_username),
276                       RECV_ATTR_STR(MAIL_ATTR_SASL_SENDER, sasl_sender),
277     /* XXX Ditto if we want to pass TLS certificate info. */
278                       RECV_ATTR_STR(MAIL_ATTR_LOG_IDENT, log_ident),
279                       RECV_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, rewrite_context),
280                       RECV_ATTR_INT(MAIL_ATTR_RCPT_COUNT, &rcpt_count),
281                       ATTR_TYPE_END) != 23) {
282           msg_warn("%s: error receiving common attributes", myname);
283           return (-1);
284     }
285     if (mail_open_ok(vstring_str(queue_name),
286                          vstring_str(queue_id), &st, &path) == 0)
287           return (-1);
288 
289     /* Don't override hand-off time after deliver_pass() delegation. */
290     if (request->msg_stats.agent_handoff.tv_sec == 0)
291           GETTIMEOFDAY(&request->msg_stats.agent_handoff);
292 
293     request->queue_name = mystrdup(vstring_str(queue_name));
294     request->queue_id = mystrdup(vstring_str(queue_id));
295     request->nexthop = mystrdup(vstring_str(nexthop));
296     request->encoding = mystrdup(vstring_str(encoding));
297     /* Fix 20140708: dedicated attribute for SMTPUTF8 etc. flags. */
298     request->sendopts = sendopts;
299     request->sender = mystrdup(vstring_str(address));
300     request->client_name = mystrdup(vstring_str(client_name));
301     request->client_addr = mystrdup(vstring_str(client_addr));
302     request->client_port = mystrdup(vstring_str(client_port));
303     request->client_proto = mystrdup(vstring_str(client_proto));
304     request->client_helo = mystrdup(vstring_str(client_helo));
305     request->sasl_method = mystrdup(vstring_str(sasl_method));
306     request->sasl_username = mystrdup(vstring_str(sasl_username));
307     request->sasl_sender = mystrdup(vstring_str(sasl_sender));
308     request->log_ident = mystrdup(vstring_str(log_ident));
309     request->rewrite_context = mystrdup(vstring_str(rewrite_context));
310     request->dsn_envid = mystrdup(vstring_str(dsn_envid));
311     request->dsn_ret = dsn_ret;
312 
313     /*
314      * Extract the recipient offset and address list. Skip over any
315      * attributes from the sender that we do not understand.
316      */
317     while (rcpt_count-- > 0) {
318           if (attr_scan(stream, ATTR_FLAG_STRICT,
319                           RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
320                           ATTR_TYPE_END) != 1) {
321               msg_warn("%s: error receiving recipient attributes", myname);
322               return (-1);
323           }
324           recipient_list_add(&request->rcpt_list, rcpt_buf->offset,
325                                  vstring_str(rcpt_buf->dsn_orcpt),
326                                  rcpt_buf->dsn_notify,
327                                  vstring_str(rcpt_buf->orig_addr),
328                                  vstring_str(rcpt_buf->address));
329     }
330     if (request->rcpt_list.len <= 0) {
331           msg_warn("%s: no recipients in delivery request for destination %s",
332                      request->queue_id, request->nexthop);
333           return (-1);
334     }
335 
336     /*
337      * Open the queue file and set a shared lock, in order to prevent
338      * duplicate deliveries when the queue is flushed immediately after queue
339      * manager restart.
340      *
341      * The queue manager locks the file exclusively when it enters the active
342      * queue, and releases the lock before starting deliveries from that
343      * file. The queue manager does not lock the file again when reading more
344      * recipients into memory. When the queue manager is restarted, the new
345      * process moves files from the active queue to the incoming queue to
346      * cool off for a while. Delivery agents should therefore never try to
347      * open a file that is locked by a queue manager process.
348      *
349      * Opening the queue file can fail for a variety of reasons, such as the
350      * system running out of resources. Instead of throwing away mail, we're
351      * raising a fatal error which forces the mail system to back off, and
352      * retry later.
353      */
354 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
355 
356     request->fp =
357           mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
358     if (request->fp == 0) {
359           if (errno != ENOENT)
360               msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
361           msg_warn("open %s %s: %m", request->queue_name, request->queue_id);
362           return (-1);
363     }
364     if (msg_verbose)
365           msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp));
366     if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0)
367           msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp));
368     close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC);
369 
370     return (0);
371 }
372 
373 /* deliver_request_alloc - allocate delivery request structure */
374 
deliver_request_alloc(void)375 static DELIVER_REQUEST *deliver_request_alloc(void)
376 {
377     DELIVER_REQUEST *request;
378 
379     request = (DELIVER_REQUEST *) mymalloc(sizeof(*request));
380     request->fp = 0;
381     request->queue_name = 0;
382     request->queue_id = 0;
383     request->nexthop = 0;
384     request->encoding = 0;
385     request->sender = 0;
386     request->data_offset = 0;
387     request->data_size = 0;
388     recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS);
389     request->hop_status = 0;
390     request->client_name = 0;
391     request->client_addr = 0;
392     request->client_port = 0;
393     request->client_proto = 0;
394     request->client_helo = 0;
395     request->sasl_method = 0;
396     request->sasl_username = 0;
397     request->sasl_sender = 0;
398     request->log_ident = 0;
399     request->rewrite_context = 0;
400     request->dsn_envid = 0;
401     return (request);
402 }
403 
404 /* deliver_request_free - clean up delivery request structure */
405 
deliver_request_free(DELIVER_REQUEST * request)406 static void deliver_request_free(DELIVER_REQUEST *request)
407 {
408     if (request->fp)
409           vstream_fclose(request->fp);
410     if (request->queue_name)
411           myfree(request->queue_name);
412     if (request->queue_id)
413           myfree(request->queue_id);
414     if (request->nexthop)
415           myfree(request->nexthop);
416     if (request->encoding)
417           myfree(request->encoding);
418     if (request->sender)
419           myfree(request->sender);
420     recipient_list_free(&request->rcpt_list);
421     if (request->hop_status)
422           dsn_free(request->hop_status);
423     if (request->client_name)
424           myfree(request->client_name);
425     if (request->client_addr)
426           myfree(request->client_addr);
427     if (request->client_port)
428           myfree(request->client_port);
429     if (request->client_proto)
430           myfree(request->client_proto);
431     if (request->client_helo)
432           myfree(request->client_helo);
433     if (request->sasl_method)
434           myfree(request->sasl_method);
435     if (request->sasl_username)
436           myfree(request->sasl_username);
437     if (request->sasl_sender)
438           myfree(request->sasl_sender);
439     if (request->log_ident)
440           myfree(request->log_ident);
441     if (request->rewrite_context)
442           myfree(request->rewrite_context);
443     if (request->dsn_envid)
444           myfree(request->dsn_envid);
445     myfree((void *) request);
446 }
447 
448 /* deliver_request_read - create and read delivery request */
449 
deliver_request_read(VSTREAM * stream)450 DELIVER_REQUEST *deliver_request_read(VSTREAM *stream)
451 {
452     DELIVER_REQUEST *request;
453 
454     /*
455      * Tell the queue manager that we are ready for this request.
456      */
457     if (deliver_request_initial(stream) != 0)
458           return (0);
459 
460     /*
461      * Be prepared for the queue manager to change its mind after contacting
462      * us. This can happen when a transport or host goes bad.
463      */
464     (void) read_wait(vstream_fileno(stream), -1);
465     if (peekfd(vstream_fileno(stream)) <= 0)
466           return (0);
467 
468     /*
469      * Allocate and read the queue manager's delivery request.
470      */
471 #define XXX_DEFER_STATUS      -1
472 
473     request = deliver_request_alloc();
474     if (deliver_request_get(stream, request) < 0) {
475           deliver_request_done(stream, request, XXX_DEFER_STATUS);
476           request = 0;
477     }
478     return (request);
479 }
480 
481 /* deliver_request_done - finish delivery request */
482 
deliver_request_done(VSTREAM * stream,DELIVER_REQUEST * request,int status)483 int     deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status)
484 {
485     int     err;
486 
487     err = deliver_request_final(stream, request, status);
488     deliver_request_free(request);
489     return (err);
490 }
491