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