1/*	$NetBSD$	*/
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
129static 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	       ATTR_TYPE_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
152static 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	       ATTR_TYPE_FUNC, dsn_print, (void *) hop_status,
171	       ATTR_TYPE_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
190static 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     dsn_ret;
214
215    /*
216     * Initialize. For some reason I wanted to allow for multiple instances
217     * of a deliver_request structure, thus the hoopla with string
218     * initialization and copying.
219     */
220    if (queue_name == 0) {
221	queue_name = vstring_alloc(10);
222	queue_id = vstring_alloc(10);
223	nexthop = vstring_alloc(10);
224	encoding = vstring_alloc(10);
225	address = vstring_alloc(10);
226	client_name = vstring_alloc(10);
227	client_addr = vstring_alloc(10);
228	client_port = vstring_alloc(10);
229	client_proto = vstring_alloc(10);
230	client_helo = vstring_alloc(10);
231	sasl_method = vstring_alloc(10);
232	sasl_username = vstring_alloc(10);
233	sasl_sender = vstring_alloc(10);
234	log_ident = vstring_alloc(10);
235	rewrite_context = vstring_alloc(10);
236	dsn_envid = vstring_alloc(10);
237	rcpt_buf = rcpb_create();
238    }
239
240    /*
241     * Extract the queue file name, data offset, and sender address. Abort
242     * the conversation when they send bad information.
243     */
244    if (attr_scan(stream, ATTR_FLAG_STRICT,
245		  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request->flags,
246		  ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
247		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
248		  ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &request->data_offset,
249		  ATTR_TYPE_LONG, MAIL_ATTR_SIZE, &request->data_size,
250		  ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
251		  ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
252		  ATTR_TYPE_STR, MAIL_ATTR_SENDER, address,
253		  ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
254		  ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
255	       ATTR_TYPE_FUNC, msg_stats_scan, (void *) &request->msg_stats,
256    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
257		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, client_name,
258		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, client_addr,
259		  ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, client_port,
260		  ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, client_proto,
261		  ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, client_helo,
262    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
263		  ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, sasl_method,
264		  ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username,
265		  ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender,
266    /* XXX Ditto if we want to pass TLS certificate info. */
267		  ATTR_TYPE_STR, MAIL_ATTR_LOG_IDENT, log_ident,
268		  ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, rewrite_context,
269		  ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, &rcpt_count,
270		  ATTR_TYPE_END) != 22) {
271	msg_warn("%s: error receiving common attributes", myname);
272	return (-1);
273    }
274    if (mail_open_ok(vstring_str(queue_name),
275		     vstring_str(queue_id), &st, &path) == 0)
276	return (-1);
277
278    /* Don't override hand-off time after deliver_pass() delegation. */
279    if (request->msg_stats.agent_handoff.tv_sec == 0)
280	GETTIMEOFDAY(&request->msg_stats.agent_handoff);
281
282    request->queue_name = mystrdup(vstring_str(queue_name));
283    request->queue_id = mystrdup(vstring_str(queue_id));
284    request->nexthop = mystrdup(vstring_str(nexthop));
285    request->encoding = mystrdup(vstring_str(encoding));
286    request->sender = mystrdup(vstring_str(address));
287    request->client_name = mystrdup(vstring_str(client_name));
288    request->client_addr = mystrdup(vstring_str(client_addr));
289    request->client_port = mystrdup(vstring_str(client_port));
290    request->client_proto = mystrdup(vstring_str(client_proto));
291    request->client_helo = mystrdup(vstring_str(client_helo));
292    request->sasl_method = mystrdup(vstring_str(sasl_method));
293    request->sasl_username = mystrdup(vstring_str(sasl_username));
294    request->sasl_sender = mystrdup(vstring_str(sasl_sender));
295    request->log_ident = mystrdup(vstring_str(log_ident));
296    request->rewrite_context = mystrdup(vstring_str(rewrite_context));
297    request->dsn_envid = mystrdup(vstring_str(dsn_envid));
298    request->dsn_ret = dsn_ret;
299
300    /*
301     * Extract the recipient offset and address list. Skip over any
302     * attributes from the sender that we do not understand.
303     */
304    while (rcpt_count-- > 0) {
305	if (attr_scan(stream, ATTR_FLAG_STRICT,
306		      ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
307		      ATTR_TYPE_END) != 1) {
308	    msg_warn("%s: error receiving recipient attributes", myname);
309	    return (-1);
310	}
311	recipient_list_add(&request->rcpt_list, rcpt_buf->offset,
312			   vstring_str(rcpt_buf->dsn_orcpt),
313			   rcpt_buf->dsn_notify,
314			   vstring_str(rcpt_buf->orig_addr),
315			   vstring_str(rcpt_buf->address));
316    }
317    if (request->rcpt_list.len <= 0) {
318	msg_warn("%s: no recipients in delivery request for destination %s",
319		 request->queue_id, request->nexthop);
320	return (-1);
321    }
322
323    /*
324     * Open the queue file and set a shared lock, in order to prevent
325     * duplicate deliveries when the queue is flushed immediately after queue
326     * manager restart.
327     *
328     * The queue manager locks the file exclusively when it enters the active
329     * queue, and releases the lock before starting deliveries from that
330     * file. The queue manager does not lock the file again when reading more
331     * recipients into memory. When the queue manager is restarted, the new
332     * process moves files from the active queue to the incoming queue to
333     * cool off for a while. Delivery agents should therefore never try to
334     * open a file that is locked by a queue manager process.
335     *
336     * Opening the queue file can fail for a variety of reasons, such as the
337     * system running out of resources. Instead of throwing away mail, we're
338     * raising a fatal error which forces the mail system to back off, and
339     * retry later.
340     */
341#define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
342
343    request->fp =
344	mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
345    if (request->fp == 0) {
346	if (errno != ENOENT)
347	    msg_fatal("open %s %s: %m", request->queue_name, request->queue_id);
348	msg_warn("open %s %s: %m", request->queue_name, request->queue_id);
349	return (-1);
350    }
351    if (msg_verbose)
352	msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp));
353    if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0)
354	msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp));
355    close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC);
356
357    return (0);
358}
359
360/* deliver_request_alloc - allocate delivery request structure */
361
362static DELIVER_REQUEST *deliver_request_alloc(void)
363{
364    DELIVER_REQUEST *request;
365
366    request = (DELIVER_REQUEST *) mymalloc(sizeof(*request));
367    request->fp = 0;
368    request->queue_name = 0;
369    request->queue_id = 0;
370    request->nexthop = 0;
371    request->encoding = 0;
372    request->sender = 0;
373    request->data_offset = 0;
374    request->data_size = 0;
375    recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS);
376    request->hop_status = 0;
377    request->client_name = 0;
378    request->client_addr = 0;
379    request->client_port = 0;
380    request->client_proto = 0;
381    request->client_helo = 0;
382    request->sasl_method = 0;
383    request->sasl_username = 0;
384    request->sasl_sender = 0;
385    request->log_ident = 0;
386    request->rewrite_context = 0;
387    request->dsn_envid = 0;
388    return (request);
389}
390
391/* deliver_request_free - clean up delivery request structure */
392
393static void deliver_request_free(DELIVER_REQUEST *request)
394{
395    if (request->fp)
396	vstream_fclose(request->fp);
397    if (request->queue_name)
398	myfree(request->queue_name);
399    if (request->queue_id)
400	myfree(request->queue_id);
401    if (request->nexthop)
402	myfree(request->nexthop);
403    if (request->encoding)
404	myfree(request->encoding);
405    if (request->sender)
406	myfree(request->sender);
407    recipient_list_free(&request->rcpt_list);
408    if (request->hop_status)
409	dsn_free(request->hop_status);
410    if (request->client_name)
411	myfree(request->client_name);
412    if (request->client_addr)
413	myfree(request->client_addr);
414    if (request->client_port)
415	myfree(request->client_port);
416    if (request->client_proto)
417	myfree(request->client_proto);
418    if (request->client_helo)
419	myfree(request->client_helo);
420    if (request->sasl_method)
421	myfree(request->sasl_method);
422    if (request->sasl_username)
423	myfree(request->sasl_username);
424    if (request->sasl_sender)
425	myfree(request->sasl_sender);
426    if (request->log_ident)
427	myfree(request->log_ident);
428    if (request->rewrite_context)
429	myfree(request->rewrite_context);
430    if (request->dsn_envid)
431	myfree(request->dsn_envid);
432    myfree((char *) request);
433}
434
435/* deliver_request_read - create and read delivery request */
436
437DELIVER_REQUEST *deliver_request_read(VSTREAM *stream)
438{
439    DELIVER_REQUEST *request;
440
441    /*
442     * Tell the queue manager that we are ready for this request.
443     */
444    if (deliver_request_initial(stream) != 0)
445	return (0);
446
447    /*
448     * Be prepared for the queue manager to change its mind after contacting
449     * us. This can happen when a transport or host goes bad.
450     */
451    (void) read_wait(vstream_fileno(stream), -1);
452    if (peekfd(vstream_fileno(stream)) <= 0)
453	return (0);
454
455    /*
456     * Allocate and read the queue manager's delivery request.
457     */
458#define XXX_DEFER_STATUS	-1
459
460    request = deliver_request_alloc();
461    if (deliver_request_get(stream, request) < 0) {
462	deliver_request_done(stream, request, XXX_DEFER_STATUS);
463	request = 0;
464    }
465    return (request);
466}
467
468/* deliver_request_done - finish delivery request */
469
470int     deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status)
471{
472    int     err;
473
474    err = deliver_request_final(stream, request, status);
475    deliver_request_free(request);
476    return (err);
477}
478