2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 01:49:47 +00:00

postfix-3.10-20241025

This commit is contained in:
Wietse Z Venema 2024-10-25 00:00:00 -05:00 committed by Viktor Dukhovni
parent c7a9de81bf
commit a559c5da49
10 changed files with 346 additions and 102 deletions

View File

@ -28439,3 +28439,11 @@ Apologies for any names omitted.
Documentation: in a pgsql: client configuration, the setting
"dbname" is required, but ignored when the setting "hosts"
contains an URI with a database name. File: proto/pgsql_table.
20241025
Cleanup: accept any well-formed URI prefix as a pgsql: client
connection target (the PostgreSQL URI parser decides what
is allowed). The dbname setting is now optional if the hosts
setting specifies only URIs. Files: util/valid_uri_scheme.[hc],
proto/pgsql_table.

View File

@ -43,22 +43,30 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
<b><a name="pgsql_parameters">PGSQL PARAMETERS</a></b>
<b>hosts</b> The hosts that Postfix will try to connect to and query from.
Besides a <b>postgresql://</b> connection URI, this setting supports
the historical forms <b>unix:/</b><i>pathname</i> for UNIX-domain sockets and
Besides a PostgreSQL connection URI, this setting supports the
historical forms <b>unix:/</b><i>pathname</i> for UNIX-domain sockets and
<b>inet:</b><i>host:port</i> for TCP connections, where the <b>unix:</b> and <b>inet:</b>
prefixes are accepted and ignored for backwards compatibility.
Examples:
hosts = postgresql://username@example.com/<i>databasename</i>?sslmode=require
hosts = postgres://user:secret@localhost
hosts = inet:host1.some.domain inet:host2.some.domain:port
hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
See <a href="https://www.postgresql.org/docs/current/libpq-connect.html">https://www.postgresql.org/docs/current/libpq-connect.html</a>
for the supported connection URI syntax.
The hosts are tried in random order. The connections are auto-
matically closed after being idle for about 1 minute, and are
re-opened as necessary.
re-opened as necessary. See <b>idle_interval</b> for details.
NOTE: if "hosts" specifies one load balancer and no alternative
servers, specify the load balancer multiple times in the "hosts"
NOTE: if <b>hosts</b> specifies a PostgreSQL connection URI, the Post-
greSQL client library will ignore the <b>dbname</b> setting for that
connection.
NOTE: if <b>hosts</b> specifies one load balancer and no alternative
servers, specify the load balancer multiple times in the <b>hosts</b>
line. Without the duplicate info, the Postfix PostgreSQL client
would not reconnect immediately to the same load balancer after
a PostgreSQL server failure.
@ -71,12 +79,15 @@ PGSQL_TABLE(5) PGSQL_TABLE(5)
user = someone
password = some_password
<b>dbname</b> (required)
The database name on the servers. Example:
<b>dbname</b> The database name on the servers. Example:
dbname = customer_database
This setting is required, but ignored when a postgresql:// URI
specifies a database name.
The <b>dbname</b> setting is ignored for <b>hosts</b> connections that are
specified as an URI.
The <b>dbname</b> setting is required with Postfix 3.10 and later, when
<b>hosts</b> specifies any non-URI connection; it is always required
with earlier Postfix versions.
<b>encoding</b>
The encoding used by the database client. The default setting

View File

@ -55,7 +55,7 @@ return the key itself or a constant value.
.fi
.IP "\fBhosts\fR"
The hosts that Postfix will try to connect to and query
from. Besides a \fBpostgresql://\fR connection URI, this
from. Besides a PostgreSQL connection URI, this
setting supports the historical forms \fBunix:/\fIpathname\fR
for UNIX\-domain sockets and \fBinet:\fIhost:port\fR for TCP
connections, where the \fBunix:\fR and \fBinet:\fR prefixes
@ -63,18 +63,28 @@ are accepted and ignored for backwards compatibility.
Examples:
.nf
hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
hosts = postgres://user:secret@localhost
hosts = inet:host1.some.domain inet:host2.some.domain:port
hosts = host1.some.domain host2.some.domain:port
hosts = unix:/file/name
.fi
See https://www.postgresql.org/docs/current/libpq\-connect.html
for the supported connection URI syntax.
The hosts are tried in random order. The connections are
automatically closed after being idle for about 1 minute,
and are re\-opened as necessary.
and are re\-opened as necessary. See \fBidle_interval\fR
for details.
NOTE: if "hosts" specifies one load balancer and no alternative
NOTE: if \fBhosts\fR specifies a PostgreSQL connection URI,
the PostgreSQL client library will ignore the \fBdbname\fR
setting for that connection.
NOTE: if \fBhosts\fR specifies one load balancer and no
alternative
servers, specify the load balancer multiple times in the
"hosts" line. Without the duplicate info, the Postfix
\fBhosts\fR line. Without the duplicate info, the Postfix
PostgreSQL client would not reconnect immediately to the
same load balancer after a PostgreSQL server failure.
.IP "\fBuser\fR"
@ -85,14 +95,18 @@ Example:
user = someone
password = some_password
.fi
.IP "\fBdbname\fR (required)"
.IP "\fBdbname\fR"
The database name on the servers. Example:
.nf
dbname = customer_database
.fi
.sp
This setting is required, but ignored when a postgresql://
URI specifies a database name.
The \fBdbname\fR setting is ignored for \fBhosts\fR connections
that are specified as an URI.
The \fBdbname\fR setting is required with Postfix 3.10 and later,
when \fBhosts\fR specifies any non\-URI connection; it is always
required with earlier Postfix versions.
.IP "\fBencoding\fR"
The encoding used by the database client. The default setting
is:

View File

@ -45,7 +45,7 @@
# .fi
# .IP "\fBhosts\fR"
# The hosts that Postfix will try to connect to and query
# from. Besides a \fBpostgresql://\fR connection URI, this
# from. Besides a PostgreSQL connection URI, this
# setting supports the historical forms \fBunix:/\fIpathname\fR
# for UNIX-domain sockets and \fBinet:\fIhost:port\fR for TCP
# connections, where the \fBunix:\fR and \fBinet:\fR prefixes
@ -53,18 +53,28 @@
# Examples:
# .nf
# hosts = postgresql://username@example.com/\fIdatabasename\fR?sslmode=require
# hosts = postgres://user:secret@localhost
# hosts = inet:host1.some.domain inet:host2.some.domain:port
# hosts = host1.some.domain host2.some.domain:port
# hosts = unix:/file/name
# .fi
#
# See https://www.postgresql.org/docs/current/libpq-connect.html
# for the supported connection URI syntax.
#
# The hosts are tried in random order. The connections are
# automatically closed after being idle for about 1 minute,
# and are re-opened as necessary.
# and are re-opened as necessary. See \fBidle_interval\fR
# for details.
#
# NOTE: if "hosts" specifies one load balancer and no alternative
# NOTE: if \fBhosts\fR specifies a PostgreSQL connection URI,
# the PostgreSQL client library will ignore the \fBdbname\fR
# setting for that connection.
#
# NOTE: if \fBhosts\fR specifies one load balancer and no
# alternative
# servers, specify the load balancer multiple times in the
# "hosts" line. Without the duplicate info, the Postfix
# \fBhosts\fR line. Without the duplicate info, the Postfix
# PostgreSQL client would not reconnect immediately to the
# same load balancer after a PostgreSQL server failure.
# .IP "\fBuser\fR"
@ -75,14 +85,18 @@
# user = someone
# password = some_password
# .fi
# .IP "\fBdbname\fR (required)"
# .IP "\fBdbname\fR"
# The database name on the servers. Example:
# .nf
# dbname = customer_database
# .fi
# .sp
# This setting is required, but ignored when a postgresql://
# URI specifies a database name.
# The \fBdbname\fR setting is ignored for \fBhosts\fR connections
# that are specified as an URI.
#
# The \fBdbname\fR setting is required with Postfix 3.10 and later,
# when \fBhosts\fR specifies any non-URI connection; it is always
# required with earlier Postfix versions.
# .IP "\fBencoding\fR"
# The encoding used by the database client. The default setting
# is:

View File

@ -1252,6 +1252,7 @@ dict_pgsql.o: ../../include/myrand.h
dict_pgsql.o: ../../include/split_at.h
dict_pgsql.o: ../../include/stringops.h
dict_pgsql.o: ../../include/sys_defs.h
dict_pgsql.o: ../../include/valid_uri_scheme.h
dict_pgsql.o: ../../include/vbuf.h
dict_pgsql.o: ../../include/vstream.h
dict_pgsql.o: ../../include/vstring.h

View File

@ -92,6 +92,7 @@
#include "myrand.h"
#include "events.h"
#include "stringops.h"
#include "valid_uri_scheme.h"
/* Global library. */
@ -127,6 +128,7 @@ typedef struct {
typedef struct {
int len_hosts; /* number of hosts */
HOST **db_hosts; /* hosts on which databases reside */
char *non_uri_target; /* require dbname to be specified */
} PLPGSQL;
typedef struct {
@ -626,7 +628,7 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 0, 0);
dict_pgsql->encoding = cfg_get_str(p, "encoding", "UTF8", 1, 0);
dict_pgsql->retry_interval = cfg_get_int(p, "retry_interval",
DEF_RETRY_INTV, 1, 0);
@ -723,6 +725,21 @@ DICT *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
if (dict_pgsql->pldb == NULL)
msg_fatal("couldn't initialize pldb!\n");
if (msg_verbose && dict_pgsql->pldb->non_uri_target == 0
&& dict_pgsql->dbname[0] != 0)
msg_info("%s:%s table ignores 'dbname' field -- "
"all 'hosts' targets are URIs",
DICT_TYPE_PGSQL, name);
if (dict_pgsql->pldb->non_uri_target && dict_pgsql->dbname[0] == 0) {
DICT *ret;
ret == (dict_surrogate(DICT_TYPE_PGSQL, name, open_flags, dict_flags,
"%s:%s host target '%s' requires dbname setting",
DICT_TYPE_PGSQL, name,
dict_pgsql->pldb->non_uri_target));
dict_pgsql_close(&dict_pgsql->dict);
return (ret);
}
dict_pgsql->dict.owner = cfg_get_owner(dict_pgsql->parser);
return (DICT_DEBUG (&dict_pgsql->dict));
}
@ -737,8 +754,12 @@ static PLPGSQL *plpgsql_init(ARGV *hosts)
PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
PLDB->len_hosts = hosts->argc;
PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
for (i = 0; i < hosts->argc; i++)
PLDB->non_uri_target = 0;
for (i = 0; i < hosts->argc; i++) {
PLDB->db_hosts[i] = host_init(hosts->argv[i]);
if (PLDB->db_hosts[i]->type != TYPECONNSTR)
PLDB->non_uri_target = PLDB->db_hosts[i]->name;
}
return PLDB;
}
@ -758,9 +779,9 @@ static HOST *host_init(const char *hostname)
host->ts = 0;
/*
* Modern syntax: "postgresql://connection-info".
* Modern syntax: connection URI.
*/
if (strncmp(d, "postgresql:", 11) == 0) {
if (valid_uri_scheme(d)) {
host->type = TYPECONNSTR;
host->name = mystrdup(d);
host->port = 0;

View File

@ -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 "20241024"
#define MAIL_RELEASE_DATE "20241025"
#define MAIL_VERSION_NUMBER "3.10"
#ifdef SNAPSHOT

View File

@ -46,7 +46,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
inet_addr_sizes.c quote_for_json.c mystrerror.c \
sane_sockaddr_to_hostaddr.c normalize_ws.c
sane_sockaddr_to_hostaddr.c normalize_ws.c valid_uri_scheme.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 \
@ -94,7 +94,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
quote_for_json.o mystrerror.o sane_sockaddr_to_hostaddr.o \
normalize_ws.o
normalize_ws.o valid_uri_scheme.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@ -126,7 +126,7 @@ HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
valid_utf8_hostname.h midna_domain.h dict_union.h dict_inline.h \
check_arg.h argv_attr.h msg_logger.h logwriter.h byte_mask.h \
known_tcp_ports.h sane_strtol.h hash_fnv.h ldseed.h mkmap.h \
inet_prefix_top.h inet_addr_sizes.h
inet_prefix_top.h inet_addr_sizes.h valid_uri_scheme.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE)
@ -149,7 +149,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
vbuf_print split_qnameval vstream msg_logger byte_mask \
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
clean_env inet_prefix_top printable readlline quote_for_json \
normalize_ws
normalize_ws valid_uri_scheme
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1
@ -618,6 +618,11 @@ normalize_ws: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
valid_uri_scheme: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test host_port_test dict_tests \
@ -629,7 +634,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
binhash_test argv_test inet_prefix_top_test printable_test \
valid_utf8_string_test readlline_test quote_for_json_test \
normalize_ws_test
normalize_ws_test valid_uri_scheme_test
dict_tests: all dict_test \
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@ -1109,6 +1114,9 @@ quote_for_json_test: quote_for_json
normalize_ws_test: normalize_ws
$(SHLIB_ENV) ${VALGRIND} ./normalize_ws
valid_uri_scheme_test: valid_uri_scheme
$(SHLIB_ENV) ${VALGRIND} ./valid_uri_scheme
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
@ -2893,6 +2901,16 @@ valid_hostname.o: valid_hostname.c
valid_hostname.o: valid_hostname.h
valid_hostname.o: vbuf.h
valid_hostname.o: vstring.h
valid_uri_scheme.o: check_arg.h
valid_uri_scheme.o: msg.h
valid_uri_scheme.o: msg_vstream.h
valid_uri_scheme.o: stringops.h
valid_uri_scheme.o: sys_defs.h
valid_uri_scheme.o: valid_uri_scheme.c
valid_uri_scheme.o: valid_uri_scheme.h
valid_uri_scheme.o: vbuf.h
valid_uri_scheme.o: vstream.h
valid_uri_scheme.o: vstring.h
valid_utf8_hostname.o: check_arg.h
valid_utf8_hostname.o: midna_domain.h
valid_utf8_hostname.o: msg.h

View File

@ -0,0 +1,129 @@
/*++
/* NAME
/* valid_uri_scheme 3
/* SUMMARY
/* validate scheme:// prefix
/* SYNOPSIS
/* #include <valid_uri_scheme.h>
/*
/* int valid_uri_scheme(const char *str)
/* DESCRIPTION
/* valid_uri_scheme() takes a null-terminated string and returns
/* the length of a valid scheme:// prefix, or zero if no valid
/* prefix was found.
/*
/* This function requires that input is encoded in ASCII or UTF-8.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#include <ctype.h>
#include <stdlib.h>
/*
* Utility library.
*/
#include <valid_uri_scheme.h>
#include <msg.h>
#include <msg_vstream.h>
#include <stringops.h>
/* valid_uri_scheme - predicate that string starts with scheme:// */
ssize_t valid_uri_scheme(const char *str)
{
const char *cp = str;
int ch = *cp++;
/* Per RFC 3986, a valid scheme starts with ALPHA. */
if (!ISALPHA(ch))
return (0);
while ((ch = *cp++) != 0) {
/* A valid scheme continues with ALPHA | DIGIT | '+' | '-'. */
if (ISALNUM(ch) || ch == '+' || ch == '-')
continue;
/* A valid scheme is followed by "://". */
if (ch == ':' && *cp++ == '/' && *cp++ == '/')
return (cp - str);
/* Not a valid scheme. */
break;
}
/* Not a valid scheme. */
return (0);
}
#ifdef TEST
typedef struct TEST_CASE {
const char *label;
const char *input;
const ssize_t want;
} TEST_CASE;
#define PASS (0)
#define FAIL (1)
static const TEST_CASE test_cases[] = {
{"accepts_alpha_scheme", "abcd://blah", sizeof("abcd://") - 1},
{"accepts_mixed_scheme", "a-bcd+123://blah", sizeof("a-bcd+123://") - 1},
{"rejects_minus_first", "-bcd+123://blah'", 0},
{"rejects_plus_first", "+123://blah", 0},
{"rejects_digit_first", "123://blah", 0},
{"rejects_other_first", "?123://blah", 0},
{"rejects_other_middle", "abcd?123://blah", 0},
{"rejects_other_end", "abcd-123?://blah", 0},
{"rejects_non_scheme", "inet:host:port", 0},
{"rejects_no_colon", "inet", 0},
{"rejects_colon_slash", "abcd:/blah", 0},
{"rejects_empty", "", 0},
{0,}
};
static int test_validate_scheme(const TEST_CASE *tp)
{
int got;
got = valid_uri_scheme(tp->input);
if (got != tp->want) {
msg_warn("got '%ld', want '%ld'", (long) got, (long) tp->want);
return (FAIL);
}
return (PASS);
}
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
for (tp = test_cases; tp->label != 0; tp++) {
int test_failed;
msg_info("RUN %s", tp->label);
test_failed = test_validate_scheme(tp);
if (test_failed) {
msg_info("FAIL %s", tp->label);
fail++;
} else {
msg_info("PASS %s", tp->label);
pass++;
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
exit(fail != 0);
}
#endif

View File

@ -0,0 +1,28 @@
#ifndef _VALID_SCHEME_H_INCLUDED_
#define _VALID_SCHEME_H_INCLUDED_
/*++
/* NAME
/* valid_uri_scheme 3h
/* SUMMARY
/* validate scheme:// prefix
/* SYNOPSIS
/* #include <valid_uri_scheme.h>
/* DESCRIPTION
/* .nf
/*
* External interface.
*/
extern ssize_t valid_uri_scheme(const char *);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
#endif