-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
Portability: on LINUX systems, if <db_185.h> exists, don't
look for <db/db.h>.
- Workaround: on Solaris 2.x, don't put an flock()/fcntl() lock
- on mailboxes, to avoid clashes with the mailtool application.
+ Workaround: specify "sun_mailtool_compatibility = yes" to
+ avoid clashes with the mailtool application. This disables
+ kernel locks on mailbox files. Use only where needed.
Portability: renamed readline to readlline, to avoid clashes
with mysql.
+19990409
+
+ Bugfix: ignore temp queue files that aren't old enough.
+ Problem reported by Vivek Khera, Khera Communications, Inc.
+
+ Bugfix: fixed typo in dict_db.c that caused processes to
+ not release DB shared locks.
+
+ Feature: auto-detection of changes to DB or DBM lookup
+ tables. This avoids the need to run "postfix reload" after
+ change to the smtp access table and other tables.
+
+ Feature: regular expression checks for message headers.
+ This requires support for POSIX or for PCRE regular
+ expressions. Specify "header_checks = regexp:/file/name"
+ or "header_checks = pcre:/file/name", and specify
+ "/^header-name: badstuff/ REJECT" in the pattern file
+ (patterns are case-insensitive by default). Code by Lamont
+ Jones, Hewlett-Packard. It is to be expected that full
+ content filtering will be delegated to an external command.
+
Future:
Planned: must be able to list the same hash table in
-Incompatible changes with snapshot-19990408:
+Incompatible changes with snapshot-19990409:
===========================================
- If an address extension (+foo) matches a user's .forward+foo file
listed in the .forward+foo file. This is more consistent with the
way Postfix expands aliases.
-Major changes with snapshot-19990408:
+Major changes with snapshot-19990409:
=====================================
In addition to several little bugfixes, none related to security,
lots of internal code cleanup, lots of new functionality, and lots
of Solaris workarounds.
+- POSIX regular expression support, enabled by default on 4.4BSD,
+LINUX, and HP-UX. See conf/sample-regexp.cf. Initial code by Lamont
+Jones, Hewlett-Packard, borrowing heavily from the PCRE implementation
+by Andrew McNamara, connect.com.au Pty. Ltd., Australia.
+
+- Regular expression checks for message headers. This requires
+support for POSIX or for PCRE regular expressions. Specify
+"header_checks = regexp:/file/name" or "header_checks = pcre:/file/name",
+and specify "/^header-name: badstuff/ REJECT" in the pattern file
+(patterns are case-insensitive by default). Code by Lamont Jones,
+Hewlett-Packard. It is to be expected that full content filtering
+will be delegated to an external command.
+
- Regular expression support for all lookup tables, including access
control (full mail addresses only), address rewriting (canonical/virtual,
full mail addresses only) and transport tables (full domain names
only). However, regular expressions are not allowed for aliases.
-- POSIX regular expression support, enabled by default on 4.4BSD,
-LINUX, and HP-UX. See conf/sample-regexp.cf. Initial code by Lamont
-Jones, Hewlett-Packard, borrowing heavily from the PCRE implementation
-by Andrew McNamara, connect.com.au Pty. Ltd., Australia.
+- Automatic detection of changes to DB or DBM lookup tables. This
+eliminates the need to run "postfix reload" after each change to
+the SMTP access table, or to the canonical, virtual, transport or
+aliases tables.
- forward_path configuration parameter for .forward files (default:
$home/.forward$recipient_delimiter$extension,$home/.forward).
even with Solaris 7.
- Workaround for the Solaris mailtool, which keeps an exclusive
-fcntl/flock lock on the mailbox while its window is not iconified
-(specify "deliver_lock_disable = yes" in main.cf).
+kernel lock on the mailbox while its window is not iconified (specify
+"sun_mailtool_compatibility = yes" in main.cf).
- Questionable workaround for Solaris, which reportedly loses
long-lived exclusive locks that are held by the master daemon.
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <vstream.h>
#include <mymalloc.h>
#include <iostuff.h>
+#include <dict.h>
/* Global library. */
char *var_virtual_maps; /* virtual maps */
char *var_masq_domains; /* masquerade domains */
char *var_masq_exceptions; /* users not masqueraded */
+char *var_header_checks; /* any header checks */
int var_dup_filter_limit; /* recipient dup filter */
char *var_empty_addr; /* destination of bounced bounces */
int var_delay_warn_time; /* delay that triggers warning */
MAPS *cleanup_comm_canon_maps;
MAPS *cleanup_send_canon_maps;
MAPS *cleanup_rcpt_canon_maps;
+MAPS *cleanup_header_checks;
MAPS *cleanup_virtual_maps;
ARGV *cleanup_masq_domains;
DICT_FLAG_LOCK);
if (*var_masq_domains)
cleanup_masq_domains = argv_split(var_masq_domains, " ,\t\r\n");
+ if (*var_header_checks)
+ cleanup_header_checks =
+ maps_create(VAR_HEADER_CHECKS, var_header_checks, DICT_FLAG_LOCK);
+}
+
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
}
/* post_jail_init - initialize after entering the chroot jail */
VAR_MASQ_DOMAINS, DEF_MASQ_DOMAINS, &var_masq_domains, 0, 0,
VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0,
+ VAR_HEADER_CHECKS, DEF_HEADER_CHECKS, &var_header_checks, 0, 0,
0,
};
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_PRE_INIT, pre_jail_init,
MAIL_SERVER_POST_INIT, post_jail_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
extern MAPS *cleanup_comm_canon_maps;
extern MAPS *cleanup_send_canon_maps;
extern MAPS *cleanup_rcpt_canon_maps;
+extern MAPS *cleanup_header_checks;
extern MAPS *cleanup_virtual_maps;
extern ARGV *cleanup_masq_domains;
static void cleanup_header(void)
{
+ char *myname = "cleanup_header";
HEADER_OPTS *hdr_opts;
+ if (msg_verbose)
+ msg_info("%s: '%s'", myname, vstring_str(cleanup_header_buf));
+
+ if (cleanup_header_checks) {
+ char *header = vstring_str(cleanup_header_buf);
+ const char *value;
+
+ if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) {
+ if (strcasecmp(value, "REJECT") == 0) {
+ msg_warn("%s: reject: header %.100s", cleanup_queue_id, header);
+ cleanup_errs |= CLEANUP_STAT_CONT;
+ }
+ }
+ }
/*
* If this is an "unknown" header, just copy it to the output without
# The controls listed here are only a very small subset. See the file
# sample-smtpd.cf for an elaborate list of anti-UCE controls.
+# The header_checks parameter restricts what may appear in message
+# headers. This requires that POSIX or PCRE regular expression support
+# is built-in. Specify "/^header-name: stuff you don not want/ REJECT"
+# in the pattern file. Patterns are case-insensitive by default.
+#
+#header_checks = regexp:/etc/postfix/filename
+#header_checks = pcre:/etc/postfix/filename
+
# The relay_domains parameter restricts what domains (and subdomains
# thereof) this mail system will relay mail from or to. See the
# smtpd_recipient_restrictions restriction in the file sample-smtpd.cf.
delay_warning_time = 0
deliver_lock_attempts = 5
deliver_lock_delay = 1
-deliver_lock_disable = no
disable_dns_lookups = no
dont_remove = 0
double_bounce_sender = double-bounce
forward_path = $home/.forward${recipient_delimiter}${extension},$home/.forward
hash_queue_depth = 2
hash_queue_names = defer
+header_checks =
header_size_limit = 102400
home_mailbox =
hopcount_limit = 50
mail_name = Postfix
mail_owner = postfix
mail_spool_directory = /var/mail
-mail_version = Snapshot-19990408
+mail_version = Snapshot-19990409
mailbox_command =
mailbox_transport =
maps_rbl_domains = rbl.maps.vix.com
smtpd_timeout = 300
soft_bounce = no
stale_lock_time = 500
+sun_mailtool_compatibility = no
swap_bangpath = yes
transport_maps =
transport_retry_time = 60
# default_transport = uucp
default_transport = smtp
-# The deliver_lock_disable parameter disables fcntl/flock file locking
-# on mailboxes. This is needed on SUN workstations because the mailtool
-# program keeps an exclusive fcntl/flock lock while its window is open.
-# SUN software uses user.lock files only. Unless you remove all SUN
-# mail software, fcntl/flock just gives a false sense of security.
-#
-#deliver_lock_disable = yes
-deliver_lock_disable = no
-
# The double_bounce_sender parameter specifies the sender address
# for mail that must be discarded when it cannot be delivered. This
# must be a unique name. All mail to this name is silently discarded,
# relocated_maps = hash:/etc/postfix/relocated
relocated_maps =
+# The sun_mailtool_compatibility parameter disables kernel file locks
+# on mailboxes. This is needed on SUN workstations because the mailtool
+# program keeps an exclusive kernel lock while its window is open.
+# SUN software uses user.lock files only. Unless you remove all SUN
+# mail software, kernel locks just give a false sense of security.
+#
+#sun_mailtool_compatibility = yes
+sun_mailtool_compatibility = no
+
# The trigger_timeout parameter limits the time to send a trigger to
# a Postfix daemon. This prevents programs from getting stuck when the
# mail system is under heavy load.
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
/* int fd;
/* VSTRING *why;
/* DESCRIPTION
-/* deliver_flock() sets one exclusive lock on an open file
+/* deliver_flock() sets one exclusive kernel lock on an open file
/* for the purpose of mail delivery. It attempts to acquire
/* the exclusive lock several times before giving up.
/*
/* CONFIGURATION PARAMETERS
/* deliver_lock_attempts, number of locking attempts
/* deliver_lock_delay, time in seconds between attempts
-/* deliver_lock_disable, disable exclusive locking
+/* sun_mailtool_compatibility, disable kernel locking
/* LICENSE
/* .ad
/* .fi
{
int i;
- if (var_flock_disable)
+ if (var_mailtool_compat)
return (0);
for (i = 0; /* void */ ; i++) {
/* int var_flock_tries;
/* int var_flock_delay;
/* int var_flock_stale;
-/* int var_flock_disable;
+/* int var_mailtool_compat;
/* int var_disable_dns;
/* int var_soft_bounce;
/* time_t var_starttime;
int var_flock_tries;
int var_flock_delay;
int var_flock_stale;
-int var_flock_disable;
+int var_mailtool_compat;
int var_disable_dns;
int var_soft_bounce;
time_t var_starttime;
VAR_DISABLE_DNS, DEF_DISABLE_DNS, &var_disable_dns,
VAR_SOFT_BOUNCE, DEF_SOFT_BOUNCE, &var_soft_bounce,
VAR_OWNREQ_SPECIAL, DEF_OWNREQ_SPECIAL, &var_ownreq_special,
- VAR_FLOCK_DISABLE, DEF_FLOCK_DISABLE, &var_flock_disable,
+ VAR_MAILTOOL_COMPAT, DEF_MAILTOOL_COMPAT, &var_mailtool_compat,
0,
};
#define DEF_QUEUE_MINFREE 0
extern int var_queue_minfree;
+#define VAR_HEADER_CHECKS "header_checks"
+#define DEF_HEADER_CHECKS ""
+extern char *var_header_checks;
+
/*
* Bounce service: truncate bounce message that exceed $bounce_size_limit.
*/
#define DEF_FLOCK_STALE 500
extern int var_flock_stale;
-#define VAR_FLOCK_DISABLE "deliver_lock_disable"
-#define DEF_FLOCK_DISABLE 0
-extern int var_flock_disable;
+#define VAR_MAILTOOL_COMPAT "sun_mailtool_compatibility"
+#define DEF_MAILTOOL_COMPAT 0
+extern int var_mailtool_compat;
/*
* How long a daemon command may take to receive or deliver a message etc.
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-19990408"
+#define DEF_MAIL_VERSION "Snapshot-19990409"
extern char *var_mail_version;
/* LICENSE
<ul>
+<li> <a href="#header_checks">Header filtering</a>
+
+<p>
+
<li> <a href="#smtpd_client_restrictions">Client name/address
restrictions</a>
</ul>
+<a name="header_checks">
+
+<h2> Header filtering</h2>
+
+The <b>header_checks</b> parameter restricts what
+is allowed in message headers.
+
+<p>
+
+<dl>
+
+<dt>Default:
+
+<dd>Allow anything in message headers.
+
+<p>
+
+<dt>Syntax:
+
+<dd>Specify a list of zero or more lookup tables. Whenever a
+header matches a table, a REJECT result means reject the
+message; an OK result means pass.
+
+</dl>
+
+<p>
+
+<dt>Examples:
+
+<dd> <b>header_checks = regexp:/etc/postfix/header_checks</b>
+
+<dd> <b>header_checks = pcre:/etc/postfix/header_checks</b>
+
+<p>
+
<a name="smtpd_client_restrictions">
<h2> Client name/address restrictions</h2>
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <iostuff.h>
#include <name_mask.h>
#include <set_eugid.h>
+#include <dict.h>
/* Global library. */
local_cmd_deliver_mask = name_mask(command_mask, var_allow_commands);
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* post_init - post-jail initialization */
static void post_init(void)
MAIL_SERVER_RAW_TABLE, raw_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_POST_INIT, post_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#define MAIL_SERVER_POST_INIT 11
#define MAIL_SERVER_LOOP 12
#define MAIL_SERVER_EXIT 13
+#define MAIL_SERVER_PRE_ACCEPT 14
typedef void (*MAIL_SERVER_INIT_FN) (void);
typedef int (*MAIL_SERVER_LOOP_FN) (void);
typedef void (*MAIL_SERVER_EXIT_FN) (void);
+typedef void (*MAIL_SERVER_ACCEPT_FN) (void);
/*
* single_server.c
/* .IP "MAIL_SERVER_EXIT (void *(void))"
/* A pointer to function that is executed immediately before normal
/* process termination.
+/* .IP "MAIL_SERVER_PRE_ACCEPT (void *(void))"
+/* Function to be executed prior to accepting a new connection.
/* .PP
/* multi_server_disconnect() should be called by the application
/* when a client disconnects.
static char **multi_server_argv;
static void (*multi_server_accept) (int, char *);
static void (*multi_server_onexit) (void);
+static void (*multi_server_pre_accept) (void);
static VSTREAM *multi_server_lock;
/* multi_server_exit - normal termination */
if (client_count == 0 && var_idle_limit > 0)
time_left = event_cancel_timer(multi_server_timeout, (char *) 0);
+ if (multi_server_pre_accept)
+ multi_server_pre_accept();
fd = LOCAL_ACCEPT(listen_fd);
if (multi_server_lock != 0
&& myflock(vstream_fileno(multi_server_lock), MYFLOCK_NONE) < 0)
case MAIL_SERVER_EXIT:
multi_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
/* .IP "MAIL_SERVER_EXIT (void *(void))"
/* A pointer to function that is executed immediately before normal
/* process termination.
+/* .IP "MAIL_SERVER_PRE_ACCEPT (void *(void))"
+/* Function to be executed prior to accepting a new connection.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
static char **single_server_argv;
static void (*single_server_accept) (int, char *);
static void (*single_server_onexit) (void);
+static void (*single_server_pre_accept) (void);
static VSTREAM *single_server_lock;
/* single_server_exit - normal termination */
if (var_idle_limit > 0)
time_left = event_cancel_timer(single_server_timeout, (char *) 0);
+ if (single_server_pre_accept)
+ single_server_pre_accept();
fd = LOCAL_ACCEPT(listen_fd);
if (single_server_lock != 0
&& myflock(vstream_fileno(single_server_lock), MYFLOCK_NONE) < 0)
case MAIL_SERVER_EXIT:
single_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
/* .IP "MAIL_SERVER_EXIT (void *(void))"
/* A pointer to function that is executed immediately before normal
/* process termination.
+/* .IP "MAIL_SERVER_PRE_ACCEPT (void *(void))"
+/* Function to be executed prior to accepting a new request.
/* .PP
/* The var_use_limit variable limits the number of clients that
/* a server can service before it commits suicide.
static char **trigger_server_argv;
static void (*trigger_server_accept) (int, char *);
static void (*trigger_server_onexit) (void);
+static void (*trigger_server_pre_accept) (void);
static VSTREAM *trigger_server_lock;
/* trigger_server_exit - normal termination */
* Read whatever the other side wrote into the FIFO. The FIFO read end is
* non-blocking so we won't get stuck when multiple processes wake up.
*/
+ if (trigger_server_pre_accept)
+ trigger_server_pre_accept();
trigger_server_wakeup(listen_fd);
}
if (var_idle_limit > 0)
time_left = event_cancel_timer(trigger_server_timeout, (char *) 0);
+ if (trigger_server_pre_accept)
+ trigger_server_pre_accept();
fd = LOCAL_ACCEPT(listen_fd);
if (trigger_server_lock != 0
&& myflock(vstream_fileno(trigger_server_lock), MYFLOCK_NONE) < 0)
case MAIL_SERVER_EXIT:
trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
}
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* drop_privileges - drop privileges most of the time */
static void drop_privileges(void)
single_server_main(argc, argv, pipe_service,
MAIL_SERVER_INT_TABLE, int_table,
MAIL_SERVER_POST_INIT, drop_privileges,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
continue;
}
+ /*
+ * Skip temporary files that aren't old enough.
+ */
+ if (mail_queue_id_ok(path) == 0)
+ continue;
+
/*
* See if this file sits in the right place in the file system
* hierarchy. Its place may be wrong after a change to the
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <msg.h>
#include <events.h>
#include <vstream.h>
+#include <dict.h>
/* Global library. */
return (WAIT_FOR_EVENT);
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* qmgr_pre_init - pre-jail initialization */
static void qmgr_pre_init(void)
MAIL_SERVER_PRE_INIT, qmgr_pre_init,
MAIL_SERVER_POST_INIT, qmgr_post_init,
MAIL_SERVER_LOOP, qmgr_loop,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
+#include <dict.h>
/* Utility library. */
}
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* main - pass control to the single-threaded skeleton */
int main(int argc, char **argv)
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_PRE_INIT, debug_peer_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <events.h>
#include <smtp_stream.h>
#include <valid_hostname.h>
+#include <dict.h>
/* Global library. */
return (-1);
}
}
+ state->time = time((time_t *) 0);
if (SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_mail(state, argv[3].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
} else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
state->error_mask |= MAIL_ERROR_BOUNCE;
smtpd_chat_reply(state, "554 Error: too many hops");
+ } else if ((state->err & CLEANUP_STAT_CONT) != 0) {
+ state->error_mask |= MAIL_ERROR_BOUNCE;
+ smtpd_chat_reply(state, "552 Error: content rejected");
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error");
exit(sig);
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("lookup table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* post_jail_init - post-jail initialization */
static void post_jail_init(void)
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_PRE_INIT, pre_jail_init,
MAIL_SERVER_POST_INIT, post_jail_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
state->err = CLEANUP_STAT_OK;
state->client = stream;
state->buffer = vstring_alloc(100);
- state->time = event_time();
state->name = mystrdup(name);
state->addr = mystrdup(addr);
state->error_count = 0;
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
#include <vstring_vstream.h>
#include <split_at.h>
#include <stringops.h>
+#include <dict.h>
/* Global library. */
multi_server_disconnect(stream);
}
+/* pre_accept - see if tables have changed */
+
+static void pre_accept(void)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ exit(0);
+ }
+}
+
/* pre_jail_init - initialize before entering chroot jail */
static void pre_jail_init(void)
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_PRE_INIT, pre_jail_init,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
-TVSTREAM
-TVSTRING
-TWAIT_STATUS_T
+-TWATCH_FD
unsafe.c username.c valid_hostname.c vbuf.c vbuf_print.c \
vstream.c vstream_popen.c vstring.c vstring_vstream.c writable.c \
write_buf.c write_wait.c dict_unix.c dict_pcre.c stream_listen.c \
- stream_connect.c stream_trigger.c dict_regexp.c
+ stream_connect.c stream_trigger.c dict_regexp.c watch_fd.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_ni.o dict_nis.o \
unsafe.o username.o valid_hostname.o vbuf.o vbuf_print.o \
vstream.o vstream_popen.o vstring.o vstring_vstream.o writable.o \
write_buf.o write_wait.o dict_unix.o dict_pcre.o stream_listen.o \
- stream_connect.o stream_trigger.o dict_regexp.o
+ stream_connect.o stream_trigger.o dict_regexp.o watch_fd.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_ni.h dict_nis.h \
dict_nisplus.h dir_forest.h events.h exec_command.h find_inet.h \
sigdelay.h split_at.h stat_as.h stringops.h sys_defs.h \
timed_connect.h timed_wait.h trigger.h username.h valid_hostname.h \
vbuf.h vbuf_print.h vstream.h vstring.h vstring_vstream.h \
- dict_unix.h dict_pcre.h dict_regexp.h
+ dict_unix.h dict_pcre.h dict_regexp.h watch_fd.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
dict_open.o: dict_regexp.h
dict_open.o: stringops.h
dict_open.o: split_at.h
+dict_open.o: htable.h
dict_pcre.o: dict_pcre.c
dict_pcre.o: sys_defs.h
dict_regexp.o: dict_regexp.c
vstring_vstream.o: vbuf.h
vstring_vstream.o: vstream.h
vstring_vstream.o: vstring_vstream.h
+watch_fd.o: watch_fd.c
+watch_fd.o: sys_defs.h
+watch_fd.o: msg.h
+watch_fd.o: binhash.h
+watch_fd.o: mymalloc.h
+watch_fd.o: watch_fd.h
writable.o: writable.c
writable.o: sys_defs.h
writable.o: msg.h
/* extern int dict_errno;
/*
/* void dict_register(dict_name, dict_info)
-/* const const char *dict_name;
+/* const char *dict_name;
/* DICT *dict_info;
/*
/* DICT *dict_handle(dict_name)
-/* const const char *dict_name;
+/* const char *dict_name;
/*
/* void dict_unregister(dict_name)
-/* const const char *dict_name;
+/* const char *dict_name;
/*
/* void dict_update(dict_name, member, value)
-/* const const char *dict_name;
-/* const const char *member;
+/* const char *dict_name;
+/* const char *member;
/* const char *value;
/*
/* const char *dict_lookup(dict_name, member)
-/* const const char *dict_name;
-/* const const char *member;
+/* const char *dict_name;
+/* const char *member;
/*
/* const char *dict_eval(dict_name, string, int recursive)
-/* const const char *dict_name;
+/* const char *dict_name;
/* const char *string;
/* int recursive;
+/*
+/* int dict_walk(action, context)
+/* void (*action)(dict_name, dict_handle, context)
+/* char *context;
+/*
+/* int dict_changed()
/* AUXILIARY FUNCTIONS
/* void dict_load_file(dict_name, path)
-/* const const char *dict_name;
-/* const const char *path;
+/* const char *dict_name;
+/* const char *path;
/*
/* void dict_load_fp(dict_name, fp)
-/* const const char *dict_name;
+/* const char *dict_name;
/* FILE *fp;
/* DESCRIPTION
/* This module maintains a collection of name-value dictionaries.
/* \fIrecursive\fR argument is non-zero, macros references are
/* expanded recursively.
/*
+/* dict_walk() iterates over all registered dictionaries in some
+/* arbitrary order, and invokes the specified action routine with
+/* as arguments:
+/* .IP "const char *dict_name"
+/* Dictionary name.
+/* .IP "DICT *dict_handle"
+/* Generic dictionary handle.
+/* .IP "char *context"
+/* Application context from the caller.
+/* .PP
+/* dict_changed() returns non-zero when any dictionary needs to
+/* be re-opened because it has changed.
+/*
/* dict_load_file() reads name-value entries from the named file.
/* Lines that begin with whitespace are concatenated to the preceding
/* line (the newline is deleted).
/* System libraries. */
#include "sys_defs.h"
+#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
return (STR(buf));
}
+
+/* dict_walk - iterate over all dictionaries in arbitrary order */
+
+void dict_walk(DICT_WALK_ACTION action, char *ptr)
+{
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ HTABLE_INFO *h;
+
+ ht_info_list = htable_list(dict_table);
+ for (ht = ht_info_list; (h = *ht) != 0; ht++)
+ action(h->key, (DICT *) h->value, ptr);
+ myfree((char *) ht_info_list);
+}
+
+/* dict_changed - see if any dictionary has changed */
+
+int dict_changed(void)
+{
+ char *myname = "dict_changed";
+ struct stat st;
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ HTABLE_INFO *h;
+ int status;
+ DICT *dict;
+
+ ht_info_list = htable_list(dict_table);
+ for (status = 0, ht = ht_info_list; status == 0 && (h = *ht) != 0; ht++) {
+ dict = ((DICT_NODE *) h->value)->dict;
+ if (dict->fd < 0) /* not file-based */
+ continue;
+ if (dict->mtime == 0) /* not bloody likely */
+ msg_warn("%s: table %s: null time stamp", myname, h->key);
+ if (fstat(dict->fd, &st) < 0)
+ msg_fatal("%s: fstat: %m", myname);
+ status = (st.st_mtime != dict->mtime);
+ }
+ myfree((char *) ht_info_list);
+ return (status);
+}
void (*update) (struct DICT *, const char *, const char *);
void (*close) (struct DICT *);
int fd; /* for dict_update() lock */
+ time_t mtime; /* mod time at open */
} DICT;
#define DICT_FLAG_DUP_WARN (1<<0) /* if file, warn about dups */
*/
extern DICT *dict_open(const char *, int, int);
extern DICT *dict_open3(const char *, const char *, int, int);
-extern void dict_open_register(const char *, DICT *(*)(const char *, int, int));
+extern void dict_open_register(const char *, DICT *(*) (const char *, int, int));
#define dict_get(dp, key) (dp)->lookup((dp), (key))
#define dict_put(dp, key, val) (dp)->update((dp), (key), (val))
#define dict_close(dp) (dp)->close(dp)
+typedef void (*DICT_WALK_ACTION) (const char *, DICT *, char *);
+extern void dict_walk(DICT_WALK_ACTION, char *);
+extern int dict_changed(void);
/* LICENSE
/* .ad
/* System library. */
+#include <sys/stat.h>
#include <limits.h>
#ifdef PATH_DB_H
#include PATH_DB_H
/*
* Release the shared lock.
*/
- if ((dict->fd & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
+ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0)
msg_fatal("%s: unlock dictionary: %m", dict_db->path);
return (result);
void *tweak, int dict_flags)
{
DICT_DB *dict_db;
+ struct stat st;
DB *db;
char *db_path;
dict_db->dict.update = dict_db_update;
dict_db->dict.close = dict_db_close;
dict_db->dict.fd = db->fd(db);
+ if (fstat(dict_db->dict.fd, &st) < 0)
+ msg_fatal("dict_db_open: fstat: %m");
+ dict_db->dict.mtime = st.st_mtime;
close_on_exec(dict_db->dict.fd, CLOSE_ON_EXEC);
dict_db->dict.flags = dict_flags | DICT_FLAG_FIXED;
if ((flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
/* System library. */
+#include <sys/stat.h>
#include <ndbm.h>
#include <string.h>
DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
{
DICT_DBM *dict_dbm;
+ struct stat st;
DBM *dbm;
/*
dict_dbm->dict.update = dict_dbm_update;
dict_dbm->dict.close = dict_dbm_close;
dict_dbm->dict.fd = dbm_dirfno(dbm);
+ if (fstat(dict_dbm->dict.fd, &st) < 0)
+ msg_fatal("dict_dbm_open: fstat: %m");
+ dict_dbm->dict.mtime = st.st_mtime;
close_on_exec(dict_dbm->dict.fd, CLOSE_ON_EXEC);
dict_dbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
else
msg_fatal("unknown access mode: %s", argv[2]);
dict_name = argv[optind];
- dict = dict_open(dict_name, open_flags, 0);
+ dict = dict_open(dict_name, open_flags, DICT_FLAG_LOCK);
+ dict_register(dict_name, dict);
while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) {
+ if (dict_changed()) {
+ msg_warn("dictionary has changed -- exiting");
+ exit(0);
+ }
if ((key = strtok(vstring_str(keybuf), " =")) == 0)
continue;
if ((value = strtok((char *) 0, " =")) == 0) {
--- /dev/null
+/*++
+/* NAME
+/* watch_fd 3
+/* SUMMARY
+/* monitor file descriptors for change
+/* SYNOPSIS
+/* #include <watch_fd.h>
+/*
+/* void watch_fd_register(fd)
+/* int fd;
+/*
+/* void watch_fd_remove(fd)
+/* int fd;
+/*
+/* int watch_fd_changed()
+/* DESCRIPTION
+/* This module monitors file modification times of arbitrary file
+/* descriptors.
+/*
+/* watch_fd_register() records information about the specified
+/* file descriptor.
+/*
+/* watch_fd_remove() releases storage allocated by watch_fd_register().
+/*
+/* watch_fd_changed() returns non-zero if any of the registered
+/* SEE ALSO
+/* msg(3) diagnostics interface
+/* DIAGNOSTICS
+/* Problems are reported via the msg(3) diagnostics routines:
+/* the requested amount of memory is not available; improper use
+/* is detected; other fatal errors.
+/* 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 <sys/stat.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <binhash.h>
+#include <mymalloc.h>
+#include <watch_fd.h>
+
+/* Application-specific. */
+
+typedef struct {
+ int fd; /* file descriptor */
+ time_t mtime; /* initial modification time */
+} WATCH_FD;
+
+static BINHASH *watch_fd_table;
+
+/* watch_fd_register - register file descriptor */
+
+void watch_fd_register(int fd)
+{
+ char *myname = "watch_fd_enter";
+ WATCH_FD *info;
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ msg_fatal("%s: fstat: %m", myname);
+
+ info = (WATCH_FD *) mymalloc(sizeof(*info));
+ info->fd = fd;
+ info->mtime = st.st_mtime;
+
+ if (watch_fd_table == 0)
+ watch_fd_table = binhash_create(0);
+ if (binhash_find(watch_fd_table, (char *) &fd, sizeof(fd)))
+ msg_panic("%s: entry %d exists", myname, fd);
+ binhash_enter(watch_fd_table, (char *) &fd, sizeof(fd), (char *) info);
+}
+
+/* watch_fd_remove - remove file descriptor */
+
+void watch_fd_remove(int fd)
+{
+ binhash_delete(watch_fd_table, (char *) &fd, sizeof(fd), myfree);
+}
+
+/* watch_fd_changed - see if any file has changed */
+
+int watch_fd_changed(void)
+{
+ char *myname = "watch_fd_changed";
+ struct stat st;
+ BINHASH_INFO **ht_info_list;
+ BINHASH_INFO **ht;
+ BINHASH_INFO *h;
+ WATCH_FD *info;
+ int status;
+
+ ht_info_list = binhash_list(watch_fd_table);
+ for (status = 0, ht = ht_info_list; status == 0 && (h = *ht) != 0; ht++) {
+ info = (WATCH_FD *) h->value;
+ if (fstat(info->fd, &st) < 0)
+ msg_fatal("%s: fstat: %m", myname);
+ status = (st.st_mtime != info->mtime);
+ }
+ myfree((char *) ht_info_list);
+ return (status);
+}
--- /dev/null
+#ifndef _WATCH_FD_H_INCLUDED_
+#define _WATCH_FD_H_INCLUDED_
+
+/*++
+/* NAME
+/* watch_fd 3h
+/* SUMMARY
+/* monitor file descriptors for change
+/* SYNOPSIS
+/* #include "watch_fd.h"
+ DESCRIPTION
+ .nf
+
+ /*
+ * External interface.
+ */
+extern void watch_fd_register(int);
+extern void watch_fd_remove(int);
+extern int watch_fd_changed(void);
+
+/* 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