From: Cody Cutrer Date: Wed, 18 Jan 2012 17:32:01 +0000 (-0700) Subject: auth_user support X-Git-Tag: pgbouncer_1_6_rc1~21^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=35a8a5768c68da946689fb31bc6a0867e62edfc6;p=pgbouncer auth_user support query the database's pg_shadow table for user information, instead of using a local userlist.txt. Brings transparent support for Postgres 9.0+ which no longer has a compatible file format. Tested with trust and md5 auth_types only. --- diff --git a/doc/config.txt b/doc/config.txt index 87ccb1b..7e3c646 100644 --- a/doc/config.txt +++ b/doc/config.txt @@ -527,6 +527,12 @@ for this database. Otherwise PgBouncer tries to log into the destination database with client username, meaning that there will be one pool per user. +==== auth_user ==== + +If +auth_user+ is set, any user not specified in auth_file will be +queried from pg_shadow in the database using auth_user. Auth_user's +password will be taken from auth_file. + === Pool configuration === ==== pool_size ==== @@ -610,7 +616,7 @@ So user `admin` with password `1234` will have MD5-hidden password === Minimal config === [databases] - template1 = host=127.0.0.1 dbname=template1 + template1 = host=127.0.0.1 dbname=template1 auth_user=someuser [pgbouncer] pool_mode = session diff --git a/doc/todo.txt b/doc/todo.txt index 5724092..a8f6d36 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -4,9 +4,6 @@ Significant amount of users feel the need for those. - * auth_conn - access to pg_shadow, so auth_file is not needed. - [ flat-text files are gone in 9.0+ ] - * Protocol-level plan cache. * LISTEN/NOTIFY. Requires strict SQL format. diff --git a/doc/usage.txt b/doc/usage.txt index f554b14..d0ec52d 100644 --- a/doc/usage.txt +++ b/doc/usage.txt @@ -149,7 +149,7 @@ database +pgbouncer+ Only users listed in configuration parameters +admin_users+ or +stats_users+ are allowed to login to the console. (Except when `auth_mode=any`, then -any user is allowed in as an admin.) +any user is allowed in as a stats_user.) Additionally, the username +pgbouncer+ is allowed to log in without password, if the login comes via Unix socket and the client has same Unix user uid diff --git a/include/admin.h b/include/admin.h index 9f0e338..188f255 100644 --- a/include/admin.h +++ b/include/admin.h @@ -16,7 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ bool admin_handle_client(PgSocket *client, PktHdr *pkt) _MUSTCHECK; -bool admin_pre_login(PgSocket *client) _MUSTCHECK; +bool admin_pre_login(PgSocket *client, const char *username) _MUSTCHECK; +bool admin_post_login(PgSocket *client) _MUSTCHECK; void admin_setup(void); bool admin_error(PgSocket *console, const char *fmt, ...) _PRINTF(2, 3) /* _MUSTCHECK */; void admin_pause_done(void); diff --git a/include/bouncer.h b/include/bouncer.h index 88f66f7..bfeb0b2 100644 --- a/include/bouncer.h +++ b/include/bouncer.h @@ -49,6 +49,7 @@ enum SocketState { CL_JUSTFREE, /* justfree_client_list */ CL_LOGIN, /* login_client_list */ CL_WAITING, /* pool->waiting_client_list */ + CL_WAITING_LOGIN, /* - but return to CL_LOGIN instead of CL_ACTIVE */ CL_ACTIVE, /* pool->active_client_list */ CL_CANCEL, /* pool->cancel_req_list */ @@ -270,6 +271,7 @@ struct PgDatabase { struct PktBuf *startup_params; /* partial StartupMessage (without user) be sent to server */ PgUser *forced_user; /* if not NULL, the user/psw is forced */ + PgUser *auth_user; /* if not NULL, users not in userlist.txt will be looked up on the server */ const char *host; /* host or unix socket name */ int port; @@ -285,6 +287,8 @@ struct PgDatabase { usec_t inactive_time; /* when auto-database became inactive (to kill it after timeout) */ unsigned active_stamp; /* set if autodb has connections */ + + struct AATree user_tree; /* users that have been queried on this database */ }; @@ -309,6 +313,8 @@ struct PgSocket { bool exec_on_connect:1; /* server: executing connect_query */ bool wait_for_welcome:1;/* client: no server yet in pool, cannot send welcome msg */ + bool wait_for_user_conn:1;/* client: waiting for auth_conn server connection */ + bool wait_for_user:1; /* client: waiting for auth_conn query results */ bool suspended:1; /* client/server: if the socket is suspended */ @@ -324,7 +330,10 @@ struct PgSocket { PgAddr remote_addr; /* ip:port for remote endpoint */ PgAddr local_addr; /* ip:port for local endpoint */ - struct DNSToken *dns_token; /* ongoing request */ + union { + struct DNSToken *dns_token; /* ongoing request */ + PgDatabase *db; /* cache db while doing auth query */ + }; VarCache vars; /* state of interesting server parameters */ diff --git a/include/client.h b/include/client.h index aaa1431..3253532 100644 --- a/include/client.h +++ b/include/client.h @@ -17,6 +17,5 @@ */ bool client_proto(SBuf *sbuf, SBufEvent evtype, struct MBuf *pkt) _MUSTCHECK; -bool set_pool(PgSocket *client, const char *dbname, const char *username) _MUSTCHECK; - - +bool set_pool(PgSocket *client, const char *dbname, const char *username, bool takeover) _MUSTCHECK; +bool handle_auth_response(PgSocket *client, PktHdr *pkt); diff --git a/include/objects.h b/include/objects.h index 218244b..3735565 100644 --- a/include/objects.h +++ b/include/objects.h @@ -44,6 +44,7 @@ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) _ PgDatabase * add_database(const char *name) _MUSTCHECK; PgDatabase *register_auto_database(const char *name); PgUser * add_user(const char *name, const char *passwd) _MUSTCHECK; +PgUser * add_db_user(PgDatabase *db, const char *name, const char *passwd) _MUSTCHECK; PgUser * force_user(PgDatabase *db, const char *username, const char *passwd) _MUSTCHECK; void accept_cancel_request(PgSocket *req); diff --git a/src/admin.c b/src/admin.c index cb19476..945669a 100644 --- a/src/admin.c +++ b/src/admin.c @@ -1316,12 +1316,11 @@ bool admin_handle_client(PgSocket *admin, PktHdr *pkt) * Client is unauthenticated, look if it wants to connect * to special "pgbouncer" user. */ -bool admin_pre_login(PgSocket *client) +bool admin_pre_login(PgSocket *client, const char *username) { uid_t peer_uid = -1; gid_t peer_gid = -1; int res; - const char *username = client->auth_user->name; client->admin_user = 0; client->own_user = 0; @@ -1332,6 +1331,7 @@ bool admin_pre_login(PgSocket *client) if (res >= 0 && peer_uid == getuid() && strcmp("pgbouncer", username) == 0) { + client->auth_user = admin_pool->db->forced_user; client->own_user = 1; client->admin_user = 1; slog_info(client, "pgbouncer access from unix socket"); @@ -1341,21 +1341,33 @@ bool admin_pre_login(PgSocket *client) /* * auth_mode=any does not keep original username around, - * so username based checks do not work. + * so username based check has to take place here */ if (cf_auth_type == AUTH_ANY) { - if (cf_log_connections) - slog_info(client, "auth_mode=any: allowing anybody in as admin"); - client->admin_user = 1; - return true; + if (strlist_contains(cf_admin_users, username)) { + client->admin_user = 1; + return true; + } else if (strlist_contains(cf_stats_users, username)) { + return true; + } } + return false; +} + +bool admin_post_login(PgSocket *client) +{ + const char *username = client->auth_user->name; - if (strlist_contains(cf_admin_users, username)) { + if (cf_auth_type == AUTH_ANY) + return true; + + if (client->admin_user || strlist_contains(cf_admin_users, username)) { client->admin_user = 1; return true; } else if (strlist_contains(cf_stats_users, username)) { return true; } + disconnect_client(client, true, "not allowed"); return false; } diff --git a/src/client.c b/src/client.c index 4a8faf5..4ea6d72 100644 --- a/src/client.c +++ b/src/client.c @@ -22,6 +22,8 @@ #include "bouncer.h" +#include + static const char *hdr2hex(const struct MBuf *data, char *buf, unsigned buflen) { const uint8_t *bin = data->data + data->read_pos; @@ -58,17 +60,113 @@ static bool check_client_passwd(PgSocket *client, const char *passwd) return false; } -bool set_pool(PgSocket *client, const char *dbname, const char *username) +/* mask to get offset into valid_crypt_salt[] */ +#define SALT_MASK 0x3F + +static const char valid_crypt_salt[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static bool send_client_authreq(PgSocket *client) +{ + uint8_t saltlen = 0; + int res; + int auth = cf_auth_type; + uint8_t randbuf[2]; + + if (auth == AUTH_CRYPT) { + saltlen = 2; + get_random_bytes(randbuf, saltlen); + client->tmp_login_salt[0] = valid_crypt_salt[randbuf[0] & SALT_MASK]; + client->tmp_login_salt[1] = valid_crypt_salt[randbuf[1] & SALT_MASK]; + client->tmp_login_salt[2] = 0; + } else if (cf_auth_type == AUTH_MD5) { + saltlen = 4; + get_random_bytes((void*)client->tmp_login_salt, saltlen); + } else if (auth == AUTH_ANY) + auth = AUTH_TRUST; + + SEND_generic(res, client, 'R', "ib", auth, client->tmp_login_salt, saltlen); + return res; +} + +static void start_auth_request(PgSocket *client, const char *username) +{ + int res; + char quoted_username[64], query[128]; + + client->auth_user = client->db->auth_user; + /* have to fetch user info from db */ + client->pool = get_pool(client->db, client->db->auth_user); + if (!find_server(client)) { + client->wait_for_user_conn = true; + return; + } + slog_noise(client, "Doing auth_conn query"); + client->wait_for_user_conn = false; + client->wait_for_user = true; + if (!sbuf_pause(&client->sbuf)) { + release_server(client->link); + disconnect_client(client, true, "pause failed"); + return; + } + client->link->ready = 0; + + pg_quote_literal(quoted_username, username, sizeof(quoted_username)); + snprintf(query, sizeof(query), "SELECT usename, passwd FROM pg_shadow WHERE usename=%s", quoted_username); + SEND_generic(res, client->link, 'Q', "s", query); + if (!res) + disconnect_server(client->link, false, "unable to send login query"); +} + +static bool finish_set_pool(PgSocket *client, bool takeover) { - PgDatabase *db; - PgUser *user; + PgUser *user = client->auth_user; + /* pool user may be forced */ + if (client->db->forced_user) { + user = client->db->forced_user; + } + client->pool = get_pool(client->db, user); + if (!client->pool) { + disconnect_client(client, true, "no memory for pool"); + return false; + } + + if (cf_log_connections) + slog_info(client, "login attempt: db=%s user=%s", client->db->name, client->auth_user->name); + + if (!check_fast_fail(client)) + return false; + + if (takeover) + return true; + + if (client->pool->db->admin) { + if (!admin_post_login(client)) + return false; + } + + if (cf_auth_type <= AUTH_TRUST || client->own_user) { + if (!finish_client_login(client)) + return false; + } else { + if (!send_client_authreq(client)) { + disconnect_client(client, false, "failed to send auth req"); + return false; + } + } + return true; +} +bool set_pool(PgSocket *client, const char *dbname, const char *username, bool takeover) +{ /* find database */ - db = find_database(dbname); - if (!db) { - db = register_auto_database(dbname); - if (!db) { + client->db = find_database(dbname); + if (!client->db) { + client->db = register_auto_database(dbname); + if (!client->db) { disconnect_client(client, true, "No such database: %s", dbname); + if (cf_log_connections) + slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } else { @@ -77,42 +175,128 @@ bool set_pool(PgSocket *client, const char *dbname, const char *username) } /* are new connections allowed? */ - if (db->db_disabled) { + if (client->db->db_disabled) { disconnect_client(client, true, "database does not allow connections: %s", dbname); return false; } + if (client->db->admin) { + if (admin_pre_login(client, username)) + return finish_set_pool(client, takeover); + } + /* find user */ if (cf_auth_type == AUTH_ANY) { /* ignore requested user */ - user = NULL; - - if (db->forced_user == NULL) { + if (client->db->forced_user == NULL) { slog_error(client, "auth_type=any requires forced user"); disconnect_client(client, true, "bouncer config error"); return false; } - client->auth_user = db->forced_user; + client->auth_user = client->db->forced_user; } else { /* the user clients wants to log in as */ - user = find_user(username); - if (!user) { + client->auth_user = find_user(username); + if (!client->auth_user && client->db->auth_user) { + if (takeover) { + client->auth_user = add_db_user(client->db, username, ""); + return finish_set_pool(client, takeover); + } + start_auth_request(client, username); + return false; + } + if (!client->auth_user) { disconnect_client(client, true, "No such user: %s", username); + if (cf_log_connections) + slog_info(client, "login failed: db=%s user=%s", dbname, username); return false; } - client->auth_user = user; } + return finish_set_pool(client, takeover); +} - /* pool user may be forced */ - if (db->forced_user) - user = db->forced_user; - client->pool = get_pool(db, user); - if (!client->pool) { - disconnect_client(client, true, "no memory for pool"); +bool handle_auth_response(PgSocket *client, PktHdr *pkt) { + uint16_t columns; + uint32_t length; + const char *username, *password; + PgUser user; + + switch(pkt->type) { + case 'T': /* RowDescription */ + if (!mbuf_get_uint16be(&pkt->data, &columns)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + if (columns != 2u) { + disconnect_server(client->link, false, "expected 1 column from login query, not %hu", columns); + return false; + } + break; + case 'D': /* DataRow */ + memset(&user, 0, sizeof(user)); + if (!mbuf_get_uint16be(&pkt->data, &columns)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + if (columns != 2u) { + disconnect_server(client->link, false, "expected 1 column from login query, not %hu", columns); + return false; + } + if (!mbuf_get_uint32be(&pkt->data, &length)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + if (!mbuf_get_chars(&pkt->data, length, &username)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + if (sizeof(user.name) - 1 < length) + length = sizeof(user.name) - 1; + memcpy(user.name, username, length); + if (!mbuf_get_uint32be(&pkt->data, &length)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + if (length == (uint32_t)-1) { + // NULL - set an md5 password with an impossible value, + // so that nothing will ever match + password = "md5"; + length = 3; + } else { + if (!mbuf_get_chars(&pkt->data, length, &password)) { + disconnect_server(client->link, false, "bad packet"); + return false; + } + } + if (sizeof(user.passwd) - 1 < length) + length = sizeof(user.passwd) - 1; + memcpy(user.passwd, password, length); + + client->auth_user = add_db_user(client->db, user.name, user.passwd); + if (!client->auth_user) { + disconnect_server(client->link, false, "unable to allocate new user for auth"); + return false; + } + break; + case 'C': /* CommandComplete */ + break; + case 'Z': /* ReadyForQuery */ + sbuf_prepare_skip(&client->link->sbuf, pkt->len); + if (!client->auth_user) { + if (cf_log_connections) + slog_info(client, "login failed: db=%s", client->db->name); + disconnect_client(client, true, "No such user"); + } else { + slog_noise(client, "auth query complete"); + sbuf_continue(&client->sbuf); + } + return true; + default: + disconnect_server(client->link, false, "unexpected response from login query"); return false; } - - return check_fast_fail(client); + sbuf_prepare_skip(&client->link->sbuf, pkt->len); + return true; } static bool decide_startup_pool(PgSocket *client, PktHdr *pkt) @@ -164,45 +348,8 @@ static bool decide_startup_pool(PgSocket *client, PktHdr *pkt) } } - /* find pool and log about it */ - if (set_pool(client, dbname, username)) { - if (cf_log_connections) - slog_info(client, "login attempt: db=%s user=%s", dbname, username); - return true; - } else { - if (cf_log_connections) - slog_info(client, "login failed: db=%s user=%s", dbname, username); - return false; - } -} - -/* mask to get offset into valid_crypt_salt[] */ -#define SALT_MASK 0x3F - -static const char valid_crypt_salt[] = -"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static bool send_client_authreq(PgSocket *client) -{ - uint8_t saltlen = 0; - int res; - int auth = cf_auth_type; - uint8_t randbuf[2]; - - if (auth == AUTH_CRYPT) { - saltlen = 2; - get_random_bytes(randbuf, saltlen); - client->tmp_login_salt[0] = valid_crypt_salt[randbuf[0] & SALT_MASK]; - client->tmp_login_salt[1] = valid_crypt_salt[randbuf[1] & SALT_MASK]; - client->tmp_login_salt[2] = 0; - } else if (cf_auth_type == AUTH_MD5) { - saltlen = 4; - get_random_bytes((void*)client->tmp_login_salt, saltlen); - } else if (auth == AUTH_ANY) - auth = AUTH_TRUST; - - SEND_generic(res, client, 'R', "ib", auth, client->tmp_login_salt, saltlen); - return res; + /* find pool */ + return set_pool(client, dbname, username, false); } /* decide on packets of client in login phase */ @@ -244,28 +391,19 @@ static bool handle_client_startup(PgSocket *client, PktHdr *pkt) disconnect_client(client, true, "Old V2 protocol not supported"); return false; case PKT_STARTUP: - if (client->pool) { + if (client->pool && !client->wait_for_user_conn && !client->wait_for_user) { disconnect_client(client, true, "client re-sent startup pkt"); return false; } - if (!decide_startup_pool(client, pkt)) - return false; - - if (client->pool->db->admin) { - if (!admin_pre_login(client)) + if (client->wait_for_user) { + client->wait_for_user = false; + if (!finish_set_pool(client, false)) return false; + } else if (!decide_startup_pool(client, pkt)) { + return false; } - if (cf_auth_type <= AUTH_TRUST || client->own_user) { - if (!finish_client_login(client)) - return false; - } else { - if (!send_client_authreq(client)) { - disconnect_client(client, false, "failed to send auth req"); - return false; - } - } break; case 'p': /* PasswordMessage */ /* haven't requested it */ diff --git a/src/janitor.c b/src/janitor.c index 26c7c57..b51fbdd 100644 --- a/src/janitor.c +++ b/src/janitor.c @@ -331,7 +331,7 @@ static void pool_client_maint(PgPool *pool) if (cf_query_timeout > 0 || cf_query_wait_timeout > 0) { statlist_for_each_safe(item, &pool->waiting_client_list, tmp) { client = container_of(item, PgSocket, head); - Assert(client->state == CL_WAITING); + Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); if (client->query_start == 0) { age = now - client->request_time; //log_warning("query_start==0"); @@ -649,6 +649,7 @@ static void kill_database(PgDatabase *db) statlist_remove(&autodatabase_idle_list, &db->head); else statlist_remove(&database_list, &db->head); + aatree_destroy(&db->user_tree); slab_free(db_cache, db); } diff --git a/src/loader.c b/src/loader.c index 870ddf8..6b83774 100644 --- a/src/loader.c +++ b/src/loader.c @@ -189,6 +189,7 @@ bool parse_database(void *base, const char *name, const char *connstr) char *port = "5432"; char *username = NULL; char *password = ""; + char *auth_username = NULL; char *client_encoding = NULL; char *datestyle = NULL; char *timezone = NULL; @@ -228,6 +229,8 @@ bool parse_database(void *base, const char *name, const char *connstr) username = val; else if (strcmp("password", key) == 0) password = val; + else if (strcmp("auth_user", key) == 0) + auth_username = val; else if (strcmp("client_encoding", key) == 0) client_encoding = val; else if (strcmp("datestyle", key) == 0) @@ -357,6 +360,15 @@ bool parse_database(void *base, const char *name, const char *connstr) pktbuf_put_string(msg, appname); } + if (auth_username != NULL) { + db->auth_user = find_user(auth_username); + if (!db->auth_user) { + db->auth_user = add_user(auth_username, ""); + } + } else if (db->auth_user) { + db->auth_user = NULL; + } + /* if user is forces, create fake object for it */ if (username != NULL) { if (!force_user(db, username, password)) diff --git a/src/objects.c b/src/objects.c index b48f774..1bac9ce 100644 --- a/src/objects.c +++ b/src/objects.c @@ -94,6 +94,13 @@ static int user_node_cmp(uintptr_t userptr, struct AANode *node) return strcmp(name, user->name); } +/* destroy PgUser, for usage with btree */ +static void user_node_release(struct AANode *node, void *arg) +{ + PgUser *user = container_of(node, PgUser, tree_node); + slab_free(user_cache, user); +} + /* initialization before config loading */ void init_objects(void) { @@ -133,8 +140,13 @@ void change_client_state(PgSocket *client, SocketState newstate) statlist_remove(&justfree_client_list, &client->head); break; case CL_LOGIN: + if (newstate == CL_WAITING) + newstate = CL_WAITING_LOGIN; statlist_remove(&login_client_list, &client->head); break; + case CL_WAITING_LOGIN: + if (newstate == CL_ACTIVE) + newstate = CL_LOGIN; case CL_WAITING: statlist_remove(&pool->waiting_client_list, &client->head); break; @@ -163,6 +175,7 @@ void change_client_state(PgSocket *client, SocketState newstate) statlist_append(&login_client_list, &client->head); break; case CL_WAITING: + case CL_WAITING_LOGIN: statlist_append(&pool->waiting_client_list, &client->head); break; case CL_ACTIVE: @@ -308,6 +321,7 @@ PgDatabase *add_database(const char *name) slab_free(db_cache, db); return NULL; } + aatree_init(&db->user_tree, user_node_cmp, user_node_release); put_in_order(&db->head, &database_list, cmp_database); } @@ -367,6 +381,31 @@ PgUser *add_user(const char *name, const char *passwd) return user; } +/* add or update db users */ +PgUser *add_db_user(PgDatabase *db, const char *name, const char *passwd) +{ + PgUser *user = NULL; + struct AANode *node; + + node = aatree_search(&db->user_tree, (uintptr_t)name); + user = node ? container_of(node, PgUser, tree_node) : NULL; + + if (user == NULL) { + user = slab_alloc(user_cache); + if (!user) + return NULL; + + list_init(&user->head); + list_init(&user->pool_list); + safe_strcpy(user->name, name, sizeof(user->name)); + + aatree_insert(&db->user_tree, (uintptr_t)user->name, &user->tree_node); + user->pool_mode = POOL_INHERIT; + } + safe_strcpy(user->passwd, passwd, sizeof(user->passwd)); + return user; +} + /* create separate user object for storing server user info */ PgUser *force_user(PgDatabase *db, const char *name, const char *passwd) { @@ -472,7 +511,7 @@ PgPool *get_pool(PgDatabase *db, PgUser *user) /* deactivate socket and put into wait queue */ static void pause_client(PgSocket *client) { - Assert(client->state == CL_ACTIVE); + Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); slog_debug(client, "pause_client"); change_client_state(client, CL_WAITING); @@ -483,7 +522,7 @@ static void pause_client(PgSocket *client) /* wake client from wait */ void activate_client(PgSocket *client) { - Assert(client->state == CL_WAITING); + Assert(client->state == CL_WAITING || client->state == CL_WAITING_LOGIN); slog_debug(client, "activate_client"); change_client_state(client, CL_ACTIVE); @@ -531,7 +570,7 @@ bool find_server(PgSocket *client) bool res; bool varchange = false; - Assert(client->state == CL_ACTIVE); + Assert(client->state == CL_ACTIVE || client->state == CL_LOGIN); if (client->link) return true; @@ -799,6 +838,7 @@ void disconnect_client(PgSocket *client, bool notify, const char *reason, ...) } case CL_LOGIN: case CL_WAITING: + case CL_WAITING_LOGIN: case CL_CANCEL: break; default: @@ -1158,7 +1198,7 @@ bool use_client_socket(int fd, PgAddr *addr, return false; client->suspended = 1; - if (!set_pool(client, dbname, username)) + if (!set_pool(client, dbname, username, true)) return false; change_client_state(client, CL_ACTIVE); diff --git a/src/server.c b/src/server.c index f21ba7a..9a1e3e9 100644 --- a/src/server.c +++ b/src/server.c @@ -300,15 +300,19 @@ static bool handle_server_work(PgSocket *server, PktHdr *pkt) Assert(client); sbuf_prepare_skip(sbuf, pkt->len); } else if (client) { - sbuf_prepare_send(sbuf, &client->sbuf, pkt->len); - if (ready && client->query_start) { - usec_t total; - total = get_cached_time() - client->query_start; - client->query_start = 0; - server->pool->stats.query_time += total; - slog_debug(client, "query time: %d us", (int)total); - } else if (ready) { - slog_warning(client, "FIXME: query end, but query_start == 0"); + if (client->state == CL_LOGIN) { + return handle_auth_response(client, pkt); + } else { + sbuf_prepare_send(sbuf, &client->sbuf, pkt->len); + if (ready && client->query_start) { + usec_t total; + total = get_cached_time() - client->query_start; + client->query_start = 0; + server->pool->stats.query_time += total; + slog_debug(client, "query time: %d us", (int)total); + } else if (ready) { + slog_warning(client, "FIXME: query end, but query_start == 0"); + } } } else { if (server->state != SV_TESTED)