pgbouncer/src/proto.c 里的部分源码如下:
#include "bouncer.h"
#include "scram.h"
/*
* parse protocol header from struct MBuf
*/
/* parses pkt header from buffer, returns false if failed */
bool get_header(struct MBuf *data, PktHdr *pkt)
{
unsigned type;
uint32_t len;
unsigned got;
unsigned avail;
uint16_t len16;
uint8_t type8;
uint32_t code;
struct MBuf hdr;
const uint8_t *ptr;
mbuf_copy(data, &hdr);
if (mbuf_avail_for_read(&hdr) < NEW_HEADER_LEN) {
log_noise("get_header: less than 5 bytes available");
return false;
}
if (!mbuf_get_byte(&hdr, &type8))
return false;
type = type8;
if (type != 0) {
/* wire length does not include type byte */
if (!mbuf_get_uint32be(&hdr, &len))
return false;
len++;
got = NEW_HEADER_LEN;
} else {
if (!mbuf_get_byte(&hdr, &type8))
return false;
if (type8 != 0) {
log_noise("get_header: unknown special pkt");
return false;
}
/* don't tolerate partial pkt */
if (mbuf_avail_for_read(&hdr) < OLD_HEADER_LEN - 2) {
log_noise("get_header: less than 8 bytes for special pkt");
return false;
}
if (!mbuf_get_uint16be(&hdr, &len16))
return false;
len = len16;
if (!mbuf_get_uint32be(&hdr, &code))
return false;
if (code == PKT_CANCEL) {
type = PKT_CANCEL;
} else if (code == PKT_SSLREQ) {
type = PKT_SSLREQ;
} else if (code == PKT_GSSENCREQ) {
type = PKT_GSSENCREQ;
} else if ((code >> 16) == 3 && (code & 0xFFFF) < 2) {
type = PKT_STARTUP;
} else if (code == PKT_STARTUP_V2) {
type = PKT_STARTUP_V2;
} else {
log_noise("get_header: unknown special pkt: len=%u code=%u", len, code);
return false;
}
got = OLD_HEADER_LEN;
}
/* don't believe nonsense */
if (len < got || len > cf_max_packet_size)
return false;
/* store pkt info */
pkt->type = type;
pkt->len = len;
/* fill pkt with only data for this packet */
if (len > mbuf_avail_for_read(data)) {
avail = mbuf_avail_for_read(data);
} else {
avail = len;
}
if (!mbuf_slice(data, avail, &pkt->data))
return false;
/* tag header as read */
return mbuf_get_bytes(&pkt->data, got, &ptr);
}
/*
* Send error message packet to client.
*/
bool send_pooler_error(PgSocket *client, bool send_ready, const char *msg)
{
uint8_t tmpbuf[512];
PktBuf buf;
if (cf_log_pooler_errors)
slog_warning(client, "pooler error: %s", msg);
pktbuf_static(&buf, tmpbuf, sizeof(tmpbuf));
pktbuf_write_generic(&buf, 'E', "cscscsc",
'S', "ERROR", 'C', "08P01", 'M', msg, 0);
if (send_ready)
pktbuf_write_ReadyForQuery(&buf);
return pktbuf_send_immediate(&buf, client);
}
/*
* Parse server error message and log it.
*/
void parse_server_error(PktHdr *pkt, const char **level_p, const char **msg_p)
{
const char *level = NULL, *msg = NULL, *val;
uint8_t type;
while (mbuf_avail_for_read(&pkt->data)) {
if (!mbuf_get_byte(&pkt->data, &type))
break;
if (type == 0)
break;
if (!mbuf_get_string(&pkt->data, &val))
break;
if (type == 'S') {
level = val;
} else if (type == 'M') {
msg = val;
}
}
*level_p = level;
*msg_p = msg;
}
void log_server_error(const char *note, PktHdr *pkt)
{
const char *level = NULL, *msg = NULL;
parse_server_error(pkt, &level, &msg);
if (!msg || !level) {
log_error("%s: partial error message, cannot log", note);
} else {
log_error("%s: %s: %s", note, level, msg);
}
}
/*
* Preparation of welcome message for client connection.
*/
/* add another server parameter packet to cache */
bool add_welcome_parameter(PgPool *pool, const char *key, const char *val)
{
PktBuf *msg = pool->welcome_msg;
if (pool->welcome_msg_ready)
return true;
if (!msg) {
msg = pktbuf_dynamic(128);
if (!msg)
return false;
pool->welcome_msg = msg;
}
/* first packet must be AuthOk */
if (msg->write_pos == 0)
pktbuf_write_AuthenticationOk(msg);
/* if not stored in ->orig_vars, write full packet */
if (!varcache_set(&pool->orig_vars, key, val))
pktbuf_write_ParameterStatus(msg, key, val);
return !msg->failed;
}
/* all parameters processed */
void finish_welcome_msg(PgSocket *server)
{
PgPool *pool = server->pool;
if (pool->welcome_msg_ready)
return;
pool->welcome_msg_ready = 1;
}
bool welcome_client(PgSocket *client)
{
int res;
PgPool *pool = client->pool;
const PktBuf *pmsg = pool->welcome_msg;
PktBuf *msg;
slog_noise(client, "P: welcome_client");
/* copy prepared stuff around */
msg = pktbuf_temp();
pktbuf_put_bytes(msg, pmsg->buf, pmsg->write_pos);
/* fill vars */
varcache_fill_unset(&pool->orig_vars, client);
varcache_add_params(msg, &client->vars);
/* give each client its own cancel key */
get_random_bytes(client->cancel_key, 8);
pktbuf_write_BackendKeyData(msg, client->cancel_key);
/* finish */
pktbuf_write_ReadyForQuery(msg);
if (msg->failed) {
disconnect_client(client, true, "failed to prepare welcome message");
return false;
}
/* send all together */
res = pktbuf_send_immediate(msg, client);
if (!res) {
disconnect_client(client, true, "failed to send welcome message");
return false;
}
return true;
}
大致讲解下