Portability: OpenBSD 6.0. Files: makedefs, util/sys_defs.h.
+20160618
+
+ Bugfix (introduced: 20091121): with the introduction of
+ sender_dependent_default_transport_maps, the SMTP daemon
+ was not updated. This resulted in false rejects with
+ sender-dependent "error" transports. Based on a fix by
+ Russell Yanofsky. Files: global/resolve_clnt.c,
+ global/resolve_clnt.h, smtpd/smtpd_check.c, smtpd/smtpd_check.h,
+ smtpd/smtpd_milter.c, smtpd/smtpd_resolve.c, smtpd/smtpd_resolve.h.
+
+20160717
+
+ Bugfix (introduced: Postfix 1.1): the virtual(8) delivery
+ agent discarded the error result from vstream_fseek().
+ File: virtual/mailbox.c.
+
+20160730
+
+ Bugfix (introduced: 20090614): with concurrent connections
+ from the same client IP address, and after-220 tests enabled,
+ postscreen could overwrite the cached "all tests completed"
+ result of one connection that completed the after-220 tests,
+ with the "some tests not completed" result of a concurrent
+ connection where the client hung up later, without completing
+ the after-220 tests.
+
20160819
Bugfix (introduced: Postfix 3.0): the makedefs script ignored
UTF8-encoded text, specify "option_group = client" in Postfix
MySQL configuration files. This will be the default setting
with Postfix 3.2 and later.
+
+20161105
+
+ Bugfix (introduced: Postfix 1.1): the postsuper command did
+ not count a successful rename operation after error recovery.
+ Problem reported by Markus Schönhaber. File: postsuper/postsuper.c.
+
+20161206
+
+ Bugfix (introduced: Postfix 3.0): when receiving a MAIL
+ FROM...SMTPUTF8 command while smtpd_delay_reject=no, enable
+ SMTPUTF8 support before processing smtpd_sender_restrictions.
+ Problem reported by Viktor Dukhovni. File: smtpd/smtpd.c.
+
+20161220
+
+ Bugfix (introduced: Postfix 2.1.0): the Postfix SMTP daemon
+ did not query sender_canonical_maps when rejecting unknown
+ senders with "smtpd_reject_unlisted_recipient = yes" or
+ with reject_unlisted_sender. Stephen R. van den Berg (Mr.
+ procmail). Files: smtpd/smtpd.c, smtpd/smtpd_check.c.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20161001"
-#define MAIL_VERSION_NUMBER "3.0.7"
+#define MAIL_RELEASE_DATE "20170101"
+#define MAIL_VERSION_NUMBER "3.0.8"
#ifdef SNAPSHOT
#define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE
* valid.
*/
if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0
+ && state->client_info->concurrency == 1
&& psc_cache_map != 0
&& (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) {
saved_flags = state->flags;
psc_conclude(state);
return;
}
+ } else if (state->client_info->concurrency > 1) {
+ saved_flags = state->flags;
+ psc_todo_tests(state, event_time());
+ state->flags |= saved_flags;
+ if (msg_verbose)
+ msg_info("%s: new + recent flags: %s",
+ myname, psc_print_state_flags(state->flags, myname));
} else {
saved_flags = state->flags;
psc_new_tests(state);
void *unused_context)
{
PSC_STATE dummy;
+ PSC_CLIENT_INFO dummy_client_info;
/*
* This function is called by the cache cleanup pseudo thread.
* silly logging we remove the cache entry only after all tests have
* expired longer ago than the cache retention time.
*/
+ dummy.client_info = &dummy_client_info;
psc_parse_tests(&dummy, stamp_str, event_time() - var_psc_cache_ret);
return ((dummy.flags & PSC_STATE_MASK_ANY_TODO) == 0);
}
#define PSC_TINDX_BYTNAME(tname) (PSC_TINDX_ ## tname)
+ /*
+ * Per-client shared state.
+ */
+typedef struct {
+ int concurrency; /* per-client */
+ int pass_new_count; /* per-client */
+ time_t expire_time[PSC_TINDX_COUNT]; /* per-test expiration */
+} PSC_CLIENT_INFO;
+
/*
* Per-session state.
*/
char *smtp_client_port; /* client port */
char *smtp_server_addr; /* server address */
char *smtp_server_port; /* server port */
- int client_concurrency; /* per-client */
const char *final_reply; /* cause for hanging up */
VSTRING *send_buf; /* pending output */
/* Test context. */
struct timeval start_time; /* start of current test */
const char *test_name; /* name of current test */
- time_t expire_time[PSC_TINDX_COUNT]; /* per-test expiration */
+ PSC_CLIENT_INFO *client_info; /* shared client state */
VSTRING *dnsbl_reply; /* dnsbl reject text */
int dnsbl_score; /* saved DNSBL score */
const char *dnsbl_name; /* DNSBL name with largest weight */
* Emulate legacy ad-hoc variables on top of indexable time stamps. This
* avoids massive scar tissue during initial feature development.
*/
-#define pregr_stamp expire_time[PSC_TINDX_PREGR]
-#define dnsbl_stamp expire_time[PSC_TINDX_DNSBL]
-#define pipel_stamp expire_time[PSC_TINDX_PIPEL]
-#define nsmtp_stamp expire_time[PSC_TINDX_NSMTP]
-#define barlf_stamp expire_time[PSC_TINDX_BARLF]
+#define pregr_stamp client_info->expire_time[PSC_TINDX_PREGR]
+#define dnsbl_stamp client_info->expire_time[PSC_TINDX_DNSBL]
+#define pipel_stamp client_info->expire_time[PSC_TINDX_PIPEL]
+#define nsmtp_stamp client_info->expire_time[PSC_TINDX_NSMTP]
+#define barlf_stamp client_info->expire_time[PSC_TINDX_BARLF]
+
+ /* Minize the patch size for stable releases. */
+#define client_concurrency client_info->concurrency
/*
* Special expiration time values.
#define PSC_INIT_TESTS(dst) do { \
time_t *_it_stamp_p; \
(dst)->flags = 0; \
- for (_it_stamp_p = (dst)->expire_time; \
- _it_stamp_p < (dst)->expire_time + PSC_TINDX_COUNT; \
+ for (_it_stamp_p = (dst)->client_info->expire_time; \
+ _it_stamp_p < (dst)->client_info->expire_time + PSC_TINDX_COUNT; \
_it_stamp_p++) \
*_it_stamp_p = PSC_TIME_STAMP_INVALID; \
} while (0)
+#define PSC_INIT_TEST_FLAGS_ONLY(dst) do { \
+ (dst)->flags = 0; \
+ } while (0)
#define PSC_BEGIN_TESTS(state, name) do { \
(state)->test_name = (name); \
GETTIMEOFDAY(&(state)->start_time); \
} while (0)
extern void psc_new_tests(PSC_STATE *);
extern void psc_parse_tests(PSC_STATE *, const char *, time_t);
+extern void psc_todo_tests(PSC_STATE *, time_t);
extern char *psc_print_tests(VSTRING *, PSC_STATE *);
extern char *psc_print_grey_key(VSTRING *, const char *, const char *,
const char *, const char *);
state->flags |= PSC_STATE_FLAG_BYTINDX_PASS(tindx);
}
/* Update expiration even if the test was completed or disabled. */
- if (state->expire_time[tindx] < now + var_psc_dnsbl_ttl)
- state->expire_time[tindx] = now + var_psc_dnsbl_ttl;
+ if (state->client_info->expire_time[tindx] < now + var_psc_dnsbl_ttl)
+ state->client_info->expire_time[tindx] = now + var_psc_dnsbl_ttl;
}
}
}
if ((state->flags & PSC_STATE_MASK_ANY_PASS) != 0
&& (state->flags & PSC_STATE_MASK_ANY_PASS) ==
PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_ANY_TODO))
- msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0 ?
+ msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0
+ || state->client_info->pass_new_count++ > 0 ?
"OLD" : "NEW", PSC_CLIENT_ADDR_PORT(state));
/*
* Update the postscreen cache. This still supports a scenario where a
* client gets whitelisted in the course of multiple sessions, as long as
- * that client does not "fail" any test.
+ * that client does not "fail" any test. Don't try to optimize away cache
+ * updates; we want cached information to be up-to-date even if a test
+ * result is renewed during overlapping SMTP sessions, and even if
+ * 'postfix reload' happens in the middle of that.
*/
if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) != 0
&& psc_cache_map != 0) {
/* psc_new_session_state() creates a new session state object
/* for the specified client stream, and increments the
/* psc_check_queue_length counter. The flags and per-test time
-/* stamps are initialized with PSC_INIT_TESTS(). The addr and
+/* stamps are initialized with PSC_INIT_TESTS(), or for concurrent
+/* sessions, with PSC_INIT_TEST_FLAGS_ONLY(). The addr and
/* port arguments are null-terminated strings with the remote
/* SMTP client endpoint. The _reply members are set to
/* polite "try again" SMTP replies. The protocol member is set
const char *server_port)
{
PSC_STATE *state;
- HTABLE_INFO *ht;
state = (PSC_STATE *) mymalloc(sizeof(*state));
- PSC_INIT_TESTS(state);
if ((state->smtp_client_stream = stream) != 0)
psc_check_queue_length++;
state->smtp_server_fd = (-1);
/*
* Update the per-client session count.
*/
- if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0)
- ht = htable_enter(psc_client_concurrency, client_addr, (void *) 0);
- ht->value += 1;
- state->client_concurrency = CAST_ANY_PTR_TO_INT(ht->value);
+ if ((state->client_info = (PSC_CLIENT_INFO *)
+ htable_find(psc_client_concurrency, client_addr)) == 0) {
+ state->client_info = (PSC_CLIENT_INFO *)
+ mymalloc(sizeof(state->client_info[0]));
+ (void) htable_enter(psc_client_concurrency, client_addr,
+ (void *) state->client_info);
+ PSC_INIT_TESTS(state);
+ state->client_info->concurrency = 1;
+ state->client_info->pass_new_count = 0;
+ } else {
+ PSC_INIT_TEST_FLAGS_ONLY(state);
+ state->client_info->concurrency += 1;
+ }
return (state);
}
state->smtp_client_addr)) == 0)
msg_panic("%s: unknown client address: %s",
myname, state->smtp_client_addr);
- if (--(ht->value) == 0)
- htable_delete(psc_client_concurrency, state->smtp_client_addr,
- (void (*) (void *)) 0);
+ if (--(state->client_info->concurrency) == 0)
+ htable_delete(psc_client_concurrency, state->smtp_client_addr, myfree);
if (state->smtp_client_stream != 0) {
event_server_disconnect(state->smtp_client_stream);
/* const char *stamp_text;
/* time_t time_value;
/*
+/* void psc_todo_tests(state, time_value)
+/* PSC_STATE *state;
+/* const char *stamp_text;
+/* time_t time_value;
+/*
/* char *psc_print_tests(buffer, state)
/* VSTRING *buffer;
/* PSC_STATE *state;
/* zeroes all the flags bits. These values are not meant to
/* be stored into the postscreen(8) cache.
/*
+/* PSC_INIT_TEST_FLAGS_ONLY() zeroes all the flag bits. It
+/* should be used when the time stamps are already initialized.
+/*
/* psc_new_tests() sets all test expiration time stamps to
-/* PSC_TIME_STAMP_NEW, and overwrites all flags bits. Only
-/* enabled tests are flagged with PSC_STATE_FLAG_TODO; the
-/* object is flagged with PSC_STATE_FLAG_NEW.
+/* PSC_TIME_STAMP_NEW, and invokes psc_todo_tests().
+/*
+/* psc_parse_tests() parses a cache file record and invokes
+/* psc_todo_tests().
/*
-/* psc_parse_tests() parses a cache file record and overwrites
-/* all flags bits. Tests are considered "expired" when they
+/* psc_todo_tests() overwrites all per-session flag bits, and
+/* populates the flags based on test expiration time stamp
+/* information. Tests are considered "expired" when they
/* would be expired at the specified time value. Only enabled
/* tests are flagged as "expired"; the object is flagged as
/* "new" if some enabled tests have "new" time stamps.
void psc_new_tests(PSC_STATE *state)
{
- /*
- * We know this client is brand new.
- */
- state->flags = PSC_STATE_FLAG_NEW;
-
/*
* Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
* recognize cache entries that haven't passed all enabled tests. When we
state->barlf_stamp = PSC_TIME_STAMP_NEW;
/*
- * Don't flag disabled tests as "todo", because there would be no way to
- * make those bits go away.
+ * Determine what tests need to be completed.
*/
- if (PSC_PREGR_TEST_ENABLE())
- state->flags |= PSC_STATE_FLAG_PREGR_TODO;
- if (PSC_DNSBL_TEST_ENABLE())
- state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
- if (var_psc_pipel_enable)
- state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
- if (var_psc_nsmtp_enable)
- state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
- if (var_psc_barlf_enable)
- state->flags |= PSC_STATE_FLAG_BARLF_TODO;
+ psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1);
}
/* psc_parse_tests - parse test results from cache */
{
const char *start = stamp_str;
char *cp;
- time_t *time_stamps = state->expire_time;
+ time_t *time_stamps = state->client_info->expire_time;
time_t *sp;
- /*
- * We don't know what tests have expired or have never passed.
- */
- state->flags = 0;
-
/*
* Parse the cache entry, and allow for older postscreen versions that
* implemented fewer tests. We pretend that the newer tests were disabled
* at the time that the cache entry was written.
- *
- * Flag the cache entry as "new" when the cache entry has fields for all
- * enabled tests, but the remote SMTP client has not yet passed all those
- * tests.
*/
for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
*sp = strtoul(start, &cp, 10);
if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
*sp = PSC_TIME_STAMP_DISABLED;
- if (*sp == PSC_TIME_STAMP_NEW)
- state->flags |= PSC_STATE_FLAG_NEW;
if (msg_verbose)
msg_info("%s -> %lu", start, (unsigned long) *sp);
if (*cp == ';')
start = cp;
}
+ /*
+ * Determine what tests need to be completed.
+ */
+ psc_todo_tests(state, time_value);
+}
+
+/* psc_todo_tests - determine what tests to perform */
+
+void psc_todo_tests(PSC_STATE *state, time_t time_value)
+{
+ time_t *time_stamps = state->client_info->expire_time;
+ time_t *sp;
+
+ /*
+ * Reset all per-session flags.
+ */
+ state->flags = 0;
+
+ /*
+ * Flag the tests as "new" when the cache entry has fields for all
+ * enabled tests, but the remote SMTP client has not yet passed all those
+ * tests.
+ */
+ for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
+ if (*sp == PSC_TIME_STAMP_NEW)
+ state->flags |= PSC_STATE_FLAG_NEW;
+ }
+
/*
* Don't flag disabled tests as "todo", because there would be no way to
* make those bits go away.
if ((ret = sane_rename(old, new)) < 0) {
if (errno != ENOENT
|| mail_queue_mkdirs(new) < 0
- || sane_rename(old, new) < 0)
+ || (ret = sane_rename(old, new)) < 0)
if (errno != ENOENT)
msg_fatal("rename file %s as %s: %m", old, new);
} else {
smtpd_resolve.o: ../../include/nvtable.h
smtpd_resolve.o: ../../include/resolve_clnt.h
smtpd_resolve.o: ../../include/rewrite_clnt.h
+smtpd_resolve.o: ../../include/split_at.h
smtpd_resolve.o: ../../include/stringops.h
smtpd_resolve.o: ../../include/sys_defs.h
smtpd_resolve.o: ../../include/vbuf.h
int var_strict_rfc821_env;
bool var_disable_vrfy_cmd;
char *var_canonical_maps;
+char *var_send_canon_maps;
char *var_rcpt_canon_maps;
char *var_virt_alias_maps;
char *var_virt_mailbox_maps;
if ((STR(state->addr_buf)[0] == 0 && !allow_empty_addr)
|| (strict_rfc821 && STR(state->addr_buf)[0] == '@')
|| (SMTPD_STAND_ALONE(state) == 0
- && smtpd_check_addr(STR(state->addr_buf), smtputf8) != 0)) {
+ && smtpd_check_addr(strcmp(state->where, SMTPD_CMD_MAIL) == 0 ?
+ state->recipient : state->sender,
+ STR(state->addr_buf), smtputf8) != 0)) {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where,
printable(STR(arg->vstrval), '?'));
char *verp_delims = 0;
int rate;
int dsn_envid = 0;
- int smtputf8 = 0;
state->flags &= ~SMTPD_FLAG_SMTPUTF8;
state->encoding = 0;
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (strcasecmp(arg, "SMTPUTF8") == 0) { /* RFC 6531 */
- smtputf8 = 1;
+ /* Fix 20161206: allow UTF8 in smtpd_sender_restrictions. */
+ state->flags |= SMTPD_FLAG_SMTPUTF8;
break;
}
}
}
if (extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR,
- var_strict_rfc821_env, smtputf8) != 0) {
+ var_strict_rfc821_env,
+ state->flags & SMTPD_FLAG_SMTPUTF8) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 5.1.7 Bad sender address syntax");
return (-1);
state->verp_delims = mystrdup(verp_delims);
if (dsn_envid)
state->dsn_envid = mystrdup(STR(state->dsn_buf));
- if (smtputf8)
- state->flags |= SMTPD_FLAG_SMTPUTF8;
if (USE_SMTPD_PROXY(state))
state->proxy_mail = mystrdup(STR(state->buffer));
if (var_smtpd_delay_open == 0 && mail_open_stream(state) < 0) {
const char *err = 0;
int rate;
int smtputf8 = 0;
+ int saved_flags;
/*
* The SMTP standard (RFC 821) disallows unquoted special characters in
}
}
/* Use state->addr_buf, with the unquoted result from extract_addr() */
- if (SMTPD_STAND_ALONE(state) == 0
- && (err = smtpd_check_rcpt(state, STR(state->addr_buf))) != 0) {
- smtpd_chat_reply(state, "%s", err);
- return (-1);
+ if (SMTPD_STAND_ALONE(state) == 0) {
+ /* Fix 20161206: allow UTF8 in smtpd_recipient_restrictions. */
+ saved_flags = state->flags;
+ if (smtputf8)
+ state->flags |= SMTPD_FLAG_SMTPUTF8;
+ err = smtpd_check_rcpt(state, STR(state->addr_buf));
+ state->flags = saved_flags;
+ if (err != 0) {
+ smtpd_chat_reply(state, "%s", err);
+ return (-1);
+ }
}
/*
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
+ VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
/*
/* void smtpd_check_init()
/*
-/* int smtpd_check_addr(address, smtputf8)
+/* int smtpd_check_addr(sender, address, smtputf8)
+/* const char *sender;
/* const char *address;
/* int smtputf8;
/*
/* once during the process life time.
/*
/* smtpd_check_addr() sanity checks an email address and returns
-/* non-zero in case of badness.
+/* non-zero in case of badness. The sender argument provides sender
+/* context for address resolution and caching, or a null pointer
+/* if information is unavailable.
/*
/* smtpd_check_rewrite() should be called before opening a queue
/* file or proxy connection, in order to establish the proper
* trivial-rewrite resolver.
*/
static MAPS *local_rcpt_maps;
+static MAPS *send_canon_maps;
static MAPS *rcpt_canon_maps;
static MAPS *canonical_maps;
static MAPS *virt_alias_maps;
*/
static int check_sender_rcpt_maps(SMTPD_STATE *, const char *);
static int check_recipient_rcpt_maps(SMTPD_STATE *, const char *);
-static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *);
+static int check_rcpt_maps(SMTPD_STATE *, const char *, const char *,
+ const char *);
/*
* Tempfail actions;
local_rcpt_maps = maps_create(VAR_LOCAL_RCPT_MAPS, var_local_rcpt_maps,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
| DICT_FLAG_UTF8_REQUEST);
+ send_canon_maps = maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
+ DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
+ | DICT_FLAG_UTF8_REQUEST);
rcpt_canon_maps = maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
| DICT_FLAG_UTF8_REQUEST);
/*
* Resolve the address.
*/
- reply = smtpd_resolve_addr(recipient);
+ reply = smtpd_resolve_addr(state->sender, recipient);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, recipient);
/*
* Resolve the address.
*/
- reply = smtpd_resolve_addr(recipient);
+ reply = smtpd_resolve_addr(state->sender, recipient);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, recipient);
/*
* Resolve the address.
*/
- reply = smtpd_resolve_addr(addr);
+ reply = smtpd_resolve_addr(strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
+ state->recipient : state->sender, addr);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, addr);
/*
* Resolve the address.
*/
- reply = smtpd_resolve_addr(addr);
+ reply = smtpd_resolve_addr(strcmp(reply_class, SMTPD_NAME_SENDER) == 0 ?
+ state->recipient : state->sender, addr);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, addr);
* Reject if the client is logged in and does not own the sender address.
*/
if (smtpd_sender_login_maps && state->sasl_username) {
- reply = smtpd_resolve_addr(sender);
+ reply = smtpd_resolve_addr(state->recipient, sender);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, sender);
if ((owners = check_mail_addr_find(state, sender, smtpd_sender_login_maps,
* owner.
*/
if (smtpd_sender_login_maps && !state->sasl_username) {
- reply = smtpd_resolve_addr(sender);
+ reply = smtpd_resolve_addr(state->recipient, sender);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, sender);
if (check_mail_addr_find(state, sender, smtpd_sender_login_maps,
/* smtpd_check_addr - address sanity check */
-int smtpd_check_addr(const char *addr, int smtputf8)
+int smtpd_check_addr(const char *sender, const char *addr, int smtputf8)
{
const RESOLVE_REPLY *resolve_reply;
const char *myname = "smtpd_check_addr";
*/
if (addr == 0 || *addr == 0)
return (0);
- resolve_reply = smtpd_resolve_addr(addr);
+ resolve_reply = smtpd_resolve_addr(sender, addr);
if (resolve_reply->flags & RESOLVE_FLAG_ERROR)
return (-1);
if (state->warn_if_reject == 0)
/* We really validate the recipient address. */
state->recipient_rcptmap_checked = 1;
- return (check_rcpt_maps(state, recipient, SMTPD_NAME_RECIPIENT));
+ return (check_rcpt_maps(state, state->sender, recipient,
+ SMTPD_NAME_RECIPIENT));
}
/* check_sender_rcpt_maps - generic_checks() sender table check */
if (state->warn_if_reject == 0)
/* We really validate the sender address. */
state->sender_rcptmap_checked = 1;
- return (check_rcpt_maps(state, sender, SMTPD_NAME_SENDER));
+ return (check_rcpt_maps(state, state->recipient, sender,
+ SMTPD_NAME_SENDER));
}
/* check_rcpt_maps - generic_checks() interface for recipient table check */
-static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
+static int check_rcpt_maps(SMTPD_STATE *state, const char *sender,
+ const char *recipient,
const char *reply_class)
{
const RESOLVE_REPLY *reply;
DSN_SPLIT dp;
if (msg_verbose)
- msg_info(">>> CHECKING RECIPIENT MAPS <<<");
+ msg_info(">>> CHECKING %s VALIDATION MAPS <<<", reply_class);
/*
* Resolve the address.
*/
- reply = smtpd_resolve_addr(recipient);
+ reply = smtpd_resolve_addr(sender, recipient);
if (reply->flags & RESOLVE_FLAG_FAIL)
reject_dict_retry(state, recipient);
* domains.
*/
if (MATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
+ || (strcmp(reply_class, SMTPD_NAME_SENDER) == 0
+ && MATCH(send_canon_maps, CONST_STR(reply->recipient)))
|| MATCH(canonical_maps, CONST_STR(reply->recipient))
|| MATCH(virt_alias_maps, CONST_STR(reply->recipient)))
return (0);
char *var_rcpt_delim;
char *var_rest_classes;
char *var_alias_maps;
+char *var_send_canon_maps;
char *var_rcpt_canon_maps;
char *var_canonical_maps;
char *var_virt_alias_maps;
VAR_RCPT_DELIM, DEF_RCPT_DELIM, &var_rcpt_delim,
VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps,
+ VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps,
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps,
VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps,
resp = 0;
break;
}
+ if (strcasecmp(args->argv[0], VAR_SEND_CANON_MAPS) == 0) {
+ UPDATE_STRING(var_send_canon_maps, args->argv[1]);
+ UPDATE_MAPS(send_canon_maps, VAR_SEND_CANON_MAPS,
+ var_send_canon_maps, DICT_FLAG_LOCK
+ | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
+ resp = 0;
+ break;
+ }
+ if (strcasecmp(args->argv[0], VAR_RCPT_CANON_MAPS) == 0) {
+ UPDATE_STRING(var_rcpt_canon_maps, args->argv[1]);
+ UPDATE_MAPS(rcpt_canon_maps, VAR_RCPT_CANON_MAPS,
+ var_rcpt_canon_maps, DICT_FLAG_LOCK
+ | DICT_FLAG_FOLD_FIX | DICT_FLAG_UTF8_REQUEST);
+ resp = 0;
+ break;
+ }
if (strcasecmp(args->argv[0], VAR_RBL_REPLY_MAPS) == 0) {
UPDATE_STRING(var_rbl_reply_maps, args->argv[1]);
UPDATE_MAPS(rbl_reply_maps, VAR_RBL_REPLY_MAPS,
* External interface.
*/
extern void smtpd_check_init(void);
-extern int smtpd_check_addr(const char *, int);
+extern int smtpd_check_addr(const char *, const char *, int);
extern char *smtpd_check_rewrite(SMTPD_STATE *);
extern char *smtpd_check_client(SMTPD_STATE *);
extern char *smtpd_check_helo(SMTPD_STATE *, char *);
return (0);
if (state->sender[0] == 0)
return ("");
- reply = smtpd_resolve_addr(state->sender);
+ reply = smtpd_resolve_addr(state->recipient, state->sender);
/* Sendmail 8.13 does not externalize the null string. */
if (STR(reply->recipient)[0])
quote_821_local(state->expand_buf, STR(reply->recipient));
if (strcmp(name, S8_MAC_MAIL_HOST) == 0) {
if (state->sender == 0)
return (0);
- reply = smtpd_resolve_addr(state->sender);
+ reply = smtpd_resolve_addr(state->recipient, state->sender);
return (STR(reply->nexthop));
}
if (strcmp(name, S8_MAC_MAIL_MAILER) == 0) {
if (state->sender == 0)
return (0);
- reply = smtpd_resolve_addr(state->sender);
+ reply = smtpd_resolve_addr(state->recipient, state->sender);
return (STR(reply->transport));
}
cp = split_at(STR(state->expand_buf), ' ');
return (cp ? split_at(cp, ' ') : cp);
}
- reply = smtpd_resolve_addr(state->recipient);
+ reply = smtpd_resolve_addr(state->sender, state->recipient);
/* Sendmail 8.13 does not externalize the null string. */
if (STR(reply->recipient)[0])
quote_821_local(state->expand_buf, STR(reply->recipient));
(void) split_at(STR(state->expand_buf), ' ');
return (STR(state->expand_buf));
}
- reply = smtpd_resolve_addr(state->recipient);
+ reply = smtpd_resolve_addr(state->sender, state->recipient);
return (STR(reply->nexthop));
}
if (strcmp(name, S8_MAC_RCPT_MAILER) == 0) {
return (0);
if (state->milter_reject_text)
return (S8_RCPT_MAILER_ERROR);
- reply = smtpd_resolve_addr(state->recipient);
+ reply = smtpd_resolve_addr(state->sender, state->recipient);
return (STR(reply->transport));
}
return (0);
/* void smtpd_resolve_init(cache_size)
/* int cache_size;
/*
-/* const RESOLVE_REPLY *smtpd_resolve_addr(addr)
+/* const RESOLVE_REPLY *smtpd_resolve_addr(sender, addr)
+/* const char *sender;
/* const char *addr;
/* DESCRIPTION
/* This module maintains a resolve client cache that persists
/* Arguments:
/* .IP cache_size
/* The requested cache size.
+/* .IP sender
+/* The message sender, or null pointer.
/* .IP addr
/* The address to resolve.
/* DIAGNOSTICS
#include <vstring.h>
#include <ctable.h>
#include <stringops.h>
+#include <split_at.h>
/* Global library. */
static CTABLE *smtpd_resolve_cache;
#define STR(x) vstring_str(x)
+#define SENDER_ADDR_JOIN_CHAR '\n'
/* resolve_pagein - page in an address resolver result */
-static void *resolve_pagein(const char *addr, void *unused_context)
+static void *resolve_pagein(const char *sender_plus_addr, void *unused_context)
{
+ const char myname[] = "resolve_pagein";
static VSTRING *query;
+ static VSTRING *junk;
+ static VSTRING *sender_buf;
RESOLVE_REPLY *reply;
- char *tmp;
+ const char *sender;
+ const char *addr;
/*
* Initialize on the fly.
*/
- if (query == 0)
+ if (query == 0) {
query = vstring_alloc(10);
+ junk = vstring_alloc(10);
+ sender_buf = vstring_alloc(10);
+ }
/*
* Initialize.
reply = (RESOLVE_REPLY *) mymalloc(sizeof(*reply));
resolve_clnt_init(reply);
+ /*
+ * Split the sender and address.
+ */
+ vstring_strcpy(junk, sender_plus_addr);
+ sender = STR(junk);
+ if ((addr = split_at(STR(junk), SENDER_ADDR_JOIN_CHAR)) == 0)
+ msg_panic("%s: bad search key: \"%s\"", myname, sender_plus_addr);
+
/*
* Resolve the address.
*/
+ rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, sender, sender_buf);
rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, addr, query);
- resolve_clnt_query(STR(query), reply);
- tmp = mystrdup(STR(reply->recipient));
- casefold(reply->recipient, tmp); /* XXX */
- myfree(tmp);
+ resolve_clnt_query_from(STR(sender_buf), STR(query), reply);
+ vstring_strcpy(junk, STR(reply->recipient));
+ casefold(reply->recipient, STR(junk)); /* XXX */
/*
* Save the result.
resolve_pageout, (void *) 0);
}
-/* smtpd_resolve_addr - resolve cached addres */
+/* smtpd_resolve_addr - resolve cached address */
-const RESOLVE_REPLY *smtpd_resolve_addr(const char *addr)
+const RESOLVE_REPLY *smtpd_resolve_addr(const char *sender, const char *addr)
{
+ static VSTRING *sender_plus_addr_buf;
+
+ /*
+ * Initialize on the fly.
+ */
+ if (sender_plus_addr_buf == 0)
+ sender_plus_addr_buf = vstring_alloc(10);
/*
* Sanity check.
/*
* Reply from the read-through cache.
*/
- return (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr);
+ vstring_sprintf(sender_plus_addr_buf, "%s%c%s",
+ sender ? sender : RESOLVE_NULL_FROM,
+ SENDER_ADDR_JOIN_CHAR, addr);
+ return (const RESOLVE_REPLY *)
+ ctable_locate(smtpd_resolve_cache, STR(sender_plus_addr_buf));
}
* External interface.
*/
extern void smtpd_resolve_init(int);
-extern const RESOLVE_REPLY *smtpd_resolve_addr(const char *);
+extern const RESOLVE_REPLY *smtpd_resolve_addr(const char*, const char *);
/* LICENSE
/* .ad
int mail_copy_status;
int deliver_status;
int copy_flags;
- long end;
struct stat st;
/*
msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch",
VAR_STRICT_MBOX_OWNER);
} else {
- end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
+ if (vstream_fseek(mp->fp, (off_t) 0, SEEK_END) < 0)
+ msg_fatal("%s: seek queue file %s: %m",
+ myname, VSTREAM_PATH(mp->fp));
mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
copy_flags, "\n", why);
}