1 /*
2  * Copyright (c) 2017-2018	Tatsuo Ishii
3  * Copyright (c) 2018-2021	PgPool Global Development Group
4  *
5  * Permission to use, copy, modify, and distribute this software and
6  * its documentation for any purpose and without fee is hereby
7  * granted, provided that the above copyright notice appear in all
8  * copies and that both that copyright notice and this permission
9  * notice appear in supporting documentation, and that the name of the
10  * author not be used in advertising or publicity pertaining to
11  * distribution of the software without specific, written prior
12  * permission. The author makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as
14  * is" without express or implied warranty.
15  */
16 
17 #include "../../include/config.h"
18 #include "pgproto/pgproto.h"
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include "pgproto/fe_memutils.h"
27 #include <libpq-fe.h>
28 #include "pgproto/read.h"
29 #include "pgproto/send.h"
30 #include "pgproto/buffer.h"
31 #include "pgproto/extended_query.h"
32 
33 #undef DEBUG
34 
35 static void show_version(void);
36 static void usage(void);
37 static FILE *openfile(char *filename);
38 static PGconn *connect_db(char *host, char *port, char *user, char *database);
39 static void read_and_process(FILE *fd, PGconn *conn);
40 static int	process_a_line(char *buf, PGconn *con);
41 static int	process_message_type(int kind, char *buf, PGconn *conn);
42 static void process_function_call(char *buf, PGconn *conn);
43 
44 int			read_nap = 0;
45 
46 int
main(int argc,char ** argv)47 main(int argc, char **argv)
48 {
49 	int			opt;
50 	int			optindex;
51 	char	   *env;
52 	char	   *host = "";
53 	char	   *port = "5432";
54 	char	   *user = "";
55 	char	   *database = "";
56 	char	   *data_file = PGPROTODATA;
57 	int			debug = 0;
58 	FILE	   *fd;
59 	PGconn	   *con;
60 	int			var;
61 
62 	static struct option long_options[] = {
63 		{"host", optional_argument, NULL, 'h'},
64 		{"port", optional_argument, NULL, 'p'},
65 		{"username", optional_argument, NULL, 'u'},
66 		{"database", optional_argument, NULL, 'd'},
67 		{"proto-data-file", optional_argument, NULL, 'f'},
68 		{"debug", no_argument, NULL, 'D'},
69 		{"help", no_argument, NULL, '?'},
70 		{"version", no_argument, NULL, 'v'},
71 		{"read-nap", optional_argument, NULL, 'r'},
72 		{NULL, 0, NULL, 0}
73 	};
74 
75 	if ((env = getenv("PGHOST")) != NULL && *env != '\0')
76 		host = env;
77 	if ((env = getenv("PGPORT")) != NULL && *env != '\0')
78 		port = env;
79 	if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
80 		database = env;
81 	if ((env = getenv("PGUSER")) != NULL && *env != '\0')
82 		user = env;
83 
84 	while ((opt = getopt_long(argc, argv, "v?Dh:p:u:d:f:r:", long_options, &optindex)) != -1)
85 	{
86 		switch (opt)
87 		{
88 			case 'v':
89 				show_version();
90 				exit(0);
91 				break;
92 
93 			case '?':
94 				usage();
95 				exit(0);
96 				break;
97 
98 			case 'D':
99 				debug++;
100 				break;
101 
102 			case 'h':
103 				host = pg_strdup(optarg);
104 				break;
105 
106 			case 'p':
107 				port = pg_strdup(optarg);
108 				break;
109 
110 			case 'u':
111 				user = pg_strdup(optarg);
112 				break;
113 
114 			case 'd':
115 				database = pg_strdup(optarg);
116 				break;
117 
118 			case 'f':
119 				data_file = pg_strdup(optarg);
120 				break;
121 
122 			case 'r':
123 				read_nap = atoi(optarg);
124 				break;
125 
126 			default:
127 				usage();
128 				exit(1);
129 		}
130 	}
131 
132 	fd = openfile(data_file);
133 	con = connect_db(host, port, user, database);
134 	var = fcntl(PQsocket(con), F_GETFL, 0);
135 	if (var == -1)
136 	{
137 		fprintf(stderr, "fcntl failed (%s)\n", strerror(errno));
138 		exit(1);
139 	}
140 
141 	/*
142 	 * Set the socket to non block.
143 	 */
144 	if (fcntl(PQsocket(con), F_SETFL, var & ~O_NONBLOCK) == -1)
145 	{
146 		fprintf(stderr, "fcntl failed (%s)\n", strerror(errno));
147 		exit(1);
148 	}
149 
150 	read_and_process(fd, con);
151 
152 	return 0;
153 }
154 
155 static void
show_version(void)156 show_version(void)
157 {
158 	fprintf(stderr, "pgproto (%s) %s\n", PACKAGE, PACKAGE_VERSION);
159 }
160 
161 static void
usage(void)162 usage(void)
163 {
164 	printf("Usage: %s\n"
165 		   "-h, --hostname=HOSTNAME (default: UNIX domain socket)\n"
166 		   "-p, --port=PORT (default: 5432)\n"
167 		   "-u, --user USERNAME (default: OS user)\n"
168 		   "-d, --database DATABASENAME (default: same as user)\n"
169 		   "-f, --proto-data-file FILENAME (default: pgproto.data)\n"
170 		   "-r, --read-nap NAPTIME (in micro seconds. default: 0)\n"
171 		   "-D, --debug\n"
172 		   "-?, --help\n"
173 		   "-v, --version\n",
174 		   PACKAGE);
175 }
176 
177 /*
178  * Open protocol data and return the file descriptor.  If failed to open the
179  * file, do not return and exit within this function.
180  */
181 static FILE *
openfile(char * filename)182 openfile(char *filename)
183 {
184 	FILE	   *fd = fopen(filename, "r");
185 
186 	if (fd == NULL)
187 	{
188 		fprintf(stderr, "Failed to open protocol data file: %s.\n", filename);
189 		exit(1);
190 	}
191 	return fd;
192 }
193 
194 /*
195  * Connect to the specified PostgreSQL. If failed, do not return and exit
196  * within this function.
197  */
198 static PGconn *
connect_db(char * host,char * port,char * user,char * database)199 connect_db(char *host, char *port, char *user, char *database)
200 {
201 	char		conninfo[1024];
202 	PGconn	   *conn;
203 	size_t		n;
204 	char	*app_name_str = " application_name=pgproto";
205 
206 	conninfo[0] = '\0';
207 	n = sizeof(conninfo);
208 
209 	if (host && host[0] != '\0')
210 	{
211 		n -= sizeof("host=");
212 		strncat(conninfo, "host=", n);
213 		n -= strlen(host) + 1;
214 		strncat(conninfo, host, n);
215 	}
216 
217 	if (port && port[0] != '\0')
218 	{
219 		n -= sizeof("port=");
220 		strncat(conninfo, " port=", n);
221 		n -= strlen(port) + 1;
222 		strncat(conninfo, port, n);
223 	}
224 
225 	if (user && user[0] != '\0')
226 	{
227 		n -= sizeof("user=");
228 		strncat(conninfo, " user=", n);
229 		n -= strlen(user) + 1;
230 		strncat(conninfo, user, n);
231 	}
232 
233 	if (database && database[0] != '\0')
234 	{
235 		n -= sizeof("dbname=");
236 		strncat(conninfo, " dbname=", n);
237 		n -= strlen(database) + 1;
238 		strncat(conninfo, database, n);
239 	}
240 
241 	n -= strlen(app_name_str);
242 	strncat(conninfo, app_name_str, n);
243 
244 	conn = PQconnectdb(conninfo);
245 
246 	if (conn == NULL || PQstatus(conn) == CONNECTION_BAD)
247 	{
248 		fprintf(stderr, "Failed to connect to %s.\n", conninfo);
249 		exit(1);
250 	}
251 
252 	return conn;
253 }
254 
255 /*
256  * Read the protocol data file and process it.
257  */
258 static void
read_and_process(FILE * fd,PGconn * conn)259 read_and_process(FILE *fd, PGconn *conn)
260 {
261 #define PGPROTO_READBUF_LENGTH 8192
262 	int			status;
263 	char	   *buf;
264 	int			buflen;
265 	char	   *p;
266 	int			len;
267 	int			readp;
268 
269 	for (;;)
270 	{
271 		buflen = PGPROTO_READBUF_LENGTH;
272 		buf = pg_malloc(buflen);
273 		readp = 0;
274 
275 		for (;;)
276 		{
277 			p = fgets(buf + readp, buflen - readp, fd);
278 			if (p == NULL)
279 			{
280 				/* EOF detected */
281 				exit(0);
282 			}
283 
284 			/*
285 			 * if ends with backslash + new line, assume it's a continuous
286 			 * line
287 			 */
288 			len = strlen(p);
289 			if (p[len - 2] != '\\' || p[len - 1] != '\n')
290 			{
291 				break;
292 			}
293 
294 			buflen += PGPROTO_READBUF_LENGTH;
295 			buf = pg_realloc(buf, buflen);
296 			readp += len;
297 		}
298 
299 		status = process_a_line(buf, conn);
300 		pg_free(buf);
301 
302 		if (status < 0)
303 		{
304 			exit(1);
305 		}
306 	}
307 	PQfinish(conn);
308 }
309 
310 /*
311  * Process a line of protocol data.
312  */
313 static int
process_a_line(char * buf,PGconn * conn)314 process_a_line(char *buf, PGconn *conn)
315 {
316 	char	   *p = buf;
317 	int			kind;
318 
319 #ifdef DEBUG
320 	fprintf(stderr, "buf: %s", buf);
321 #endif
322 
323 	if (p == NULL)
324 	{
325 		fprintf(stderr, "process_a_line: buf is NULL\n");
326 		return -1;
327 	}
328 
329 	/* An empty line or a line starting with '#' is ignored. */
330 	if (*p == '\n' || *p == '#')
331 	{
332 		return 0;
333 	}
334 	else if (*p != '\'')
335 	{
336 		fprintf(stderr, "process_a_line: first character is not \' but %x\n", *p);
337 		return -1;
338 	}
339 
340 	p++;
341 
342 	kind = (unsigned char) (*p++);
343 
344 	if (*p != '\'')
345 	{
346 		fprintf(stderr, "process_a_line: message kind is not followed by \' but %x\n", *p);
347 		return -1;
348 	}
349 
350 	p++;
351 
352 	process_message_type(kind, p, conn);
353 
354 	return 0;
355 }
356 
357 /*
358  * Read a line of message from buf and process it.
359  */
360 static int
process_message_type(int kind,char * buf,PGconn * conn)361 process_message_type(int kind, char *buf, PGconn *conn)
362 {
363 #ifdef DEBUG
364 	fprintf(stderr, "message kind is %c\n", kind);
365 #endif
366 
367 	char	   *query;
368 	char	   *err_msg;
369 	char	   *data;
370 	char	   *bufp;
371 
372 	switch (kind)
373 	{
374 		case 'Y':
375 			read_until_ready_for_query(conn, 0);
376 			break;
377 
378 		case 'y':
379 			read_until_ready_for_query(conn, 1);
380 			break;
381 
382 		case 'X':
383 			fprintf(stderr, "FE=> Terminate\n");
384 			send_char((char) kind, conn);
385 			send_int(sizeof(int), conn);
386 			break;
387 
388 		case 'S':
389 			fprintf(stderr, "FE=> Sync\n");
390 			send_char((char) kind, conn);
391 			send_int(sizeof(int), conn);
392 			break;
393 
394 		case 'H':
395 			fprintf(stderr, "FE=> Flush\n");
396 			send_char((char) kind, conn);
397 			send_int(sizeof(int), conn);
398 			break;
399 
400 		case 'Q':
401 			buf++;
402 			query = buffer_read_string(buf, &bufp);
403 			fprintf(stderr, "FE=> Query (query=\"%s\")\n", query);
404 			send_char((char) kind, conn);
405 			send_int(sizeof(int) + strlen(query) + 1, conn);
406 			send_string(query, conn);
407 			pg_free(query);
408 			break;
409 
410 		case 'd':
411 			buf++;
412 			data = buffer_read_string(buf, &bufp);
413 			fprintf(stderr, "FE=> CopyData (copy data=\"%s\")\n", data);
414 			send_char((char) kind, conn);
415 			send_int(sizeof(int) + strlen(data), conn);
416 			send_byte(data, strlen(data), conn);
417 			pg_free(data);
418 			break;
419 
420 		case 'c':
421 			fprintf(stderr, "FE=> CopyDone\n");
422 			send_char((char) kind, conn);
423 			send_int(sizeof(int), conn);
424 			break;
425 
426 		case 'f':
427 			buf++;
428 			err_msg = buffer_read_string(buf, &bufp);
429 			fprintf(stderr, "FE=> CopyFail (error message=\"%s\")\n", err_msg);
430 			send_char((char) kind, conn);
431 			send_int(sizeof(int) + strlen(err_msg) + 1, conn);
432 			send_string(err_msg, conn);
433 			pg_free(err_msg);
434 			break;
435 
436 		case 'P':
437 			process_parse(buf, conn);
438 			break;
439 
440 		case 'B':
441 			process_bind(buf, conn);
442 			break;
443 
444 		case 'E':
445 			process_execute(buf, conn);
446 			break;
447 
448 		case 'D':
449 			process_describe(buf, conn);
450 			break;
451 
452 		case 'C':
453 			process_close(buf, conn);
454 			break;
455 
456 		case 'F':
457 			process_function_call(buf, conn);
458 			break;
459 
460 		default:
461 			fprintf(stderr, "Unknown kind: %c", kind);
462 			break;
463 	}
464 	return 0;
465 }
466 
467 /*
468  * Process function call message
469  */
470 static void
process_function_call(char * buf,PGconn * conn)471 process_function_call(char *buf, PGconn *conn)
472 {
473 	int			len;
474 	int			foid;
475 	short		nparams;
476 	short		ncodes;
477 	short		codes[MAXENTRIES];
478 	int			paramlens[MAXENTRIES];
479 	char	   *paramvals[MAXENTRIES];
480 	short		result_formatcode;
481 	int			i;
482 	char	   *bufp;
483 
484 	SKIP_TABS(buf);
485 
486 	len = sizeof(int);
487 
488 	/* function oid */
489 	foid = buffer_read_int(buf, &bufp);
490 	buf = bufp;
491 	len += sizeof(int);
492 
493 	SKIP_TABS(buf);
494 
495 	/* number of argument format codes */
496 	ncodes = buffer_read_int(buf, &bufp);
497 	len += sizeof(short) + sizeof(short) * ncodes;
498 	buf = bufp;
499 
500 	SKIP_TABS(buf);
501 
502 	if (ncodes > MAXENTRIES)
503 	{
504 		fprintf(stderr, "Too many argument format codes for function call message (%d)\n", ncodes);
505 		exit(1);
506 	}
507 
508 	/* read each format code */
509 	if (ncodes > 0)
510 	{
511 		for (i = 0; i < ncodes; i++)
512 		{
513 			codes[i] = buffer_read_int(buf, &bufp);
514 			buf = bufp;
515 			SKIP_TABS(buf);
516 		}
517 	}
518 
519 	/* number of function arguments */
520 	nparams = buffer_read_int(buf, &bufp);
521 	len += sizeof(short);
522 	buf = bufp;
523 	SKIP_TABS(buf);
524 
525 	if (nparams > MAXENTRIES)
526 	{
527 		fprintf(stderr, "Too many function arguments for function call message (%d)\n", nparams);
528 		exit(1);
529 	}
530 
531 	/* read each function argument */
532 	for (i = 0; i < nparams; i++)
533 	{
534 		paramlens[i] = buffer_read_int(buf, &bufp);
535 		len += sizeof(int);
536 		buf = bufp;
537 		SKIP_TABS(buf);
538 
539 		if (paramlens[i] > 0)
540 		{
541 			paramvals[i] = buffer_read_string(buf, &bufp);
542 			buf = bufp;
543 			SKIP_TABS(buf);
544 			len += paramlens[i];
545 		}
546 	}
547 
548 	SKIP_TABS(buf);
549 
550 	/* result format code */
551 	result_formatcode = buffer_read_int(buf, &bufp);
552 
553 	if (result_formatcode != 0 && result_formatcode != 1)
554 	{
555 		fprintf(stderr, "Result format code is not either 0 or 1 (%d)\n", result_formatcode);
556 		exit(1);
557 	}
558 
559 	buf = bufp;
560 	len += sizeof(short);
561 	SKIP_TABS(buf);
562 
563 	fprintf(stderr, "\n");
564 
565 	send_char('F', conn);
566 	send_int(len, conn);		/* message length */
567 	send_int(foid, conn);		/* function oid */
568 	send_int16(ncodes, conn);	/* number of argument format code */
569 	for (i = 0; i < ncodes; i++)	/* argument format codes */
570 	{
571 		send_int16(codes[i], conn);
572 	}
573 
574 	send_int16(nparams, conn);	/* number of function arguments */
575 	for (i = 0; i < nparams; i++)	/* function arguments */
576 	{
577 		send_int(paramlens[i], conn);	/* argument length */
578 
579 		/* NULL? */
580 		if (paramlens[i] != -1)
581 		{
582 			/*
583 			 * Actually we only support text format only for now.  To support
584 			 * binary format, we need a binary expression in the data file.
585 			 */
586 			send_byte(paramvals[i], paramlens[i], conn);
587 #ifdef NOT_USED
588 			if (ncodes == 0)	/* text format? */
589 			{
590 				send_byte(paramvals[i], paramlens[i], conn);
591 			}
592 			else if (ncodes == 1)
593 			{
594 				if (codes[0] == 0)
595 					send_byte(paramvals[i], paramlens[i], conn);
596 				else
597 					send_byte(paramvals[i], paramlens[i], conn);
598 			}
599 			else
600 			{
601 				if (codes[i] == 0)
602 					send_byte(paramvals[i], paramlens[i], conn);
603 				else
604 					send_byte(paramvals[i], paramlens[i], conn);
605 			}
606 #endif
607 		}
608 	}
609 
610 	/* result format code */
611 	send_int16(result_formatcode, conn);
612 }
613