1 /*Copyright (c) Brian B.
3 This library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Lesser General Public
5 License as published by the Free Software Foundation; either
6 version 3 of the License, or (at your option) any later version.
7 See the file LICENSE included with this distribution for more
12 #include "libpgcli/pgprov3.h"
15 #define HEADER_SIZE sizeof(char) + sizeof(int32_t)
17 static int _resize (pgmsg_t
**msg
, size_t len
) {
19 int nlen
= (*msg
)->len
+ len
;
21 bufsize
= sizeof(pgmsg_t
) + (nlen
/ CHUNKSIZE
) * CHUNKSIZE
+ CHUNKSIZE
;
22 if (bufsize
== (*msg
)->bufsize
)
24 pc_len
= (uintptr_t)(*msg
)->pc
- (uintptr_t)(*msg
)->body
.ptr
;
25 if (!(res
= realloc(*msg
, bufsize
)))
27 res
->bufsize
= bufsize
;
28 res
->pc
= res
->body
.ptr
+ pc_len
;
33 pgmsg_t
*pgmsg_create (char type
) {
34 size_t bufsize
= sizeof(pgmsg_t
) + CHUNKSIZE
;
35 pgmsg_t
*msg
= malloc(bufsize
);
36 if (!msg
) return NULL
;
37 msg
->bufsize
= bufsize
;
38 msg
->len
= sizeof(pgmsg_t
);
39 msg
->pc
= msg
->body
.ptr
;
40 msg
->body
.type
= type
;
41 msg
->body
.len
= htobe32(sizeof(int32_t));
45 static int _seti8 (pgmsg_t
**msg
, int8_t x
) {
46 if (-1 == _resize(msg
, sizeof(int8_t)))
48 *((int8_t*)(*msg
)->pc
) = x
;
49 (*msg
)->pc
+= sizeof(int8_t);
50 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int8_t));
51 (*msg
)->len
+= sizeof(int8_t);
55 static int _seti16 (pgmsg_t
**msg
, int16_t x
) {
56 if (-1 == _resize(msg
, sizeof(int16_t)))
58 *((int16_t*)(*msg
)->pc
) = htobe16(x
);
59 (*msg
)->pc
+= sizeof(int16_t);
60 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int16_t));
61 (*msg
)->len
+= sizeof(int16_t);
65 static int _seti32 (pgmsg_t
**msg
, int32_t x
) {
66 if (-1 == _resize(msg
, sizeof(int32_t)))
68 *((int32_t*)(*msg
)->pc
) = htobe32(x
);
69 (*msg
)->pc
+= sizeof(int32_t);
70 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int32_t));
71 (*msg
)->len
+= sizeof(int32_t);
75 static int _setstr (pgmsg_t
**msg
, const char *s
, size_t slen
) {
78 if (-1 == _resize(msg
, slen
))
80 memcpy((*msg
)->pc
, s
, slen
);
82 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + slen
);
87 static int8_t _geti8 (pgmsg_t
*msg
) {
89 msg
->pc
+= sizeof(int8_t);
93 static int16_t _geti16 (pgmsg_t
*msg
) {
94 int16_t r
= be16toh(*((int16_t*)msg
->pc
));
95 msg
->pc
+= sizeof(int16_t);
99 static int32_t _geti32 (pgmsg_t
*msg
) {
100 int32_t r
= be32toh(*((int32_t*)msg
->pc
));
101 msg
->pc
+= sizeof(int32_t);
105 static const char *_getstr (pgmsg_t
*msg
) {
106 char *p
= msg
->pc
, *e
= msg
->body
.ptr
+ be32toh(msg
->body
.len
);
107 while (*(msg
->pc
) && msg
->pc
< e
) ++msg
->pc
;
114 int pgmsg_set_param (pgmsg_t
**msg
, const char *name
, size_t name_len
, const char *value
, size_t value_len
) {
115 if (-1 == _setstr(msg
, name
, name_len
) ||
116 -1 == _seti8(msg
, 0) ||
117 -1 == _setstr(msg
, value
, value_len
) ||
118 -1 == _seti8(msg
, 0))
123 pgmsg_t
*pgmsg_create_startup (const char *user
, size_t user_len
, const char *database
, size_t database_len
) {
124 pgmsg_t
*msg
= pgmsg_create('\0');
125 _seti16(&msg
, PG_MAJOR_VER
);
126 _seti16(&msg
, PG_MINOR_VER
);
127 pgmsg_set_param(&msg
, CONST_STR_LEN("user"), user
, user_len
);
128 pgmsg_set_param(&msg
, CONST_STR_LEN("database"), database
, database_len
);
132 #define PG_USERDEFS 0x00000001
133 #define PG_DBDEFS 0x00000002
134 #define PG_ENCDEFS 0x00000004
136 static inline pgmsg_t
*_add_str_param (pgmsg_t
*msg
, const char *k
, size_t lk
, const char *v
, size_t lv
) {
137 _setstr(&msg
, k
, lk
);
139 _setstr(&msg
, v
, lv
);
144 static pgmsg_t
*_add_param (pgmsg_t
*msg
, const char *begin
, const char *end
, uint32_t *flags
) {
145 const char *p
= begin
, *p1
;
146 while (p
< end
&& '=' != *p
) ++p
;
147 if (0 == strncmp(begin
, "password", (uintptr_t)p
- (uintptr_t)begin
))
152 while (p1
> begin
&& isspace(*(p1
- 1))) --p1
;
156 while (p
< end
&& isspace(*p
)) ++p
;
159 if (0 == strncmp(begin
, "dbname", (uintptr_t)p1
- (uintptr_t)begin
)) {
160 msg
= _add_str_param(msg
, CONST_STR_LEN("database"), p
, (uintptr_t)end
- (uintptr_t)p
);
164 if (0 == strncmp(begin
, "user", (uintptr_t)p1
- (uintptr_t)begin
)) {
165 msg
= _add_str_param(msg
, begin
, (uintptr_t)p1
- (uintptr_t)begin
, p
, (uintptr_t)end
- (uintptr_t)p
);
167 *flags
|= PG_USERDEFS
;
169 if (0 != strncmp(begin
, "host", (uintptr_t)p1
- (uintptr_t)begin
) &&
170 0 != strncmp(begin
, "port", (uintptr_t)p1
- (uintptr_t)begin
))
171 msg
= _add_str_param(msg
, begin
, (uintptr_t)p1
- (uintptr_t)begin
, p
, (uintptr_t)end
- (uintptr_t)p
);
175 void *parse_conninfo (void *data
, const char *conn_info
, parse_param_h fn
, uint32_t *flags
) {
177 const char *p
= conn_info
;
180 while (isspace(*p
)) ++p
;
183 while (*p
&& !isspace(*p
)) ++p
;
184 data
= fn(data
, q
, p
, flags
);
190 static pgmsg_t
*_startup_params (pgmsg_t
*msg
, const char *conn_info
) {
192 msg
= (pgmsg_t
*)parse_conninfo((void*)msg
, conn_info
, (parse_param_h
)_add_param
, &flags
);
193 if (!(flags
& PG_USERDEFS
)) {
194 char *s
= getenv("USER");
196 msg
= _add_str_param(msg
, CONST_STR_LEN("user"), s
, strlen(s
));
198 if (!(flags
& PG_DBDEFS
)) {
199 char *s
= getenv("USER");
201 msg
= _add_str_param(msg
, CONST_STR_LEN("database"), s
, strlen(s
));
203 if (!(flags
& PG_ENCDEFS
)) {
204 char *s
= getenv("LANG");
205 if (s
&& (s
= strchr(s
, '.')) && *++s
)
206 msg
= _add_str_param(msg
, CONST_STR_LEN("client_encoding"), s
, strlen(s
));
212 pgmsg_t
*pgmsg_create_startup_params (const char *conn_info
) {
213 pgmsg_t
*msg
= pgmsg_create('\0');
214 _seti16(&msg
, PG_MAJOR_VER
);
215 _seti16(&msg
, PG_MINOR_VER
);
216 return _startup_params(msg
, conn_info
);
219 pgmsg_t
*pgmsg_create_simple_query (const char *sql
, size_t sql_len
) {
220 pgmsg_t
*msg
= pgmsg_create(PG_SIMPLEQUERY
);
221 _setstr(&msg
, sql
, sql_len
);
226 pgmsg_t
*pgmsg_create_parse (const char *name
, size_t name_len
, const char *sql
, size_t sql_len
, int fld_len
, pgfld_t
**flds
) {
227 pgmsg_t
*msg
= pgmsg_create(PG_PARSE
);
228 if (name
&& 0 == name_len
)
229 name_len
= strlen(name
);
231 sql_len
= strlen(sql
);
232 _setstr(&msg
, name
, name_len
);
234 _setstr(&msg
, sql
, sql_len
);
236 _seti16(&msg
, fld_len
);
237 for (int i
= 0; i
< fld_len
; ++i
)
238 _seti32(&msg
, flds
[i
]->oid
);
242 pgmsg_t
*pgmsg_create_bind (const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
,
243 int fld_len
, pgfld_t
**flds
, int res_fmt_len
, int *res_fmt
) {
244 pgmsg_t
*msg
= pgmsg_create(PG_BIND
);
245 _setstr(&msg
, portal
, portal_len
);
247 _setstr(&msg
, stmt
, stmt_len
);
249 _seti16(&msg
, fld_len
);
250 for (int i
= 0; i
< fld_len
; ++i
)
251 _seti16(&msg
, flds
[i
]->fmt
);
252 _seti16(&msg
, fld_len
);
253 for (int i
= 0; i
< fld_len
; ++i
) {
254 if (flds
[i
]->is_null
)
257 _seti32(&msg
, flds
[i
]->len
);
258 if (0 == flds
[i
]->fmt
|| OID_UUID
== flds
[i
]->oid
)
259 _setstr(&msg
, flds
[i
]->data
.s
, flds
[i
]->len
);
261 _setstr(&msg
, (const char*)&flds
[i
]->data
, flds
[i
]->len
);
264 _seti16(&msg
, res_fmt_len
);
265 for (int i
= 0; i
< res_fmt_len
; ++i
)
266 _seti16(&msg
, res_fmt
[i
]);
270 pgmsg_t
*pgmsg_create_describe (uint8_t op
, const char *name
, size_t name_len
) {
271 pgmsg_t
*msg
= pgmsg_create(PG_DESCRIBE
);
273 _setstr(&msg
, name
, name_len
);
278 pgmsg_t
*pgmsg_create_execute (const char *portal
, size_t portal_len
, int32_t max_rows
) {
279 pgmsg_t
*msg
= pgmsg_create(PG_EXECUTE
);
280 _setstr(&msg
, portal
, portal_len
);
282 _seti32(&msg
, max_rows
);
286 pgmsg_t
*pgmsg_create_close(char what
, const char *str
, size_t slen
) {
287 pgmsg_t
*msg
= pgmsg_create(PG_CLOSE
);
288 _seti8(&msg
, (uint8_t)what
);
289 _setstr(&msg
, str
, slen
);
294 static void _pg_md5_hash (const void *buff
, size_t len
, char *out
) {
300 md5_update(&ctx
, (uint8_t*)buff
, 512);
302 md5_update(&ctx
, (uint8_t*)buff
, len
);
307 memcpy(digest
, ctx
.digest
, sizeof digest
);
308 for (int i
= 0; i
< 16; ++i
)
309 snprintf(&(out
[i
*2]), 16*2, "%02x", (uint8_t)digest
[i
]);
312 static void _pg_md5_encrypt(const char *passwd
, const char *salt
, size_t salt_len
, char *buf
) {
313 size_t passwd_len
= strlen(passwd
);
314 char *crypt_buf
= malloc(passwd_len
+ salt_len
+ 1);
315 memcpy(crypt_buf
, passwd
, passwd_len
);
316 memcpy(crypt_buf
+ passwd_len
, salt
, salt_len
);
318 _pg_md5_hash(crypt_buf
, passwd_len
+ salt_len
, buf
+ 3);
322 pgmsg_t
*pgmsg_create_pass (int req
, const char *salt
, size_t salt_len
, const char *user
, const char *pass
) {
323 pgmsg_t
*msg
= pgmsg_create(PG_PASS
);
324 char *pwd
= malloc(2 * (PG_MD5PASS_LEN
+ 1)),
325 *pwd2
= pwd
+ PG_MD5PASS_LEN
+ 1,
327 _pg_md5_encrypt(pass
, user
, strlen(user
), pwd2
);
328 _pg_md5_encrypt(pwd2
+ sizeof("md5")-1, salt
, 4, pwd
);
335 pwd_to_send
= (char*)pass
;
337 _setstr(&msg
, pwd_to_send
, strlen(pwd_to_send
));
342 pgmsg_t
*pgmsg_create_sasl_init (conninfo_t
*cinfo
) {
343 char raw_nonce
[SCRAM_RAW_NONCE_LEN
+1];
344 strand(raw_nonce
, SCRAM_RAW_NONCE_LEN
, RAND_ALNUM
);
345 raw_nonce
[SCRAM_RAW_NONCE_LEN
] = '\0';
346 cinfo
->nonce
= cstr_b64encode(raw_nonce
, SCRAM_RAW_NONCE_LEN
);
347 str_t
*str
= strprintf("n,,n=,r=%s", cinfo
->nonce
->ptr
);
348 cinfo
->fmsg_bare
= mkcstr(str
->ptr
+3, str
->len
-3);
349 pgmsg_t
*msg
= pgmsg_create(PG_PASS
);
350 _setstr(&msg
, CONST_STR_LEN("SCRAM-SHA-256"));
352 _seti32(&msg
, str
->len
);
353 _setstr(&msg
, str
->ptr
, str
->len
);
358 static int _parse_scram_final (pgmsg_resp_t
*resp
, conninfo_t
*cinfo
) {
359 strptr_t entry
= { .ptr
= NULL
, .len
= 0 };
360 char *data
= (char*)resp
->msg_auth
.kind
.sasl_auth
.data
;
361 int len
= resp
->msg_auth
.kind
.sasl_auth
.len
;
362 if ('e' == *resp
->msg_auth
.kind
.sasl_auth
.data
)
364 if (!(cinfo
->srv_scram_msg
= strsplit(data
, len
, ',')))
366 cinfo
->fmsg_srv
= mkcstr(data
, len
);
367 while (0 == strnext(cinfo
->srv_scram_msg
, &entry
)) {
368 if (entry
.len
< 3 && '=' != entry
.ptr
[1])
370 switch (*entry
.ptr
) {
372 cinfo
->r_attr
= entry
.ptr
+2;
375 cinfo
->s_attr
= entry
.ptr
+2;
378 cinfo
->i_attr
= entry
.ptr
+2;
382 return cinfo
->r_attr
&& cinfo
->s_attr
&& cinfo
->i_attr
? 0 : -1;
385 static void _scram_create_key (uint8_t *salted_password
, uint8_t *result
) {
387 hmac_init(&ctx
, salted_password
, SCRAM_KEY_LEN
);
388 hmac_update(&ctx
, (uint8_t*)"Client Key", sizeof("Client Key")-1);
389 hmac_final(&ctx
, result
, SCRAM_KEY_LEN
);
392 char *(*on_pgauth
) (const char *prompt
, int is_echo
);
393 static struct termios term_settings
;
394 static char *_pg_auth (const char *prompt
, const char *def_auth
, int is_echo
) {
400 return on_pgauth(prompt
, is_echo
);
401 printf("%s", prompt
);
403 tcgetattr(0, &term_settings
);
406 tcsetattr(0, TCSANOW
, &tc
);
408 if (-1 == (ilen
= getline(&s
, &len
, stdin
))) {
410 tcsetattr(0, TCSANOW
, &term_settings
);
411 return strdup(def_auth
);
414 tcsetattr(0, TCSANOW
, &term_settings
);
421 static void _scram_salted_password (conninfo_t
*cinfo
, cstr_t
*salt
) {
424 cinfo
->user
= _pg_auth("Enter username: ", getenv("USER"), 1);
426 cinfo
->pass
= _pg_auth("Enter password: ", "", 0);
427 int password_len
= strlen(cinfo
->pass
);
428 uint32_t one
= htobe32(1);
429 uint8_t ui_prev
[SCRAM_KEY_LEN
],
431 int iterations
= strtol(cinfo
->i_attr
, NULL
, 0);
432 hmac_init(&ctx
, (uint8_t*)cinfo
->pass
, password_len
);
433 hmac_update(&ctx
, (uint8_t*)salt
->ptr
, strlen(salt
->ptr
));
434 hmac_update(&ctx
, (uint8_t*)&one
, sizeof(uint32_t));
435 hmac_final(&ctx
, ui_prev
, sizeof(ui_prev
));
436 memcpy(cinfo
->salted_password
, ui_prev
, SCRAM_KEY_LEN
);
437 for (int i
= 2; i
<= iterations
; ++i
) {
438 hmac_init(&ctx
, (uint8_t*)cinfo
->pass
, password_len
);
439 hmac_update(&ctx
, ui_prev
, SCRAM_KEY_LEN
);
440 hmac_final(&ctx
, ui
, SCRAM_KEY_LEN
);
441 for (int j
= 0; j
< SCRAM_KEY_LEN
; ++j
)
442 cinfo
->salted_password
[j
] ^= ui
[j
];
443 memcpy(ui_prev
, ui
, SCRAM_KEY_LEN
);
447 static void _scram_h (uint8_t *in
, int len
, uint8_t *result
) {
449 memset(&ctx
, 0, sizeof ctx
);
451 sha_update(&ctx
, in
, len
);
452 sha_final(&ctx
, result
);
455 static void _calc_scram_proof (conninfo_t
*cinfo
, uint8_t *result
) {
457 uint8_t client_key
[SCRAM_KEY_LEN
],
458 stored_key
[SCRAM_KEY_LEN
],
459 clsign_key
[SCRAM_KEY_LEN
];
460 cstr_t
*salt
= cstr_b64decode(cinfo
->s_attr
, strlen(cinfo
->s_attr
));
461 _scram_salted_password(cinfo
, salt
);
463 _scram_create_key((uint8_t*)cinfo
->salted_password
, client_key
);
464 _scram_h(client_key
, SCRAM_KEY_LEN
, stored_key
);
465 hmac_init(&ctx
, stored_key
, SCRAM_KEY_LEN
);
466 hmac_update(&ctx
, (uint8_t*)cinfo
->fmsg_bare
->ptr
, cinfo
->fmsg_bare
->len
);
467 hmac_update(&ctx
, (const uint8_t*)",", 1);
468 hmac_update(&ctx
, (uint8_t*)cinfo
->fmsg_srv
->ptr
, cinfo
->fmsg_srv
->len
);
469 hmac_update(&ctx
, (const uint8_t*)",", 1);
470 hmac_update(&ctx
, (const uint8_t*)cinfo
->fmsg_wproof
->ptr
, cinfo
->fmsg_wproof
->len
);
471 hmac_final(&ctx
, clsign_key
, SCRAM_KEY_LEN
);
472 for (int i
= 0; i
< SCRAM_KEY_LEN
; ++i
)
473 result
[i
] = client_key
[i
] ^ clsign_key
[i
];
476 pgmsg_t
*pgmsg_create_sasl_fin (pgmsg_resp_t
*resp
, conninfo_t
*cinfo
) {
477 uint8_t cln_proof_key
[SCRAM_KEY_LEN
];
478 _parse_scram_final(resp
, cinfo
);
479 str_t
*str
= strprintf("c=biws,r=%s", cinfo
->r_attr
);
480 cinfo
->fmsg_wproof
= mkcstr(str
->ptr
, str
->len
);
481 strnadd(&str
, CONST_STR_LEN(",p="));
482 _calc_scram_proof(cinfo
, cln_proof_key
);
483 cstr_t
*cln_proof
= cstr_b64encode((char*)cln_proof_key
, SCRAM_KEY_LEN
);
484 strnadd(&str
, cln_proof
->ptr
, cln_proof
->len
);
485 pgmsg_t
*msg
= pgmsg_create(PG_PASS
);
486 _setstr(&msg
, str
->ptr
, str
->len
);
492 int pgmsg_send (int fd
, pgmsg_t
*msg
) {
495 ssize_t sent
= 0, wrote
= 0;
496 if ('\0' == msg
->body
.type
) {
497 buf
= &msg
->body
.len
;
498 size
= be32toh(msg
->body
.len
);
501 size
= be32toh(msg
->body
.len
) + sizeof(char);
503 while (sent
< size
) {
505 wrote
= send(fd
, buf
, size
- sent
, 0);
506 while (wrote
< 0 && EINTR
== errno
);
510 return sent
== size
? 0 : -1;
513 int pgmsg_recv (int fd
, pgmsg_t
**msg
) {
514 ssize_t readed
= 0, total
= 0;
520 } __attribute__ ((packed
)) header
;
521 while (total
< HEADER_SIZE
&& (readed
= recv(fd
, (void*)(&header
+ total
), HEADER_SIZE
- total
, 0)) > 0)
525 header
.len
= be32toh(header
.len
);
526 bufsize
= sizeof(pgmsg_t
) + header
.len
;
527 if (!(m
= calloc(bufsize
, sizeof(int8_t))))
529 m
->body
.type
= header
.type
;
530 m
->body
.len
= header
.len
;
531 total
= (header
.len
- sizeof(int32_t));
532 char *ptr
= m
->body
.ptr
;
534 if (-1 == (readed
= recv(fd
, ptr
, total
, 0))) {
545 static int _parse_param_status (pgmsg_body_t
*body
, pgmsg_param_status_t
*pmsg
) {
546 char *p
= body
->ptr
, *e
= p
+ body
->len
, *q
= p
;
547 while (*q
&& q
< e
) ++q
;
555 static int _parse_error (pgmsg_body_t
*body
, pgmsg_error_t
*pmsg
) {
556 char *p
= body
->ptr
, *e
= p
+ body
->len
;
560 pmsg
->severity
= ++p
;
572 pmsg
->position
= ++p
;
588 while (p
< e
&& *p
) ++p
;
597 static int _parse_rowdesc (pgmsg_t
*msg
, pgmsg_rowdesc_t
*pmsg
) {
598 pmsg
->nflds
= _geti16(msg
);
599 for (int i
= 0; i
< pmsg
->nflds
; ++i
) {
600 const char *fname
= _getstr(msg
);
603 pmsg
->fields
[i
].fname
= fname
;
604 pmsg
->fields
[i
].oid_table
= _geti32(msg
);
605 pmsg
->fields
[i
].idx_field
= _geti16(msg
);
606 pmsg
->fields
[i
].oid_field
= _geti32(msg
);
607 pmsg
->fields
[i
].field_len
= _geti16(msg
);
608 pmsg
->fields
[i
].type_mod
= _geti32(msg
);
609 pmsg
->fields
[i
].field_fmt
= _geti16(msg
);
614 static int _parse_datarow (pgmsg_t
*msg
, pgmsg_datarow_t
*pmsg
) {
615 pmsg
->nflds
= _geti16(msg
);
616 for (int i
= 0; i
< pmsg
->nflds
; ++i
) {
617 int32_t len
= pmsg
->fields
[i
].len
= _geti32(msg
);
618 pmsg
->fields
[i
].data
= NULL
;
620 pmsg
->fields
[i
].data
= (uint8_t*)msg
->pc
;
627 static void _parse_copyin (pgmsg_t
*msg
, pgmsg_copyin_t
*pmsg
) {
628 pmsg
->fmt
= _geti8(msg
);
629 pmsg
->cols
= _geti16(msg
);
630 pmsg
->fmtcol
= _geti16(msg
);
633 pgmsg_resp_t
*pgmsg_parse (pgmsg_t
*msg
) {
634 pgmsg_resp_t
*resp
= NULL
;
635 msg
->pc
= msg
->body
.ptr
;
636 switch (msg
->body
.type
) {
638 switch (be32toh(*((int32_t*)msg
->body
.ptr
))) {
640 resp
= malloc(sizeof(pgmsg_auth_t
));
641 resp
->type
= msg
->body
.type
;
642 resp
->msg_auth
.success
= be32toh(*((int32_t*)msg
->body
.ptr
));
645 resp
= malloc(sizeof(pgmsg_auth_t
));
646 resp
->type
= msg
->body
.type
;
647 resp
->msg_auth
.success
= be32toh(*((int32_t*)msg
->body
.ptr
));
648 memcpy(resp
->msg_auth
.kind
.md5_auth
, msg
->body
.ptr
+ sizeof(int32_t), sizeof(uint8_t)*4);
651 resp
= malloc(sizeof(pgmsg_auth_t
));
652 resp
->type
= msg
->body
.type
;
653 resp
->msg_auth
.success
= be32toh(*((int32_t*)msg
->body
.ptr
));
657 resp
= malloc(sizeof(pgmsg_auth_t
) + sizeof(sasl_t
) + msg
->body
.len
- sizeof(int32_t) * 2);
658 resp
->type
= msg
->body
.type
;
659 resp
->msg_auth
.success
= be32toh(*((int32_t*)msg
->body
.ptr
));
660 resp
->msg_auth
.kind
.sasl_auth
.len
= msg
->body
.len
- sizeof(int32_t) * 2;
661 memcpy(resp
->msg_auth
.kind
.sasl_auth
.data
, msg
->body
.ptr
+ sizeof(int32_t), resp
->msg_auth
.kind
.sasl_auth
.len
);
666 resp
= malloc(sizeof(pgmsg_param_status_t
));
667 resp
->type
= msg
->body
.type
;
668 if (-1 == _parse_param_status(&msg
->body
, &resp
->msg_param_status
)) {
673 case PG_BACKENDKEYDATA
:
674 resp
= malloc(sizeof(pgmsg_backend_keydata_t
));
675 resp
->type
= msg
->body
.type
;
676 resp
->msg_backend_keydata
.pid
= be32toh(*((int32_t*)msg
->body
.ptr
));
677 resp
->msg_backend_keydata
.sk
= be32toh(*((int32_t*)msg
->body
.ptr
+ sizeof(int32_t)));
680 resp
= malloc(sizeof(pgmsg_ready_t
));
681 resp
->type
= msg
->body
.type
;
682 resp
->msg_ready
.tr
= msg
->body
.ptr
[0];
685 resp
=malloc(sizeof(pgmsg_auth_t
));
686 resp
->type
= msg
->body
.type
;
689 resp
= malloc(sizeof(pgmsg_error_t
));
690 resp
->type
= msg
->body
.type
;
691 if (-1 == _parse_error(&msg
->body
, &resp
->msg_error
)) {
697 resp
= malloc(sizeof(pgmsg_rowdesc_t
) + sizeof(pgmsg_field_t
) * be16toh(*((int16_t*)msg
->body
.ptr
)));
698 resp
->type
= msg
->body
.type
;
699 if (-1 == _parse_rowdesc(msg
, &resp
->msg_rowdesc
)) {
705 resp
= malloc(sizeof(pgmsg_datarow_t
) + sizeof(pgmsg_data_t
) * be16toh(*((int16_t*)msg
->body
.ptr
)));
706 resp
->type
= msg
->body
.type
;
707 if (-1 == _parse_datarow(msg
, &resp
->msg_datarow
)) {
713 resp
= malloc(sizeof(pgmsg_cmd_complete_t
));
714 resp
->type
= msg
->body
.type
;
715 resp
->msg_complete
.tag
= msg
->body
.ptr
;
718 resp
= malloc(sizeof(pgmsg_copyin_t
));
719 resp
->type
= msg
->body
.type
;
720 _parse_copyin(msg
, &resp
->msg_copyin
);
726 static int _copyin_field (pgmsg_t
**msg
, pgfld_t
*fld
) {
731 _setstr(msg
, CONST_STR_LEN("\\N"));
732 else if (PG_TEXT
== fld
->fmt
)
733 _setstr(msg
, fld
->data
.s
, fld
->len
);
737 len
= snprintf(buf
, sizeof(buf
),"%hd", be16toh(fld
->data
.i2
));
738 _setstr(msg
, buf
, len
);
741 len
= snprintf(buf
, sizeof(buf
), "%d", be32toh(fld
->data
.i4
));
742 _setstr(msg
, buf
, len
);
745 len
= snprintf(buf
, sizeof(buf
), LONG_FMT
, be64toh(fld
->data
.i8
));
746 _setstr(msg
, buf
, len
);
749 len
= snprintf(buf
, sizeof(buf
), "%f", pg_conv_float(fld
->data
.f4
));
750 _setstr(msg
, buf
, len
);
753 len
= snprintf(buf
, sizeof(buf
), "%f", pg_conv_double(fld
->data
.f8
));
754 _setstr(msg
, buf
, len
);
759 _setstr(msg
, fld
->data
.s
, fld
->len
);
763 _setstr(msg
, CONST_STR_LEN("t"));
765 _setstr(msg
, CONST_STR_LEN("f"));
768 tm_dec(be64toh(fld
->data
.tm
), &tm
);
769 len
= snprintf(buf
, sizeof(buf
), "%d-%02d-%02d", tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
);
770 _setstr(msg
, buf
, len
);
773 tm_dec(be64toh(fld
->data
.tm
), &tm
);
774 len
= snprintf(buf
, sizeof(buf
), "%d-%02d-%02d %02d:%02d:%02d", tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
, tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
775 _setstr(msg
, buf
, len
);
779 _setstr(msg
, CONST_STR_LEN("0x"));
780 for (int i
= 0; i
< fld
->len
; ++i
) {
781 len
= snprintf(buf
, sizeof(buf
), "%02x", fld
->data
.s
[i
]);
782 _setstr(msg
, buf
, len
);
786 len
= snprintf(buf
, sizeof(buf
), "X'%08x'", fld
->data
.bit32
.bit
);
787 _setstr(msg
, buf
, len
);
796 pgmsg_t
*pgmsg_copyin_flds (int len
, pgfld_t
**flds
) {
797 pgmsg_t
*msg
= pgmsg_create(PG_COPYDATA
);
798 if (-1 == _copyin_field(&msg
, flds
[0]))
800 for (int i
= 1; i
< len
; ++i
) {
801 _setstr(&msg
, CONST_STR_LEN("\t"));
802 if (-1 == _copyin_field(&msg
, flds
[i
]))