xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/deliver_request.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: deliver_request.c,v 1.2 2017/02/14 01:16: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 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 
99 /* System library. */
100 
101 #include <sys_defs.h>
102 #include <sys/stat.h>
103 #include <string.h>
104 #include <unistd.h>
105 #include <errno.h>
106 
107 /* Utility library. */
108 
109 #include <msg.h>
110 #include <vstream.h>
111 #include <vstring.h>
112 #include <mymalloc.h>
113 #include <iostuff.h>
114 #include <myflock.h>
115 
116 /* Global library. */
117 
118 #include "mail_queue.h"
119 #include "mail_proto.h"
120 #include "mail_open_ok.h"
121 #include "recipient_list.h"
122 #include "dsn.h"
123 #include "dsn_print.h"
124 #include "deliver_request.h"
125 #include "rcpt_buf.h"
126 
127 /* deliver_request_initial - send initial status code */
128 
129 static int deliver_request_initial(VSTREAM *stream)
130 {
131     int     err;
132 
133     /*
134      * The master processes runs a finite number of delivery agent processes
135      * to handle service requests. Thus, a delivery agent process must send
136      * something to inform the queue manager that it is ready to receive a
137      * delivery request; otherwise the queue manager could block in write().
138      */
139     if (msg_verbose)
140 	msg_info("deliver_request_initial: send initial status");
141     attr_print(stream, ATTR_FLAG_NONE,
142 	       SEND_ATTR_INT(MAIL_ATTR_STATUS, 0),
143 	       ATTR_TYPE_END);
144     if ((err = vstream_fflush(stream)) != 0)
145 	if (msg_verbose)
146 	    msg_warn("send initial status: %m");
147     return (err);
148 }
149 
150 /* deliver_request_final - send final delivery request status */
151 
152 static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
153 				         int status)
154 {
155     DSN    *hop_status;
156     int     err;
157 
158     /* XXX This DSN structure initialization bypasses integrity checks. */
159     static DSN dummy_dsn = {"", "", "", "", "", "", ""};
160 
161     /*
162      * Send the status and the optional reason.
163      */
164     if ((hop_status = request->hop_status) == 0)
165 	hop_status = &dummy_dsn;
166     if (msg_verbose)
167 	msg_info("deliver_request_final: send: \"%s\" %d",
168 		 hop_status->reason, status);
169     attr_print(stream, ATTR_FLAG_NONE,
170 	       SEND_ATTR_FUNC(dsn_print, (void *) hop_status),
171 	       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
172 	       ATTR_TYPE_END);
173     if ((err = vstream_fflush(stream)) != 0)
174 	if (msg_verbose)
175 	    msg_warn("send final status: %m");
176 
177     /*
178      * With some UNIX systems, stream sockets lose data when you close them
179      * immediately after writing to them. That is not how sockets are
180      * supposed to behave! The workaround is to wait until the receiver
181      * closes the connection. Calling VSTREAM_GETC() has the benefit of using
182      * whatever timeout is specified in the ipc_timeout parameter.
183      */
184     (void) VSTREAM_GETC(stream);
185     return (err);
186 }
187 
188 /* deliver_request_get - receive message delivery request */
189 
190 static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
191 {
192     const char *myname = "deliver_request_get";
193     const char *path;
194     struct stat st;
195     static VSTRING *queue_name;
196     static VSTRING *queue_id;
197     static VSTRING *nexthop;
198     static VSTRING *encoding;
199     static VSTRING *address;
200     static VSTRING *client_name;
201     static VSTRING *client_addr;
202     static VSTRING *client_port;
203     static VSTRING *client_proto;
204     static VSTRING *client_helo;
205     static VSTRING *sasl_method;
206     static VSTRING *sasl_username;
207     static VSTRING *sasl_sender;
208     static VSTRING *log_ident;
209     static VSTRING *rewrite_context;
210     static VSTRING *dsn_envid;
211     static RCPT_BUF *rcpt_buf;
212     int     rcpt_count;
213     int     smtputf8;
214     int     dsn_ret;
215 
216     /*
217      * Initialize. For some reason I wanted to allow for multiple instances
218      * of a deliver_request structure, thus the hoopla with string
219      * initialization and copying.
220      */
221     if (queue_name == 0) {
222 	queue_name = vstring_alloc(10);
223 	queue_id = vstring_alloc(10);
224 	nexthop = vstring_alloc(10);
225 	encoding = vstring_alloc(10);
226 	address = vstring_alloc(10);
227 	client_name = vstring_alloc(10);
228 	client_addr = vstring_alloc(10);
229 	client_port = vstring_alloc(10);
230 	client_proto = vstring_alloc(10);
231 	client_helo = vstring_alloc(10);
232 	sasl_method = vstring_alloc(10);
233 	sasl_username = vstring_alloc(10);
234 	sasl_sender = vstring_alloc(10);
235 	log_ident = vstring_alloc(10);
236 	rewrite_context = vstring_alloc(10);
237 	dsn_envid = vstring_alloc(10);
238 	rcpt_buf = rcpb_create();
239     }
240 
241     /*
242      * Extract the queue file name, data offset, and sender address. Abort
243      * the conversation when they send bad information.
244      */
245     if (attr_scan(stream, ATTR_FLAG_STRICT,
246 		  RECV_ATTR_INT(MAIL_ATTR_FLAGS, &request->flags),
247 		  RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
248 		  RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
249 		  RECV_ATTR_LONG(MAIL_ATTR_OFFSET, &request->data_offset),
250 		  RECV_ATTR_LONG(MAIL_ATTR_SIZE, &request->data_size),
251 		  RECV_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop),
252 		  RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
253 		  RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
254 		  RECV_ATTR_STR(MAIL_ATTR_SENDER, address),
255 		  RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
256 		  RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
257 	       RECV_ATTR_FUNC(msg_stats_scan, (void *) &request->msg_stats),
258     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
259 		  RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, client_name),
260 		  RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, client_addr),
261 		  RECV_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, client_port),
262 		  RECV_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, client_proto),
263 		  RECV_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, client_helo),
264     /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
265 		  RECV_ATTR_STR(MAIL_ATTR_SASL_METHOD, sasl_method),
266 		  RECV_ATTR_STR(MAIL_ATTR_SASL_USERNAME, sasl_username),
267 		  RECV_ATTR_STR(MAIL_ATTR_SASL_SENDER, sasl_sender),
268     /* XXX Ditto if we want to pass TLS certificate info. */
269 		  RECV_ATTR_STR(MAIL_ATTR_LOG_IDENT, log_ident),
270 		  RECV_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, rewrite_context),
271 		  RECV_ATTR_INT(MAIL_ATTR_RCPT_COUNT, &rcpt_count),
272 		  ATTR_TYPE_END) != 23) {
273 	msg_warn("%s: error receiving common attributes", myname);
274 	return (-1);
275     }
276     if (mail_open_ok(vstring_str(queue_name),
277 		     vstring_str(queue_id), &st, &path) == 0)
278 	return (-1);
279 
280     /* Don't override hand-off time after deliver_pass() delegation. */
281     if (request->msg_stats.agent_handoff.tv_sec == 0)
282 	GETTIMEOFDAY(&request->msg_stats.agent_handoff);
283 
284     request->queue_name = mystrdup(vstring_str(queue_name));
285     request->queue_id = mystrdup(vstring_str(queue_id));
286     request->nexthop = mystrdup(vstring_str(nexthop));
287     request->encoding = mystrdup(vstring_str(encoding));
288     /* Fix 20140708: dedicated smtputf8 attribute with its own flags. */
289     request->smtputf8 = smtputf8;
290     request->sender = mystrdup(vstring_str(address));
291     request->client_name = mystrdup(vstring_str(client_name));
292     request->client_addr = mystrdup(vstring_str(client_addr));
293     request->client_port = mystrdup(vstring_str(client_port));
294     request->client_proto = mystrdup(vstring_str(client_proto));
295     request->client_helo = mystrdup(vstring_str(client_helo));
296     request->sasl_method = mystrdup(vstring_str(sasl_method));
297     request->sasl_username = mystrdup(vstring_str(sasl_username));
298     request->sasl_sender = mystrdup(vstring_str(sasl_sender));
299     request->log_ident = mystrdup(vstring_str(log_ident));
300     request->rewrite_context = mystrdup(vstring_str(rewrite_context));
301     request->dsn_envid = mystrdup(vstring_str(dsn_envid));
302     request->dsn_ret = dsn_ret;
303 
304     /*
305      * Extract the recipient offset and address list. Skip over any
306      * attributes from the sender that we do not understand.
307      */
308     while (rcpt_count-- > 0) {
309 	if (attr_scan(stream, ATTR_FLAG_STRICT,
310 		      RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
311 		      ATTR_TYPE_END) != 1) {
312 	    msg_warn("%s: error receiving recipient attributes", myname);
313 	    return (-1);
314 	}
315 	recipient_list_add(&request->rcpt_list, rcpt_buf->offset,
316 			   vstring_str(rcpt_buf->dsn_orcpt),
317 			   rcpt_buf->dsn_notify,
318 			   vstring_str(rcpt_buf->orig_addr),
319 			   vstring_str(rcpt_buf->address));
320     }
321     if (request->rcpt_list.len <= 0) {
322 	msg_warn("%s: no recipients in delivery request for destination %s",
323 		 request->queue_id, request->nexthop);
324 	return (-1);
325     }
326 
327     /*
328      * Open the queue file and set a shared lock, in order to prevent
329      * duplicate deliveries when the queue is flushed immediately after queue
330      * manager restart.
331      *
332      * The queue manager locks the file exclusively when it enters the active
333      * queue, and releases the lock before starting deliveries from that
334      * file. The queue manager does not lock the file again when reading more
335      * recipients into memory. When the queue manager is restarted, the new
336      * process moves files from the active queue to the incoming queue to
337      * cool off for a while. Delivery agents should therefore never try to
338      * open a file that is locked by a queue manager process.
339      *
340      * Opening the queue file can fail for a variety of reasons, such as the
341      * system running out of resources. Instead of throwing away mail, we're
342      * raising a fatal error which forces the mail system to back off, and
343      * retry later.
344      */
345 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
346 
347     request->fp =
348 	mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
349     if (request->fp == 0) {
350 	if (errno != ENOENT)
351 	    msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
352 	msg_warn("open %s %s: %m", request->queue_name, request->queue_id);
353 	return (-1);
354     }
355     if (msg_verbose)
356 	msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp));
357     if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0)
358 	msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp));
359     close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC);
360 
361     return (0);
362 }
363 
364 /* deliver_request_alloc - allocate delivery request structure */
365 
366 static DELIVER_REQUEST *deliver_request_alloc(void)
367 {
368     DELIVER_REQUEST *request;
369 
370     request = (DELIVER_REQUEST *) mymalloc(sizeof(*request));
371     request->fp = 0;
372     request->queue_name = 0;
373     request->queue_id = 0;
374     request->nexthop = 0;
375     request->encoding = 0;
376     request->sender = 0;
377     request->data_offset = 0;
378     request->data_size = 0;
379     recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS);
380     request->hop_status = 0;
381     request->client_name = 0;
382     request->client_addr = 0;
383     request->client_port = 0;
384     request->client_proto = 0;
385     request->client_helo = 0;
386     request->sasl_method = 0;
387     request->sasl_username = 0;
388     request->sasl_sender = 0;
389     request->log_ident = 0;
390     request->rewrite_context = 0;
391     request->dsn_envid = 0;
392     return (request);
393 }
394 
395 /* deliver_request_free - clean up delivery request structure */
396 
397 static void deliver_request_free(DELIVER_REQUEST *request)
398 {
399     if (request->fp)
400 	vstream_fclose(request->fp);
401     if (request->queue_name)
402 	myfree(request->queue_name);
403     if (request->queue_id)
404 	myfree(request->queue_id);
405     if (request->nexthop)
406 	myfree(request->nexthop);
407     if (request->encoding)
408 	myfree(request->encoding);
409     if (request->sender)
410 	myfree(request->sender);
411     recipient_list_free(&request->rcpt_list);
412     if (request->hop_status)
413 	dsn_free(request->hop_status);
414     if (request->client_name)
415 	myfree(request->client_name);
416     if (request->client_addr)
417 	myfree(request->client_addr);
418     if (request->client_port)
419 	myfree(request->client_port);
420     if (request->client_proto)
421 	myfree(request->client_proto);
422     if (request->client_helo)
423 	myfree(request->client_helo);
424     if (request->sasl_method)
425 	myfree(request->sasl_method);
426     if (request->sasl_username)
427 	myfree(request->sasl_username);
428     if (request->sasl_sender)
429 	myfree(request->sasl_sender);
430     if (request->log_ident)
431 	myfree(request->log_ident);
432     if (request->rewrite_context)
433 	myfree(request->rewrite_context);
434     if (request->dsn_envid)
435 	myfree(request->dsn_envid);
436     myfree((void *) request);
437 }
438 
439 /* deliver_request_read - create and read delivery request */
440 
441 DELIVER_REQUEST *deliver_request_read(VSTREAM *stream)
442 {
443     DELIVER_REQUEST *request;
444 
445     /*
446      * Tell the queue manager that we are ready for this request.
447      */
448     if (deliver_request_initial(stream) != 0)
449 	return (0);
450 
451     /*
452      * Be prepared for the queue manager to change its mind after contacting
453      * us. This can happen when a transport or host goes bad.
454      */
455     (void) read_wait(vstream_fileno(stream), -1);
456     if (peekfd(vstream_fileno(stream)) <= 0)
457 	return (0);
458 
459     /*
460      * Allocate and read the queue manager's delivery request.
461      */
462 #define XXX_DEFER_STATUS	-1
463 
464     request = deliver_request_alloc();
465     if (deliver_request_get(stream, request) < 0) {
466 	deliver_request_done(stream, request, XXX_DEFER_STATUS);
467 	request = 0;
468     }
469     return (request);
470 }
471 
472 /* deliver_request_done - finish delivery request */
473 
474 int     deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status)
475 {
476     int     err;
477 
478     err = deliver_request_final(stream, request, status);
479     deliver_request_free(request);
480     return (err);
481 }
482