cleanup_envelope.c revision 1.3
1/* $NetBSD: cleanup_envelope.c,v 1.3 2010/06/17 18:18:15 tron Exp $ */ 2 3/*++ 4/* NAME 5/* cleanup_envelope 3 6/* SUMMARY 7/* process envelope segment 8/* SYNOPSIS 9/* #include <cleanup.h> 10/* 11/* void cleanup_envelope(state, type, buf, len) 12/* CLEANUP_STATE *state; 13/* int type; 14/* const char *buf; 15/* ssize_t len; 16/* DESCRIPTION 17/* This module processes envelope records and writes the result 18/* to the queue file. It validates the message structure, rewrites 19/* sender/recipient addresses to canonical form, and expands recipients 20/* according to entries in the virtual table. This routine absorbs but 21/* does not emit the envelope to content boundary record. 22/* 23/* Arguments: 24/* .IP state 25/* Queue file and message processing state. This state is updated 26/* as records are processed and as errors happen. 27/* .IP type 28/* Record type. 29/* .IP buf 30/* Record content. 31/* .IP len 32/* Record content length. 33/* LICENSE 34/* .ad 35/* .fi 36/* The Secure Mailer license must be distributed with this software. 37/* AUTHOR(S) 38/* Wietse Venema 39/* IBM T.J. Watson Research 40/* P.O. Box 704 41/* Yorktown Heights, NY 10598, USA 42/*--*/ 43 44/* System library. */ 45 46#include <sys_defs.h> 47#include <string.h> 48#include <stdlib.h> 49#include <ctype.h> 50 51/* Utility library. */ 52 53#include <msg.h> 54#include <vstring.h> 55#include <vstream.h> 56#include <mymalloc.h> 57#include <stringops.h> 58#include <nvtable.h> 59 60/* Global library. */ 61 62#include <record.h> 63#include <rec_type.h> 64#include <cleanup_user.h> 65#include <qmgr_user.h> 66#include <mail_params.h> 67#include <verp_sender.h> 68#include <mail_proto.h> 69#include <dsn_mask.h> 70#include <rec_attr_map.h> 71 72/* Application-specific. */ 73 74#include "cleanup.h" 75 76#define STR vstring_str 77#define STREQ(x,y) (strcmp((x), (y)) == 0) 78 79static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t); 80 81/* cleanup_envelope - initialize message envelope */ 82 83void cleanup_envelope(CLEANUP_STATE *state, int type, 84 const char *str, ssize_t len) 85{ 86 87 /* 88 * The message size and count record goes first, so it can easily be 89 * updated in place. This information takes precedence over any size 90 * estimate provided by the client. It's all in one record, data size 91 * first, for backwards compatibility reasons. 92 */ 93 cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 94 (REC_TYPE_SIZE_CAST1) 0, /* extra offs - content offs */ 95 (REC_TYPE_SIZE_CAST2) 0, /* content offset */ 96 (REC_TYPE_SIZE_CAST3) 0, /* recipient count */ 97 (REC_TYPE_SIZE_CAST4) 0, /* qmgr options */ 98 (REC_TYPE_SIZE_CAST5) 0); /* content length */ 99 100 /* 101 * Pass control to the actual envelope processing routine. 102 */ 103 state->action = cleanup_envelope_process; 104 cleanup_envelope_process(state, type, str, len); 105} 106 107/* cleanup_envelope_process - process one envelope record */ 108 109static void cleanup_envelope_process(CLEANUP_STATE *state, int type, 110 const char *buf, ssize_t len) 111{ 112 const char *myname = "cleanup_envelope_process"; 113 char *attr_name; 114 char *attr_value; 115 const char *error_text; 116 int extra_opts; 117 int junk; 118 int mapped_type = type; 119 const char *mapped_buf = buf; 120 int milter_count; 121 122#ifdef DELAY_ACTION 123 int defer_delay; 124 125#endif 126 127 if (msg_verbose) 128 msg_info("initial envelope %c %.*s", type, (int) len, buf); 129 130 if (type == REC_TYPE_FLGS) { 131 /* Not part of queue file format. */ 132 extra_opts = atoi(buf); 133 if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) 134 msg_warn("%s: ignoring bad extra flags: 0x%x", 135 state->queue_id, extra_opts); 136 else 137 state->flags |= extra_opts; 138 return; 139 } 140#ifdef DELAY_ACTION 141 if (type == REC_TYPE_DELAY) { 142 /* Not part of queue file format. */ 143 defer_delay = atoi(buf); 144 if (defer_delay <= 0) 145 msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); 146 else 147 state->defer_delay = defer_delay; 148 return; 149 } 150#endif 151 152 /* 153 * XXX We instantiate a MILTERS structure even when the filter count is 154 * zero (for example, all filters are in ACCEPT state, or the SMTP server 155 * sends a dummy MILTERS structure without any filters), otherwise the 156 * cleanup server would apply the non_smtpd_milters setting 157 * inappropriately. 158 */ 159 if (type == REC_TYPE_MILT_COUNT) { 160 /* Not part of queue file format. */ 161 if ((milter_count = atoi(buf)) >= 0) 162 cleanup_milter_receive(state, milter_count); 163 return; 164 } 165 166 /* 167 * Map DSN attribute name to pseudo record type so that we don't have to 168 * pollute the queue file with records that are incompatible with past 169 * Postfix versions. Preferably, people should be able to back out from 170 * an upgrade without losing mail. 171 */ 172 if (type == REC_TYPE_ATTR) { 173 vstring_strcpy(state->attr_buf, buf); 174 error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); 175 if (error_text != 0) { 176 msg_warn("%s: message rejected: malformed attribute: %s: %.100s", 177 state->queue_id, error_text, buf); 178 state->errs |= CLEANUP_STAT_BAD; 179 return; 180 } 181 /* Zero-length values are place holders for unavailable values. */ 182 if (*attr_value == 0) { 183 msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", 184 state->queue_id, attr_name); 185 return; 186 } 187 if ((junk = rec_attr_map(attr_name)) != 0) { 188 mapped_buf = attr_value; 189 mapped_type = junk; 190 } 191 } 192 193 /* 194 * Sanity check. 195 */ 196 if (strchr(REC_TYPE_ENVELOPE, type) == 0) { 197 msg_warn("%s: message rejected: unexpected record type %d in envelope", 198 state->queue_id, type); 199 state->errs |= CLEANUP_STAT_BAD; 200 return; 201 } 202 203 /* 204 * Although recipient records appear at the end of the initial or 205 * extracted envelope, the code for processing recipient records is first 206 * because there can be lots of them. 207 * 208 * Recipient records may be mixed with other information (such as FILTER or 209 * REDIRECT actions from SMTPD). In that case the queue manager needs to 210 * examine all queue file records before it can start delivery. This is 211 * not a problem when SMTPD recipient lists are small. 212 * 213 * However, if recipient records are not mixed with other records 214 * (typically, mailing list mail) then we can make an optimization: the 215 * queue manager does not need to examine every envelope record before it 216 * can start deliveries. This can help with very large mailing lists. 217 */ 218 219 /* 220 * On the transition from non-recipient records to recipient records, 221 * emit some records and do some sanity checks. 222 * 223 * XXX Moving the envelope sender (and the test for its presence) to the 224 * extracted segment can reduce qmqpd memory requirements because it no 225 * longer needs to read the entire message into main memory. 226 */ 227 if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 228 && strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) { 229 if (state->sender == 0) { 230 msg_warn("%s: message rejected: missing sender envelope record", 231 state->queue_id); 232 state->errs |= CLEANUP_STAT_BAD; 233 return; 234 } 235 if (state->arrival_time.tv_sec == 0) { 236 msg_warn("%s: message rejected: missing time envelope record", 237 state->queue_id); 238 state->errs |= CLEANUP_STAT_BAD; 239 return; 240 } 241 242 /* 243 * XXX This works by accident, because the sender is recorded at the 244 * beginning of the envelope segment. 245 */ 246 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0 247 && state->sender && *state->sender 248 && var_delay_warn_time > 0) { 249 cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, 250 REC_TYPE_WARN_ARG(state->arrival_time.tv_sec 251 + var_delay_warn_time)); 252 } 253 state->flags |= CLEANUP_FLAG_INRCPT; 254 } 255 256 /* 257 * Initial envelope recipient record processing. 258 */ 259 if (type == REC_TYPE_RCPT) { 260 if (state->sender == 0) { /* protect showq */ 261 msg_warn("%s: message rejected: envelope recipient precedes sender", 262 state->queue_id); 263 state->errs |= CLEANUP_STAT_BAD; 264 return; 265 } 266 if (state->orig_rcpt == 0) 267 state->orig_rcpt = mystrdup(buf); 268 cleanup_addr_recipient(state, buf); 269 if (cleanup_milters != 0 270 && state->milters == 0 271 && CLEANUP_MILTER_OK(state)) 272 cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); 273 myfree(state->orig_rcpt); 274 state->orig_rcpt = 0; 275 if (state->dsn_orcpt != 0) { 276 myfree(state->dsn_orcpt); 277 state->dsn_orcpt = 0; 278 } 279 state->dsn_notify = 0; 280 return; 281 } 282 if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { 283 if (state->orig_rcpt != 0) { 284 myfree(state->orig_rcpt); 285 state->orig_rcpt = 0; 286 } 287 if (state->dsn_orcpt != 0) { 288 myfree(state->dsn_orcpt); 289 state->dsn_orcpt = 0; 290 } 291 state->dsn_notify = 0; 292 return; 293 } 294 if (mapped_type == REC_TYPE_DSN_ORCPT) { 295 if (state->dsn_orcpt) { 296 msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", 297 state->queue_id, state->dsn_orcpt); 298 myfree(state->dsn_orcpt); 299 } 300 state->dsn_orcpt = mystrdup(mapped_buf); 301 return; 302 } 303 if (mapped_type == REC_TYPE_DSN_NOTIFY) { 304 if (state->dsn_notify) { 305 msg_warn("%s: ignoring out-of-order DSN notify record <%d>", 306 state->queue_id, state->dsn_notify); 307 state->dsn_notify = 0; 308 } 309 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 310 || DSN_NOTIFY_OK(junk) == 0) 311 msg_warn("%s: ignoring malformed DSN notify record <%.200s>", 312 state->queue_id, buf); 313 else 314 state->qmgr_opts |= 315 QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); 316 return; 317 } 318 if (type == REC_TYPE_ORCP) { 319 if (state->orig_rcpt != 0) { 320 msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", 321 state->queue_id, state->orig_rcpt); 322 myfree(state->orig_rcpt); 323 } 324 state->orig_rcpt = mystrdup(buf); 325 return; 326 } 327 if (type == REC_TYPE_MESG) { 328 state->action = cleanup_message; 329 if (state->flags & CLEANUP_FLAG_INRCPT) { 330 if (state->milters || cleanup_milters) { 331 /* Make room to append recipient. */ 332 if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) 333 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 334 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); 335 if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) 336 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 337 } 338 state->flags &= ~CLEANUP_FLAG_INRCPT; 339 } 340 return; 341 } 342 343 /* 344 * Initial envelope non-recipient record processing. 345 */ 346 if (state->flags & CLEANUP_FLAG_INRCPT) 347 /* Tell qmgr that recipient records are mixed with other information. */ 348 state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; 349 if (type == REC_TYPE_SIZE) 350 /* Use our own SIZE record instead. */ 351 return; 352 if (mapped_type == REC_TYPE_CTIME) 353 /* Use our own expiration time base record instead. */ 354 return; 355 if (type == REC_TYPE_TIME) { 356 /* First instance wins. */ 357 if (state->arrival_time.tv_sec == 0) { 358 REC_TYPE_TIME_SCAN(buf, state->arrival_time); 359 cleanup_out(state, type, buf, len); 360 } 361 /* Generate our own expiration time base record. */ 362 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%ld", 363 MAIL_ATTR_CREATE_TIME, (long) time((time_t *) 0)); 364 return; 365 } 366 if (type == REC_TYPE_FULL) { 367 /* First instance wins. */ 368 if (state->fullname == 0) { 369 state->fullname = mystrdup(buf); 370 cleanup_out(state, type, buf, len); 371 } 372 return; 373 } 374 if (type == REC_TYPE_FROM) { 375 /* Allow only one instance. */ 376 if (state->sender != 0) { 377 msg_warn("%s: message rejected: multiple envelope sender records", 378 state->queue_id); 379 state->errs |= CLEANUP_STAT_BAD; 380 return; 381 } 382 if (state->milters || cleanup_milters) { 383 /* Remember the sender record offset. */ 384 if ((state->sender_pt_offset = vstream_ftell(state->dst)) < 0) 385 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 386 } 387 cleanup_addr_sender(state, buf); 388 if (state->milters || cleanup_milters) { 389 /* Make room to replace sender. */ 390 if ((len = strlen(state->sender)) < REC_TYPE_PTR_PAYL_SIZE) 391 rec_pad(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_PAYL_SIZE - len); 392 /* Remember the after-sender record offset. */ 393 if ((state->sender_pt_target = vstream_ftell(state->dst)) < 0) 394 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); 395 } 396 if (cleanup_milters != 0 397 && state->milters == 0 398 && CLEANUP_MILTER_OK(state)) 399 cleanup_milter_emul_mail(state, cleanup_milters, state->sender); 400 return; 401 } 402 if (mapped_type == REC_TYPE_DSN_ENVID) { 403 /* Allow only one instance. */ 404 if (state->dsn_envid != 0) { 405 msg_warn("%s: message rejected: multiple DSN envelope ID records", 406 state->queue_id); 407 state->errs |= CLEANUP_STAT_BAD; 408 return; 409 } 410 if (!allprint(mapped_buf)) { 411 msg_warn("%s: message rejected: bad DSN envelope ID record", 412 state->queue_id); 413 state->errs |= CLEANUP_STAT_BAD; 414 return; 415 } 416 state->dsn_envid = mystrdup(mapped_buf); 417 cleanup_out(state, type, buf, len); 418 return; 419 } 420 if (mapped_type == REC_TYPE_DSN_RET) { 421 /* Allow only one instance. */ 422 if (state->dsn_ret != 0) { 423 msg_warn("%s: message rejected: multiple DSN RET records", 424 state->queue_id); 425 state->errs |= CLEANUP_STAT_BAD; 426 return; 427 } 428 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 429 || DSN_RET_OK(junk) == 0) { 430 msg_warn("%s: message rejected: bad DSN RET record <%.200s>", 431 state->queue_id, buf); 432 state->errs |= CLEANUP_STAT_BAD; 433 return; 434 } 435 state->dsn_ret = junk; 436 cleanup_out(state, type, buf, len); 437 return; 438 } 439 if (type == REC_TYPE_WARN) { 440 /* First instance wins. */ 441 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) { 442 state->flags |= CLEANUP_FLAG_WARN_SEEN; 443 cleanup_out(state, type, buf, len); 444 } 445 return; 446 } 447 /* XXX Needed for cleanup_bounce(); sanity check usage. */ 448 if (type == REC_TYPE_VERP) { 449 if (state->verp_delims == 0) { 450 if (state->sender == 0 || state->sender[0] == 0) { 451 msg_warn("%s: ignoring VERP request for null sender", 452 state->queue_id); 453 } else if (verp_delims_verify(buf) != 0) { 454 msg_warn("%s: ignoring bad VERP request: \"%.100s\"", 455 state->queue_id, buf); 456 } else { 457 state->verp_delims = mystrdup(buf); 458 cleanup_out(state, type, buf, len); 459 } 460 } 461 return; 462 } 463 if (type == REC_TYPE_ATTR) { 464 if (state->attr->used >= var_qattr_count_limit) { 465 msg_warn("%s: message rejected: attribute count exceeds limit %d", 466 state->queue_id, var_qattr_count_limit); 467 state->errs |= CLEANUP_STAT_BAD; 468 return; 469 } 470 if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) { 471 /* Choose header rewriting context. See also cleanup_addr.c. */ 472 if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) { 473 state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL; 474 } else if (STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)) { 475 state->hdr_rewrite_context = 476 (*var_remote_rwr_domain ? MAIL_ATTR_RWR_REMOTE : 0); 477 } else { 478 msg_warn("%s: message rejected: bad rewriting context: %.100s", 479 state->queue_id, attr_value); 480 state->errs |= CLEANUP_STAT_BAD; 481 return; 482 } 483 } 484 nvtable_update(state->attr, attr_name, attr_value); 485 cleanup_out(state, type, buf, len); 486 return; 487 } else { 488 cleanup_out(state, type, buf, len); 489 return; 490 } 491} 492