Home | History | Annotate | Line # | Download | only in global
      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 
    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 
    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 
    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 
    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 
    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 
    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 
    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