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