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