Skip to content

Commit

Permalink
Add options startup parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
JelteF committed Jul 3, 2023
1 parent 1f70d25 commit d746015
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/client.c
Expand Up @@ -548,6 +548,84 @@ bool handle_auth_query_response(PgSocket *client, PktHdr *pkt) {
return true;
}

static inline const char *skip_ws(const char *position)
{
while (*position && isspace(*position))
position++;
return position;
}

_MUSTCHECK
static bool get_escaped_string(struct MBuf *buf, const char **position_ptr)
{
const char *position = *position_ptr;
const char *unwritten_start = position;
while (*position)
{
if (*position == '\\') {
if (!mbuf_write(buf, unwritten_start, position-unwritten_start))
return false;
position++;
unwritten_start = position;
if (!*position)
break;
} else if (isspace(*position)) {
break;
}
position++;
}
if (!mbuf_write(buf, unwritten_start, position-unwritten_start))
return false;
if (!mbuf_write_byte(buf, '\0'))
return false;
*position_ptr = position;
return true;
}

static bool set_startup_options(PgSocket *client, const char *options)
{
char arg_buf[400];
struct MBuf arg;
const char *position = options;
mbuf_init_fixed_writer(&arg, arg_buf, sizeof(arg_buf));
slog_error(client, "received options: %s", options);
while (*position) {
const char *key_string, *value_string;
char *equals;
mbuf_rewind_writer(&arg);
position = skip_ws(position);
if (strncmp("-c", position, 2) != 0)
goto fail;
position += 2;
position = skip_ws(position);

if (!get_escaped_string(&arg, &position)) {
disconnect_client(client, true, "unsupported options startup parameter: parameter too long");
return false;
}

equals = strchr((char *) arg.data, '=');
if (!equals)
goto fail;
*equals = '\0';

key_string = (const char *) arg.data;
value_string = (const char *) equals + 1;
if (varcache_set(&client->vars, key_string, value_string)) {
slog_debug(client, "got var from options: %s=%s", key_string, value_string);
} else if (strlist_contains(cf_ignore_startup_params, key_string)) {
slog_debug(client, "ignoring startup parameter from options: %s=%s", key_string, value_string);
} else {
slog_warning(client, "unsupported startup parameter in options: %s=%s", key_string, value_string);
disconnect_client(client, true, "unsupported startup parameter: %s", key_string);
}
}
return true;
fail:
disconnect_client(client, true, "unsupported options startup parameter: only '-c config=val' is allowed");
return false;
}

static void set_appname(PgSocket *client, const char *app_name)
{
char buf[400], abuf[300];
Expand Down Expand Up @@ -590,6 +668,9 @@ static bool decide_startup_pool(PgSocket *client, PktHdr *pkt)
} else if (strcmp(key, "user") == 0) {
slog_debug(client, "got var: %s=%s", key, val);
username = val;
} else if (strcmp(key, "options") == 0) {
if (!set_startup_options(client, val))
return false;
} else if (strcmp(key, "application_name") == 0) {
set_appname(client, val);
appname_found = true;
Expand Down
58 changes: 58 additions & 0 deletions test/test_misc.py
Expand Up @@ -85,3 +85,61 @@ async def test_host_list(bouncer):
async def test_host_list_dummy(bouncer):
with bouncer.log_contains(r"new connection to server \(from 127.0.0.1", times=2):
await bouncer.asleep(1, dbname="hostlist2", times=2)


def test_options_startup_param(bouncer):
assert (
bouncer.sql_value("SHOW datestyle", options=" -c datestyle=German,\\ YMD")
== "German, YMD"
)

assert (
bouncer.sql_value(
"SHOW datestyle",
options="-c timezone=Portugal -c datestyle=German,\\ YMD",
)
== "German, YMD"
)

assert (
bouncer.sql_value(
"SHOW timezone",
options="-c timezone=Portugal -c datestyle=German,\\ YMD",
)
== "Portugal"
)

assert (
bouncer.sql_value(
"SHOW timezone", options="-ctimezone=Portugal -cdatestyle=German,\\ YMD"
)
== "Portugal"
)

assert (
bouncer.sql_value(
"SHOW timezone",
options="-c t\\imezone=\\P\\o\\r\\t\\ugal -c dat\\estyle\\=\\Ge\\rman,\\ YMD",
)
== "Portugal"
)

with pytest.raises(
psycopg.OperationalError,
match="unsupported options startup parameter: only '-c config=val' is allowed",
):
bouncer.test(options="-d")

with pytest.raises(
psycopg.OperationalError,
match="unsupported options startup parameter: only '-c config=val' is allowed",
):
bouncer.test(options="-c timezone")

too_long_param = "a" * 1000

with pytest.raises(
psycopg.OperationalError,
match="unsupported options startup parameter: parameter too long",
):
bouncer.test(options="-c timezone=" + too_long_param)

0 comments on commit d746015

Please sign in to comment.