summaryrefslogtreecommitdiffstats
path: root/src/global/deliver_pass.c
blob: 231b07000a17a166c24cd395aafcaf42f9477ea7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/*++
/* NAME
/*	deliver_pass 3
/* SUMMARY
/*	deliver request pass_through
/* SYNOPSIS
/*	#include <deliver_request.h>
/*
/*	int	deliver_pass(class, service, request, recipient)
/*	const char *class;
/*	const char *service;
/*	DELIVER_REQUEST *request;
/*	RECIPIENT *recipient;
/*
/*	int	deliver_pass_all(class, service, request)
/*	const char *class;
/*	const char *service;
/*	DELIVER_REQUEST *request;
/* DESCRIPTION
/*	This module implements the client side of the `queue manager
/*	to delivery agent' protocol, passing one recipient on from
/*	one delivery agent to another.
/*
/*	deliver_pass() delegates delivery of the named recipient.
/*
/*	deliver_pass_all() delegates an entire delivery request.
/*
/*	Arguments:
/* .IP class
/*	Destination delivery agent service class
/* .IP service
/*	String of the form \fItransport\fR:\fInexthop\fR. Either transport
/*	or nexthop are optional. For details see the transport map manual page.
/* .IP request
/*	Delivery request with queue file information.
/* .IP recipient
/*	Recipient information. See recipient_list(3).
/* DIAGNOSTICS
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* BUGS
/*	One recipient at a time; this is OK for mailbox deliveries.
/*
/*	Hop status information cannot be passed back.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>

/* Utility library. */

#include <msg.h>
#include <vstring.h>
#include <vstream.h>
#include <split_at.h>
#include <mymalloc.h>

/* Global library. */

#include <mail_params.h>
#include <deliver_pass.h>
#include <dsb_scan.h>
#include <defer.h>
#include <rcpt_print.h>
#include <info_log_addr_form.h>

#define DELIVER_PASS_DEFER	1
#define DELIVER_PASS_UNKNOWN	2

/* deliver_pass_initial_reply - retrieve initial delivery process response */

static int deliver_pass_initial_reply(VSTREAM *stream)
{
    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_DELIVER),
		  ATTR_TYPE_END) != 0) {
	msg_warn("%s: malformed response", VSTREAM_PATH(stream));
	return (-1);
    }
    return (0);
}

/* deliver_pass_send_request - send delivery request to delivery process */

static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
				             const char *nexthop,
				             RECIPIENT *rcpt)
{
    int     stat;

    attr_print(stream, ATTR_FLAG_NONE,
	       SEND_ATTR_INT(MAIL_ATTR_FLAGS, request->flags),
	       SEND_ATTR_STR(MAIL_ATTR_QUEUE, request->queue_name),
	       SEND_ATTR_STR(MAIL_ATTR_QUEUEID, request->queue_id),
	       SEND_ATTR_LONG(MAIL_ATTR_OFFSET, request->data_offset),
	       SEND_ATTR_LONG(MAIL_ATTR_SIZE, request->data_size),
	       SEND_ATTR_STR(MAIL_ATTR_NEXTHOP, nexthop),
	       SEND_ATTR_STR(MAIL_ATTR_ENCODING, request->encoding),
	       SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, request->smtputf8),
	       SEND_ATTR_STR(MAIL_ATTR_SENDER, request->sender),
	       SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, request->dsn_envid),
	       SEND_ATTR_INT(MAIL_ATTR_DSN_RET, request->dsn_ret),
	SEND_ATTR_FUNC(msg_stats_print, (const void *) &request->msg_stats),
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
	     SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_NAME, request->client_name),
	     SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr),
	     SEND_ATTR_STR(MAIL_ATTR_LOG_CLIENT_PORT, request->client_port),
	     SEND_ATTR_STR(MAIL_ATTR_LOG_PROTO_NAME, request->client_proto),
	       SEND_ATTR_STR(MAIL_ATTR_LOG_HELO_NAME, request->client_helo),
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
	       SEND_ATTR_STR(MAIL_ATTR_SASL_METHOD, request->sasl_method),
	     SEND_ATTR_STR(MAIL_ATTR_SASL_USERNAME, request->sasl_username),
	       SEND_ATTR_STR(MAIL_ATTR_SASL_SENDER, request->sasl_sender),
    /* XXX Ditto if we want to pass TLS certificate info. */
	       SEND_ATTR_STR(MAIL_ATTR_LOG_IDENT, request->log_ident),
	     SEND_ATTR_STR(MAIL_ATTR_RWR_CONTEXT, request->rewrite_context),
	       SEND_ATTR_INT(MAIL_ATTR_RCPT_COUNT, 1),
	       ATTR_TYPE_END);
    attr_print(stream, ATTR_FLAG_NONE,
	       SEND_ATTR_FUNC(rcpt_print, (const void *) rcpt),
	       ATTR_TYPE_END);

    if (vstream_fflush(stream)) {
	msg_warn("%s: bad write: %m", VSTREAM_PATH(stream));
	stat = -1;
    } else {
	stat = 0;
    }
    return (stat);
}

/* deliver_pass_final_reply - retrieve final delivery status response */

static int deliver_pass_final_reply(VSTREAM *stream, DSN_BUF *dsb)
{
    int     stat;

    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  RECV_ATTR_FUNC(dsb_scan, (void *) dsb),
		  RECV_ATTR_INT(MAIL_ATTR_STATUS, &stat),
		  ATTR_TYPE_END) != 2) {
	msg_warn("%s: malformed response", VSTREAM_PATH(stream));
	return (DELIVER_PASS_UNKNOWN);
    } else {
	return (stat ? DELIVER_PASS_DEFER : 0);
    }
}

/* deliver_pass - deliver one per-site queue entry */

int     deliver_pass(const char *class, const char *service,
		             DELIVER_REQUEST *request,
		             RECIPIENT *rcpt)
{
    VSTREAM *stream;
    DSN_BUF *dsb;
    DSN     dsn;
    int     status;
    char   *saved_service;
    char   *transport;
    char   *nexthop;

    /*
     * Parse service into transport:nexthop form, and allow for omission of
     * optional fields
     */
    transport = saved_service = mystrdup(service);
    if ((nexthop = split_at(saved_service, ':')) == 0 || *nexthop == 0)
	nexthop = request->nexthop;
    if (*transport == 0)
	msg_fatal("missing transport name in \"%s\"", service);

    /*
     * Initialize.
     */
    msg_info("%s: passing <%s> to transport=%s",
	     request->queue_id, info_log_addr_form_recipient(rcpt->address),
	     transport);
    stream = mail_connect_wait(class, transport);
    dsb = dsb_create();

    /*
     * Get the delivery process initial response. Send the queue file info
     * and recipient info to the delivery process. Retrieve the delivery
     * agent status report. The numerical status code indicates if delivery
     * should be tried again. The reason text is sent only when a destination
     * should be avoided for a while, so that the queue manager can log why
     * it does not even try to schedule delivery to the affected recipients.
     * XXX Can't pass back hop status info because the problem is with a
     * different transport.
     */
    if (deliver_pass_initial_reply(stream) != 0
	|| deliver_pass_send_request(stream, request, nexthop, rcpt) != 0) {
	(void) DSN_SIMPLE(&dsn, "4.3.0", "mail transport unavailable");
	status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
			      request->queue_id, &request->msg_stats,
			      rcpt, "none", &dsn);
    } else if ((status = deliver_pass_final_reply(stream, dsb))
	       == DELIVER_PASS_UNKNOWN) {
	(void) DSN_SIMPLE(&dsn, "4.3.0", "unknown mail transport error");
	status = defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
			      request->queue_id, &request->msg_stats,
			      rcpt, "none", &dsn);
    }

    /*
     * Clean up.
     */
    vstream_fclose(stream);
    dsb_free(dsb);
    myfree(saved_service);

    return (status);
}

/* deliver_pass_all - pass entire delivery request */

int     deliver_pass_all(const char *class, const char *service,
			         DELIVER_REQUEST *request)
{
    RECIPIENT_LIST *list;
    RECIPIENT *rcpt;
    int     status = 0;

    /*
     * XXX We should find out if the target transport can handle
     * multi-recipient requests. Unfortunately such code is hard to test,
     * rarely used, and therefore will be buggy.
     */
    list = &request->rcpt_list;
    for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
	status |= deliver_pass(class, service, request, rcpt);
    return (status);
}