Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / deliver_request.c
blob4b866a5dc8d31b0bf7e4cba97db2d5be3b5ccab0
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* deliver_request 3
6 /* SUMMARY
7 /* mail delivery request protocol, server side
8 /* SYNOPSIS
9 /* #include <deliver_request.h>
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 *rewrite_context;
34 /* char *dsn_envid;
35 /* int dsn_ret;
36 /* .in -5
37 /* } DELIVER_REQUEST;
39 /* DELIVER_REQUEST *deliver_request_read(stream)
40 /* VSTREAM *stream;
42 /* void deliver_request_done(stream, request, status)
43 /* VSTREAM *stream;
44 /* DELIVER_REQUEST *request;
45 /* int status;
46 /* DESCRIPTION
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
57 /* of the following:
58 /* .IP \fBDEL_REQ_FLAG_SUCCESS\fR
59 /* Delete successful recipients from the queue file.
61 /* Note: currently, this also controls whether bounced recipients
62 /* are deleted.
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.
69 /* .PP
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.
82 /* DIAGNOSTICS
83 /* Warnings: bad data sent by the client. Fatal errors: out of
84 /* memory, queue file open errors.
85 /* SEE ALSO
86 /* attr_scan(3) low-level intra-mail input routines
87 /* LICENSE
88 /* .ad
89 /* .fi
90 /* The Secure Mailer license must be distributed with this software.
91 /* AUTHOR(S)
92 /* Wietse Venema
93 /* IBM T.J. Watson Research
94 /* P.O. Box 704
95 /* Yorktown Heights, NY 10598, USA
96 /*--*/
98 /* System library. */
100 #include <sys_defs.h>
101 #include <sys/stat.h>
102 #include <string.h>
103 #include <unistd.h>
104 #include <errno.h>
106 /* Utility library. */
108 #include <msg.h>
109 #include <vstream.h>
110 #include <vstring.h>
111 #include <mymalloc.h>
112 #include <iostuff.h>
113 #include <myflock.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"
121 #include "dsn.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)
130 int err;
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().
138 if (msg_verbose)
139 msg_info("deliver_request_initial: send initial status");
140 attr_print(stream, ATTR_FLAG_NONE,
141 ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
142 ATTR_TYPE_END);
143 if ((err = vstream_fflush(stream)) != 0)
144 if (msg_verbose)
145 msg_warn("send initial status: %m");
146 return (err);
149 /* deliver_request_final - send final delivery request status */
151 static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
152 int status)
154 DSN *hop_status;
155 int err;
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;
165 if (msg_verbose)
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,
171 ATTR_TYPE_END);
172 if ((err = vstream_fflush(stream)) != 0)
173 if (msg_verbose)
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);
184 return (err);
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";
192 const char *path;
193 struct stat st;
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;
210 int rcpt_count;
211 int dsn_ret;
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);
268 return (-1);
270 if (mail_open_ok(vstring_str(queue_name),
271 vstring_str(queue_id), &st, &path) == 0)
272 return (-1);
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);
304 return (-1);
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);
315 return (-1);
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
321 * manager restart.
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
334 * retry later.
336 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
338 request->fp =
339 mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0);
340 if (request->fp == 0) {
341 if (errno != ENOENT)
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);
344 return (-1);
346 if (msg_verbose)
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);
352 return (0);
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));
362 request->fp = 0;
363 request->queue_name = 0;
364 request->queue_id = 0;
365 request->nexthop = 0;
366 request->encoding = 0;
367 request->sender = 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;
382 return (request);
385 /* deliver_request_free - clean up delivery request structure */
387 static void deliver_request_free(DELIVER_REQUEST *request)
389 if (request->fp)
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);
399 if (request->sender)
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)
437 return (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)
445 return (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);
455 request = 0;
457 return (request);
460 /* deliver_request_done - finish delivery request */
462 int deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int status)
464 int err;
466 err = deliver_request_final(stream, request, status);
467 deliver_request_free(request);
468 return (err);