2 #include "libpgcli/pgprov3.h"
5 #define HEADER_SIZE sizeof(char) + sizeof(int32_t)
7 static int _resize (pgmsg_t
**msg
, size_t len
) {
9 int nlen
= (*msg
)->len
+ len
;
11 bufsize
= sizeof(pgmsg_t
) + (nlen
/ CHUNKSIZE
) * CHUNKSIZE
+ CHUNKSIZE
;
12 if (bufsize
== (*msg
)->bufsize
)
14 pc_len
= (uintptr_t)(*msg
)->pc
- (uintptr_t)(*msg
)->body
.ptr
;
15 if (!(res
= realloc(*msg
, bufsize
)))
17 res
->bufsize
= bufsize
;
18 res
->pc
= res
->body
.ptr
+ pc_len
;
23 pgmsg_t
*pgmsg_create (char type
) {
24 size_t bufsize
= sizeof(pgmsg_t
) + CHUNKSIZE
;
25 pgmsg_t
*msg
= malloc(bufsize
);
26 if (!msg
) return NULL
;
27 msg
->bufsize
= bufsize
;
28 msg
->len
= sizeof(pgmsg_t
);
29 msg
->pc
= msg
->body
.ptr
;
30 msg
->body
.type
= type
;
31 msg
->body
.len
= htobe32(sizeof(int32_t));
35 static int _seti8 (pgmsg_t
**msg
, int8_t x
) {
36 if (-1 == _resize(msg
, sizeof(int8_t)))
38 *((int8_t*)(*msg
)->pc
) = x
;
39 (*msg
)->pc
+= sizeof(int8_t);
40 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int8_t));
41 (*msg
)->len
+= sizeof(int8_t);
45 static int _seti16 (pgmsg_t
**msg
, int16_t x
) {
46 if (-1 == _resize(msg
, sizeof(int16_t)))
48 *((int16_t*)(*msg
)->pc
) = htobe16(x
);
49 (*msg
)->pc
+= sizeof(int16_t);
50 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int16_t));
51 (*msg
)->len
+= sizeof(int16_t);
55 static int _seti32 (pgmsg_t
**msg
, int32_t x
) {
56 if (-1 == _resize(msg
, sizeof(int32_t)))
58 *((int32_t*)(*msg
)->pc
) = htobe32(x
);
59 (*msg
)->pc
+= sizeof(int32_t);
60 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + sizeof(int32_t));
61 (*msg
)->len
+= sizeof(int32_t);
65 static int _setstr (pgmsg_t
**msg
, const char *s
, size_t slen
) {
68 if (-1 == _resize(msg
, slen
))
70 memcpy((*msg
)->pc
, s
, slen
);
72 (*msg
)->body
.len
= htobe32(be32toh((*msg
)->body
.len
) + slen
);
77 static int16_t _geti16 (pgmsg_t
*msg
) {
78 int16_t r
= be16toh(*((int16_t*)msg
->pc
));
79 msg
->pc
+= sizeof(int16_t);
83 static int32_t _geti32 (pgmsg_t
*msg
) {
84 int32_t r
= be32toh(*((int32_t*)msg
->pc
));
85 msg
->pc
+= sizeof(int32_t);
89 static const char *_getstr (pgmsg_t
*msg
) {
90 char *p
= msg
->pc
, *e
= msg
->body
.ptr
+ be32toh(msg
->body
.len
);
91 while (*(msg
->pc
) && msg
->pc
< e
) ++msg
->pc
;
98 int pgmsg_set_param (pgmsg_t
**msg
, const char *name
, size_t name_len
, const char *value
, size_t value_len
) {
99 if (-1 == _setstr(msg
, name
, name_len
) ||
100 -1 == _seti8(msg
, 0) ||
101 -1 == _setstr(msg
, value
, value_len
) ||
102 -1 == _seti8(msg
, 0))
107 pgmsg_t
*pgmsg_create_startup (const char *user
, size_t user_len
, const char *database
, size_t database_len
) {
108 pgmsg_t
*msg
= pgmsg_create('\0');
109 _seti16(&msg
, PG_MAJOR_VER
);
110 _seti16(&msg
, PG_MINOR_VER
);
111 pgmsg_set_param(&msg
, CONST_STR_LEN("user"), user
, user_len
);
112 pgmsg_set_param(&msg
, CONST_STR_LEN("database"), database
, database_len
);
116 #define PG_USERDEFS 0x00000001
117 #define PG_DBDEFS 0x00000002
118 #define PG_ENCDEFS 0x00000004
120 static inline pgmsg_t
*_add_str_param (pgmsg_t
*msg
, const char *k
, size_t lk
, const char *v
, size_t lv
) {
121 _setstr(&msg
, k
, lk
);
123 _setstr(&msg
, v
, lv
);
128 static pgmsg_t
*_add_param (pgmsg_t
*msg
, const char *begin
, const char *end
, uint32_t *flags
) {
129 const char *p
= begin
, *p1
;
130 while (p
< end
&& '=' != *p
) ++p
;
134 while (p1
> begin
&& isspace(*(p1
- 1))) --p1
;
138 while (p
< end
&& isspace(*p
)) ++p
;
141 if (0 == strncmp(begin
, "dbname", (uintptr_t)p1
- (uintptr_t)begin
)) {
142 msg
= _add_str_param(msg
, CONST_STR_LEN("database"), p
, (uintptr_t)end
- (uintptr_t)p
);
146 if (0 == strncmp(begin
, "user", (uintptr_t)p1
- (uintptr_t)begin
)) {
147 msg
= _add_str_param(msg
, begin
, (uintptr_t)p1
- (uintptr_t)begin
, p
, (uintptr_t)end
- (uintptr_t)p
);
149 *flags
|= PG_USERDEFS
;
151 if (0 != strncmp(begin
, "host", (uintptr_t)p1
- (uintptr_t)begin
) &&
152 0 != strncmp(begin
, "port", (uintptr_t)p1
- (uintptr_t)begin
))
153 msg
= _add_str_param(msg
, begin
, (uintptr_t)p1
- (uintptr_t)begin
, p
, (uintptr_t)end
- (uintptr_t)p
);
157 void *parse_conninfo (void *data
, const char *conn_info
, parse_param_h fn
, uint32_t *flags
) {
159 const char *p
= conn_info
;
162 while (isspace(*p
)) ++p
;
165 while (*p
&& !isspace(*p
)) ++p
;
166 data
= fn(data
, q
, p
, flags
);
172 static pgmsg_t
*_startup_params (pgmsg_t
*msg
, const char *conn_info
) {
174 msg
= (pgmsg_t
*)parse_conninfo((void*)msg
, conn_info
, (parse_param_h
)_add_param
, &flags
);
175 if (!(flags
& PG_USERDEFS
)) {
176 char *s
= getenv("USER");
178 msg
= _add_str_param(msg
, CONST_STR_LEN("user"), s
, strlen(s
));
180 if (!(flags
& PG_DBDEFS
)) {
181 char *s
= getenv("USER");
183 msg
= _add_str_param(msg
, CONST_STR_LEN("database"), s
, strlen(s
));
185 if (!(flags
& PG_ENCDEFS
)) {
186 char *s
= getenv("LANG");
187 if (s
&& (s
= strchr(s
, '.')) && *++s
)
188 msg
= _add_str_param(msg
, CONST_STR_LEN("client_encoding"), s
, strlen(s
));
194 pgmsg_t
*pgmsg_create_startup_params (const char *conn_info
) {
195 pgmsg_t
*msg
= pgmsg_create('\0');
196 _seti16(&msg
, PG_MAJOR_VER
);
197 _seti16(&msg
, PG_MINOR_VER
);
198 return _startup_params(msg
, conn_info
);
201 pgmsg_t
*pgmsg_create_simple_query (const char *sql
, size_t sql_len
) {
202 pgmsg_t
*msg
= pgmsg_create(PG_SIMPLEQUERY
);
203 _setstr(&msg
, sql
, sql_len
);
208 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
) {
209 pgmsg_t
*msg
= pgmsg_create(PG_PARSE
);
210 if (name
&& 0 == name_len
)
211 name_len
= strlen(name
);
213 sql_len
= strlen(sql
);
214 _setstr(&msg
, name
, name_len
);
216 _setstr(&msg
, sql
, sql_len
);
218 _seti16(&msg
, fld_len
);
219 for (int i
= 0; i
< fld_len
; ++i
)
220 _seti32(&msg
, flds
[i
]->oid
);
224 pgmsg_t
*pgmsg_create_bind (const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
,
225 int fld_len
, pgfld_t
**flds
, int res_fmt_len
, int *res_fmt
) {
226 pgmsg_t
*msg
= pgmsg_create(PG_BIND
);
227 _setstr(&msg
, portal
, portal_len
);
229 _setstr(&msg
, stmt
, stmt_len
);
231 _seti16(&msg
, fld_len
);
232 for (int i
= 0; i
< fld_len
; ++i
)
233 _seti16(&msg
, flds
[i
]->fmt
);
234 _seti16(&msg
, fld_len
);
235 for (int i
= 0; i
< fld_len
; ++i
) {
236 if (flds
[i
]->is_null
)
239 _seti32(&msg
, flds
[i
]->len
);
240 if (0 == flds
[i
]->fmt
|| OID_UUID
== flds
[i
]->oid
)
241 _setstr(&msg
, flds
[i
]->data
.s
, flds
[i
]->len
);
243 _setstr(&msg
, (const char*)&flds
[i
]->data
, flds
[i
]->len
);
246 _seti16(&msg
, res_fmt_len
);
247 for (int i
= 0; i
< res_fmt_len
; ++i
)
248 _seti16(&msg
, res_fmt
[i
]);
252 pgmsg_t
*pgmsg_create_describe (uint8_t op
, const char *name
, size_t name_len
) {
253 pgmsg_t
*msg
= pgmsg_create(PG_DESCRIBE
);
255 _setstr(&msg
, name
, name_len
);
260 pgmsg_t
*pgmsg_create_execute (const char *portal
, size_t portal_len
, int32_t max_rows
) {
261 pgmsg_t
*msg
= pgmsg_create(PG_EXECUTE
);
262 _setstr(&msg
, portal
, portal_len
);
264 _seti32(&msg
, max_rows
);
268 pgmsg_t
*pgmsg_create_close(char what
, const char *str
, size_t slen
) {
269 pgmsg_t
*msg
= pgmsg_create(PG_CLOSE
);
270 _seti8(&msg
, (uint8_t)what
);
271 _setstr(&msg
, str
, slen
);
276 static void _pg_md5_hash (const void *buff
, size_t len
, char *out
) {
282 md5Update(&ctx
, (uint8_t*)buff
, 512);
284 md5Update(&ctx
, (uint8_t*)buff
, len
);
289 memcpy(digest
, ctx
.digest
, 16);
290 for (int i
= 0; i
< 16; ++i
)
291 snprintf(&(out
[i
*2]), 16*2, "%02x", (uint8_t)digest
[i
]);
294 static void _pg_md5_encrypt(const char *passwd
, const char *salt
, size_t salt_len
, char *buf
) {
295 size_t passwd_len
= strlen(passwd
);
296 char *crypt_buf
= malloc(passwd_len
+ salt_len
+ 1);
297 memcpy(crypt_buf
, passwd
, passwd_len
);
298 memcpy(crypt_buf
+ passwd_len
, salt
, salt_len
);
300 _pg_md5_hash(crypt_buf
, passwd_len
+ salt_len
, buf
+ 3);
304 pgmsg_t
*pgmsg_create_pass (int req
, const char *salt
, size_t salt_len
, const char *user
, const char *pass
) {
305 pgmsg_t
*msg
= pgmsg_create(PG_PASS
);
306 char *pwd
= malloc(2 * (PG_MD5PASS_LEN
+ 1)),
307 *pwd2
= pwd
+ PG_MD5PASS_LEN
+ 1,
309 _pg_md5_encrypt(pass
, user
, strlen(user
), pwd2
);
310 _pg_md5_encrypt(pwd2
+ sizeof("md5")-1, salt
, 4, pwd
);
317 pwd_to_send
= (char*)pass
;
319 _setstr(&msg
, pwd_to_send
, strlen(pwd_to_send
));
324 int pgmsg_send (int fd
, pgmsg_t
*msg
) {
327 ssize_t sent
= 0, wrote
= 0;
328 if ('\0' == msg
->body
.type
) {
329 buf
= &msg
->body
.len
;
330 size
= be32toh(msg
->body
.len
);
333 size
= be32toh(msg
->body
.len
) + sizeof(char);
335 while (sent
< size
) {
337 wrote
= send(fd
, buf
, size
- sent
, 0);
338 while (wrote
< 0 && EINTR
== errno
);
342 return sent
== size
? 0 : -1;
345 int pgmsg_recv (int fd
, pgmsg_t
**msg
) {
346 ssize_t readed
= 0, total
= 0;
352 } __attribute__ ((packed
)) header
;
353 while (total
< HEADER_SIZE
&& (readed
= recv(fd
, (void*)(&header
+ total
), HEADER_SIZE
- total
, 0)) > 0)
357 header
.len
= be32toh(header
.len
);
358 bufsize
= sizeof(pgmsg_t
) + header
.len
;
359 if (!(m
= calloc(bufsize
, sizeof(int8_t))))
361 m
->body
.type
= header
.type
;
362 m
->body
.len
= header
.len
;
363 total
= header
.len
- sizeof(int32_t);
365 char *p
= m
->body
.ptr
;
366 if (-1 == (readed
= recv(fd
, p
, total
, 0))) {
377 static int _parse_param_status (pgmsg_body_t
*body
, pgmsg_param_status_t
*pmsg
) {
378 char *p
= body
->ptr
, *e
= p
+ body
->len
, *q
= p
;
379 while (*q
&& q
< e
) ++q
;
387 static int _parse_error (pgmsg_body_t
*body
, pgmsg_error_t
*pmsg
) {
388 char *p
= body
->ptr
, *e
= p
+ body
->len
;
392 pmsg
->severity
= ++p
;
404 pmsg
->position
= ++p
;
420 while (p
< e
&& *p
) ++p
;
429 static int _parse_rowdesc (pgmsg_t
*msg
, pgmsg_rowdesc_t
*pmsg
) {
430 pmsg
->nflds
= _geti16(msg
);
431 for (int i
= 0; i
< pmsg
->nflds
; ++i
) {
432 const char *fname
= _getstr(msg
);
435 pmsg
->fields
[i
].fname
= fname
;
436 pmsg
->fields
[i
].oid_table
= _geti32(msg
);
437 pmsg
->fields
[i
].idx_field
= _geti16(msg
);
438 pmsg
->fields
[i
].oid_field
= _geti32(msg
);
439 pmsg
->fields
[i
].field_len
= _geti16(msg
);
440 pmsg
->fields
[i
].type_mod
= _geti32(msg
);
441 pmsg
->fields
[i
].field_fmt
= _geti16(msg
);
446 static int _parse_datarow (pgmsg_t
*msg
, pgmsg_datarow_t
*pmsg
) {
447 pmsg
->nflds
= _geti16(msg
);
448 for (int i
= 0; i
< pmsg
->nflds
; ++i
) {
449 int32_t len
= pmsg
->fields
[i
].len
= _geti32(msg
);
450 pmsg
->fields
[i
].data
= NULL
;
452 pmsg
->fields
[i
].data
= (uint8_t*)msg
->pc
;
459 pgmsg_resp_t
*pgmsg_parse (pgmsg_t
*msg
) {
460 pgmsg_resp_t
*resp
= NULL
;
461 msg
->pc
= msg
->body
.ptr
;
462 switch (msg
->body
.type
) {
464 resp
= malloc(sizeof(pgmsg_auth_t
));
465 resp
->type
= msg
->body
.type
;
466 switch (resp
->msg_auth
.success
= be32toh(*((int32_t*)msg
->body
.ptr
))) {
470 memcpy(resp
->msg_auth
.kind
.md5_auth
, msg
->body
.ptr
+ sizeof(int32_t), sizeof(uint8_t)*4);
479 resp
= malloc(sizeof(pgmsg_param_status_t
));
480 resp
->type
= msg
->body
.type
;
481 if (-1 == _parse_param_status(&msg
->body
, &resp
->msg_param_status
)) {
486 case PG_BACKENDKEYDATA
:
487 resp
= malloc(sizeof(pgmsg_backend_keydata_t
));
488 resp
->type
= msg
->body
.type
;
489 resp
->msg_backend_keydata
.pid
= be32toh(*((int32_t*)msg
->body
.ptr
));
490 resp
->msg_backend_keydata
.sk
= be32toh(*((int32_t*)msg
->body
.ptr
+ sizeof(int32_t)));
493 resp
= malloc(sizeof(pgmsg_ready_t
));
494 resp
->type
= msg
->body
.type
;
495 resp
->msg_ready
.tr
= msg
->body
.ptr
[0];
498 resp
=malloc(sizeof(pgmsg_auth_t
));
499 resp
->type
= msg
->body
.type
;
502 resp
= malloc(sizeof(pgmsg_error_t
));
503 resp
->type
= msg
->body
.type
;
504 if (-1 == _parse_error(&msg
->body
, &resp
->msg_error
)) {
510 resp
= malloc(sizeof(pgmsg_rowdesc_t
) + sizeof(pgmsg_field_t
) * be16toh(*((int16_t*)msg
->body
.ptr
)));
511 resp
->type
= msg
->body
.type
;
512 if (-1 == _parse_rowdesc(msg
, &resp
->msg_rowdesc
)) {
518 resp
= malloc(sizeof(pgmsg_datarow_t
) + sizeof(pgmsg_data_t
) * be16toh(*((int16_t*)msg
->body
.ptr
)));
519 resp
->type
= msg
->body
.type
;
520 if (-1 == _parse_datarow(msg
, &resp
->msg_datarow
)) {
526 resp
= malloc(sizeof(pgmsg_cmd_complete_t
));
527 resp
->type
= msg
->body
.type
;
528 resp
->msg_complete
.tag
= msg
->body
.ptr
;