2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 13:48:06 +00:00

snapshot-20010729

This commit is contained in:
Wietse Venema
2001-07-29 00:00:00 -05:00
committed by Viktor Dukhovni
parent 04d0db6c63
commit 4fe7066ddd
32 changed files with 929 additions and 174 deletions

2
postfix/.indent.pro vendored
View File

@@ -20,6 +20,8 @@
-TCONFIG_STR_TABLE
-TCONFIG_TIME_FN_TABLE
-TCONFIG_TIME_TABLE
-TCTABLE
-TCTABLE_ENTRY
-TDELIVER_ATTR
-TDELIVER_REQUEST
-TDICT

View File

@@ -5350,13 +5350,27 @@ Apologies for any names omitted.
20010719
Feature: stiffer coupling between mail arrival rates and
mail delivery rates, using a trivial token-based scheme
where qmgr provides a token when it retrieves mail from
the incoming queue, and where the cleanup daemon wants a
token after enqueueing mail. If no token is available the
cleanup server pauses briefly and proceeds anyway. This
allows input rates to gradually exceed output rates. After
sustained exposure to high input loads Postfix reverts to
the old situation where it sacrifices output rates in favor
of receiving mail.
Feature: stiffer coupling between mail receiving rates and
mail delivery rates, using a trivial token-based scheme,
implemented by reading and writing an in-memory pipe. The
queue manager produces one token when it retrieves mail
from the incoming queue. The cleanup daemon consumes one
token when it adds mail to the incoming queue. If no token
is available the cleanup server pauses for $in_flow_delay
seconds and proceeds anyway. The delay allows mail sending
process to catch up and access the disk. Valid delays are
0..10 seconds.
20010727
Bugfix: updated LDAP client module from LaMont Jones, HP.
20010729
Bugfix: recursive restrictions could clobber non-reentrant
address resolving results. Problem found by Victor Duchovni,
morganstanley.com.
Bugfix: the not yet published DUNNO table lookup result
did not prevent further partial key lookups in the same
table. Problem found by Victor Duchovni, morganstanley.com.

View File

@@ -12,9 +12,10 @@ the mysqlclient library (and libm) to AUXLIBS, for example:
make -f Makefile.init makefiles \
'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include' \
'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lm'
'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lz -lm'
then, just run 'make'.
then, just run 'make'. This requires libz, the compression library.
Older mysql implementations build without libz.
Postfix installations which may benefit from using mysql map types
include sites that have a need for instantaneous updates of

View File

@@ -23,6 +23,13 @@
#ldap_server_port = 389
# The ldap_query_filter parameter specifies the filter used for queries.
# The replacement for "%s" is the address input into the map; e.g.
# for alias maps, the "user" part (the RFC 2822 local-part) of
# "user@domain.com" for To: addresses destined for local delivery
# (those matching $mydestination or a virtual domain), and all of
# "user@domain.com" (the RFC 2822 addr-spec) for other addresses.
# "%u" provides just the user portion of the input, and "%d" provides
# just the hostname.
#
#ldap_query_filter = (mailacceptinggeneralid=%s)
@@ -31,6 +38,13 @@
#
#ldap_result_attribute = maildrop
# The ldap_special_result_attribute lists the attribute(s) of an
# entry which contain links, either ldap url's or distinguished names.
# The entries referenced by these links are (recursively) treated as if
# they were contained in the referencing entity.
#
# ldap_special_result_attribute =
# The ldap_scope parameter specifies the LDAP search scope: sub, base, or one.
#
#ldap_scope = sub

View File

@@ -296,6 +296,9 @@ domains with "relay access denied"</a>
<li><a href="#domain_mailbox">Receiving a virtual domain in a
mailbox</a>
<li><a href="#virtual_logging">Postfix logs delivery to virtual
address with the wrong name</a>
</ul>
<a name="address_rewriting"><h3>Address rewriting</h3>
@@ -2716,6 +2719,44 @@ your system supports, use the command <b>postconf -m</b>.
<hr>
<a name="virtual_logging"><h3>Postfix logs delivery to virtual
address with the wrong name</h3></a>
When Postfix delivers mail for a virtual address <i>vuser@vdomain.name</i>
that is aliased to a local user, then Postfix logs the local username
instead of the virtual one.
<p>
Changing this requires non-trivial changes, because Postfix only
remembers the address that it delivers to, not the address that
was original specified in, for example, the SMTP MAIL FROM command.
<p>
A workaround exists. It uses regular expressions. This
can be expensive if you have many virtual domains.
<p>
<blockquote>
<pre>
/etc/postfix/main.cf:
virtual_maps = regexp:/etc/postfix/virtual_regexp
recipient_delimiter = +
/etc/postfix/virtual_regexp:
/^vdomain\.name$/ whatever
/(.*)@vdomain\.name$/ localuser+$1=vdomain.name
</pre>
</blockquote>
<p>
This delivers the mail as
<i>localuser+vuser=vdomain.name@your.domain</i>.
<hr>
<a name="masquerade"><h3>Address masquerading with exceptions</h3></a>
For people outside your organization it can be desirable to only

View File

@@ -271,6 +271,6 @@ int main(int argc, char **argv)
MAIL_SERVER_PRE_INIT, cleanup_pre_jail,
MAIL_SERVER_POST_INIT, cleanup_post_jail,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
MAIL_SERVER_FLOW_CTL,
MAIL_SERVER_IN_FLOW_DELAY, var_in_flow_delay,
0);
}

View File

@@ -69,7 +69,7 @@
/* char *var_export_environ;
/* char *var_debug_peer_list;
/* int var_debug_peer_level;
/* int var_glob_flow_ctl;
/* int var_in_flow_delay;
/*
/* void mail_params_init()
/* DESCRIPTION
@@ -186,7 +186,7 @@ char *var_def_transport;
char *var_mynetworks_style;
char *var_verp_delims;
char *var_verp_filter;
int var_glob_flow_ctl;
int var_in_flow_delay;
char *var_import_environ;
char *var_export_environ;
@@ -327,7 +327,6 @@ void mail_params_init()
VAR_FORK_TRIES, DEF_FORK_TRIES, &var_fork_tries, 1, 0,
VAR_FLOCK_TRIES, DEF_FLOCK_TRIES, &var_flock_tries, 1, 0,
VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0,
VAR_GLOB_FLOW_CTL, DEF_GLOB_FLOW_CTL, &var_glob_flow_ctl, 0, 0,
0,
};
static CONFIG_TIME_TABLE time_defaults[] = {
@@ -339,6 +338,7 @@ void mail_params_init()
VAR_FLOCK_DELAY, DEF_FLOCK_DELAY, &var_flock_delay, 1, 0,
VAR_FLOCK_STALE, DEF_FLOCK_STALE, &var_flock_stale, 1, 0,
VAR_DAEMON_TIMEOUT, DEF_DAEMON_TIMEOUT, &var_daemon_timeout, 1, 0,
VAR_IN_FLOW_DELAY, DEF_IN_FLOW_DELAY, &var_in_flow_delay, 0, 10,
0,
};
static CONFIG_BOOL_TABLE bool_defaults[] = {

View File

@@ -1284,15 +1284,17 @@ extern char *var_verp_delims;
extern char *var_verp_filter;
/*
* Global flow control. This allows for a stiffer coupling between receiving
* programs and the queue manager, so that receiving processes cannot easily
* overwhelm the file system. The coupling is not so tight that Postfix
* stops receiving mail althogether. It just slows down a bit so that
* sending processes get a chance to read from the disk.
* Inbound mail flow control. This allows for a stiffer coupling between
* receiving mail and sending mail. A sending process produces one token for
* each message that it takes from the incoming queue; a receiving process
* consumes one token for each message that it adds to the incoming queue.
* When no token is available (Postfix receives more mail than it is able to
* deliver) a receiving process pauses for $in_flow_delay seconds so that
* the sending processes get a chance to access the disk.
*/
#define VAR_GLOB_FLOW_CTL "global_mail_flow_control"
#define DEF_GLOB_FLOW_CTL 0
extern int var_glob_flow_ctl;
#define VAR_IN_FLOW_DELAY "in_flow_delay"
#define DEF_IN_FLOW_DELAY "1s"
extern int var_in_flow_delay;
/* LICENSE
/* .ad

View File

@@ -15,7 +15,7 @@
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-20010722"
#define DEF_MAIL_VERSION "Snapshot-20010729"
extern char *var_mail_version;
/* LICENSE

View File

@@ -232,6 +232,7 @@ multi_server.o: ../../include/mail_params.h
multi_server.o: ../../include/mail_conf.h
multi_server.o: ../../include/timed_ipc.h
multi_server.o: ../../include/resolve_local.h
multi_server.o: mail_flow.h
multi_server.o: master_proto.h
multi_server.o: mail_server.h
single_server.o: single_server.c
@@ -259,6 +260,7 @@ single_server.o: ../../include/debug_process.h
single_server.o: ../../include/mail_conf.h
single_server.o: ../../include/timed_ipc.h
single_server.o: ../../include/resolve_local.h
single_server.o: mail_flow.h
single_server.o: master_proto.h
single_server.o: mail_server.h
trigger_server.o: trigger_server.c
@@ -285,5 +287,6 @@ trigger_server.o: ../../include/mail_task.h
trigger_server.o: ../../include/debug_process.h
trigger_server.o: ../../include/mail_conf.h
trigger_server.o: ../../include/resolve_local.h
trigger_server.o: mail_flow.h
trigger_server.o: master_proto.h
trigger_server.o: mail_server.h

View File

@@ -11,6 +11,8 @@
/*
/* void mail_flow_put(count)
/* int count;
/*
/* int mail_flow_count()
/* DESCRIPTION
/* This module implements a simple flow control mechanism that
/* is based on tokens that are consumed by mail receiving processes
@@ -23,6 +25,8 @@
/* mail_flow_put() produces the specified number of tokens. The
/* token producing process is expected to produce new tokens
/* whenever it falls idle and no more tokens are available.
/*
/* mail_flow_count() returns the number of available tokens.
/* BUGS
/* The producer needs to wake up periodically to ensure that
/* tokens are not lost due to leakage.
@@ -110,3 +114,15 @@ int mail_flow_put(int len)
msg_info("%s: %d %d", myname, len, len - count);
return (len - count);
}
/* mail_flow_count - return number of available tokens */
int mail_flow_count(void)
{
char *myname = "mail_flow_count";
int count;
if ((count = peekfd(MASTER_FLOW_READ)) < 0)
msg_warn("%s: %m", myname);
return (count);
}

View File

@@ -16,6 +16,7 @@
*/
extern int mail_flow_get(int);
extern int mail_flow_put(int);
extern int mail_flow_count(void);
/* LICENSE
/* .ad

View File

@@ -28,7 +28,7 @@
#define MAIL_SERVER_EXIT 13
#define MAIL_SERVER_PRE_ACCEPT 14
#define MAIL_SERVER_FLOW_CTL 20
#define MAIL_SERVER_IN_FLOW_DELAY 20
typedef void (*MAIL_SERVER_INIT_FN) (char *, char **);
typedef int (*MAIL_SERVER_LOOP_FN) (char *, char **);

View File

@@ -27,4 +27,7 @@ void master_flow_init(void)
non_blocking(master_flow_pipe[0], NON_BLOCKING);
non_blocking(master_flow_pipe[1], NON_BLOCKING);
close_on_exec(master_flow_pipe[0], CLOSE_ON_EXEC);
close_on_exec(master_flow_pipe[1], CLOSE_ON_EXEC);
}

View File

@@ -95,6 +95,9 @@
/* Function to be executed prior to accepting a new connection.
/* .sp
/* Only the last instance of this parameter type is remembered.
/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)"
/* The amount of seconds to pause when no "mail flow control token"
/* is available. A token is consumed for each connection request.
/* .PP
/* multi_server_disconnect() should be called by the application
/* when a client disconnects.
@@ -168,6 +171,7 @@
#include <mail_conf.h>
#include <timed_ipc.h>
#include <resolve_local.h>
#include <mail_flow.h>
/* Process manager. */
@@ -190,6 +194,7 @@ static void (*multi_server_accept) (int, char *);
static void (*multi_server_onexit) (char *, char **);
static void (*multi_server_pre_accept) (char *, char **);
static VSTREAM *multi_server_lock;
static int multi_server_in_flow_delay;
/* multi_server_exit - normal termination */
@@ -253,6 +258,15 @@ static void multi_server_execute(int unused_event, char *context)
event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit);
}
/* multi_server_enable_read - enable read events */
static void multi_server_enable_read(int unused_event, char *context)
{
VSTREAM *stream = (VSTREAM *) context;
event_enable_read(vstream_fileno(stream), multi_server_execute, (char *) stream);
}
/* multi_server_wakeup - wake up application */
static void multi_server_wakeup(int fd)
@@ -266,7 +280,11 @@ static void multi_server_wakeup(int fd)
client_count++;
stream = vstream_fdopen(fd, O_RDWR);
timed_ipc_setup(stream);
event_enable_read(fd, multi_server_execute, (char *) stream);
if (multi_server_in_flow_delay > 0 && mail_flow_get(1) < 0)
event_request_timer(multi_server_enable_read, (char *) stream,
multi_server_in_flow_delay);
else
multi_server_enable_read(0, (char *) stream);
}
/* multi_server_accept_local - accept client connection request */
@@ -498,6 +516,9 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
case MAIL_SERVER_PRE_ACCEPT:
multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
case MAIL_SERVER_IN_FLOW_DELAY:
multi_server_in_flow_delay = va_arg(ap, int);
break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}

View File

@@ -92,6 +92,9 @@
/* Function to be executed prior to accepting a new connection.
/* .sp
/* Only the last instance of this parameter type is remembered.
/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)"
/* The amount of seconds to pause when no "mail flow control token"
/* is available. A token is consumed for each connection request.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
@@ -184,7 +187,7 @@ static void (*single_server_accept) (int, char *);
static void (*single_server_onexit) (char *, char **);
static void (*single_server_pre_accept) (char *, char **);
static VSTREAM *single_server_lock;
static int single_server_flow_ctl;
static int single_server_in_flow_delay;
/* single_server_exit - normal termination */
@@ -233,6 +236,8 @@ static void single_server_wakeup(int fd)
timed_ipc_setup(stream);
if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0)
single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
if (single_server_in_flow_delay > 0 && mail_flow_get(1) < 0)
doze(single_server_in_flow_delay * 1000000);
single_server_service(stream, single_server_name, single_server_argv);
(void) vstream_fclose(stream);
if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0)
@@ -240,9 +245,6 @@ static void single_server_wakeup(int fd)
if (msg_verbose)
msg_info("connection closed");
use_count++;
if (single_server_flow_ctl)
if (mail_flow_get(1) < 0)
rand_sleep(single_server_flow_ctl, 0);
if (var_idle_limit > 0)
event_request_timer(single_server_timeout, (char *) 0, var_idle_limit);
}
@@ -474,8 +476,8 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
case MAIL_SERVER_PRE_ACCEPT:
single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
case MAIL_SERVER_FLOW_CTL:
single_server_flow_ctl = var_glob_flow_ctl;
case MAIL_SERVER_IN_FLOW_DELAY:
single_server_in_flow_delay = va_arg(ap, int);
break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);

View File

@@ -99,6 +99,9 @@
/* Function to be executed prior to accepting a new request.
/* .sp
/* Only the last instance of this parameter type is remembered.
/* .IP "MAIL_SERVER_IN_FLOW_DELAY (int)"
/* The amount of seconds to pause when no "mail flow control token"
/* is available. A token is consumed for each connection request.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
@@ -169,6 +172,7 @@
#include <debug_process.h>
#include <mail_conf.h>
#include <resolve_local.h>
#include <mail_flow.h>
/* Process manager. */
@@ -190,6 +194,7 @@ static void (*trigger_server_accept) (int, char *);
static void (*trigger_server_onexit) (char *, char **);
static void (*trigger_server_pre_accept) (char *, char **);
static VSTREAM *trigger_server_lock;
static int trigger_server_in_flow_delay;
/* trigger_server_exit - normal termination */
@@ -230,6 +235,8 @@ static void trigger_server_wakeup(int fd)
*/
if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0)
trigger_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
if (trigger_server_in_flow_delay > 0 && mail_flow_get(1) < 0)
doze(trigger_server_in_flow_delay * 1000000);
if ((len = read(fd, buf, sizeof(buf))) >= 0)
trigger_server_service(buf, len, trigger_server_name,
trigger_server_argv);
@@ -469,6 +476,9 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,..
case MAIL_SERVER_PRE_ACCEPT:
trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
break;
case MAIL_SERVER_IN_FLOW_DELAY:
trigger_server_in_flow_delay = va_arg(ap, int);
break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}

View File

@@ -421,7 +421,7 @@ static int qmgr_loop(char *unused_name, char **unused_argv)
{
char *in_path = 0;
char *df_path = 0;
int token_count;
int token_count;
/*
* This routine runs as part of the event handling loop, after the event
@@ -456,10 +456,10 @@ int token_count;
* Global flow control. If enabled, slow down receiving processes that
* get ahead of the queue manager, but don't block them completely.
*/
if (var_glob_flow_ctl) {
if (var_in_flow_delay > 0) {
if (in_path != 0)
mail_flow_put(1);
else if ((token_count = peekfd(MASTER_FLOW_READ)) < var_proc_limit)
else if ((token_count = mail_flow_count()) < var_proc_limit)
mail_flow_put(var_proc_limit - token_count);
else if (token_count > var_proc_limit)
mail_flow_get(token_count - var_proc_limit);
@@ -514,7 +514,7 @@ static void qmgr_post_init(char *unused_name, char **unused_argv)
qmgr_incoming = qmgr_scan_create(MAIL_QUEUE_INCOMING);
qmgr_deferred = qmgr_scan_create(MAIL_QUEUE_DEFERRED);
qmgr_scan_request(qmgr_incoming, QMGR_SCAN_START);
qmgr_deferred_run_event(0, (char *) 0);
qmgr_deferred_run_event(0, (char *)0);
}
/* main - the main program */

View File

@@ -418,10 +418,10 @@ static int qmgr_loop(char *unused_name, char **unused_argv)
* Global flow control. If enabled, slow down receiving processes that
* get ahead of the queue manager, but don't block them completely.
*/
if (var_glob_flow_ctl) {
if (var_in_flow_delay > 0) {
if (in_path != 0)
mail_flow_put(1);
else if ((token_count = peekfd(MASTER_FLOW_READ)) < var_proc_limit)
else if ((token_count = mail_flow_count()) < var_proc_limit)
mail_flow_put(var_proc_limit - token_count);
else if (token_count > var_proc_limit)
mail_flow_get(token_count - var_proc_limit);

View File

@@ -67,6 +67,7 @@ typedef struct SMTPD_STATE {
int recursion;
off_t msg_size;
int junk_cmds;
VSTRING *error_text;
#ifdef USE_SASL_AUTH
char *sasl_mechanism_list;
char *sasl_method;

View File

@@ -269,6 +269,7 @@
#include <mymalloc.h>
#include <dict.h>
#include <htable.h>
#include <ctable.h>
/* DNS library. */
@@ -310,9 +311,8 @@ static jmp_buf smtpd_check_buf;
* Intermediate results. These are static to avoid unnecessary stress on the
* memory manager routines.
*/
static RESOLVE_REPLY reply;
static VSTRING *query;
static VSTRING *error_text;
static CTABLE *smtpd_resolve_cache;
/*
* Pre-opened SMTP recipient maps so we can reject mail for unknown users.
@@ -346,7 +346,7 @@ static HTABLE *smtpd_rest_classes;
/*
* The routine that recursively applies restrictions.
*/
static int generic_checks(SMTPD_STATE *, ARGV *, char *, char *, char *);
static int generic_checks(SMTPD_STATE *, ARGV *, const char *, const char *, const char *);
/*
* Reject context.
@@ -361,6 +361,49 @@ static int generic_checks(SMTPD_STATE *, ARGV *, char *, char *, char *);
* YASLM.
*/
#define STR vstring_str
#define CONST_STR(x) ((const char *) vstring_str(x))
/* resolve_pagein - page in an address resolver result */
static void *resolve_pagein(const char *addr, void *unused_context)
{
static VSTRING *query;
RESOLVE_REPLY *reply;
/*
* Initialize on the fly.
*/
if (query == 0)
query = vstring_alloc(10);
/*
* Initialize.
*/
reply = (RESOLVE_REPLY *) mymalloc(sizeof(*reply));
resolve_clnt_init(reply);
/*
* Resolve the address.
*/
canon_addr_internal(query, addr);
resolve_clnt_query(STR(query), reply);
lowercase(STR(reply->recipient));
/*
* Save the result.
*/
return ((void *) reply);
}
/* resolve_pageout - page out an address resolver result */
static void resolve_pageout(void *data, void *unused_context)
{
RESOLVE_REPLY *reply = (RESOLVE_REPLY *) data;
resolve_clnt_free(reply);
myfree((void *) reply);
}
/* smtpd_check_parse - pre-parse restrictions */
@@ -474,13 +517,16 @@ void smtpd_check_init(void)
DICT_FLAG_LOCK);
/*
* Reply is used as a cache for resolved addresses, and error_text is
* used for returning error responses.
* error_text is used for returning error responses.
*/
resolve_clnt_init(&reply);
query = vstring_alloc(10);
error_text = vstring_alloc(10);
/*
* Initialize the resolved address cache.
*/
smtpd_resolve_cache = ctable_create(100, resolve_pagein,
resolve_pageout, (void *) 0);
/*
* Pre-parse the restriction lists. At the same time, pre-open tables
* before going to jail.
@@ -623,8 +669,10 @@ static const char *check_maps_find(SMTPD_STATE *state, const char *reply_name,
/* check_mail_addr_find - reject with temporary failure if dict lookup fails */
static const char *check_mail_addr_find(SMTPD_STATE *state, const char *reply_name,
MAPS *maps, const char *key, char **ext)
static const char *check_mail_addr_find(SMTPD_STATE *state,
const char *reply_name,
MAPS *maps, const char *key,
char **ext)
{
const char *result;
@@ -819,8 +867,8 @@ static int reject_unknown_hostname(SMTPD_STATE *state, char *name,
/* reject_unknown_mailhost - fail if name has no A or MX record */
static int reject_unknown_mailhost(SMTPD_STATE *state, char *name,
char *reply_name, char *reply_class)
static int reject_unknown_mailhost(SMTPD_STATE *state, const char *name,
const char *reply_name, const char *reply_class)
{
char *myname = "reject_unknown_mailhost";
int dns_status;
@@ -876,7 +924,8 @@ static int check_relay_domains(SMTPD_STATE *state, char *recipient,
static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
{
char *myname = "permit_auth_destination";
char *domain;
const RESOLVE_REPLY *reply;
const char *domain;
if (msg_verbose)
msg_info("%s: %s", myname, recipient);
@@ -884,14 +933,13 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
/*
* Resolve the address.
*/
canon_addr_internal(query, recipient);
resolve_clnt_query(STR(query), &reply);
lowercase(STR(reply.recipient));
reply = (const RESOLVE_REPLY *)
ctable_locate(smtpd_resolve_cache, recipient);
/*
* Handle special case that is not supposed to happen.
*/
if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
@@ -909,7 +957,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient)
/*
* Skip source-routed mail (uncertain destination).
*/
if (var_allow_untrust_route == 0 && (reply.flags & RESOLVE_FLAG_ROUTED))
if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED))
return (SMTPD_CHECK_DUNNO);
/*
@@ -968,7 +1016,7 @@ static int reject_unauth_pipelining(SMTPD_STATE *state)
/* has_my_addr - see if this host name lists one of my network addresses */
static int has_my_addr(char *host)
static int has_my_addr(const char *host)
{
char *myname = "has_my_addr";
struct in_addr addr;
@@ -1012,7 +1060,8 @@ static int has_my_addr(char *host)
static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
{
char *myname = "permit_mx_backup";
char *domain;
const RESOLVE_REPLY *reply;
const char *domain;
DNS_RR *mx_list;
DNS_RR *mx;
@@ -1024,15 +1073,14 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
/*
* Resolve the address.
*/
canon_addr_internal(query, recipient);
resolve_clnt_query(STR(query), &reply);
lowercase(STR(reply.recipient));
reply = (const RESOLVE_REPLY *)
ctable_locate(smtpd_resolve_cache, recipient);
/*
* If the destination is local, it is acceptable, because we are
* supposedly MX for our own address.
*/
if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
return (SMTPD_CHECK_OK);
domain += 1;
if (resolve_local(domain)
@@ -1048,7 +1096,7 @@ static int permit_mx_backup(SMTPD_STATE *state, const char *recipient)
/*
* Skip source-routed mail (uncertain destination).
*/
if (var_allow_untrust_route == 0 && (reply.flags & RESOLVE_FLAG_ROUTED))
if (var_allow_untrust_route == 0 && (reply->flags & RESOLVE_FLAG_ROUTED))
return (SMTPD_CHECK_DUNNO);
/*
@@ -1157,11 +1205,12 @@ static int reject_non_fqdn_address(SMTPD_STATE *state, char *addr,
/* reject_unknown_address - fail if address does not resolve */
static int reject_unknown_address(SMTPD_STATE *state, char *addr,
char *reply_name, char *reply_class)
static int reject_unknown_address(SMTPD_STATE *state, const char *addr,
const char *reply_name, const char *reply_class)
{
char *myname = "reject_unknown_address";
char *domain;
const RESOLVE_REPLY *reply;
const char *domain;
if (msg_verbose)
msg_info("%s: %s", myname, addr);
@@ -1169,14 +1218,12 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr,
/*
* Resolve the address.
*/
canon_addr_internal(query, addr);
resolve_clnt_query(STR(query), &reply);
lowercase(STR(reply.recipient));
reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr);
/*
* Skip local destinations and non-DNS forms.
*/
if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
return (SMTPD_CHECK_DUNNO);
domain += 1;
if (resolve_local(domain)
@@ -1198,10 +1245,11 @@ static int reject_unknown_address(SMTPD_STATE *state, char *addr,
/* check_table_result - translate table lookup result into pass/reject */
static int check_table_result(SMTPD_STATE *state, char *table,
static int check_table_result(SMTPD_STATE *state, const char *table,
const char *value, const char *datum,
char *reply_name, char *reply_class,
char *def_acl)
const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "check_table_result";
int code;
@@ -1296,17 +1344,20 @@ static int check_table_result(SMTPD_STATE *state, char *table,
/* check_access - table lookup without substring magic */
static int check_access(SMTPD_STATE *state, char *table, char *name, int flags,
char *reply_name, char *reply_class, char *def_acl)
static int check_access(SMTPD_STATE *state, const char *table, const char *name,
int flags, int *found, const char *reply_name,
const char *reply_class, const char *def_acl)
{
char *myname = "check_access";
char *low_name = lowercase(mystrdup(name));
const char *value;
DICT *dict;
#define CHK_ACCESS_RETURN(x) { myfree(low_name); return(x); }
#define CHK_ACCESS_RETURN(x,y) { *found = y; myfree(low_name); return(x); }
#define FULL 0
#define PARTIAL DICT_FLAG_FIXED
#define FOUND 1
#define MISSED 0
if (msg_verbose)
msg_info("%s: %s", myname, name);
@@ -1317,19 +1368,20 @@ static int check_access(SMTPD_STATE *state, char *table, char *name, int flags,
if ((value = dict_get(dict, low_name)) != 0)
CHK_ACCESS_RETURN(check_table_result(state, table, value, name,
reply_name, reply_class,
def_acl));
def_acl), FOUND);
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
}
CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO);
CHK_ACCESS_RETURN(SMTPD_CHECK_DUNNO, MISSED);
}
/* check_domain_access - domainname-based table lookup */
static int check_domain_access(SMTPD_STATE *state, char *table,
char *domain, int flags,
char *reply_name, char *reply_class,
char *def_acl)
static int check_domain_access(SMTPD_STATE *state, const char *table,
const char *domain, int flags,
int *found, const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "check_domain_access";
char *low_domain = lowercase(mystrdup(domain));
@@ -1344,7 +1396,7 @@ static int check_domain_access(SMTPD_STATE *state, char *table,
/*
* Try the name and its parent domains. Including top-level domains.
*/
#define CHK_DOMAIN_RETURN(x) { myfree(low_domain); return(x); }
#define CHK_DOMAIN_RETURN(x,y) { *found = y; myfree(low_domain); return(x); }
if ((dict = dict_handle(table)) == 0)
msg_panic("%s: dictionary not found: %s", myname, table);
@@ -1353,7 +1405,7 @@ static int check_domain_access(SMTPD_STATE *state, char *table,
if ((value = dict_get(dict, name)) != 0)
CHK_DOMAIN_RETURN(check_table_result(state, table, value,
domain, reply_name, reply_class,
def_acl));
def_acl), FOUND);
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
}
@@ -1361,15 +1413,16 @@ static int check_domain_access(SMTPD_STATE *state, char *table,
break;
flags = PARTIAL;
}
CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO);
CHK_DOMAIN_RETURN(SMTPD_CHECK_DUNNO, MISSED);
}
/* check_addr_access - address-based table lookup */
static int check_addr_access(SMTPD_STATE *state, char *table,
char *address, int flags,
char *reply_name, char *reply_class,
char *def_acl)
static int check_addr_access(SMTPD_STATE *state, const char *table,
const char *address, int flags,
int *found, const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "check_addr_access";
char *addr;
@@ -1382,6 +1435,8 @@ static int check_addr_access(SMTPD_STATE *state, char *table,
/*
* Try the address and its parent networks.
*/
#define CHK_ADDR_RETURN(x,y) { *found = y; return(x); }
addr = STR(vstring_strcpy(error_text, address));
if ((dict = dict_handle(table)) == 0)
@@ -1389,24 +1444,26 @@ static int check_addr_access(SMTPD_STATE *state, char *table,
do {
if (flags == 0 || (flags & dict->flags) != 0) {
if ((value = dict_get(dict, addr)) != 0)
return (check_table_result(state, table, value, address,
CHK_ADDR_RETURN(check_table_result(state, table, value, address,
reply_name, reply_class,
def_acl));
def_acl), FOUND);
if (dict_errno != 0)
msg_fatal("%s: table lookup problem", table);
}
flags = PARTIAL;
} while (split_at_right(addr, '.'));
return (SMTPD_CHECK_DUNNO);
CHK_ADDR_RETURN(SMTPD_CHECK_DUNNO, MISSED);
}
/* check_namadr_access - OK/FAIL based on host name/address lookup */
static int check_namadr_access(SMTPD_STATE *state, char *table,
char *name, char *addr, int flags,
char *reply_name, char *reply_class,
char *def_acl)
static int check_namadr_access(SMTPD_STATE *state, const char *table,
const char *name, const char *addr,
int flags, int *found,
const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "check_namadr_access";
int status;
@@ -1419,16 +1476,16 @@ static int check_namadr_access(SMTPD_STATE *state, char *table,
* wildcard may pre-empt a more specific address table entry.
*/
if ((status = check_domain_access(state, table, name, flags,
reply_name, reply_class,
def_acl)) != 0)
found, reply_name, reply_class,
def_acl)) != 0 || *found)
return (status);
/*
* Look up the network address, or parent networks thereof.
*/
if ((status = check_addr_access(state, table, addr, flags,
reply_name, reply_class,
def_acl)) != 0)
found, reply_name, reply_class,
def_acl)) != 0 || *found)
return (status);
/*
@@ -1439,12 +1496,15 @@ static int check_namadr_access(SMTPD_STATE *state, char *table,
/* check_mail_access - OK/FAIL based on mail address lookup */
static int check_mail_access(SMTPD_STATE *state, char *table, char *addr,
char *reply_name, char *reply_class,
char *def_acl)
static int check_mail_access(SMTPD_STATE *state, const char *table,
const char *addr, int *found,
const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "check_mail_access";
char *ratsign;
const RESOLVE_REPLY *reply;
const char *ratsign;
int status;
char *local_at;
@@ -1454,39 +1514,39 @@ static int check_mail_access(SMTPD_STATE *state, char *table, char *addr,
/*
* Resolve the address.
*/
canon_addr_internal(query, addr);
resolve_clnt_query(STR(query), &reply);
lowercase(STR(reply.recipient));
reply = (const RESOLVE_REPLY *) ctable_locate(smtpd_resolve_cache, addr);
/*
* Garbage in, garbage out. Every address from canon_addr_internal() and
* from resolve_clnt_query() must be fully qualified.
*/
if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) {
msg_warn("%s: no @domain in address: %s", myname, STR(reply.recipient));
if ((ratsign = strrchr(CONST_STR(reply->recipient), '@')) == 0) {
msg_warn("%s: no @domain in address: %s", myname, CONST_STR(reply->recipient));
return (0);
}
/*
* Look up the full address.
*/
if ((status = check_access(state, table, STR(reply.recipient), FULL,
reply_name, reply_class, def_acl)) != 0)
if ((status = check_access(state, table, CONST_STR(reply->recipient), FULL,
found, reply_name, reply_class, def_acl)) != 0
|| *found)
return (status);
/*
* Look up the domain name, or parent domains thereof.
*/
if ((status = check_domain_access(state, table, ratsign + 1, PARTIAL,
reply_name, reply_class, def_acl)) != 0)
found, reply_name, reply_class, def_acl)) != 0
|| *found)
return (status);
/*
* Look up localpart@
*/
local_at = mystrndup(STR(reply.recipient),
ratsign - STR(reply.recipient) + 1);
status = check_access(state, table, local_at, PARTIAL,
local_at = mystrndup(CONST_STR(reply->recipient),
ratsign - CONST_STR(reply->recipient) + 1);
status = check_access(state, table, local_at, PARTIAL, found,
reply_name, reply_class, def_acl);
myfree(local_at);
if (status != 0)
@@ -1587,7 +1647,7 @@ static int reject_maps_rbl(SMTPD_STATE *state)
/* is_map_command - restriction has form: check_xxx_access type:name */
static int is_map_command(char *name, char *command, char ***argp)
static int is_map_command(const char *name, const char *command, char ***argp)
{
/*
@@ -1610,13 +1670,16 @@ static int is_map_command(char *name, char *command, char ***argp)
/* generic_checks - generic restrictions */
static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
char *reply_name, char *reply_class, char *def_acl)
const char *reply_name,
const char *reply_class,
const char *def_acl)
{
char *myname = "generic_checks";
char **cpp;
char *name;
const char *name;
int status = 0;
ARGV *list;
int found;
if (msg_verbose)
msg_info("%s: START", myname);
@@ -1663,7 +1726,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
status = permit_mynetworks(state);
} else if (is_map_command(name, CHECK_CLIENT_ACL, &cpp)) {
status = check_namadr_access(state, *cpp, state->name, state->addr,
FULL, state->namaddr,
FULL, &found, state->namaddr,
SMTPD_NAME_CLIENT, def_acl);
} else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
status = reject_maps_rbl(state);
@@ -1675,7 +1738,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
else if (is_map_command(name, CHECK_HELO_ACL, &cpp)) {
if (state->helo_name)
status = check_domain_access(state, *cpp, state->helo_name,
FULL, state->helo_name,
FULL, &found, state->helo_name,
SMTPD_NAME_HELO, def_acl);
} else if (strcasecmp(name, REJECT_INVALID_HOSTNAME) == 0) {
if (state->helo_name) {
@@ -1719,7 +1782,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
else if (is_map_command(name, CHECK_SENDER_ACL, &cpp)) {
if (state->sender && *state->sender)
status = check_mail_access(state, *cpp, state->sender,
state->sender,
&found, state->sender,
SMTPD_NAME_SENDER, def_acl);
} else if (strcasecmp(name, REJECT_UNKNOWN_ADDRESS) == 0) {
if (state->sender && *state->sender)
@@ -1741,7 +1804,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
else if (is_map_command(name, CHECK_RECIP_ACL, &cpp)) {
if (state->recipient)
status = check_mail_access(state, *cpp, state->recipient,
state->recipient,
&found, state->recipient,
SMTPD_NAME_RECIPIENT, def_acl);
} else if (strcasecmp(name, PERMIT_MX_BACKUP) == 0) {
if (state->recipient)
@@ -1781,7 +1844,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
else if (is_map_command(name, CHECK_ETRN_ACL, &cpp)) {
if (state->etrn_name)
status = check_domain_access(state, *cpp, state->etrn_name,
FULL, state->etrn_name,
FULL, &found, state->etrn_name,
SMTPD_NAME_ETRN, def_acl);
}
@@ -2023,7 +2086,8 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
{
char *myname = "smtpd_check_rcptmap";
char *saved_recipient;
char *domain;
const RESOLVE_REPLY *reply;
const char *domain;
int status;
/*
@@ -2050,14 +2114,13 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
/*
* Resolve the address.
*/
canon_addr_internal(query, recipient);
resolve_clnt_query(STR(query), &reply);
lowercase(STR(reply.recipient));
reply = (const RESOLVE_REPLY *)
ctable_locate(smtpd_resolve_cache, recipient);
/*
* Skip non-DNS forms. Skip non-local numerical forms.
*/
if ((domain = strrchr(STR(reply.recipient), '@')) == 0)
if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0)
SMTPD_CHECK_RCPT_RETURN(0);
domain += 1;
if (domain[0] == '#' || domain[0] == '[')
@@ -2072,11 +2135,11 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
*/
if (*var_virtual_maps
&& (check_maps_find(state, recipient, virtual_maps, domain, 0))) {
if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
&& NOMATCH(canonical_maps, STR(reply.recipient))
&& NOMATCH(relocated_maps, STR(reply.recipient))
&& NOMATCH(virt_mailbox_maps, STR(reply.recipient))
&& NOMATCH(virtual_maps, STR(reply.recipient))) {
if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
&& NOMATCH(canonical_maps, CONST_STR(reply->recipient))
&& NOMATCH(relocated_maps, CONST_STR(reply->recipient))
&& NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient))
&& NOMATCH(virtual_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown", 550, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
@@ -2088,11 +2151,11 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
*/
if (*var_virt_mailbox_maps
&& (check_maps_find(state, recipient, virt_mailbox_maps, domain, 0))) {
if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
&& NOMATCH(canonical_maps, STR(reply.recipient))
&& NOMATCH(relocated_maps, STR(reply.recipient))
&& NOMATCH(virt_mailbox_maps, STR(reply.recipient))
&& NOMATCH(virtual_maps, STR(reply.recipient))) {
if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
&& NOMATCH(canonical_maps, CONST_STR(reply->recipient))
&& NOMATCH(relocated_maps, CONST_STR(reply->recipient))
&& NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient))
&& NOMATCH(virtual_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown", 550, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
@@ -2105,12 +2168,12 @@ char *smtpd_check_rcptmap(SMTPD_STATE *state, char *recipient)
* Sendmail-style virtual domains.
*/
if (*var_local_rcpt_maps && resolve_local(domain)) {
if (NOMATCH(rcpt_canon_maps, STR(reply.recipient))
&& NOMATCH(canonical_maps, STR(reply.recipient))
&& NOMATCH(relocated_maps, STR(reply.recipient))
&& NOMATCH(virt_mailbox_maps, STR(reply.recipient))
&& NOMATCH(virtual_maps, STR(reply.recipient))
&& NOMATCH(local_rcpt_maps, STR(reply.recipient))) {
if (NOMATCH(rcpt_canon_maps, CONST_STR(reply->recipient))
&& NOMATCH(canonical_maps, CONST_STR(reply->recipient))
&& NOMATCH(relocated_maps, CONST_STR(reply->recipient))
&& NOMATCH(virt_mailbox_maps, CONST_STR(reply->recipient))
&& NOMATCH(virtual_maps, CONST_STR(reply->recipient))
&& NOMATCH(local_rcpt_maps, CONST_STR(reply->recipient))) {
(void) smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
"%d <%s>: User unknown", 550, recipient);
SMTPD_CHECK_RCPT_RETURN(STR(error_text));
@@ -2377,11 +2440,19 @@ static void rest_class(char *class)
void resolve_clnt_init(RESOLVE_REPLY *reply)
{
reply->flags = 0;
reply->transport = vstring_alloc(100);
reply->nexthop = vstring_alloc(100);
reply->recipient = vstring_alloc(100);
}
void resolve_clnt_free(RESOLVE_REPLY *reply)
{
vstring_free(reply->transport);
vstring_free(reply->nexthop);
vstring_free(reply->recipient);
}
#ifdef USE_SASL_AUTH
bool var_smtpd_sasl_enable = 0;
@@ -2424,7 +2495,7 @@ VSTRING *canon_addr_internal(VSTRING *result, const char *addr)
void resolve_clnt_query(const char *addr, RESOLVE_REPLY *reply)
{
if (addr == STR(reply->recipient))
if (addr == CONST_STR(reply->recipient))
msg_panic("resolve_clnt_query: result clobbers input");
vstring_strcpy(reply->transport, "foo");
vstring_strcpy(reply->nexthop, "foo");
@@ -2446,7 +2517,7 @@ static NORETURN usage(char *myname)
msg_fatal("usage: %s", myname);
}
main(int argc, char **argv)
int main(int argc, char **argv)
{
VSTRING *buf = vstring_alloc(100);
SMTPD_STATE state;

View File

@@ -91,6 +91,7 @@ void smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream)
state->recursion = 0;
state->msg_size = 0;
state->junk_cmds = 0;
state->error_text = vstring_alloc(100);
#ifdef USE_SASL_AUTH
if (SMTPD_STAND_ALONE(state))
@@ -123,6 +124,7 @@ void smtpd_state_reset(SMTPD_STATE *state)
if (state->buffer)
vstring_free(state->buffer);
smtpd_peer_reset(state);
vstring_free(state->error_text);
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)

View File

@@ -196,7 +196,7 @@ int smtpd_token(char *cp, SMTPD_TOKEN **argvp)
#include <vstream.h>
#include <vstring_vstream.h>
main(int unused_argc, char **unused_argv)
int main(int unused_argc, char **unused_argv)
{
VSTRING *vp = vstring_alloc(10);
int tok_argc;

View File

@@ -23,7 +23,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
sane_socketpair.c myrand.c netstring.c
sane_socketpair.c myrand.c netstring.c ctable.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@@ -48,7 +48,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
sane_socketpair.o myrand.o netstring.o
sane_socketpair.o myrand.o netstring.o ctable.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@@ -64,7 +64,7 @@ HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \
dict_unix.h dict_pcre.h dict_regexp.h mac_expand.h clean_env.h \
watchdog.h spawn_command.h sane_fsops.h dict_tcp.h hex_quote.h \
sane_time.h sane_socketpair.h myrand.h netstring.h
sane_time.h sane_socketpair.h myrand.h netstring.h ctable.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
@@ -80,7 +80,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
inet_addr_host inet_addr_local mac_parse make_dirs msg_syslog \
mystrtok sigdelay translit valid_hostname vstream_popen \
vstring vstring_vstream doze select_bug stream_test mac_expand \
watchdog unescape hex_quote name_mask rand_sleep sane_time
watchdog unescape hex_quote name_mask rand_sleep sane_time ctable
LIB_DIR = ../../lib
INC_DIR = ../../include
@@ -274,6 +274,11 @@ sane_time: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
ctable: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
@@ -286,7 +291,7 @@ stream_test: stream_test.c $(LIB)
$(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS)
tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test
hex_quote_test cache_test
valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
@@ -309,6 +314,11 @@ hex_quote_test: hex_quote
cmp hex_quote.ref hex_quote.tmp
rm -f hex_quote.ref hex_quote.tmp
ctable_test: ctable
./ctable <ctable.in >ctable.tmp 2>&1
diff ctable.ref ctable.tmp
rm -f ctable.tmp
DB_TYPE = `../postconf/postconf -h default_database_type`
dict_test: dict_open testdb dict_test.in dict_test.ref
@@ -368,6 +378,13 @@ concatenate.o: mymalloc.h
concatenate.o: stringops.h
concatenate.o: vstring.h
concatenate.o: vbuf.h
ctable.o: ctable.c
ctable.o: sys_defs.h
ctable.o: msg.h
ctable.o: mymalloc.h
ctable.o: ring.h
ctable.o: htable.h
ctable.o: ctable.h
dict.o: dict.c
dict.o: sys_defs.h
dict.o: msg.h

BIN
postfix/src/util/cache Executable file

Binary file not shown.

26
postfix/src/util/cache.in Normal file
View File

@@ -0,0 +1,26 @@
a
1
b
2
c
3
d
4
e
5
f
6
f
e
d
c
b
a
1
b
c
d
e
f
6
f

273
postfix/src/util/ctable.c Normal file
View File

@@ -0,0 +1,273 @@
/*++
/* NAME
/* ctable 3
/* SUMMARY
/* cache manager
/* SYNOPSIS
/* #include <ctable.h>
/*
/* CTABLE *ctable_create(limit, create, delete, context)
/* int limit;
/* void *(*create)(const char *key, void *context);
/* void (*delete)(void *value, void *context);
/* void *context;
/*
/* const void *ctable_locate(cache, key)
/* CTABLE *cache;
/* const char *key;
/*
/* void ctable_free(cache)
/* CTABLE *cache;
/*
/* void ctable_walk(cache, action)
/* CTABLE *cache;
/* void (*action)(const char *key, const void *value);
/* DESCRIPTION
/* This module maintains multiple caches. Cache items are purged
/* automatically when the number of items exceeds a configurable
/* limit. Caches never shrink. Each cache entry consists of a
/* string-valued lookup key and a generic data pointer value.
/*
/* ctable_create() creates a cache with the specified size limit, and
/* returns a pointer to the result. The create and delete arguments
/* specify pointers to call-back functions that create a value, given
/* a key, and delete a given value, respectively. The context argument
/* is passed on to the call-back routines.
/*
/* ctable_locate() looks up or generates the value that corresponds to
/* the specified key, and returns that value.
/*
/* ctable_free() destroys the specified cache, including its contents.
/*
/* ctable_walk() iterates over all elements in the cache, and invokes
/* the action function for each cache element with the corresponding
/* key and value as arguments. This function is useful mainly for
/* cache performance debugging.
/* DIAGNOSTICS
/* Fatal errors: out of memory. Panic: interface violation.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h>
#include <stddef.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <ring.h>
#include <htable.h>
#include <ctable.h>
/*
* Cache entries are kept in most-recently used order. We use a hash table
* to quickly locate cache entries.
*/
#define CTABLE_ENTRY struct ctable_entry
struct ctable_entry {
RING ring; /* MRU linkage */
const char *key; /* lookup key */
void *value; /* corresponding value */
};
#define RING_TO_CTABLE_ENTRY(ring_ptr) \
RING_TO_APPL(ring_ptr, CTABLE_ENTRY, ring)
#define RING_PTR_OF(x) (&((x)->ring))
struct ctable {
HTABLE *table; /* table with key, ctable_entry pairs */
unsigned limit; /* max nr of entries */
unsigned used; /* current nr of entries */
CTABLE_CREATE_FN create; /* constructor */
CTABLE_DELETE_FN delete; /* destructor */
RING ring; /* MRU linkage */
void *context; /* application context */
};
#define CTABLE_MIN_SIZE 5
/* ctable_create - create empty cache */
CTABLE *ctable_create(int limit, CTABLE_CREATE_FN create,
CTABLE_DELETE_FN delete, void *context)
{
CTABLE *cache = (CTABLE *) mymalloc(sizeof(CTABLE));
char *myname = "ctable_create";
if (limit < 1)
msg_panic("%s: bad cache limit: %d", myname, limit);
cache->table = htable_create(limit);
cache->limit = (limit < CTABLE_MIN_SIZE ? CTABLE_MIN_SIZE : limit);
cache->used = 0;
cache->create = create;
cache->delete = delete;
ring_init(RING_PTR_OF(cache));
cache->context = context;
return (cache);
}
/* ctable_locate - look up or create cache item */
const void *ctable_locate(CTABLE *cache, const char *key)
{
char *myname = "ctable_locate";
CTABLE_ENTRY *entry;
/*
* If the entry is not in the cache, make sure there is room for a new
* entry and install it at the front of the MRU chain. Otherwise, move
* the entry to the front of the MRU chain if it is not already there.
* All this means that the cache never shrinks.
*/
if ((entry = (CTABLE_ENTRY *) htable_find(cache->table, key)) == 0) {
if (cache->used >= cache->limit) {
entry = RING_TO_CTABLE_ENTRY(ring_pred(RING_PTR_OF(cache)));
if (msg_verbose)
msg_info("%s: purge entry key %s", myname, entry->key);
ring_detach(RING_PTR_OF(entry));
cache->delete(entry->value, cache->context);
htable_delete(cache->table, entry->key, (void (*) (char *)) 0);
} else {
entry = (CTABLE_ENTRY *) mymalloc(sizeof(CTABLE_ENTRY));
cache->used++;
}
entry->value = cache->create(key, cache->context);
entry->key = htable_enter(cache->table, key, (char *) entry)->key;
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
if (msg_verbose)
msg_info("%s: install entry key %s", myname, entry->key);
} else if (entry == RING_TO_CTABLE_ENTRY(ring_succ(RING_PTR_OF(cache)))) {
if (msg_verbose)
msg_info("%s: leave existing entry key %s", myname, entry->key);
} else {
ring_detach(RING_PTR_OF(entry));
ring_append(RING_PTR_OF(cache), RING_PTR_OF(entry));
if (msg_verbose)
msg_info("%s: move existing entry key %s", myname, entry->key);
}
return (entry->value);
}
static CTABLE *ctable_free_cache;
/* ctable_free_callback - callback function */
static void ctable_free_callback(char *ptr)
{
CTABLE_ENTRY *entry = (CTABLE_ENTRY *) ptr;
ctable_free_cache->delete(entry->value, ctable_free_cache->context);
myfree((char *) entry);
}
/* ctable_free - destroy cache and contents */
void ctable_free(CTABLE *cache)
{
CTABLE *saved_cache = ctable_free_cache;
/*
* XXX the hash table does not pass application context so we have to
* store it in a global variable.
*/
ctable_free_cache = cache;
htable_free(cache->table, ctable_free_callback);
myfree((char *) cache);
ctable_free_cache = saved_cache;
}
/* ctable_walk - iterate over all cache entries */
void ctable_walk(CTABLE *cache, void (*action) (const char *, const void *))
{
RING *entry = RING_PTR_OF(cache);
/* Walking down the MRU chain is less work than using ht_walk(). */
while ((entry = ring_succ(entry)) != RING_PTR_OF(cache))
action((RING_TO_CTABLE_ENTRY(entry)->key),
(RING_TO_CTABLE_ENTRY(entry)->value));
}
#ifdef TEST
/*
* Proof-of-concept test program. Read keys from stdin, ask for values not
* in cache.
*/
#include <unistd.h>
#include <vstream.h>
#include <vstring.h>
#include <vstring_vstream.h>
#include <msg_vstream.h>
#define STR(x) vstring_str(x)
static void *ask(const char *key, void *context)
{
VSTRING *data_buf = (VSTRING *) context;
vstream_printf("ask: %s = ", key);
vstream_fflush(VSTREAM_OUT);
if (vstring_get_nonl(data_buf, VSTREAM_IN) == VSTREAM_EOF)
vstream_longjmp(VSTREAM_IN, 1);
if (!isatty(0)) {
vstream_printf("%s\n", STR(data_buf));
vstream_fflush(VSTREAM_OUT);
}
return (mystrdup(STR(data_buf)));
}
static void drop(void *data, void *unused_context)
{
myfree(data);
}
int main(int unused_argc, char **argv)
{
VSTRING *key_buf;
VSTRING *data_buf;
CTABLE *cache;
const char *value;
msg_vstream_init(argv[0], VSTREAM_ERR);
key_buf = vstring_alloc(100);
data_buf = vstring_alloc(100);
cache = ctable_create(1, ask, drop, (void *) data_buf);
msg_verbose = 1;
vstream_control(VSTREAM_IN, VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END);
if (vstream_setjmp(VSTREAM_IN) == 0) {
for (;;) {
vstream_printf("key = ");
vstream_fflush(VSTREAM_OUT);
if (vstring_get_nonl(key_buf, VSTREAM_IN) == VSTREAM_EOF)
vstream_longjmp(VSTREAM_IN, 1);
if (!isatty(0)) {
vstream_printf("%s\n", STR(key_buf));
vstream_fflush(VSTREAM_OUT);
}
value = ctable_locate(cache, STR(key_buf));
vstream_printf("result: %s\n", value);
}
}
ctable_free(cache);
vstring_free(key_buf);
vstring_free(data_buf);
return (0);
}
#endif

39
postfix/src/util/ctable.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef _CTABLE_H_INCLUDED_
#define _CTABLE_H_INCLUDED_
/*++
/* NAME
/* ctable 5
/* SUMMARY
/* cache manager
/* SYNOPSIS
/* #include <ctable.h>
/* DESCRIPTION
/* .nf
/*
* Interface of the cache manager. The structure of a cache is not visible
* to the caller.
*/
#define CTABLE struct ctable
typedef void *(*CTABLE_CREATE_FN) (const char *, void *);
typedef void (*CTABLE_DELETE_FN) (void *, void *);
extern CTABLE *ctable_create(int, CTABLE_CREATE_FN, CTABLE_DELETE_FN, void *);
extern void ctable_free(CTABLE *);
extern void ctable_walk(CTABLE *, void (*) (const char *, const void *));
extern const void *ctable_locate(CTABLE *, const char *);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
#endif

View File

@@ -0,0 +1,39 @@
a
1
b
2
c
3
d
4
e
5
f
6
f
a
1
b
2
c
3
d
4
e
5
f
6
f
e
d
c
b
a
1
b
c
d
e
f
6
f

View File

@@ -0,0 +1,99 @@
key = a
ask: a = 1
./ctable: ctable_locate: install entry key a
result: 1
key = b
ask: b = 2
./ctable: ctable_locate: install entry key b
result: 2
key = c
ask: c = 3
./ctable: ctable_locate: install entry key c
result: 3
key = d
ask: d = 4
./ctable: ctable_locate: install entry key d
result: 4
key = e
ask: e = 5
./ctable: ctable_locate: install entry key e
result: 5
key = f
./ctable: ctable_locate: purge entry key a
ask: f = 6
./ctable: ctable_locate: install entry key f
result: 6
key = f
./ctable: ctable_locate: leave existing entry key f
result: 6
key = a
./ctable: ctable_locate: purge entry key b
ask: a = 1
./ctable: ctable_locate: install entry key a
result: 1
key = b
./ctable: ctable_locate: purge entry key c
ask: b = 2
./ctable: ctable_locate: install entry key b
result: 2
key = c
./ctable: ctable_locate: purge entry key d
ask: c = 3
./ctable: ctable_locate: install entry key c
result: 3
key = d
./ctable: ctable_locate: purge entry key e
ask: d = 4
./ctable: ctable_locate: install entry key d
result: 4
key = e
./ctable: ctable_locate: purge entry key f
ask: e = 5
./ctable: ctable_locate: install entry key e
result: 5
key = f
./ctable: ctable_locate: purge entry key a
ask: f = 6
./ctable: ctable_locate: install entry key f
result: 6
key = f
./ctable: ctable_locate: leave existing entry key f
result: 6
key = e
./ctable: ctable_locate: move existing entry key e
result: 5
key = d
./ctable: ctable_locate: move existing entry key d
result: 4
key = c
./ctable: ctable_locate: move existing entry key c
result: 3
key = b
./ctable: ctable_locate: move existing entry key b
result: 2
key = a
./ctable: ctable_locate: purge entry key f
ask: a = 1
./ctable: ctable_locate: install entry key a
result: 1
key = b
./ctable: ctable_locate: move existing entry key b
result: 2
key = c
./ctable: ctable_locate: move existing entry key c
result: 3
key = d
./ctable: ctable_locate: move existing entry key d
result: 4
key = e
./ctable: ctable_locate: move existing entry key e
result: 5
key = f
./ctable: ctable_locate: purge entry key a
ask: f = 6
./ctable: ctable_locate: install entry key f
result: 6
key = f
./ctable: ctable_locate: leave existing entry key f
result: 6
key =

View File

@@ -151,6 +151,10 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
void (*saved_alarm) (int);
int rc = 0;
#ifdef LDAP_API_FEATURE_X_MEMCACHE
LDAPMemCache *dircache;
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT
struct timeval mytimeval;
@@ -162,7 +166,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
msg_info("%s: Connecting to server %s", myname,
dict_ldap->server_host);
#ifdef UNTESTED_LDAP_OPT_NETWORK_TIMEOUT
#ifdef LDAP_OPT_NETWORK_TIMEOUT
dict_ldap->ld = ldap_init(dict_ldap->server_host,
(int) dict_ldap->server_port);
if (dict_ldap->ld == NULL) {
@@ -247,6 +251,27 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
myname, dict_ldap->cache_size, dict_ldap->ldapsource,
dict_ldap->cache_expiry);
#ifdef LDAP_API_FEATURE_X_MEMCACHE
rc = ldap_memcache_init(dict_ldap->cache_expiry, dict_ldap->cache_size,
NULL, NULL, &dircache);
if (rc != LDAP_SUCCESS) {
msg_warn
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
} else {
rc = ldap_memcache_set(dict_ldap->ld, dircache);
if (rc != LDAP_SUCCESS) {
msg_warn
("%s: Unable to configure cache for %s: %d (%s) -- continuing",
myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
} else {
if (msg_verbose)
msg_info("%s: Caching enabled for %s",
myname, dict_ldap->ldapsource);
}
}
#else
rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry,
dict_ldap->cache_size);
if (rc != LDAP_SUCCESS) {
@@ -258,6 +283,8 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
msg_info("%s: Caching enabled for %s",
myname, dict_ldap->ldapsource);
}
#endif
}
if (msg_verbose)
msg_info("%s: Cached connection handle for LDAP source %s",
@@ -315,7 +342,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
if (strcasecmp(dict_ldap->result_attributes->argv[i],
attr) == 0) {
if (msg_verbose)
msg_info("%s: search returned value(s) for requested result attribute %s", myname, attr);
msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr);
break;
}
}
@@ -390,7 +417,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* load on the LDAP server.
*/
if (dict_ldap->domain) {
char *p=strrchr(name,'@');
const char *p=strrchr(name,'@');
if (p != 0)
p=p+1;
else
@@ -482,7 +509,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
/*
* Does the supplied query_filter even include a substitution?
*/
if ((char *) strstr(dict_ldap->query_filter, "%s") == NULL) {
if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {
/*
* No, log the fact and continue.
@@ -494,21 +521,40 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
/*
* Yes, replace all instances of %s with the address to look up.
* Replace %u with the user portion, and %d with the domain portion.
*/
sub = dict_ldap->query_filter;
end = sub + strlen(dict_ldap->query_filter);
while (sub < end) {
/*
* Make sure it's %s and not something else, though it wouldn't
* really matter; the token could be any single character.
* Make sure it's %[sud] and not something else. For backward
* compatibilty, treat anything other than %u or %d as %s, with
* a warning.
*/
if (*(sub) == '%') {
if ((sub + 1) != end && *(sub + 1) != 's')
char *u=vstring_str(escaped_name);
char *p=strchr(u,'@');
switch (*(sub+1)) {
case 'd':
if (p)
vstring_strcat(filter_buf, p+1);
break;
case 'u':
if (p)
vstring_strncat(filter_buf, u, p-u);
else
vstring_strcat(filter_buf, u);
break;
default:
msg_warn
("%s: Invalid lookup substitution format '%%%c'!",
myname, *(sub + 1));
vstring_strcat(filter_buf, vstring_str(escaped_name));
/* fall through */
case 's':
vstring_strcat(filter_buf, u);
break;
}
sub++;
} else
vstring_strncat(filter_buf, sub, 1);
@@ -607,6 +653,7 @@ static void dict_ldap_close(DICT *dict)
myfree(dict_ldap->ldapsource);
myfree(dict_ldap->server_host);
myfree(dict_ldap->search_base);
if (dict_ldap->domain)
match_list_free(dict_ldap->domain);
myfree(dict_ldap->query_filter);
argv_free(dict_ldap->result_attributes);
@@ -626,15 +673,15 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
char *scope;
char *attr;
if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource);
dict_ldap = (DICT_LDAP *) dict_alloc(DICT_TYPE_LDAP, ldapsource,
sizeof(*dict_ldap));
dict_ldap->dict.lookup = dict_ldap_lookup;
dict_ldap->dict.close = dict_ldap_close;
dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (msg_verbose)
msg_info("%s: Using LDAP source %s", myname, ldapsource);
dict_ldap->ldapsource = mystrdup(ldapsource);
config_param = vstring_alloc(15);

View File

@@ -29,6 +29,17 @@ extern void ring_detach(RING *);
#define ring_succ(c) ((c)->succ)
#define ring_pred(c) ((c)->pred)
/*
* Typically, an application will embed a RING structure into a larger
* structure that also contains application-specific members. This approach
* gives us the best of both worlds. The application can still use the
* generic RING primitives for manipulating RING structures. The macro below
* transforms a pointer from RING structure to the structure that contains
* it.
*/
#define RING_TO_APPL(ring_ptr,app_type,ring_member) \
((app_type *) (((char *) (ring_ptr)) - offsetof(app_type,ring_member)))
/* LICENSE
/* .ad
/* .fi