summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2020-04-06 00:28:06 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2020-04-06 13:14:05 +0100
commit0b4dfe7aa1f12214abdfa1037497d6c49a471612 (patch)
tree7a033655ce31568e0fd78417b4b98fa3143668cd /src
parentba74fb8d95d2e9af2122e0a95c4d5334b4f0466c (diff)
MySQL, pgsql: per-query server options outside the lookup string. Bug 2546
Diffstat (limited to 'src')
-rw-r--r--src/src/acl.c9
-rw-r--r--src/src/lookups/lf_functions.h5
-rw-r--r--src/src/lookups/lf_sqlperform.c118
-rw-r--r--src/src/lookups/mysql.c31
-rw-r--r--src/src/lookups/pgsql.c5
-rw-r--r--src/src/lookups/redis.c5
-rw-r--r--src/src/string.c4
-rw-r--r--src/src/verify.c7
8 files changed, 108 insertions, 76 deletions
diff --git a/src/src/acl.c b/src/src/acl.c
index 02251b197..3ea8df1b1 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -3477,13 +3477,13 @@ for (; cb; cb = cb->next)
{
uschar *endcipher = NULL;
uschar *cipher = Ustrchr(tls_in.cipher, ':');
- if (cipher == NULL) cipher = tls_in.cipher; else
+ if (!cipher) cipher = tls_in.cipher; else
{
endcipher = Ustrchr(++cipher, ':');
- if (endcipher != NULL) *endcipher = 0;
+ if (endcipher) *endcipher = 0;
}
rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL);
- if (endcipher != NULL) *endcipher = ':';
+ if (endcipher) *endcipher = ':';
}
break;
@@ -3496,8 +3496,7 @@ for (; cb; cb = cb->next)
case ACLC_HOSTS:
rc = verify_check_this_host(&arg, sender_host_cache, NULL,
- (sender_host_address == NULL)? US"" : sender_host_address,
- CUSS &host_data);
+ sender_host_address ? sender_host_address : US"", CUSS &host_data);
if (rc == DEFER) *log_msgptr = search_error_message;
if (host_data) host_data = string_copy_perm(host_data, TRUE);
break;
diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h
index 4d9ae9595..7e7ac4d24 100644
--- a/src/src/lookups/lf_functions.h
+++ b/src/src/lookups/lf_functions.h
@@ -12,7 +12,8 @@ extern int lf_check_file(int, const uschar *, int, int, uid_t *, gid_t *,
extern gstring *lf_quote(uschar *, uschar *, int, gstring *);
extern int lf_sqlperform(const uschar *, const uschar *, const uschar *,
const uschar *, uschar **,
- uschar **, uint *, int(*)(const uschar *, uschar *, uschar **,
- uschar **, BOOL *, uint *));
+ uschar **, uint *, const uschar *,
+ int(*)(const uschar *, uschar *, uschar **,
+ uschar **, BOOL *, uint *, const uschar *));
/* End of lf_functions.h */
diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c
index cc894e0ed..e2636bfe9 100644
--- a/src/src/lookups/lf_sqlperform.c
+++ b/src/src/lookups/lf_sqlperform.c
@@ -36,55 +36,35 @@ Returns: the return from the lookup function, or DEFER
int
lf_sqlperform(const uschar *name, const uschar *optionname,
const uschar *optserverlist, const uschar *query,
- uschar **result, uschar **errmsg, uint *do_cache,
- int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *))
+ uschar **result, uschar **errmsg, uint *do_cache, const uschar * opts,
+ int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *))
{
-int sep, rc;
+int rc;
uschar *server;
-const uschar *serverlist;
-uschar buffer[512];
BOOL defer_break = FALSE;
-DEBUG(D_lookup) debug_printf_indent("%s query: %s\n", name, query);
-
-/* Handle queries that do not have server information at the start. */
-
-if (Ustrncmp(query, "servers", 7) != 0)
- {
- sep = 0;
- serverlist = optserverlist;
- while ((server = string_nextinlist(&serverlist, &sep, buffer,
- sizeof(buffer))) != NULL)
- {
- rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache);
- if (rc != DEFER || defer_break) return rc;
- }
- if (optserverlist == NULL)
- *errmsg = string_sprintf("no %s servers defined (%s option)", name,
- optionname);
- }
+DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts);
/* Handle queries that do have server information at the start. */
-else
+if (Ustrncmp(query, "servers", 7) == 0)
{
- int qsep;
+ int qsep = 0;
const uschar *s, *ss;
const uschar *qserverlist;
uschar *qserver;
- uschar qbuffer[512];
s = query + 7;
- while (isspace(*s)) s++;
+ skip_whitespace(&s);
if (*s++ != '=')
{
*errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
return DEFER;
}
- while (isspace(*s)) s++;
+ skip_whitespace(&s);
ss = Ustrchr(s, ';');
- if (ss == NULL)
+ if (!ss)
{
*errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
name);
@@ -99,27 +79,21 @@ else
}
qserverlist = string_sprintf("%.*s", (int)(ss - s), s);
- qsep = 0;
- while ((qserver = string_nextinlist(&qserverlist, &qsep, qbuffer,
- sizeof(qbuffer))) != NULL)
+ while ((qserver = string_nextinlist(&qserverlist, &qsep, NULL, 0)))
{
- if (Ustrchr(qserver, '/') != NULL)
+ if (Ustrchr(qserver, '/'))
server = qserver;
else
{
int len = Ustrlen(qserver);
+ const uschar * serverlist = optserverlist;
- sep = 0;
- serverlist = optserverlist;
- while ((server = string_nextinlist(&serverlist, &sep, buffer,
- sizeof(buffer))) != NULL)
- {
+ for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);)
if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/')
break;
- }
- if (server == NULL)
+ if (!server)
{
*errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
qserver, optionname);
@@ -127,11 +101,73 @@ else
}
}
- rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache);
+ if (is_tainted(server))
+ {
+ *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
+ return DEFER;
+ }
+
+ rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts);
if (rc != DEFER || defer_break) return rc;
}
}
+/* Handle queries that do not have server information at the start. */
+
+else
+ {
+ const uschar * serverlist = NULL;
+
+ /* If options are present, scan for a server definition. Default to
+ the "optserverlist" srgument. */
+
+ if (opts)
+ {
+ uschar * ele;
+ for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
+ if (Ustrncmp(ele, "servers=", 8) == 0)
+ { serverlist = ele + 8; break; }
+ }
+
+ if (!serverlist)
+ serverlist = optserverlist;
+ if (!serverlist)
+ *errmsg = string_sprintf("no %s servers defined (%s option)", name,
+ optionname);
+ else
+ for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); )
+ {
+ /* If not a full spec assume from options; scan main list for matching
+ hostname */
+
+ if (!Ustrchr(server, '/'))
+ {
+ int len = Ustrlen(server);
+ const uschar * slist = optserverlist;
+ uschar * ele;
+ for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
+ if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
+ break;
+ if (!ele)
+ {
+ *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
+ server, optionname);
+ return DEFER;
+ }
+ server = ele;
+ }
+
+ if (is_tainted(server))
+ {
+ *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server);
+ return DEFER;
+ }
+
+ rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
+ if (rc != DEFER || defer_break) return rc;
+ }
+ }
+
return DEFER;
}
diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c
index 7651353a2..220aba1c3 100644
--- a/src/src/lookups/mysql.c
+++ b/src/src/lookups/mysql.c
@@ -122,6 +122,7 @@ Arguments:
errmsg where to point an error message
defer_break TRUE if no more servers are to be tried after DEFER
do_cache set zero if data is changed
+ opts options
The server string is of the form "host/dbname/user/password". The host can be
host:port. This string is in a nextinlist temporary buffer, so can be
@@ -132,7 +133,7 @@ Returns: OK, FAIL, or DEFER
static int
perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr,
- uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts)
{
MYSQL *mysql_handle = NULL; /* Keep compilers happy */
MYSQL_RES *mysql_result = NULL;
@@ -155,7 +156,7 @@ has the password removed. This copy is also used for debugging output. */
for (int i = 3; i > 0; i--)
{
uschar *pp = Ustrrchr(server, '/');
- if (pp == NULL)
+ if (!pp)
{
*errmsg = string_sprintf("incomplete MySQL server data: %s",
(i == 3)? server : server_copy);
@@ -172,10 +173,7 @@ sdata[0] = server; /* What's left at the start */
for (cn = mysql_connections; cn; cn = cn->next)
if (Ustrcmp(cn->server, server_copy) == 0)
- {
- mysql_handle = cn->handle;
- break;
- }
+ { mysql_handle = cn->handle; break; }
/* If no cached connection, we must set one up. Mysql allows for a host name
and port to be specified. It also allows the name of a Unix socket to be used.
@@ -283,7 +281,7 @@ up. Setting do_cache zero requests this. */
if (!(mysql_result = mysql_use_result(mysql_handle)))
{
- if ( mysql_field_count(mysql_handle) == 0 )
+ if (mysql_field_count(mysql_handle) == 0)
{
DEBUG(D_lookup) debug_printf_indent("MYSQL: query was not one that returns data\n");
result = string_cat(result,
@@ -328,18 +326,15 @@ while ((mysql_row_data = mysql_fetch_row(mysql_result)))
we don't expect any more results. */
while((i = mysql_next_result(mysql_handle)) >= 0)
- {
- if(i == 0) /* Just ignore more results */
+ if(i != 0)
{
- DEBUG(D_lookup) debug_printf_indent("MYSQL: got unexpected more results\n");
- continue;
+ *errmsg = string_sprintf(
+ "MYSQL: lookup result error when checking for more results: %s\n",
+ mysql_error(mysql_handle));
+ goto MYSQL_EXIT;
}
-
- *errmsg = string_sprintf(
- "MYSQL: lookup result error when checking for more results: %s\n",
- mysql_error(mysql_handle));
- goto MYSQL_EXIT;
- }
+ else /* just ignore more results */
+ DEBUG(D_lookup) debug_printf_indent("MYSQL: got unexpected more results\n");
/* If result is NULL then no data has been found and so we return FAIL.
Otherwise, we must terminate the string which has been built; string_cat()
@@ -394,7 +389,7 @@ mysql_find(void * handle, const uschar * filename, const uschar * query,
const uschar * opts)
{
return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query,
- result, errmsg, do_cache, perform_mysql_search);
+ result, errmsg, do_cache, opts, perform_mysql_search);
}
diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c
index 90dda601a..505f5e5ce 100644
--- a/src/src/lookups/pgsql.c
+++ b/src/src/lookups/pgsql.c
@@ -113,13 +113,14 @@ Arguments:
errmsg where to point an error message
defer_break set TRUE if no more servers are to be tried after DEFER
do_cache set FALSE if data is changed
+ opts options list
Returns: OK, FAIL, or DEFER
*/
static int
perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr,
- uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts)
{
PGconn *pg_conn = NULL;
PGresult *pg_result = NULL;
@@ -386,7 +387,7 @@ pgsql_find(void * handle, const uschar * filename, const uschar * query,
const uschar * opts)
{
return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query,
- result, errmsg, do_cache, perform_pgsql_search);
+ result, errmsg, do_cache, opts, perform_pgsql_search);
}
diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c
index 84a2dc6c7..337fdae15 100644
--- a/src/src/lookups/redis.c
+++ b/src/src/lookups/redis.c
@@ -63,6 +63,7 @@ single server.
errmsg where to point an error message
defer_break TRUE if no more servers are to be tried after DEFER
do_cache set false if data is changed
+ opts options
The server string is of the form "host/dbnumber/password". The host can be
host:port. This string is in a nextinlist temporary buffer, so can be
@@ -73,7 +74,7 @@ single server.
static int
perform_redis_search(const uschar *command, uschar *server, uschar **resultptr,
- uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts)
{
redisContext *redis_handle = NULL; /* Keep compilers happy */
redisReply *redis_reply = NULL;
@@ -380,7 +381,7 @@ redis_find(void * handle __attribute__((unused)),
uint * do_cache, const uschar * opts)
{
return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command,
- result, errmsg, do_cache, perform_redis_search);
+ result, errmsg, do_cache, opts, perform_redis_search);
}
diff --git a/src/src/string.c b/src/src/string.c
index 80cf49fdf..1192a554e 100644
--- a/src/src/string.c
+++ b/src/src/string.c
@@ -574,18 +574,14 @@ uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s));
while (*s != 0)
{
if (*s != '\\')
- {
*ss++ = *s++;
- }
else if (isdigit(s[1]))
{
*ss++ = (s[1] - '0')*100 + (s[2] - '0')*10 + s[3] - '0';
s += 4;
}
else if (*(++s) != 0)
- {
*ss++ = *s++;
- }
}
*ss = 0;
diff --git a/src/src/verify.c b/src/src/verify.c
index cd9df1f71..dda51a5a2 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -2909,7 +2909,6 @@ provided that host name matching is permitted; if it's "@[]" match against the
local host's IP addresses. */
if (*ss == '@')
- {
if (ss[1] == 0)
{
if (isiponly) return ERROR;
@@ -2921,7 +2920,6 @@ if (*ss == '@')
if (Ustrcmp(ip->address, cb->host_address) == 0) return OK;
return FAIL;
}
- }
/* If the pattern is an IP address, optionally followed by a bitmask count, do
a (possibly masked) comparison with the current IP address. */
@@ -2951,6 +2949,11 @@ if (*t == 0 || (*t == '/' && t != ss))
if ((semicolon = Ustrchr(ss, ';')))
endname = (opts = Ustrchr(ss, ',')) ? opts : semicolon;
+if (opts)
+ {
+ opts++;
+ opts = string_copyn(opts, semicolon - opts);
+ }
/* If we are doing an IP address only match, then all lookups must be IP
address lookups, even if there is no "net-". */