diff --git a/postfix/HISTORY b/postfix/HISTORY index 418176d83..523ecbd42 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -11177,7 +11177,7 @@ Apologies for any names omitted. due to expensive crypto operations. Files: global/anvil_clnt.c, anvil/anvil.c, smtpd/smtpd.c. - Cleanup: eliminated massive code duplicatiom in the anvil + Cleanup: eliminated massive code duplication in the anvil server that resulted from adding similar features one at a time. File: anvil/anvil.c. @@ -11191,6 +11191,37 @@ Apologies for any names omitted. many MAIL FROM or RCPT TO commands) when we aren't closing the connection. File: smtpd/smtpd.c. +20051012 + + Polishing: content of comments and sequence of code blocks + in the anvil server, TLS request rate error message in the + smtp server, and documentation, but no changes in code. + Files: anvil/anvil.c, smtpd/smtpd.c. + +20051013 + + Horror: some systems have basename() and dirname() and some + don't; some implementations modify their input and some + don't; and some implementations use a private buffer that + is overwritten upon the next call. Postfix will use its own + safer versions called sane_basename() and sane_dirname(). + These never modify the input, and allow the caller to control + how memory is allocated for the result. File: + util/sane_basename.c. + + Feature: "sendmail -C path-to-main.cf" and "sendmail -C + config_directory" now do what one would expect. File: + sendmail/sendmail.c. + + Bugfix: don't do smtpd_end_of_data_restrictions after the + transaction failed due to, e.g., a write error. File: + smtpd/smtpd.c. + + Cleanup: the SMTP server now enforces the message_size_limit + even when the client did not send SIZE information with the + MAIL FROM command. This protects before-queue content + filters against over-size messages. File: smtpd/smtpd.c. + Open problems: Try to recognize that Resent- headers appear in blocks, @@ -11199,11 +11230,6 @@ Open problems: Hard limits on cache sizes (anvil, specifically). - Look for systems with XPG basename() declared in , - and prepare for phasing out the Postfix-supplied one. - Beware, however, that XPG basename() takes (char *), and - not (const char *) because it may change its argument. - Laptop friendliness: make the qmgr remember when the next deferred queue scan needs to be done, and have the pickup server stat() the maildrop directory before searching it. @@ -11213,11 +11239,6 @@ Open problems: Or do we punt the issue and issue X-Postfix for all errors except SMTP? - Implement smtp_greet() routine to distinguish between reject - before versus after sending HELO/EHLO; this is needed to - eliminate the hack that uses one character lookahead to - find out if the server wants to talk to us. - Low: replace_sender/replace_recipient actions in access maps? diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index a6fd5a217..d6b254f51 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -26,10 +26,11 @@ files. With deeply nested aliases or .forward files, this can greatly reduce the number of queue files and cleanup process instances. To get the earlier behavior, specify "frozen_delivered_to = no". -The frozen_delivered_to feature also fixes a long-standing problem -with multiple deliveries to recipients that are listed in multiple -nested aliases, but does so only when only the top-level alias has -an owner- alias, and none of the subordinate aliases. +The frozen_delivered_to feature can help to alleviate a long-standing +problem with multiple deliveries to recipients that are listed +multiple times in a hierarchy of nested aliases. For this to work, +only the top-level alias should have an owner- alias, and none of +the subordinate aliases. Major changes with snapshot 20051011 ==================================== diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html index 734f9a921..faa2317e4 100644 --- a/postfix/html/sendmail.1.html +++ b/postfix/html/sendmail.1.html @@ -107,10 +107,16 @@ SENDMAIL(1) SENDMAIL(1) This feature is available in Postfix version 2.1 and later. - -C config_file (ignored) - The path name of the sendmail.cf file. Postfix con- - figuration files are kept in the /etc/postfix - directory. + -C config_file + + -C config_dir + The path name of the Postfix main.cf file, or of + its parent directory. This information is ignored + with Postfix versions before 2.3. + + With older Postfix versions, specify a directory + pathname with the MAIL_CONFIG environment variable + to override the location of configuration files. -F full_name Set the sender full name. This is used only with diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1 index 928248351..92f7e3a5c 100644 --- a/postfix/man/man1/sendmail.1 +++ b/postfix/man/man1/sendmail.1 @@ -91,9 +91,15 @@ report after verifying each recipient address. This is useful for testing address rewriting and routing configurations. .sp This feature is available in Postfix version 2.1 and later. -.IP "\fB-C \fIconfig_file\fR (ignored)" -The path name of the \fBsendmail.cf\fR file. Postfix configuration -files are kept in the \fB/etc/postfix\fR directory. +.IP "\fB-C \fIconfig_file\fR" +.IP "\fB-C \fIconfig_dir\fR" +The path name of the Postfix \fBmain.cf\fR file, or of its +parent directory. This information is ignored with Postfix +versions before 2.3. + +With older Postfix versions, specify a directory pathname +with the MAIL_CONFIG environment variable to override the +location of configuration files. .IP "\fB-F \fIfull_name\fR Set the sender full name. This is used only with messages that have no \fBFrom:\fR message header. diff --git a/postfix/src/anvil/anvil.c b/postfix/src/anvil/anvil.c index 972144470..fb0aa89e6 100644 --- a/postfix/src/anvil/anvil.c +++ b/postfix/src/anvil/anvil.c @@ -276,17 +276,168 @@ /* Application-specific. */ + /* + * Configuration parameters. + */ int var_anvil_time_unit; int var_anvil_stat_time; /* - * State. + * Global dynamic state. */ static HTABLE *anvil_remote_map; /* indexed by service+ remote client */ /* - * Absent a real-time query interface, these are logged at process exit time - * and at regular intervals. + * Remote connection state, one instance for each (service, client) pair. + */ +typedef struct { + char *ident; /* lookup key */ + int count; /* connection count */ + int rate; /* connection rate */ + int mail; /* message rate */ + int rcpt; /* recipient rate */ + int ntls; /* new TLS session rate */ + time_t start; /* time of first rate sample */ +} ANVIL_REMOTE; + + /* + * Local server state, one instance per anvil client connection. This allows + * us to clean up remote connection state when a local server goes away + * without cleaning up. + */ +typedef struct { + ANVIL_REMOTE *anvil_remote; /* XXX should be list */ +} ANVIL_LOCAL; + + /* + * The following operations are implemented as macros with recognizable + * names so that we don't lose sight of what the code is trying to do. + * + * Related operations are defined side by side so that the code implementing + * them isn't pages apart. + */ + +/* Create new (service, client) state. */ + +#define ANVIL_REMOTE_FIRST_CONN(remote, id) \ + do { \ + (remote)->ident = mystrdup(id); \ + (remote)->count = 1; \ + (remote)->rate = 1; \ + (remote)->mail = 0; \ + (remote)->rcpt = 0; \ + (remote)->ntls = 0; \ + (remote)->start = event_time(); \ + } while(0) + +/* Destroy unused (service, client) state. */ + +#define ANVIL_REMOTE_FREE(remote) \ + do { \ + myfree((remote)->ident); \ + myfree((char *) (remote)); \ + } while(0) + +/* Reset or update rate information for existing (service, client) state. */ + +#define ANVIL_REMOTE_RSET_RATE(remote, _start) \ + do { \ + (remote)->rate = 0; \ + (remote)->mail = 0; \ + (remote)->rcpt = 0; \ + (remote)->ntls = 0; \ + (remote)->start = _start; \ + } while(0) + +#define ANVIL_REMOTE_INCR_RATE(remote, _what) \ + do { \ + time_t _now = event_time(); \ + if ((remote)->start + var_anvil_time_unit < _now) \ + ANVIL_REMOTE_RSET_RATE((remote), _now); \ + if ((remote)->_what < INT_MAX) \ + (remote)->_what += 1; \ + } while(0) + +/* Update existing (service, client) state. */ + +#define ANVIL_REMOTE_NEXT_CONN(remote) \ + do { \ + ANVIL_REMOTE_INCR_RATE((remote), rate); \ + if ((remote)->count == 0) \ + event_cancel_timer(anvil_remote_expire, (char *) remote); \ + (remote)->count++; \ + } while(0) + +#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail) + +#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt) + +#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls) + +/* Drop connection from (service, client) state. */ + +#define ANVIL_REMOTE_DROP_ONE(remote) \ + do { \ + if ((remote) && (remote)->count > 0) { \ + if (--(remote)->count == 0) \ + event_request_timer(anvil_remote_expire, (char *) remote, \ + var_anvil_time_unit); \ + } \ + } while(0) + +/* Create local server state. */ + +#define ANVIL_LOCAL_INIT(local) \ + do { \ + (local)->anvil_remote = 0; \ + } while(0) + +/* Add remote connection to local server. */ + +#define ANVIL_LOCAL_ADD_ONE(local, remote) \ + do { \ + /* XXX allow multiple remote clients per local server. */ \ + if ((local)->anvil_remote) \ + ANVIL_REMOTE_DROP_ONE((local)->anvil_remote); \ + (local)->anvil_remote = (remote); \ + } while(0) + +/* Test if this remote connection is listed for this local server. */ + +#define ANVIL_LOCAL_REMOTE_LINKED(local, remote) \ + ((local)->anvil_remote == (remote)) + +/* Drop specific remote connection from local server. */ + +#define ANVIL_LOCAL_DROP_ONE(local, remote) \ + do { \ + /* XXX allow multiple remote clients per local server. */ \ + if ((local)->anvil_remote == (remote)) \ + (local)->anvil_remote = 0; \ + } while(0) + +/* Drop all remote connections from local server. */ + +#define ANVIL_LOCAL_DROP_ALL(stream, local) \ + do { \ + /* XXX allow multiple remote clients per local server. */ \ + if ((local)->anvil_remote) \ + anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \ + } while (0) + + /* + * Lookup table to map request names to action routines. + */ +typedef struct { + const char *name; + void (*action) (VSTREAM *, const char *); +} ANVIL_REQ_TABLE; + + /* + * Run-time statistics for maximal connection counts and event rates. These + * store the peak resource usage, remote connection, and time. Absent a + * query interface, this information is logged at process exit time and at + * configurable intervals. */ typedef struct { int value; /* peak value */ @@ -336,151 +487,6 @@ static time_t max_cache_time; /* time of peak size */ } \ } while (0); - /* - * Remote connection state, one instance for each (service, client) pair. - */ -typedef struct { - char *ident; /* lookup key */ - int count; /* connection count */ - int rate; /* connection rate */ - int mail; /* message rate */ - int rcpt; /* recipient rate */ - int ntls; /* new TLS session rate */ - time_t start; /* time of first rate sample */ -} ANVIL_REMOTE; - - /* - * Local server state, one per server instance. This allows us to clean up - * connection state when a local server goes away without cleaning up. - */ -typedef struct { - ANVIL_REMOTE *anvil_remote; /* XXX should be list */ -} ANVIL_LOCAL; - - /* - * The following operations are implemented as macros with recognizable - * names so that we don't lose sight of what the code is trying to do. - * - * Related operations are defined side by side so that the code implementing - * them isn't pages apart. - */ - -/* Create new (service, client) state. */ - -#define ANVIL_REMOTE_FIRST_CONN(remote, id) \ - do { \ - (remote)->ident = mystrdup(id); \ - (remote)->count = 1; \ - (remote)->rate = 1; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->ntls = 0; \ - (remote)->start = event_time(); \ - } while(0) - -/* Destroy unused (service, client) state. */ - -#define ANVIL_REMOTE_FREE(remote) \ - do { \ - myfree((remote)->ident); \ - myfree((char *) (remote)); \ - } while(0) - -/* Reset event rate counters and start of data collection interval. */ - -#define ANVIL_REMOTE_RSET_RATE(remote, _start) \ - do { \ - (remote)->rate = 0; \ - (remote)->mail = 0; \ - (remote)->rcpt = 0; \ - (remote)->ntls = 0; \ - (remote)->start = _start; \ - } while(0) - -/* Add connection to (service, client) state. */ - -#define ANVIL_REMOTE_INCR_RATE(remote, _what) \ - do { \ - time_t _now = event_time(); \ - if ((remote)->start + var_anvil_time_unit < _now) \ - ANVIL_REMOTE_RSET_RATE((remote), _now); \ - if ((remote)->_what < INT_MAX) \ - (remote)->_what += 1; \ - } while(0) - -#define ANVIL_REMOTE_NEXT_CONN(remote) \ - do { \ - ANVIL_REMOTE_INCR_RATE((remote), rate); \ - if ((remote)->count == 0) \ - event_cancel_timer(anvil_remote_expire, (char *) remote); \ - (remote)->count++; \ - } while(0) - -#define ANVIL_REMOTE_INCR_MAIL(remote) ANVIL_REMOTE_INCR_RATE((remote), mail) - -#define ANVIL_REMOTE_INCR_RCPT(remote) ANVIL_REMOTE_INCR_RATE((remote), rcpt) - -#define ANVIL_REMOTE_INCR_NTLS(remote) ANVIL_REMOTE_INCR_RATE((remote), ntls) - -/* Drop connection from (service, client) state. */ - -#define ANVIL_REMOTE_DROP_ONE(remote) \ - do { \ - if ((remote) && (remote)->count > 0) { \ - if (--(remote)->count == 0) \ - event_request_timer(anvil_remote_expire, (char *) remote, \ - var_anvil_time_unit); \ - } \ - } while(0) - -/* Create local server state. */ - -#define ANVIL_LOCAL_INIT(local) \ - do { \ - (local)->anvil_remote = 0; \ - } while(0) - -/* Add connection to local server. */ - -#define ANVIL_LOCAL_ADD_ONE(local, remote) \ - do { \ - /* XXX allow multiple remote clients per local server. */ \ - if ((local)->anvil_remote) \ - ANVIL_REMOTE_DROP_ONE((local)->anvil_remote); \ - (local)->anvil_remote = (remote); \ - } while(0) - -/* Test if this remote site is listed for this local client. */ - -#define ANVIL_LOCAL_REMOTE_LINKED(local, remote) \ - ((local)->anvil_remote == (remote)) - -/* Drop connection from local server. */ - -#define ANVIL_LOCAL_DROP_ONE(local, remote) \ - do { \ - /* XXX allow multiple remote clients per local server. */ \ - if ((local)->anvil_remote == (remote)) \ - (local)->anvil_remote = 0; \ - } while(0) - -/* Drop all connections from local server. */ - -#define ANVIL_LOCAL_DROP_ALL(stream, local) \ - do { \ - /* XXX allow multiple remote clients per local server. */ \ - if ((local)->anvil_remote) \ - anvil_remote_disconnect((stream), (local)->anvil_remote->ident); \ - } while (0) - - /* - * Lookup table to map request names to action routines. - */ -typedef struct { - const char *name; - void (*action) (VSTREAM *, const char *); -} ANVIL_REQ_TABLE; - /* * Silly little macros. */ @@ -587,8 +593,8 @@ static ANVIL_REMOTE *anvil_remote_conn_update(VSTREAM *client_stream, const char } /* - * Record this connection under the local client information, so that we - * can clean up all its connection state when the local client goes away. + * Record this connection under the local server information, so that we + * can clean up all its connection state when the local server goes away. */ if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) == 0) { anvil_local = (ANVIL_LOCAL *) mymalloc(sizeof(*anvil_local)); @@ -617,7 +623,7 @@ static void anvil_remote_connect(VSTREAM *client_stream, const char *ident) anvil_remote = anvil_remote_conn_update(client_stream, ident); /* - * Respond to the local client. + * Respond to the local server. */ attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, @@ -648,7 +654,7 @@ static void anvil_remote_mail(VSTREAM *client_stream, const char *ident) anvil_remote = anvil_remote_conn_update(client_stream, ident); /* - * Update message delivery request rate and respond to local client. + * Update message delivery request rate and respond to local server. */ ANVIL_REMOTE_INCR_MAIL(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, @@ -677,7 +683,7 @@ static void anvil_remote_rcpt(VSTREAM *client_stream, const char *ident) anvil_remote = anvil_remote_conn_update(client_stream, ident); /* - * Update recipient address rate and respond to local client. + * Update recipient address rate and respond to local server. */ ANVIL_REMOTE_INCR_RCPT(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, @@ -706,7 +712,7 @@ static void anvil_remote_newtls(VSTREAM *client_stream, const char *ident) anvil_remote = anvil_remote_conn_update(client_stream, ident); /* - * Update newtls rate and respond to local client. + * Update newtls rate and respond to local server. */ ANVIL_REMOTE_INCR_NTLS(anvil_remote); attr_print_plain(client_stream, ATTR_FLAG_NONE, @@ -747,7 +753,7 @@ static void anvil_remote_newtls_stat(VSTREAM *client_stream, const char *ident) } /* - * Respond to local client. + * Respond to local server. */ attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, @@ -769,8 +775,8 @@ static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident) (unsigned long) client_stream, ident); /* - * Update local and remote info if this remote site is listed for this - * local client. + * Update local and remote info if this remote connection is listed for + * this local server. */ if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0 && (anvil_remote = @@ -784,7 +790,7 @@ static void anvil_remote_disconnect(VSTREAM *client_stream, const char *ident) myname, (unsigned long) anvil_local); /* - * Respond to the local client. + * Respond to the local server. */ attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_NUM, ANVIL_ATTR_STATUS, ANVIL_STAT_OK, @@ -805,8 +811,8 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service, (unsigned long) client_stream); /* - * Look up the local client, and get rid of open remote connection state - * that we still have for this local client. Do not destroy remote client + * Look up the local server, and get rid of any remote connection state + * that we still have for this local server. Do not destroy remote client * status information before it expires. */ if ((anvil_local = (ANVIL_LOCAL *) vstream_context(client_stream)) != 0) { @@ -820,6 +826,31 @@ static void anvil_service_done(VSTREAM *client_stream, char *unused_service, vstream_fileno(client_stream)); } +/* anvil_status_dump - log and reset extreme usage */ + +static void anvil_status_dump(char *unused_name, char **unused_argv) +{ + ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection"); + ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection"); + ANVIL_MAX_RATE_REPORT(max_mail_rate, "message"); + ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient"); + ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls"); + + if (max_cache_size > 0) { + msg_info("statistics: max cache size %d at %.15s", + max_cache_size, ctime(&max_cache_time) + 4); + max_cache_size = 0; + } +} + +/* anvil_status_update - log and reset extreme usage periodically */ + +static void anvil_status_update(int unused_event, char *context) +{ + anvil_status_dump((char *) 0, (char **) 0); + event_request_timer(anvil_status_update, context, var_anvil_stat_time); +} + /* anvil_service - perform service for client */ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv) @@ -889,8 +920,6 @@ static void anvil_service(VSTREAM *client_stream, char *unused_service, char **a /* post_jail_init - post-jail initialization */ -static void anvil_status_update(int, char *); - static void post_jail_init(char *unused_name, char **unused_argv) { @@ -910,31 +939,6 @@ static void post_jail_init(char *unused_name, char **unused_argv) var_use_limit = 0; } -/* anvil_status_dump - log and reset extreme usage */ - -static void anvil_status_dump(char *unused_name, char **unused_argv) -{ - ANVIL_MAX_RATE_REPORT(max_conn_rate, "connection"); - ANVIL_MAX_COUNT_REPORT(max_conn_count, "connection"); - ANVIL_MAX_RATE_REPORT(max_mail_rate, "message"); - ANVIL_MAX_RATE_REPORT(max_rcpt_rate, "recipient"); - ANVIL_MAX_RATE_REPORT(max_ntls_rate, "newtls"); - - if (max_cache_size > 0) { - msg_info("statistics: max cache size %d at %.15s", - max_cache_size, ctime(&max_cache_time) + 4); - max_cache_size = 0; - } -} - -/* anvil_status_update - log and reset extreme usage periodically */ - -static void anvil_status_update(int unused_event, char *context) -{ - anvil_status_dump((char *) 0, (char **) 0); - event_request_timer(anvil_status_update, context, var_anvil_stat_time); -} - /* main - pass control to the multi-threaded skeleton */ int main(int argc, char **argv) diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 343b7fa40..780c2441a 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20051011" +#define MAIL_RELEASE_DATE "20051014" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/postdrop/postdrop.c b/postfix/src/postdrop/postdrop.c index d8592b9cd..222756224 100644 --- a/postfix/src/postdrop/postdrop.c +++ b/postfix/src/postdrop/postdrop.c @@ -249,7 +249,7 @@ int main(int argc, char **argv) */ argv[0] = "postdrop"; msg_vstream_init(argv[0], VSTREAM_ERR); - msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY); + msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* diff --git a/postfix/src/postqueue/Makefile.in b/postfix/src/postqueue/Makefile.in index be70e25f7..49bc5f5f5 100644 --- a/postfix/src/postqueue/Makefile.in +++ b/postfix/src/postqueue/Makefile.in @@ -59,7 +59,6 @@ postqueue.o: ../../include/argv.h postqueue.o: ../../include/attr.h postqueue.o: ../../include/clean_env.h postqueue.o: ../../include/connect.h -postqueue.o: ../../include/debug_process.h postqueue.o: ../../include/flush_clnt.h postqueue.o: ../../include/iostuff.h postqueue.o: ../../include/mail_conf.h diff --git a/postfix/src/postqueue/postqueue.c b/postfix/src/postqueue/postqueue.c index 76925d55e..70f34cfb5 100644 --- a/postfix/src/postqueue/postqueue.c +++ b/postfix/src/postqueue/postqueue.c @@ -184,7 +184,6 @@ #include #include #include -#include #include #include #include diff --git a/postfix/src/sendmail/sendmail.c b/postfix/src/sendmail/sendmail.c index 955137de6..2f2182224 100644 --- a/postfix/src/sendmail/sendmail.c +++ b/postfix/src/sendmail/sendmail.c @@ -85,9 +85,15 @@ /* for testing address rewriting and routing configurations. /* .sp /* This feature is available in Postfix version 2.1 and later. -/* .IP "\fB-C \fIconfig_file\fR (ignored)" -/* The path name of the \fBsendmail.cf\fR file. Postfix configuration -/* files are kept in the \fB/etc/postfix\fR directory. +/* .IP "\fB-C \fIconfig_file\fR" +/* .IP "\fB-C \fIconfig_dir\fR" +/* The path name of the Postfix \fBmain.cf\fR file, or of its +/* parent directory. This information is ignored with Postfix +/* versions before 2.3. +/* +/* With older Postfix versions, specify a directory pathname +/* with the MAIL_CONFIG environment variable to override the +/* location of configuration files. /* .IP "\fB-F \fIfull_name\fR /* Set the sender full name. This is used only with messages that /* have no \fBFrom:\fR message header. @@ -901,6 +907,7 @@ int main(int argc, char **argv) char *rewrite_context = MAIL_ATTR_RWR_LOCAL; int dsn_notify = 0; const char *dsn_envid = 0; + int saved_optind; /* * Be consistent with file permissions. @@ -959,8 +966,24 @@ int main(int argc, char **argv) } /* - * Further initialization... + * Further initialization. Load main.cf first, so that command-line + * options can override main.cf settings. Pre-scan the argument list so + * that we load the right main.cf file. */ +#define GETOPT_LIST "A:B:C:F:GIL:N:O:R:UV:X:b:ce:f:h:imno:p:r:q:tvx" + + saved_optind = optind; + while ((c = GETOPT(argc, argv, GETOPT_LIST)) > 0) { + VSTRING *buf = vstring_alloc(1); + + if (c == 'C' + && setenv(CONF_ENV_PATH, + strcmp(sane_basename(buf, optarg), MAIN_CONF_FILE) == 0 ? + sane_dirname(buf, optarg) : optarg, 1) < 0) + msg_fatal_status(EX_UNAVAILABLE, "out of memory"); + vstring_free(buf); + } + optind = saved_optind; mail_conf_read(); get_mail_conf_str_table(str_table); @@ -1024,7 +1047,7 @@ int main(int argc, char **argv) optind++; continue; } - if ((c = GETOPT(argc, argv, "A:B:C:F:GIL:N:O:R:UV:X:b:ce:f:h:imno:p:r:q:tvx")) <= 0) + if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0) break; switch (c) { default: diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 92becfb57..8728e6752 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -2253,13 +2253,18 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) if (prev_rec_type != REC_TYPE_CONT && *start == '.' && (state->proxy == 0 ? (++start, --len) == 0 : len == 1)) break; - state->act_size += len + 2; - if (state->err == CLEANUP_STAT_OK - && out_record(out_stream, curr_rec_type, start, len) < 0) - state->err = out_error; + if (state->err == CLEANUP_STAT_OK) { + state->act_size += len + 2; + if (var_message_limit > 0 && state->act_size > var_message_limit) + state->err = CLEANUP_STAT_SIZE; + else if (out_record(out_stream, curr_rec_type, start, len) < 0) + state->err = out_error; + } } state->where = SMTPD_AFTER_DOT; - if (SMTPD_STAND_ALONE(state) == 0 && (err = smtpd_check_eod(state)) != 0) { + if (state->err == CLEANUP_STAT_OK + && SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_eod(state)) != 0) { smtpd_chat_reply(state, "%s", err); if (state->proxy) { smtpd_proxy_close(state); @@ -2285,8 +2290,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) if (state->err == CLEANUP_STAT_OK && *STR(state->proxy_buffer) != '2') state->err = CLEANUP_STAT_CONT; - } else { - state->error_mask |= MAIL_ERROR_SOFTWARE; + } else if (state->err != CLEANUP_STAT_SIZE) { state->err |= CLEANUP_STAT_PROXY; detail = cleanup_stat_detail(CLEANUP_STAT_PROXY); vstring_sprintf(state->proxy_buffer, @@ -3077,7 +3081,7 @@ static void smtpd_start_tls(SMTPD_STATE *state) smtpd_chat_reply(state, "421 4.7.0 %s Error: too many new TLS sessions from %s", var_myhostname, state->namaddr); - msg_warn("Too many new TLS sessions: %d from %s for service %s", + msg_warn("New TLS session rate limit exceeded: %d from %s for service %s", rate, state->namaddr, state->service); /* XXX Use regular return to signal end of session. */ vstream_longjmp(state->client, SMTP_ERR_QUIET); diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 21beffee7..df1168c68 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -29,7 +29,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ unix_recv_fd.c unix_send_fd.c unix_trigger.c unsafe.c uppercase.c \ username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ - write_buf.c write_wait.c + write_buf.c write_wait.c sane_basename.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -60,7 +60,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ unix_recv_fd.o unix_send_fd.o unix_trigger.o unsafe.o uppercase.o \ username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \ vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \ - write_buf.o write_wait.o + write_buf.o write_wait.o sane_basename.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -96,7 +96,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \ attr_scan0 host_port attr_scan_plain attr_print_plain htable \ unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code \ - myaddrinfo myaddrinfo4 inet_proto + myaddrinfo myaddrinfo4 inet_proto sane_basename LIB_DIR = ../../lib INC_DIR = ../../include @@ -387,6 +387,11 @@ inet_proto: $(LIB) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) mv junk $@.o +sane_basename: $(LIB) + mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) + mv junk $@.o + stream_test: stream_test.c $(LIB) $(CC) $(CFLAGS) -o $@ $@.c $(LIB) $(SYSLIBS) @@ -433,6 +438,11 @@ inet_addr_list_test: inet_addr_list diff inet_addr_list.ref inet_addr_list.tmp rm -f inet_addr_list.tmp +sane_basename_test: sane_basename + ./sane_basename sane_basename.tmp 2>&1 + diff sane_basename.ref sane_basename.tmp + rm -f sane_basename.tmp + base64_code_test: base64_code ./base64_code @@ -1299,6 +1309,14 @@ sane_accept.o: msg.h sane_accept.o: sane_accept.c sane_accept.o: sane_accept.h sane_accept.o: sys_defs.h +sane_basename.o: msg.h +sane_basename.o: mymalloc.c +sane_basename.o: mymalloc.h +sane_basename.o: sane_basename.c +sane_basename.o: stringops.h +sane_basename.o: sys_defs.h +sane_basename.o: vbuf.h +sane_basename.o: vstring.h sane_connect.o: msg.h sane_connect.o: sane_connect.c sane_connect.o: sane_connect.h diff --git a/postfix/src/util/sane_basename.c b/postfix/src/util/sane_basename.c new file mode 100644 index 000000000..3dea9e16f --- /dev/null +++ b/postfix/src/util/sane_basename.c @@ -0,0 +1,180 @@ +/*++ +/* NAME +/* sane_basename 3 +/* SUMMARY +/* split pathname into last component and parent directory +/* SYNOPSIS +/* #include +/* +/* char *sane_basename(buf, path) +/* VSTRING *buf; +/* const char *path; +/* +/* char *sane_dirname(buf, path) +/* VSTRING *buf; +/* const char *path; +/* DESCRIPTION +/* These functions split a pathname into its last component +/* and its parent directory, excluding any trailing "/" +/* characters from the input. The result is a pointer to "/" +/* when the input is all "/" characters, or a pointer to "." +/* when the input is a null pointer or zero-length string. +/* +/* sane_basename() and sane_dirname() differ as follows +/* from standard basename() and dirname() implementations: +/* .IP \(bu +/* They can use caller-provided storage or private storage. +/* .IP \(bu +/* They never modify their input. +/* .PP +/* sane_basename() returns a pointer to string with the last +/* pathname component. +/* +/* sane_dirname() returns a pointer to string with the parent +/* directory. The result is a pointer to "." when the input +/* contains no '/' character. +/* +/* Arguments: +/* .IP buf +/* Result storage. If a null pointer is specified, each function +/* uses its own private memory that is overwritten upon each call. +/* .IP path +/* The input pathname. +/* 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 + +/* Utility library. */ + +#include +#include + +#define STR(x) vstring_str(x) + +/* sane_basename - skip directory prefix */ + +char *sane_basename(VSTRING *bp, const char *path) +{ + static VSTRING *buf; + const char *first; + const char *last; + + /* + * Your buffer or mine? + */ + if (bp == 0) { + bp = buf; + if (bp == 0) + bp = buf = vstring_alloc(10); + } + + /* + * Special case: return "." for null or zero-length input. + */ + if (path == 0 || *path == 0) + return (STR(vstring_strcpy(bp, "."))); + + /* + * Remove trailing '/' characters from input. Return "/" if input is all + * '/' characters. + */ + last = path + strlen(path) - 1; + while (*last == '/') { + if (last == path) + return (STR(vstring_strcpy(bp, "/"))); + last--; + } + + /* + * The pathname does not end in '/'. Skip to last '/' character if any. + */ + first = last - 1; + while (first >= path && *first != '/') + first--; + + return (STR(vstring_strncpy(bp, first + 1, last - first))); +} + +/* sane_dirname - keep directory prefix */ + +char *sane_dirname(VSTRING *bp, const char *path) +{ + static VSTRING *buf; + const char *last; + + /* + * Your buffer or mine? + */ + if (bp == 0) { + bp = buf; + if (bp == 0) + bp = buf = vstring_alloc(10); + } + + /* + * Special case: return "." for null or zero-length input. + */ + if (path == 0 || *path == 0) + return (STR(vstring_strcpy(bp, "."))); + + /* + * Remove trailing '/' characters from input. Return "/" if input is all + * '/' characters. + */ + last = path + strlen(path) - 1; + while (*last == '/') { + if (last == path) + return (STR(vstring_strcpy(bp, "/"))); + last--; + } + + /* + * This pathname does not end in '/'. Skip to last '/' character if any. + */ + while (last >= path && *last != '/') + last--; + if (last < path) /* no '/' */ + return (STR(vstring_strcpy(bp, "."))); + + /* + * Strip trailing '/' characters from dirname (not strictly needed). + */ + while (last > path && *last == '/') + last--; + + return (STR(vstring_strncpy(bp, path, last - path + 1))); +} + +#ifdef TEST +#include + +int main(int argc, char **argv) +{ + VSTRING *buf = vstring_alloc(10); + char *dir; + char *base; + + while (vstring_get_nonl(buf, VSTREAM_IN) > 0) { + dir = sane_dirname((VSTRING *) 0, STR(buf)); + base = sane_basename((VSTRING *) 0, STR(buf)); + vstream_printf("input=\"%s\" dir=\"%s\" base=\"%s\"\n", + STR(buf), dir, base); + } + vstream_fflush(VSTREAM_OUT); + vstring_free(buf); + return (0); +} + +#endif diff --git a/postfix/src/util/sane_basename.in b/postfix/src/util/sane_basename.in new file mode 100644 index 000000000..75d613ddf --- /dev/null +++ b/postfix/src/util/sane_basename.in @@ -0,0 +1,18 @@ +/// +/ +fo2/// +fo2/ +fo2 +///fo2 +/fo2 +///fo2///bar/// +fo2///bar/// +fo2///bar/ +fo2/bar + +/usr/lib +/usr/ +usr +/ +. +.. diff --git a/postfix/src/util/sane_basename.ref b/postfix/src/util/sane_basename.ref new file mode 100644 index 000000000..8edcfcd32 --- /dev/null +++ b/postfix/src/util/sane_basename.ref @@ -0,0 +1,18 @@ +input="///" dir="/" base="/" +input="/" dir="/" base="/" +input="fo2///" dir="." base="fo2" +input="fo2/" dir="." base="fo2" +input="fo2" dir="." base="fo2" +input="///fo2" dir="/" base="fo2" +input="/fo2" dir="/" base="fo2" +input="///fo2///bar///" dir="///fo2" base="bar" +input="fo2///bar///" dir="fo2" base="bar" +input="fo2///bar/" dir="fo2" base="bar" +input="fo2/bar" dir="fo2" base="bar" +input="" dir="." base="." +input="/usr/lib" dir="/usr" base="lib" +input="/usr/" dir="/" base="usr" +input="usr" dir="." base="usr" +input="/" dir="/" base="/" +input="." dir="." base="." +input=".." dir="." base=".." diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index 47cfeb631..02ea3079a 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -29,8 +29,11 @@ extern char *concatenate(const char *,...); extern char *mystrtok(char **, const char *); extern char *translit(char *, const char *, const char *); #ifndef HAVE_BASENAME +#define basename postfix_basename extern char *basename(const char *); #endif +extern char *sane_basename(VSTRING *, const char *); +extern char *sane_dirname(VSTRING *, const char *); extern VSTRING *unescape(VSTRING *, const char *); extern int alldig(const char *); extern int allprint(const char *);