7 /* mail delivery request protocol, server side
9 /* #include <deliver_request.h>
11 /* typedef struct DELIVER_REQUEST {
22 /* MSG_STATS msg_stats;
23 /* RECIPIENT_LIST rcpt_list;
28 /* char *client_proto;
31 /* char *sasl_username;
33 /* char *rewrite_context;
39 /* DELIVER_REQUEST *deliver_request_read(stream)
42 /* void deliver_request_done(stream, request, status)
44 /* DELIVER_REQUEST *request;
47 /* This module implements the delivery agent side of the `queue manager
48 /* to delivery agent' protocol. In this game, the queue manager is
49 /* the client, while the delivery agent is the server.
51 /* deliver_request_read() reads a client message delivery request,
52 /* opens the queue file, and acquires a shared lock.
53 /* A null result means that the client sent bad information or that
54 /* it went away unexpectedly.
56 /* The \fBflags\fR structure member is the bit-wise OR of zero or more
58 /* .IP \fBDEL_REQ_FLAG_SUCCESS\fR
59 /* Delete successful recipients from the queue file.
61 /* Note: currently, this also controls whether bounced recipients
64 /* Note: the deliver_completed() function ignores this request
65 /* when the recipient queue file offset is -1.
66 /* .IP \fBDEL_REQ_FLAG_BOUNCE\fR
67 /* Delete bounced recipients from the queue file. Currently,
68 /* this flag is non-functional.
70 /* The \fBDEL_REQ_FLAG_DEFLT\fR constant provides a convenient shorthand
71 /* for the most common case: delete successful and bounced recipients.
73 /* The \fIhop_status\fR member must be updated by the caller
74 /* when all delivery to the destination in \fInexthop\fR should
75 /* be deferred. This member is passed to to dsn_free().
77 /* deliver_request_done() reports the delivery status back to the
78 /* client, including the optional \fIhop_status\fR etc. information,
79 /* closes the queue file,
80 /* and destroys the DELIVER_REQUEST structure. The result is
81 /* non-zero when the status could not be reported to the client.
83 /* Warnings: bad data sent by the client. Fatal errors: out of
84 /* memory, queue file open errors.
86 /* attr_scan(3) low-level intra-mail input routines
90 /* The Secure Mailer license must be distributed with this software.
93 /* IBM T.J. Watson Research
95 /* Yorktown Heights, NY 10598, USA
100 #include <sys_defs.h>
101 #include <sys/stat.h>
106 /* Utility library. */
111 #include <mymalloc.h>
115 /* Global library. */
117 #include "mail_queue.h"
118 #include "mail_proto.h"
119 #include "mail_open_ok.h"
120 #include "recipient_list.h"
122 #include "dsn_print.h"
123 #include "deliver_request.h"
124 #include "rcpt_buf.h"
126 /* deliver_request_initial - send initial status code */
128 static int deliver_request_initial(VSTREAM
*stream
)
133 * The master processes runs a finite number of delivery agent processes
134 * to handle service requests. Thus, a delivery agent process must send
135 * something to inform the queue manager that it is ready to receive a
136 * delivery request; otherwise the queue manager could block in write().
139 msg_info("deliver_request_initial: send initial status");
140 attr_print(stream
, ATTR_FLAG_NONE
,
141 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, 0,
143 if ((err
= vstream_fflush(stream
)) != 0)
145 msg_warn("send initial status: %m");
149 /* deliver_request_final - send final delivery request status */
151 static int deliver_request_final(VSTREAM
*stream
, DELIVER_REQUEST
*request
,
157 /* XXX This DSN structure initialization bypasses integrity checks. */
158 static DSN dummy_dsn
= {"", "", "", "", "", "", ""};
161 * Send the status and the optional reason.
163 if ((hop_status
= request
->hop_status
) == 0)
164 hop_status
= &dummy_dsn
;
166 msg_info("deliver_request_final: send: \"%s\" %d",
167 hop_status
->reason
, status
);
168 attr_print(stream
, ATTR_FLAG_NONE
,
169 ATTR_TYPE_FUNC
, dsn_print
, (void *) hop_status
,
170 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, status
,
172 if ((err
= vstream_fflush(stream
)) != 0)
174 msg_warn("send final status: %m");
177 * With some UNIX systems, stream sockets lose data when you close them
178 * immediately after writing to them. That is not how sockets are
179 * supposed to behave! The workaround is to wait until the receiver
180 * closes the connection. Calling VSTREAM_GETC() has the benefit of using
181 * whatever timeout is specified in the ipc_timeout parameter.
183 (void) VSTREAM_GETC(stream
);
187 /* deliver_request_get - receive message delivery request */
189 static int deliver_request_get(VSTREAM
*stream
, DELIVER_REQUEST
*request
)
191 const char *myname
= "deliver_request_get";
194 static VSTRING
*queue_name
;
195 static VSTRING
*queue_id
;
196 static VSTRING
*nexthop
;
197 static VSTRING
*encoding
;
198 static VSTRING
*address
;
199 static VSTRING
*client_name
;
200 static VSTRING
*client_addr
;
201 static VSTRING
*client_port
;
202 static VSTRING
*client_proto
;
203 static VSTRING
*client_helo
;
204 static VSTRING
*sasl_method
;
205 static VSTRING
*sasl_username
;
206 static VSTRING
*sasl_sender
;
207 static VSTRING
*rewrite_context
;
208 static VSTRING
*dsn_envid
;
209 static RCPT_BUF
*rcpt_buf
;
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.
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 rewrite_context
= vstring_alloc(10);
233 dsn_envid
= vstring_alloc(10);
234 rcpt_buf
= rcpb_create();
238 * Extract the queue file name, data offset, and sender address. Abort
239 * the conversation when they send bad information.
241 if (attr_scan(stream
, ATTR_FLAG_STRICT
,
242 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &request
->flags
,
243 ATTR_TYPE_STR
, MAIL_ATTR_QUEUE
, queue_name
,
244 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, queue_id
,
245 ATTR_TYPE_LONG
, MAIL_ATTR_OFFSET
, &request
->data_offset
,
246 ATTR_TYPE_LONG
, MAIL_ATTR_SIZE
, &request
->data_size
,
247 ATTR_TYPE_STR
, MAIL_ATTR_NEXTHOP
, nexthop
,
248 ATTR_TYPE_STR
, MAIL_ATTR_ENCODING
, encoding
,
249 ATTR_TYPE_STR
, MAIL_ATTR_SENDER
, address
,
250 ATTR_TYPE_STR
, MAIL_ATTR_DSN_ENVID
, dsn_envid
,
251 ATTR_TYPE_INT
, MAIL_ATTR_DSN_RET
, &dsn_ret
,
252 ATTR_TYPE_FUNC
, msg_stats_scan
, (void *) &request
->msg_stats
,
253 /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
254 ATTR_TYPE_STR
, MAIL_ATTR_LOG_CLIENT_NAME
, client_name
,
255 ATTR_TYPE_STR
, MAIL_ATTR_LOG_CLIENT_ADDR
, client_addr
,
256 ATTR_TYPE_STR
, MAIL_ATTR_LOG_CLIENT_PORT
, client_port
,
257 ATTR_TYPE_STR
, MAIL_ATTR_LOG_PROTO_NAME
, client_proto
,
258 ATTR_TYPE_STR
, MAIL_ATTR_LOG_HELO_NAME
, client_helo
,
259 /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
260 ATTR_TYPE_STR
, MAIL_ATTR_SASL_METHOD
, sasl_method
,
261 ATTR_TYPE_STR
, MAIL_ATTR_SASL_USERNAME
, sasl_username
,
262 ATTR_TYPE_STR
, MAIL_ATTR_SASL_SENDER
, sasl_sender
,
263 /* XXX Ditto if we want to pass TLS certificate info. */
264 ATTR_TYPE_STR
, MAIL_ATTR_RWR_CONTEXT
, rewrite_context
,
265 ATTR_TYPE_INT
, MAIL_ATTR_RCPT_COUNT
, &rcpt_count
,
266 ATTR_TYPE_END
) != 21) {
267 msg_warn("%s: error receiving common attributes", myname
);
270 if (mail_open_ok(vstring_str(queue_name
),
271 vstring_str(queue_id
), &st
, &path
) == 0)
274 /* Don't override hand-off time after deliver_pass() delegation. */
275 if (request
->msg_stats
.agent_handoff
.tv_sec
== 0)
276 GETTIMEOFDAY(&request
->msg_stats
.agent_handoff
);
278 request
->queue_name
= mystrdup(vstring_str(queue_name
));
279 request
->queue_id
= mystrdup(vstring_str(queue_id
));
280 request
->nexthop
= mystrdup(vstring_str(nexthop
));
281 request
->encoding
= mystrdup(vstring_str(encoding
));
282 request
->sender
= mystrdup(vstring_str(address
));
283 request
->client_name
= mystrdup(vstring_str(client_name
));
284 request
->client_addr
= mystrdup(vstring_str(client_addr
));
285 request
->client_port
= mystrdup(vstring_str(client_port
));
286 request
->client_proto
= mystrdup(vstring_str(client_proto
));
287 request
->client_helo
= mystrdup(vstring_str(client_helo
));
288 request
->sasl_method
= mystrdup(vstring_str(sasl_method
));
289 request
->sasl_username
= mystrdup(vstring_str(sasl_username
));
290 request
->sasl_sender
= mystrdup(vstring_str(sasl_sender
));
291 request
->rewrite_context
= mystrdup(vstring_str(rewrite_context
));
292 request
->dsn_envid
= mystrdup(vstring_str(dsn_envid
));
293 request
->dsn_ret
= dsn_ret
;
296 * Extract the recipient offset and address list. Skip over any
297 * attributes from the sender that we do not understand.
299 while (rcpt_count
-- > 0) {
300 if (attr_scan(stream
, ATTR_FLAG_STRICT
,
301 ATTR_TYPE_FUNC
, rcpb_scan
, (void *) rcpt_buf
,
302 ATTR_TYPE_END
) != 1) {
303 msg_warn("%s: error receiving recipient attributes", myname
);
306 recipient_list_add(&request
->rcpt_list
, rcpt_buf
->offset
,
307 vstring_str(rcpt_buf
->dsn_orcpt
),
308 rcpt_buf
->dsn_notify
,
309 vstring_str(rcpt_buf
->orig_addr
),
310 vstring_str(rcpt_buf
->address
));
312 if (request
->rcpt_list
.len
<= 0) {
313 msg_warn("%s: no recipients in delivery request for destination %s",
314 request
->queue_id
, request
->nexthop
);
319 * Open the queue file and set a shared lock, in order to prevent
320 * duplicate deliveries when the queue is flushed immediately after queue
323 * The queue manager locks the file exclusively when it enters the active
324 * queue, and releases the lock before starting deliveries from that
325 * file. The queue manager does not lock the file again when reading more
326 * recipients into memory. When the queue manager is restarted, the new
327 * process moves files from the active queue to the incoming queue to cool
328 * off for a while. Delivery agents should therefore never try to open a
329 * file that is locked by a queue manager process.
331 * Opening the queue file can fail for a variety of reasons, such as the
332 * system running out of resources. Instead of throwing away mail, we're
333 * raising a fatal error which forces the mail system to back off, and
336 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
339 mail_queue_open(request
->queue_name
, request
->queue_id
, O_RDWR
, 0);
340 if (request
->fp
== 0) {
342 msg_fatal("open %s %s: %m", request
->queue_name
, request
->queue_id
);
343 msg_warn("open %s %s: %m", request
->queue_name
, request
->queue_id
);
347 msg_info("%s: file %s", myname
, VSTREAM_PATH(request
->fp
));
348 if (myflock(vstream_fileno(request
->fp
), INTERNAL_LOCK
, DELIVER_LOCK_MODE
) < 0)
349 msg_fatal("shared lock %s: %m", VSTREAM_PATH(request
->fp
));
350 close_on_exec(vstream_fileno(request
->fp
), CLOSE_ON_EXEC
);
355 /* deliver_request_alloc - allocate delivery request structure */
357 static DELIVER_REQUEST
*deliver_request_alloc(void)
359 DELIVER_REQUEST
*request
;
361 request
= (DELIVER_REQUEST
*) mymalloc(sizeof(*request
));
363 request
->queue_name
= 0;
364 request
->queue_id
= 0;
365 request
->nexthop
= 0;
366 request
->encoding
= 0;
368 request
->data_offset
= 0;
369 request
->data_size
= 0;
370 recipient_list_init(&request
->rcpt_list
, RCPT_LIST_INIT_STATUS
);
371 request
->hop_status
= 0;
372 request
->client_name
= 0;
373 request
->client_addr
= 0;
374 request
->client_port
= 0;
375 request
->client_proto
= 0;
376 request
->client_helo
= 0;
377 request
->sasl_method
= 0;
378 request
->sasl_username
= 0;
379 request
->sasl_sender
= 0;
380 request
->rewrite_context
= 0;
381 request
->dsn_envid
= 0;
385 /* deliver_request_free - clean up delivery request structure */
387 static void deliver_request_free(DELIVER_REQUEST
*request
)
390 vstream_fclose(request
->fp
);
391 if (request
->queue_name
)
392 myfree(request
->queue_name
);
393 if (request
->queue_id
)
394 myfree(request
->queue_id
);
395 if (request
->nexthop
)
396 myfree(request
->nexthop
);
397 if (request
->encoding
)
398 myfree(request
->encoding
);
400 myfree(request
->sender
);
401 recipient_list_free(&request
->rcpt_list
);
402 if (request
->hop_status
)
403 dsn_free(request
->hop_status
);
404 if (request
->client_name
)
405 myfree(request
->client_name
);
406 if (request
->client_addr
)
407 myfree(request
->client_addr
);
408 if (request
->client_port
)
409 myfree(request
->client_port
);
410 if (request
->client_proto
)
411 myfree(request
->client_proto
);
412 if (request
->client_helo
)
413 myfree(request
->client_helo
);
414 if (request
->sasl_method
)
415 myfree(request
->sasl_method
);
416 if (request
->sasl_username
)
417 myfree(request
->sasl_username
);
418 if (request
->sasl_sender
)
419 myfree(request
->sasl_sender
);
420 if (request
->rewrite_context
)
421 myfree(request
->rewrite_context
);
422 if (request
->dsn_envid
)
423 myfree(request
->dsn_envid
);
424 myfree((char *) request
);
427 /* deliver_request_read - create and read delivery request */
429 DELIVER_REQUEST
*deliver_request_read(VSTREAM
*stream
)
431 DELIVER_REQUEST
*request
;
434 * Tell the queue manager that we are ready for this request.
436 if (deliver_request_initial(stream
) != 0)
440 * Be prepared for the queue manager to change its mind after contacting
441 * us. This can happen when a transport or host goes bad.
443 (void) read_wait(vstream_fileno(stream
), -1);
444 if (peekfd(vstream_fileno(stream
)) <= 0)
448 * Allocate and read the queue manager's delivery request.
450 #define XXX_DEFER_STATUS -1
452 request
= deliver_request_alloc();
453 if (deliver_request_get(stream
, request
) < 0) {
454 deliver_request_done(stream
, request
, XXX_DEFER_STATUS
);
460 /* deliver_request_done - finish delivery request */
462 int deliver_request_done(VSTREAM
*stream
, DELIVER_REQUEST
*request
, int status
)
466 err
= deliver_request_final(stream
, request
, status
);
467 deliver_request_free(request
);