[pgpool-hackers: 2519] [New feature] Allow specifying the hostnames in pool_hba

Muhammad Usama m.usama at gmail.com
Thu Sep 7 21:46:40 JST 2017


Hi All

As of now pool_hba configuration only allows CIDR addresses to be specified
in the pool_hba records in the address field and this patch enables the
support of hostnames to be used instead of IP address.
This feature can be very useful in environments where the availability of
same IP address can't be ensured every time and the user can do the
pool-hba configuration using the hostnames instead.

Along with allowing the hostnames in address field of the HBA record the
patch also made the following notable enhancements in the area.

-- pool_hba records are now completely parsed at the loading time and we
now keep the structured data of records instead of raw record lines,  This
saves us the parsing at every new connection time and however little it may
be but its will deliver a performance enhancement.

-- Enhanced parsing now gives the better descriptive error/log messages.

-- Better handling of auth-options field


Please note that the most of the code is borrowed from PostgreSQL and the
documentation part is still remaining.


Thanks
Best Regards
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.sraoss.jp/pipermail/pgpool-hackers/attachments/20170907/41073cb7/attachment-0001.html>
-------------- next part --------------
diff --git a/src/include/auth/pool_hba.h b/src/include/auth/pool_hba.h
new file mode 100644
index 00000000..ff39aeb9
--- /dev/null
+++ b/src/include/auth/pool_hba.h
@@ -0,0 +1,85 @@
+/* -*-pgsql-c-*- */
+/*
+ *
+ * $Header$
+ *
+ * pgpool: a language independent connection pool server for PostgreSQL
+ * written by Tatsuo Ishii
+ *
+ * Copyright (c) 2003-2017	PgPool Global Development Group
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of the
+ * author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * pool_passwd.h: pool_passwd related definitions.
+ *
+ */
+
+#ifndef POOL_HBA_H
+#define POOL_HBA_H
+
+#include "parser/pg_list.h"
+#include "pool.h"
+
+/* UserAuth type used for HBA which indicates the authentication method */
+typedef enum UserAuth
+{
+	uaImplicitReject,
+	uaReject,
+	/*  uaKrb4, */
+	/*  uaKrb5, */
+	uaTrust,
+	/*  uaIdent, */
+	/*  uaPassword, */
+	/*  uaCrypt, */
+	uaMD5
+#ifdef USE_PAM
+	,uaPAM
+#endif /* USE_PAM */
+}
+UserAuth;
+
+typedef enum ConnType
+{
+	ctLocal,
+	ctHost,
+	ctHostSSL,
+	ctHostNoSSL
+} ConnType;
+
+typedef enum IPCompareMethod
+{
+	ipCmpMask,
+	ipCmpSameHost,
+	ipCmpSameNet,
+	ipCmpAll
+} IPCompareMethod;
+
+typedef struct HbaLine
+{
+	int         linenumber;
+	char       *rawline;
+	ConnType    conntype;
+	List       *databases;
+	List       *users;
+	struct sockaddr_storage addr;
+	struct sockaddr_storage mask;
+	IPCompareMethod ip_cmp_method;
+	char       *hostname;
+	UserAuth    auth_method;
+	char		*pamservice;
+	bool		pam_use_hostname;
+} HbaLine;
+
+extern bool load_hba(char *hbapath);
+extern void ClientAuthentication(POOL_CONNECTION *frontend);
+
+#endif /* POOL_HBA_H */
diff --git a/src/auth/pool_auth.c b/src/auth/pool_auth.c
index cc3fbd12..00400235 100644
--- a/src/auth/pool_auth.c
+++ b/src/auth/pool_auth.c
@@ -26,6 +26,7 @@
 #include "context/pool_session_context.h"
 #include "utils/pool_stream.h"
 #include "pool_config.h"
+#include "auth/pool_hba.h"
 #include "auth/pool_passwd.h"
 #include "utils/elog.h"
 #include "utils/palloc.h"
@@ -218,7 +219,7 @@ int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
 		/* If MD5 auth is not active in pool_hba.conf, it cannot be
 		 * used with other than raw mode.
 		 */
-		if (frontend->auth_method != uaMD5 && !RAW_MODE && NUM_BACKENDS > 1)
+		if ((frontend->pool_hba == NULL || frontend->pool_hba->auth_method != uaMD5) && !RAW_MODE && NUM_BACKENDS > 1)
 		{
 			pool_send_error_message(frontend, protoMajor, AUTHFAIL_ERRORCODE,
 									"MD5 authentication is unsupported in replication and master-slave modes.",
diff --git a/src/auth/pool_hba.c b/src/auth/pool_hba.c
index 9ad0d45c..18679c31 100644
--- a/src/auth/pool_hba.c
+++ b/src/auth/pool_hba.c
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Portions Copyright (c) 2003-2015	PgPool Global Development Group
+ * Portions Copyright (c) 2003-2017	PgPool Global Development Group
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  * Permission to use, copy, modify, and distribute this software and
@@ -31,6 +31,7 @@
 #include <netdb.h>
 
 #include "pool.h"
+#include "auth/pool_hba.h"
 #include "utils/pool_path.h"
 #include "utils/pool_ip.h"
 #include "utils/pool_stream.h"
@@ -43,27 +44,96 @@
 #include "auth/pool_passwd.h"
 
 #define MULTI_VALUE_SEP "\001" /* delimiter for multi-valued column strings */
+
 #define MAX_TOKEN	256
+#define MAX_LINE	8192
+
+static MemoryContext parsed_hba_context = NULL;
+static List *parsed_hba_lines = NIL;
+static char *HbaFileName;
+
+
+#define token_is_keyword(t, k)	(!t->quoted && strcmp(t->string, k) == 0)
+#define token_matches(t, k)  (strcmp(t->string, k) == 0)
+
+
+/*
+ * TokenizedLine represents one line lexed from a config file.
+ * Each item in the "fields" list is a sub-list of HbaTokens.
+ * We don't emit a TokenizedLine for empty or all-comment lines,
+ * so "fields" is never NIL (nor are any of its sub-lists).
+ * Exception: if an error occurs during tokenization, we might
+ * have fields == NIL, in which case err_msg != NULL.
+ */
+typedef struct TokenizedLine
+{
+	List	   *fields;			/* List of lists of HbaTokens */
+	int			line_num;		/* Line number */
+	char	   *raw_line;		/* Raw line text */
+	char	   *err_msg;		/* Error message if any */
+} TokenizedLine;
+
+/*
+ * A single string token lexed from a config file, together with whether
+ * the token had been quoted.
+ */
+typedef struct HbaToken
+{
+	char	   *string;
+	bool		quoted;
+} HbaToken;
+/* callback data for check_network_callback */
+typedef struct check_network_data
+{
+	IPCompareMethod method;		/* test method */
+	SockAddr   *raddr;			/* client's actual address */
+	bool		result;			/* set to true if match */
+} check_network_data;
 
-static List *hba_lines = NIL;
-static List *hba_line_nums = NIL;
-static char *hbaFileName;
 
+static HbaToken *
+copy_hba_token(HbaToken *in);
+static HbaToken *
+make_hba_token(const char *token, bool quoted);
+
+static bool
+parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
+				   int elevel, char **err_msg);
+
+static MemoryContext tokenize_file(const char *filename, FILE *file,
+								   List **tok_lines, int elevel);
 static void sendAuthRequest(POOL_CONNECTION *frontend, AuthRequest areq);
 static void auth_failed(POOL_CONNECTION *frontend);
 static void close_all_backend_connections(void);
 static bool hba_getauthmethod(POOL_CONNECTION *frontend);
 static bool check_hba(POOL_CONNECTION *frontend);
-static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend, bool *found_p, bool *error_p);
-static void parse_hba_auth(ListCell **line_item, UserAuth *userauth_p, char **auth_arg_p, bool *error_p);
-static bool check_user(char *user, char *param_str);
-static bool check_db(char *dbname, char *user, char *param_str);
-static void free_lines(List **lines, List **line_nums);
-static void tokenize_file(const char *filename, FILE *file, List **lines, List **line_nums);
-static char *tokenize_inc_file(const char *outer_filename, const char *inc_filename);
+static bool check_user(char *user, List *tokens);
+static bool check_db(const char *dbname, const char *user, List *tokens);
+static List * tokenize_inc_file(List *tokens,
+				  const char *outer_filename,
+				  const char *inc_filename,
+				  int elevel,
+								char **err_msg);
+static bool
+check_hostname(POOL_CONNECTION *frontend, const char *hostname);
+static bool
+check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask);
+static bool
+check_same_host_or_net(SockAddr *raddr, IPCompareMethod method);
+static void
+check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
+					   void *cb_data);
+
+static HbaLine *
+parse_hba_line(TokenizedLine *tok_line, int elevel);
 static bool pg_isblank(const char c);
-static void next_token(FILE *fp, char *buf, int bufsz);
-static char * next_token_expand(const char *filename, FILE *file);
+static bool
+next_token(char **lineptr, char *buf, int bufsz,
+		   bool *initial_quote, bool *terminating_comma,
+		   int elevel, char **err_msg);
+static List *
+next_field_expand(const char *filename, char **lineptr,
+				  int elevel, char **err_msg);
 static POOL_STATUS CheckMd5Auth(char *username);
 
 #ifdef USE_PAM
@@ -98,37 +168,584 @@ static POOL_CONNECTION *pam_frontend_kludge; /* Workaround for passing
 
 
 /*
- * read in hba config file
+ * Read the config file and create a List of HbaLine records for the contents.
+ *
+ * The configuration is read into a temporary list, and if any parse error
+ * occurs the old list is kept in place and false is returned.  Only if the
+ * whole file parses OK is the list replaced, and the function returns true.
+ *
+ * On a false result, caller will take care of reporting a FATAL error in case
+ * this is the initial startup.  If it happens on reload, we just keep running
+ * with the old data.
  */
-int load_hba(char *hbapath)
+bool
+load_hba(char *hbapath)
 {
-	FILE *file;
+	FILE	   *file;
+	List	   *hba_lines = NIL;
+	ListCell   *line;
+	List	   *new_parsed_lines = NIL;
+	bool		ok = true;
+	MemoryContext linecxt;
+	MemoryContext oldcxt;
+	MemoryContext hbacxt;
+
+	HbaFileName = pstrdup(hbapath);
+
 	file = fopen(hbapath, "r");
-	if (!file)
+	if (file == NULL)
 	{
-		ereport(WARNING,
-			(errmsg("failed while loading hba configuration from file:\"%s\"",hbapath),
-				 errdetail("fopen failed with error: \"%s\"",strerror(errno))));
-		return -1;
+		ereport(LOG,
+				(errmsg("could not open configuration file \"%s\": %m",
+						HbaFileName)));
+		return false;
 	}
 
-	ereport(DEBUG1,
-		(errmsg("loading hba configuration"),
-			errdetail("loading file :\"%s\" for client authentication configuration file",
-				   hbapath)));
+	linecxt = tokenize_file(HbaFileName, file, &hba_lines, LOG);
+	fclose(file);
+
+	/* Now parse all the lines */
+	hbacxt = AllocSetContextCreate(TopMemoryContext,
+								   "hba parser context",
+								   ALLOCSET_SMALL_SIZES);
+	oldcxt = MemoryContextSwitchTo(hbacxt);
+	foreach(line, hba_lines)
+	{
+		TokenizedLine *tok_line = (TokenizedLine *) lfirst(line);
+		HbaLine    *newline;
+
+		/* don't parse lines that already have errors */
+		if (tok_line->err_msg != NULL)
+		{
+			ok = false;
+			continue;
+		}
 
-	MemoryContext oldContext = MemoryContextSwitchTo(TopMemoryContext);
+		if ((newline = parse_hba_line(tok_line, LOG)) == NULL)
+		{
+			/* Parse error; remember there's trouble */
+			ok = false;
 
-	if (hba_lines || hba_line_nums)
-		free_lines(&hba_lines, &hba_line_nums);
+			/*
+			 * Keep parsing the rest of the file so we can report errors on
+			 * more than the first line.  Error has already been logged, no
+			 * need for more chatter here.
+			 */
+			continue;
+		}
 
-	tokenize_file(hbapath, file, &hba_lines, &hba_line_nums);
-	fclose(file);
+		new_parsed_lines = lappend(new_parsed_lines, newline);
+	}
 
-	hbaFileName = pstrdup(hbapath);
+	/*
+	 * A valid HBA file must have at least one entry; else there's no way to
+	 * connect to the postmaster.  But only complain about this if we didn't
+	 * already have parsing errors.
+	 */
+	if (ok && new_parsed_lines == NIL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("configuration file \"%s\" contains no entries",
+						HbaFileName)));
+		ok = false;
+	}
+
+	/* Free tokenizer memory */
+	MemoryContextDelete(linecxt);
+	MemoryContextSwitchTo(oldcxt);
+
+	if (!ok)
+	{
+		/* File contained one or more errors, so bail out */
+		MemoryContextDelete(hbacxt);
+		return false;
+	}
+
+	/* Loaded new file successfully, replace the one we use */
+	if (parsed_hba_context != NULL)
+		MemoryContextDelete(parsed_hba_context);
+	parsed_hba_context = hbacxt;
+	parsed_hba_lines = new_parsed_lines;
 
-	MemoryContextSwitchTo(oldContext);
-	return 0;
+	return true;
+}
+
+static HbaLine *
+parse_hba_line(TokenizedLine *tok_line, int elevel)
+{
+	int			line_num = tok_line->line_num;
+	char	  **err_msg = &tok_line->err_msg;
+	char	   *str;
+	struct addrinfo *gai_result;
+	struct addrinfo hints;
+	int			ret;
+	char	   *cidr_slash;
+	ListCell   *field;
+	List	   *tokens;
+	ListCell   *tokencell;
+	HbaToken   *token;
+	HbaLine    *parsedline;
+
+	parsedline = palloc0(sizeof(HbaLine));
+	parsedline->linenumber = line_num;
+	parsedline->rawline = pstrdup(tok_line->raw_line);
+
+	/* Check the record type. */
+	Assert(tok_line->fields != NIL);
+	field = list_head(tok_line->fields);
+	tokens = lfirst(field);
+	if (tokens->length > 1)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("multiple values specified for connection type"),
+				 errhint("Specify exactly one connection type per line."),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "multiple values specified for connection type";
+		return NULL;
+	}
+	token = linitial(tokens);
+	if (strcmp(token->string, "local") == 0)
+	{
+		parsedline->conntype = ctLocal;
+	}
+	else if (strcmp(token->string, "host") == 0 ||
+			 strcmp(token->string, "hostssl") == 0 ||
+			 strcmp(token->string, "hostnossl") == 0)
+	{
+
+		if (token->string[4] == 's')	/* "hostssl" */
+		{
+			parsedline->conntype = ctHostSSL;
+			/* Log a warning if SSL support is not active */
+#ifdef USE_SSL
+			if (!pool_config->ssl)
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("hostssl record cannot match because SSL is disabled"),
+						 errhint("Set ssl = on in pgpool.conf"),
+						 errcontext("line %d of configuration file \"%s\"",
+									line_num, HbaFileName)));
+				*err_msg = "hostssl record cannot match because SSL is disabled";
+			}
+#else
+			ereport(elevel,
+					(errmsg("hostssl record cannot match because SSL is not supported by this build"),
+					 errhint("Compile with --with-openssl to use SSL connections."),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "hostssl record cannot match because SSL is not supported by this build";
+#endif
+		}
+		else if (token->string[4] == 'n')	/* "hostnossl" */
+		{
+			parsedline->conntype = ctHostNoSSL;
+		}
+		else
+		{
+			/* "host" */
+			parsedline->conntype = ctHost;
+		}
+	}							/* record type */
+	else
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("invalid connection type \"%s\"",
+						token->string),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = psprintf("invalid connection type \"%s\"", token->string);
+		return NULL;
+	}
+
+	/* Get the databases. */
+	field = lnext(field);
+	if (!field)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("end-of-line before database specification"),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "end-of-line before database specification";
+		return NULL;
+	}
+	parsedline->databases = NIL;
+	tokens = lfirst(field);
+	foreach(tokencell, tokens)
+	{
+		parsedline->databases = lappend(parsedline->databases,
+										copy_hba_token(lfirst(tokencell)));
+	}
+
+	/* Get the users. */
+	field = lnext(field);
+	if (!field)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("end-of-line before role specification"),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "end-of-line before role specification";
+		return NULL;
+	}
+	parsedline->users = NIL;
+	tokens = lfirst(field);
+	foreach(tokencell, tokens)
+	{
+		parsedline->users = lappend(parsedline->users,
+									copy_hba_token(lfirst(tokencell)));
+	}
+
+	if (parsedline->conntype != ctLocal)
+	{
+		/* Read the IP address field. (with or without CIDR netmask) */
+		field = lnext(field);
+		if (!field)
+		{
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("end-of-line before IP address specification"),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "end-of-line before IP address specification";
+			return NULL;
+		}
+		tokens = lfirst(field);
+		if (tokens->length > 1)
+		{
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("multiple values specified for host address"),
+					 errhint("Specify one address range per line."),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "multiple values specified for host address";
+			return NULL;
+		}
+		token = linitial(tokens);
+
+		if (token_is_keyword(token, "all"))
+		{
+			parsedline->ip_cmp_method = ipCmpAll;
+		}
+		else if (token_is_keyword(token, "samehost"))
+		{
+			/* Any IP on this host is allowed to connect */
+			parsedline->ip_cmp_method = ipCmpSameHost;
+		}
+		else if (token_is_keyword(token, "samenet"))
+		{
+			/* Any IP on the host's subnets is allowed to connect */
+			parsedline->ip_cmp_method = ipCmpSameNet;
+		}
+		else
+		{
+			/* IP and netmask are specified */
+			parsedline->ip_cmp_method = ipCmpMask;
+
+			/* need a modifiable copy of token */
+			str = pstrdup(token->string);
+
+			/* Check if it has a CIDR suffix and if so isolate it */
+			cidr_slash = strchr(str, '/');
+			if (cidr_slash)
+				*cidr_slash = '\0';
+
+			/* Get the IP address either way */
+			hints.ai_flags = AI_NUMERICHOST;
+			hints.ai_family = AF_UNSPEC;
+			hints.ai_socktype = 0;
+			hints.ai_protocol = 0;
+			hints.ai_addrlen = 0;
+			hints.ai_canonname = NULL;
+			hints.ai_addr = NULL;
+			hints.ai_next = NULL;
+
+			ret = getaddrinfo_all(str, NULL, &hints, &gai_result);
+			if (ret == 0 && gai_result)
+				memcpy(&parsedline->addr, gai_result->ai_addr,
+					   gai_result->ai_addrlen);
+			else if (ret == EAI_NONAME)
+				parsedline->hostname = str;
+			else
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("invalid IP address \"%s\": %s",
+								str, gai_strerror(ret)),
+						 errcontext("line %d of configuration file \"%s\"",
+									line_num, HbaFileName)));
+				*err_msg = psprintf("invalid IP address \"%s\": %s",
+									str, gai_strerror(ret));
+				if (gai_result)
+					freeaddrinfo_all(hints.ai_family, gai_result);
+				return NULL;
+			}
+
+			freeaddrinfo_all(hints.ai_family, gai_result);
+
+			/* Get the netmask */
+			if (cidr_slash)
+			{
+				if (parsedline->hostname)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("specifying both host name and CIDR mask is invalid: \"%s\"",
+									token->string),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = psprintf("specifying both host name and CIDR mask is invalid: \"%s\"",
+										token->string);
+					return NULL;
+				}
+
+				if (SockAddr_cidr_mask(&parsedline->mask, cidr_slash + 1,
+										  parsedline->addr.ss_family) < 0)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("invalid CIDR mask in address \"%s\"",
+									token->string),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = psprintf("invalid CIDR mask in address \"%s\"",
+										token->string);
+					return NULL;
+				}
+				pfree(str);
+			}
+			else if (!parsedline->hostname)
+			{
+				/* Read the mask field. */
+				pfree(str);
+				field = lnext(field);
+				if (!field)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("end-of-line before netmask specification"),
+							 errhint("Specify an address range in CIDR notation, or provide a separate netmask."),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = "end-of-line before netmask specification";
+					return NULL;
+				}
+				tokens = lfirst(field);
+				if (tokens->length > 1)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("multiple values specified for netmask"),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = "multiple values specified for netmask";
+					return NULL;
+				}
+				token = linitial(tokens);
+
+				ret = getaddrinfo_all(token->string, NULL,
+										 &hints, &gai_result);
+				if (ret || !gai_result)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("invalid IP mask \"%s\": %s",
+									token->string, gai_strerror(ret)),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = psprintf("invalid IP mask \"%s\": %s",
+										token->string, gai_strerror(ret));
+					if (gai_result)
+						freeaddrinfo_all(hints.ai_family, gai_result);
+					return NULL;
+				}
+
+				memcpy(&parsedline->mask, gai_result->ai_addr,
+					   gai_result->ai_addrlen);
+				freeaddrinfo_all(hints.ai_family, gai_result);
+
+				if (parsedline->addr.ss_family != parsedline->mask.ss_family)
+				{
+					ereport(elevel,
+							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+							 errmsg("IP address and mask do not match"),
+							 errcontext("line %d of configuration file \"%s\"",
+										line_num, HbaFileName)));
+					*err_msg = "IP address and mask do not match";
+					return NULL;
+				}
+			}
+		}
+	}							/* != ctLocal */
+
+	/* Get the authentication method */
+	field = lnext(field);
+	if (!field)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("end-of-line before authentication method"),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "end-of-line before authentication method";
+		return NULL;
+	}
+	tokens = lfirst(field);
+	if (tokens->length > 1)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("multiple values specified for authentication type"),
+				 errhint("Specify exactly one authentication type per line."),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "multiple values specified for authentication type";
+		return NULL;
+	}
+	token = linitial(tokens);
+
+	if (strcmp(token->string, "trust") == 0)
+		parsedline->auth_method = uaTrust;
+	else if (strcmp(token->string, "reject") == 0)
+		parsedline->auth_method = uaReject;
+	else if (strcmp(token->string, "md5") == 0)
+		parsedline->auth_method = uaMD5;
+#ifdef USE_PAM
+	else if (strcmp(token->string, "pam") == 0)
+		parsedline->auth_method = uaPAM;
+#endif
+	else
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("invalid authentication method \"%s\"",
+						token->string),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = psprintf("invalid authentication method \"%s\"",
+							token->string);
+		return NULL;
+	}
+	/* Parse remaining arguments */
+	while ((field = lnext(field)) != NULL)
+	{
+		tokens = lfirst(field);
+		foreach(tokencell, tokens)
+		{
+			char	   *val;
+
+			token = lfirst(tokencell);
+
+			str = pstrdup(token->string);
+			val = strchr(str, '=');
+			if (val == NULL)
+			{
+				/*
+				 * Got something that's not a name=value pair.
+				 */
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("authentication option not in name=value format: %s", token->string),
+						 errcontext("line %d of configuration file \"%s\"",
+									line_num, HbaFileName)));
+				*err_msg = psprintf("authentication option not in name=value format: %s",
+									token->string);
+				return NULL;
+			}
+
+			*val++ = '\0';		/* str now holds "name", val holds "value" */
+			if (!parse_hba_auth_opt(str, val, parsedline, elevel, err_msg))
+			/* parse_hba_auth_opt already logged the error message */
+				return NULL;
+			pfree(str);
+		}
+	}
+
+	return parsedline;
+}
+
+/*
+ * Parse one name-value pair as an authentication option into the given
+ * HbaLine.  Return true if we successfully parse the option, false if we
+ * encounter an error.  In the event of an error, also log a message at
+ * ereport level elevel, and store a message string into *err_msg.
+ */
+static bool
+parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
+				   int elevel, char **err_msg)
+{
+	int			line_num = hbaline->linenumber;
+	if (strcmp(name, "pamservice") == 0)
+	{
+#ifdef USE_PAM
+		if (hbaline->auth_method != uaPAM)
+		{
+			ereport(elevel,
+					(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "pamservice authentication option can only be configured for authentication method \"pam\"";
+		}
+		else
+			hbaline->pamservice = pstrdup(val);
+#else
+		ereport(elevel,
+				(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
+				 errhint("Compile with --with-pam to use PAM authentication."),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "pamservice authentication option cannot be used because PAM is not supported by this build";
+
+#endif
+	}
+	else if (strcmp(name, "pam_use_hostname") == 0)
+	{
+#ifdef USE_PAM
+		if (hbaline->auth_method != uaPAM)
+		{
+			ereport(elevel,
+					(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "pamservice authentication option can only be configured for authentication method \"pam\"";
+		}
+		else
+		{
+			if (strcmp(val, "1") == 0)
+				hbaline->pam_use_hostname = true;
+			else
+				hbaline->pam_use_hostname = false;
+
+		}
+#else
+		ereport(elevel,
+				(errmsg("pamservice authentication option can only be configured for authentication method \"pam\""),
+				 errhint("Compile with --with-pam to use PAM authentication."),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "pamservice authentication option cannot be used because PAM is not supported by this build";
+
+#endif
+	}
+	else
+	{
+		ereport(elevel,
+				(errmsg("unrecognized authentication option name: \"%s\"",
+						name),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = psprintf("unrecognized authentication option name: \"%s\"",
+							name);
+		return false;
+	}
+	return true;
 }
 
 /*
@@ -147,8 +764,9 @@ void ClientAuthentication(POOL_CONNECTION *frontend)
                             errhint("see pgpool log for details")));
 
 
-        switch (frontend->auth_method)
+        switch (frontend->pool_hba->auth_method)
         {
+			case uaImplicitReject:
             case uaReject:
             {
                 /*
@@ -333,8 +951,9 @@ static void auth_failed(POOL_CONNECTION *frontend)
 	messagelen = strlen(frontend->username) + 100;
 	errmessage = (char *)palloc(messagelen+1);
 
-	switch (frontend->auth_method)
+	switch (frontend->pool_hba->auth_method)
 	{
+		case uaImplicitReject:
 		case uaReject:
 			snprintf(errmessage, messagelen,
 					 "authentication with pgpool failed for user \"%s\": host rejected",
@@ -441,486 +1060,388 @@ static bool hba_getauthmethod(POOL_CONNECTION *frontend)
 
 
 /*
- *  Scan the (pre-parsed) hba file line by line, looking for a match
- *  to the port's connection request.
- */
-static bool check_hba(POOL_CONNECTION *frontend)
+*	Scan the pre-parsed hba file, looking for a match to the port's connection
+*	request.
+*/
+static bool
+check_hba(POOL_CONNECTION *frontend)
 {
-	bool found_entry = false;
-	bool error = false;
-	ListCell *line;
-	ListCell *line_num;
+	ListCell   *line;
+	HbaLine    *hba;
+	MemoryContext oldcxt;
 
-	forboth(line, hba_lines, line_num, hba_line_nums)
-	{
-		parse_hba(lfirst(line), lfirst_int(line_num),
-				  frontend, &found_entry, &error);
-		if (found_entry || error)
-			break;
-	}
+	if (parsed_hba_lines == NULL)
+		return false;
 
-	if (!error)
+	foreach(line, parsed_hba_lines)
 	{
-		/* If no matching entry was found, synthesize 'reject' entry. */
-		if (!found_entry)
-			frontend->auth_method = uaReject;
+		hba = (HbaLine *) lfirst(line);
+
+		/* Check connection type */
+		if (hba->conntype == ctLocal)
+		{
+			if (!IS_AF_UNIX(frontend->raddr.addr.ss_family))
+				continue;
+		}
+		else
+		{
+			if (IS_AF_UNIX(frontend->raddr.addr.ss_family))
+				continue;
+
+			/* Check SSL state */
+#ifdef USE_SSL
+			if (frontend->ssl)
+			{
+				/* Connection is SSL, match both "host" and "hostssl" */
+				if (hba->conntype == ctHostNoSSL)
+					continue;
+			}
+			else
+#endif
+			{
+				/* Connection is not SSL, match both "host" and "hostnossl" */
+				if (hba->conntype == ctHostSSL)
+					continue;
+			}
+
+			/* Check IP address */
+			switch (hba->ip_cmp_method)
+			{
+				case ipCmpMask:
+					if (hba->hostname)
+					{
+						if (!check_hostname(frontend,
+											hba->hostname))
+							continue;
+					}
+					else
+					{
+						if (!check_ip(&frontend->raddr,
+									  (struct sockaddr *) &hba->addr,
+									  (struct sockaddr *) &hba->mask))
+							continue;
+					}
+					break;
+				case ipCmpAll:
+					break;
+				case ipCmpSameHost:
+				case ipCmpSameNet:
+					if (!check_same_host_or_net(&frontend->raddr,
+												hba->ip_cmp_method))
+						continue;
+					break;
+				default:
+					/* shouldn't get here, but deem it no-match if so */
+					continue;
+			}
+		}
+
+		/* Check database and role */
+		if (!check_db(frontend->database, frontend->username, hba->databases))
+			continue;
+
+		if (!check_user(frontend->username, hba->users))
+			continue;
+
+		/* Found a record that matched! */
+		frontend->pool_hba = hba;
 		return true;
 	}
-	else
-		return false;
+
+	/* If no matching entry was found, then implicitly reject. */
+	oldcxt = MemoryContextSwitchTo(ProcessLoopContext);
+	hba = palloc0(sizeof(HbaLine));
+	MemoryContextSwitchTo(oldcxt);
+	hba->auth_method = uaImplicitReject;
+	frontend->pool_hba = hba;
+	return true;
 }
 
+static bool
+ipv4eq(struct sockaddr_in *a, struct sockaddr_in *b)
+{
+	return (a->sin_addr.s_addr == b->sin_addr.s_addr);
+}
 
-/*
- *  Process one line from the hba config file.
- *
- *  See if it applies to a connection from a frontend with IP address
- *  frontend->raddr to a database named frontend->database.  If so, return
- *  *found_p true and fill in the auth arguments into the appropriate
- *  frontend fields. If not, leave *found_p as it was.  If the record has
- *  a syntax error, return *error_p true, after issuing a message to the
- *  log.  If no error, leave *error_p as it was.
- */
-static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend,
-					  bool *found_p, bool *error_p)
+
+static bool
+ipv6eq(struct sockaddr_in6 *a, struct sockaddr_in6 *b)
 {
-	char *token;
-	char *db, *db_tmp;
-	char *user, *user_tmp;
-	struct addrinfo *gai_result;
-	struct addrinfo hints;
-	int ret;
-	struct sockaddr_storage addr;
-	struct sockaddr_storage mask;
-	char *cidr_slash;
-	ListCell *line_item;
+	int			i;
 
-	line_item = list_head(line);
-	/* Check the record type. */
-	token = lfirst(line_item);
-	if (strcmp(token, "local") == 0)
-	{
-		/* Get the database. */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		db = lfirst(line_item);
-
-		/* Get the user. */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		user = lfirst(line_item);
-
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-
-		/* Read the rest of the line. */
-		parse_hba_auth(&line_item, &frontend->auth_method,
-					   &frontend->auth_arg, error_p);
-		if (*error_p)
-			goto hba_syntax;
-
-        /* Disallow auth methods that always need TCP/IP sockets to work */
-		/*
-		if (frontend->auth_method == uaKrb4 ||
-			frontend->auth_method == uaKrb5)
-			goto hba_syntax;
-		*/
-
-		/* Does not match if connection isn't AF_UNIX */
-		if (!IS_AF_UNIX(frontend->raddr.addr.ss_family))
-			return;
-	}
-	else if (strcmp(token, "host") == 0
-			 || strcmp(token, "hostssl") == 0
-			 || strcmp(token, "hostnossl") == 0)
-	{
-		if (token[4] == 's')    /* "hostssl" */
-		{
-#ifdef USE_SSL
-			/* Record does not match if we are not on an SSL connection */
-			if (!frontend->ssl)
-				return;
+	for (i = 0; i < 16; i++)
+		if (a->sin6_addr.s6_addr[i] != b->sin6_addr.s6_addr[i])
+			return false;
 
-			/* Placeholder to require specific SSL level, perhaps? */
-			/* Or a client certificate */
+	return true;
+}
 
-			/* Since we were on SSL, proceed as with normal 'host' mode */
-#else
-			/* We don't accept this keyword at all if no SSL support */
-			goto hba_syntax;
-#endif
-		}
-#ifdef USE_SSL
-		else if (token[4] == 'n')       /* "hostnossl" */
-		{
-			/* Record does not match if we are on an SSL connection */
-			if (frontend->ssl)
-				return;
-		}
-#endif
 
-        /* Get the database. */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		db = lfirst(line_item);
+/*
+ * Check whether host name matches pattern.
+ */
+static bool
+hostname_match(const char *pattern, const char *actual_hostname)
+{
+	if (pattern[0] == '.')		/* suffix match */
+	{
+		size_t		plen = strlen(pattern);
+		size_t		hlen = strlen(actual_hostname);
+
+		if (hlen < plen)
+			return false;
 
-		/* Get the user. */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		user = lfirst(line_item);
+		return (strcasecmp(pattern, actual_hostname + (hlen - plen)) == 0);
+	}
+	else
+		return (strcasecmp(pattern, actual_hostname) == 0);
+}
 
-		/* Read the IP address field. (with or without CIDR netmask) */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		token = lfirst(line_item);
-
-		/* Check if it has a CIDR suffix and if so isolate it */
-		cidr_slash = strchr(token, '/');
-		if (cidr_slash)
-			*cidr_slash = '\0';
-
-		/* Get the IP address either way */
-		hints.ai_flags = AI_NUMERICHOST;
-		hints.ai_family = PF_UNSPEC;
-		hints.ai_socktype = 0;
-		hints.ai_protocol = 0;
-		hints.ai_addrlen = 0;
-		hints.ai_canonname = NULL;
-		hints.ai_addr = NULL;
-		hints.ai_next = NULL;
-
-		ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
-		if (ret || !gai_result)
-		{
-			ereport(LOG,
-				(errmsg("parsing pool hba configuration file"),
-				 errdetail("invalid IP address \"%s\" in file \"%s\" line %d: %s",
-						   token, hbaFileName, line_num, gai_strerror(ret))));
-			if (cidr_slash)
-				*cidr_slash = '/';
-            if (gai_result)
-				freeaddrinfo_all(hints.ai_family, gai_result);
-			goto hba_other_error;
-		}
+/*
+ * Check to see if a connecting IP matches a given host name.
+ */
+static bool
+check_hostname(POOL_CONNECTION *frontend, const char *hostname)
+{
+	struct addrinfo *gai_result,
+	*gai;
+	int			ret;
+	bool		found;
 
-		if (cidr_slash)
-			*cidr_slash = '/';
+	/* Quick out if remote host name already known bad */
+	if (frontend->remote_hostname_resolv < 0)
+		return false;
 
-		memcpy(&addr, gai_result->ai_addr, gai_result->ai_addrlen);
-		freeaddrinfo_all(hints.ai_family, gai_result);
+	/* Lookup remote host name if not already done */
+	if (!frontend->remote_hostname)
+	{
+		char		remote_hostname[NI_MAXHOST];
 
-		/* Get the netmask */
-		if (cidr_slash)
+		ret = getnameinfo_all(&frontend->raddr.addr, frontend->raddr.salen,
+								 remote_hostname, sizeof(remote_hostname),
+								 NULL, 0,
+								 NI_NAMEREQD);
+		if (ret != 0)
 		{
-			if (SockAddr_cidr_mask(&mask, cidr_slash + 1, addr.ss_family) < 0)
-				goto hba_syntax;
+			/* remember failure; don't complain in the Pgpool-II log yet */
+			frontend->remote_hostname_resolv = -2;
+			//frontend->remote_hostname_errcode = ret;
+			return false;
 		}
-		else
-		{
-			/* Read the mask field. */
-			line_item = lnext(line_item);
-			if (!line_item)
-				goto hba_syntax;
-			token = lfirst(line_item);
-
-			ret = getaddrinfo_all(token, NULL, &hints, &gai_result);
-			if (ret || !gai_result)
-			{
-				ereport(LOG,
-					(errmsg("parsing pool hba configuration file"),
-						 errdetail("invalid IP mask \"%s\" in file \"%s\" line %d: %s",
-								   token, hbaFileName, line_num, gai_strerror(ret))));
 
-				if (gai_result)
-					freeaddrinfo_all(hints.ai_family, gai_result);
-				goto hba_other_error;
-			}
+		frontend->remote_hostname = pstrdup(remote_hostname);
+	}
 
-			memcpy(&mask, gai_result->ai_addr, gai_result->ai_addrlen);
-			freeaddrinfo_all(hints.ai_family, gai_result);
+	/* Now see if remote host name matches this pg_hba line */
+	if (!hostname_match(hostname, frontend->remote_hostname))
+		return false;
 
-			if (addr.ss_family != mask.ss_family)
-			{
-				ereport(LOG,
-					(errmsg("parsing pool hba configuration file"),
-						errdetail("IP address and mask do not match in file \"%s\" line %d",
-							   hbaFileName, line_num)));
-				goto hba_other_error;
-			}
-		}
+	/* If we already verified the forward lookup, we're done */
+	if (frontend->remote_hostname_resolv == +1)
+		return true;
+
+	/* Lookup IP from host name and check against original IP */
+	ret = getaddrinfo(frontend->remote_hostname, NULL, NULL, &gai_result);
+	if (ret != 0)
+	{
+		/* remember failure; don't complain in the postmaster log yet */
+		frontend->remote_hostname_resolv = -2;
+		//frontend->remote_hostname_errcode = ret;
+		return false;
+	}
 
-		if (addr.ss_family != frontend->raddr.addr.ss_family)
+	found = false;
+	for (gai = gai_result; gai; gai = gai->ai_next)
+	{
+		if (gai->ai_addr->sa_family == frontend->raddr.addr.ss_family)
 		{
-			/*
-			 * Wrong address family.  We allow only one case: if the file
-			 * has IPv4 and the port is IPv6, promote the file address to
-			 * IPv6 and try to match that way.
-			 */
-			if (addr.ss_family == AF_INET && frontend->raddr.addr.ss_family == AF_INET6)
+			if (gai->ai_addr->sa_family == AF_INET)
 			{
-				promote_v4_to_v6_addr(&addr);
-				promote_v4_to_v6_mask(&mask);
+				if (ipv4eq((struct sockaddr_in *) gai->ai_addr,
+						   (struct sockaddr_in *) &frontend->raddr.addr))
+				{
+					found = true;
+					break;
+				}
 			}
-			else
+			else if (gai->ai_addr->sa_family == AF_INET6)
 			{
-				/* Line doesn't match client port, so ignore it. */
-				return;
+				if (ipv6eq((struct sockaddr_in6 *) gai->ai_addr,
+						   (struct sockaddr_in6 *) &frontend->raddr.addr))
+				{
+					found = true;
+					break;
+				}
 			}
 		}
-
-		/* Ignore line if client port is not in the matching addr range. */
-		if (!rangeSockAddr(&frontend->raddr.addr, &addr, &mask))
-			return;
-
-		/* Read the rest of the line. */
-		line_item = lnext(line_item);
-		if (!line_item)
-			goto hba_syntax;
-		parse_hba_auth(&line_item, &frontend->auth_method,
-					   &frontend->auth_arg, error_p);
-		if (*error_p)
-			goto hba_syntax;
 	}
-	else
-		goto hba_syntax;
 
-	/* Does the entry match database and user? */
-	/*
-	 * duplicate db and username since strtok() in check_db() and check_user()
-	 * will override '\001' with '\0'.
-	 */
-	db_tmp = pstrdup(db);
-	if (!check_db(frontend->database, frontend->username, db_tmp))
-	{
-		pfree(db_tmp);
-		return;
-	}
-	pfree(db_tmp);
+	if (gai_result)
+		freeaddrinfo(gai_result);
 
-	user_tmp = pstrdup(user);
-	if (!check_user(frontend->username, user_tmp))
-	{
-		pfree(user_tmp);
-        return;
-	}
-	pfree(user_tmp);
+	if (!found)
+		ereport(DEBUG2,
+				(errmsg("pool_hba.conf host name \"%s\" rejected because address resolution did not return a match with IP address of client",
+			 hostname)));
 
-	/* Success */
-	*found_p = true;
-	return;
+	frontend->remote_hostname_resolv = found ? +1 : -1;
 
- hba_syntax:
-	if (line_item)
-		ereport(LOG,
-				(errmsg("parsing pool hba configuration file"),
-					errdetail("invalid entry in file \"%s\" at line %d, token \"%s\"",
-						   hbaFileName, line_num, (char *) lfirst(line_item))));
-	else
-		ereport(LOG,
-				(errmsg("parsing pool hba configuration file"),
-					errdetail("missing field in file \"%s\" at end of line %d",
-						   hbaFileName, line_num)));
-	/* Come here if suitable message already logged */
- hba_other_error:
-	*error_p = true;
+	return found;
 }
-
-
 /*
- *  Scan the rest of a host record (after the mask field)
- *  and return the interpretation of it as *userauth_p, *auth_arg_p, and
- *  *error_p.  *line_item points to the next token of the line, and is
- *  advanced over successfully-read tokens.
+ * pg_foreach_ifaddr callback: does client addr match this machine interface?
  */
-static void parse_hba_auth(ListCell **line_item, UserAuth *userauth_p,
-						   char **auth_arg_p, bool *error_p)
+static void
+check_network_callback(struct sockaddr *addr, struct sockaddr *netmask,
+					   void *cb_data)
 {
-	char *token;
+	check_network_data *cn = (check_network_data *) cb_data;
+	struct sockaddr_storage mask;
 
-	*auth_arg_p = NULL;
+	/* Already found a match? */
+	if (cn->result)
+		return;
 
-	if (!*line_item)
+	if (cn->method == ipCmpSameHost)
 	{
-		*error_p = true;
-		return;
+		/* Make an all-ones netmask of appropriate length for family */
+		SockAddr_cidr_mask(&mask, NULL, addr->sa_family);
+		cn->result = check_ip(cn->raddr, addr, (struct sockaddr *) &mask);
 	}
-
-	token = lfirst(*line_item);
-	if (strcmp(token, "trust") == 0)
-		*userauth_p = uaTrust;
-	/*
-	else if (strcmp(token, "ident") == 0)
-		*userauth_p = uaIdent;
-	else if (strcmp(token, "password") == 0)
-		*userauth_p = uaPassword;
-	else if (strcmp(token, "krb4") == 0)
-		*userauth_p = uaKrb4;
-	else if (strcmp(token, "krb5") == 0)
-		*userauth_p = uaKrb5;
-	*/
-	else if (strcmp(token, "reject") == 0)
-		*userauth_p = uaReject;
-	else if (strcmp(token, "md5") == 0)
-		*userauth_p = uaMD5;
-	/*
-	else if (strcmp(token, "crypt") == 0)
-		*userauth_p = uaCrypt;
-	*/
-#ifdef USE_PAM
-	else if (strcmp(token, "pam") == 0)
-		*userauth_p = uaPAM;
-#endif /* USE_PAM */
 	else
 	{
-		*error_p = true;
-		return;
+		/* Use the netmask of the interface itself */
+		cn->result = check_ip(cn->raddr, addr, netmask);
 	}
-	*line_item = lnext(*line_item);
+}
+
+/*
+ * Use pg_foreach_ifaddr to check a samehost or samenet match
+ */
+static bool
+check_same_host_or_net(SockAddr *raddr, IPCompareMethod method)
+{
+	check_network_data cn;
+
+	cn.method = method;
+	cn.raddr = raddr;
+	cn.result = false;
 
-	/* Get the authentication argument token, if any */
-	if (*line_item)
+	errno = 0;
+	if (pg_foreach_ifaddr(check_network_callback, &cn) < 0)
 	{
-		token = lfirst(*line_item);
-        *auth_arg_p = pstrdup(token);
-		*line_item = lnext(*line_item);
-		/* If there is more on the line, it is an error */
-		if (*line_item)
-			*error_p = true;
+		elog(LOG, "error enumerating network interfaces: %m");
+		return false;
 	}
+
+	return cn.result;
+}
+/*
+ * Check to see if a connecting IP matches the given address and netmask.
+ */
+static bool
+check_ip(SockAddr *raddr, struct sockaddr *addr, struct sockaddr *mask)
+{
+	if (raddr->addr.ss_family == addr->sa_family &&
+		rangeSockAddr(&raddr->addr,
+						  (struct sockaddr_storage *) addr,
+						  (struct sockaddr_storage *) mask))
+		return true;
+	return false;
 }
 
 
 /*
  * Check comma user list for a specific user, handle group names.
  */
-static bool check_user(char *user, char *param_str)
+static bool check_user(char *user, List *tokens)
 {
-	char *tok;
+	ListCell   *cell;
+	HbaToken   *tok;
 
-	for (tok = strtok(param_str, MULTI_VALUE_SEP);
-		 tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
+	foreach(cell, tokens)
 	{
-		if (tok[0] == '+')
+		tok = lfirst(cell);
+		if (!tok->quoted && tok->string[0] == '+')
 		{
 			/*
 			 * pgpool cannot accept groups. commented lines below are the
 			 * original code.
 			 */
 			ereport(LOG,
-				(errmsg("group token \"+\" is not supported in pgpool")));
+					(errmsg("group token \"+\" is not supported by Pgpool-II")));
 			return false;
-/* 			if (check_group(tok + 1, user)) */
-/* 				return true; */
 		}
-		else if (strcmp(tok, user) == 0 || strcmp(tok, "all\n") == 0)
+		else if (token_matches(tok, user) ||
+				 token_is_keyword(tok, "all"))
 			return true;
 	}
-
 	return false;
+
 }
 
 
+
 /*
  * Check to see if db/user combination matches param string.
  */
-static bool check_db(char *dbname, char *user, char *param_str)
+
+static bool
+check_db(const char *dbname, const char *user, List *tokens)
 {
-	char *tok;
+	ListCell   *cell;
+	HbaToken   *tok;
 
-	for (tok = strtok(param_str, MULTI_VALUE_SEP);
-		 tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
+	foreach(cell, tokens)
 	{
-		if (strcmp(tok, "all\n") == 0)
+		tok = lfirst(cell);
+		if (token_is_keyword(tok, "all"))
 			return true;
-		else if (strcmp(tok, "sameuser\n") == 0)
+		else if (token_is_keyword(tok, "sameuser"))
 		{
 			if (strcmp(dbname, user) == 0)
 				return true;
 		}
-		else if (strcmp(tok, "samegroup\n") == 0)
+		else if (token_is_keyword(tok, "samegroup") ||
+				 token_is_keyword(tok, "samerole"))
 		{
-			/*
-			 * pgpool cannot accept groups. commented lines below are the
-			 * original code.
-			 */
 			ereport(LOG,
-				(errmsg("group token \"samegroup\" is not supported in pgpool")));
+					(errmsg("group tokens \"samegroup\" and \"samerole\" are not supported by Pgpool-II")));
 			return false;
-/* 			if (check_group(dbname, user)) */
-/* 				return true; */
 		}
-		else if (strcmp(tok, dbname) == 0)
+		else if (token_matches(tok, dbname))
 			return true;
 	}
-
 	return false;
 }
 
-
 /*
- * tokenize the given file, storing the resulting data into two lists:
- * a list of sublists, each sublist containing the tokens in a line of
- * the file, and a list of line numbers.
+ * tokenize_inc_file
+ *		Expand a file included from another file into an hba "field"
  *
- * filename must be the absolute path to the target file.
+ * Opens and tokenises a file included from another HBA config file with @,
+ * and returns all values found therein as a flat list of HbaTokens.  If a
+ * @-token is found, recursively expand it.  The newly read tokens are
+ * appended to "tokens" (so that foo,bar, at baz does what you expect).
+ * All new tokens are allocated in caller's memory context.
+ *
+ * In event of an error, log a message at ereport level elevel, and also
+ * set *err_msg to a string describing the error.  Note that the result
+ * may be non-NIL anyway, so *err_msg must be tested to determine whether
+ * there was an error.
  */
-static void tokenize_file(const char *filename, FILE *file,
-						  List **lines, List **line_nums)
-{
-	List *current_line = NIL;
-	int line_number = 1;
-	char *buf;
-
-	*lines = *line_nums = NIL;
-
-	while (!feof(file))
-	{
-		buf = next_token_expand(filename, file);
-
-		/* add token to list, unless we are at EOL or comment start */
-		if (buf[0])
-		{
-			if (current_line == NIL)
-			{
-				/* make a new line List, record its line number */
-				current_line = lappend(current_line, buf);
-				*lines = lappend(*lines, current_line);
-				*line_nums = lappend_int(*line_nums, line_number);
-			}
-			else
-			{
-				/* append token to current line's list */
-				current_line = lappend(current_line, buf);
-			}
-		}
-		else
-		{
-			/* we are at real or logical EOL, so force a new line List */
-			current_line = NIL;
-			/* Advance line number whenever we reach EOL */
-			line_number++;
-			/* Don't forget to free the next_token_expand result */
-			pfree(buf);
-		}
-	}
-}
-
-
-static char * tokenize_inc_file(const char *outer_filename,
-								const char *inc_filename)
+static List *
+tokenize_inc_file(List *tokens,
+				  const char *outer_filename,
+				  const char *inc_filename,
+				  int elevel,
+				  char **err_msg)
 {
-	char *inc_fullname;
-	FILE *inc_file;
-	List *inc_lines;
-	List *inc_line_nums;
-	ListCell *line;
-	char *comma_str;
+	char	   *inc_fullname;
+	FILE	   *inc_file;
+	List	   *inc_lines;
+	ListCell   *inc_line;
+	MemoryContext linecxt;
 
 	if (is_absolute_path(inc_filename))
 	{
@@ -930,8 +1451,8 @@ static char * tokenize_inc_file(const char *outer_filename,
 	else
 	{
 		/* relative path is relative to dir of calling file */
-		inc_fullname = (char *)palloc(strlen(outer_filename) + 1 +
-									  strlen(inc_filename) + 1);
+		inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
+									   strlen(inc_filename) + 1);
 		strcpy(inc_fullname, outer_filename);
 		get_parent_directory(inc_fullname);
 		join_path_components(inc_fullname, inc_fullname, inc_filename);
@@ -941,61 +1462,54 @@ static char * tokenize_inc_file(const char *outer_filename,
 	inc_file = fopen(inc_fullname, "r");
 	if (inc_file == NULL)
 	{
-		ereport(WARNING,
-			(errmsg("failed to open secondary authentication file:\"%s\" as \"%s\"", inc_fullname,inc_fullname),
-				 errdetail("fopen failed with error: \"%s\"",strerror(errno))));
+		int			save_errno = errno;
 
+		ereport(elevel,
+				(errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
+						inc_filename, inc_fullname)));
+		*err_msg = psprintf("could not open secondary authentication file \"@%s\" as \"%s\": %s",
+							inc_filename, inc_fullname, strerror(save_errno));
 		pfree(inc_fullname);
-
-		/* return single space, it matches nothing */
-		return pstrdup(" ");
+		return tokens;
 	}
 
-    /* There is possible recursion here if the file contains @ */
-	tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums);
-
-	/*FreeFile(inc_file);*/
+	/* There is possible recursion here if the file contains @ */
+	linecxt = tokenize_file(inc_fullname, inc_file, &inc_lines, elevel);
 	fclose(inc_file);
-	pfree(inc_fullname);
 
-	/* Create comma-separated string from List */
-	comma_str = pstrdup("");
-	foreach(line, inc_lines)
+	/* Copy all tokens found in the file and append to the tokens list */
+	foreach(inc_line, inc_lines)
 	{
-		List *token_list = (List *) lfirst(line);
-		ListCell *token;
+		TokenizedLine *tok_line = (TokenizedLine *) lfirst(inc_line);
+		ListCell   *inc_field;
 
-		foreach(token, token_list)
+		/* If any line has an error, propagate that up to caller */
+		if (tok_line->err_msg)
 		{
-			int oldlen = strlen(comma_str);
-			int needed;
-
-			needed = oldlen + strlen(lfirst(token)) + 1;
-			if (oldlen > 0)
-				needed++;
-			comma_str = repalloc(comma_str, needed);
-			if (oldlen > 0)
-				strcat(comma_str, MULTI_VALUE_SEP);
-			strcat(comma_str, lfirst(token));
+			*err_msg = pstrdup(tok_line->err_msg);
+			break;
 		}
-	}
 
-	free_lines(&inc_lines, &inc_line_nums);
+		foreach(inc_field, tok_line->fields)
+		{
+			List	   *inc_tokens = lfirst(inc_field);
+			ListCell   *inc_token;
 
-	/* if file is empty, return single space rather than empty string */
-	if (strlen(comma_str) == 0)
-	{
-		char *returnVal;
+			foreach(inc_token, inc_tokens)
+			{
+				HbaToken   *token = lfirst(inc_token);
 
-		pfree(comma_str);
-		returnVal = pstrdup(" ");
-		return returnVal;
+				tokens = lappend(tokens, copy_hba_token(token));
+			}
+		}
 	}
 
-	return comma_str;
+	MemoryContextDelete(linecxt);
+	return tokens;
 }
 
 
+
 /*
  * isblank() exists in the ISO C99 spec, but it's not very portable yet,
  * so provide our own version.
@@ -1005,132 +1519,239 @@ static bool pg_isblank(const char c)
 	return c == ' ' || c == '\t' || c == '\r';
 }
 
-
 /*
- *	 Tokenize file and handle file inclusion and comma lists. We have
- *	 to  break	apart  the	commas	to	expand	any  file names then
- *	 reconstruct with commas.
+ * Tokenize the given file.
+ *
+ * The output is a list of TokenizedLine structs; see struct definition above.
+ *
+ * filename: the absolute path to the target file
+ * file: the already-opened target file
+ * tok_lines: receives output list
+ * elevel: message logging level
+ *
+ * Errors are reported by logging messages at ereport level elevel and by
+ * adding TokenizedLine structs containing non-null err_msg fields to the
+ * output list.
  *
- * The result is always a malloc'd string.  If it's zero-length then
- * we have reached EOL.
+ * Return value is a memory context which contains all memory allocated by
+ * this function (it's a child of caller's context).
  */
-static char * next_token_expand(const char *filename, FILE *file)
+static MemoryContext
+tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
 {
-	char buf[MAX_TOKEN];
-	char *comma_str;
-	bool trailing_comma;
-	char *incbuf;
-	int needed;
+	int			line_number = 1;
+	MemoryContext linecxt;
+	MemoryContext oldcxt;
 
-	comma_str = pstrdup("");
+	linecxt = AllocSetContextCreate(CurrentMemoryContext,
+									"tokenize_file",
+									ALLOCSET_SMALL_SIZES);
+	oldcxt = MemoryContextSwitchTo(linecxt);
 
-	do
+	*tok_lines = NIL;
+
+	while (!feof(file) && !ferror(file))
 	{
-		next_token(file, buf, sizeof(buf));
-		if (!buf[0])
-			break;
+		char		rawline[MAX_LINE];
+		char	   *lineptr;
+		List	   *current_line = NIL;
+		char	   *err_msg = NULL;
 
-		if (buf[strlen(buf) - 1] == ',')
+		if (!fgets(rawline, sizeof(rawline), file))
 		{
-			trailing_comma = true;
-			buf[strlen(buf) - 1] = '\0';
+			int			save_errno = errno;
+
+			if (!ferror(file))
+				break;			/* normal EOF */
+			/* I/O error! */
+			ereport(elevel,
+					(errmsg("could not read file \"%s\": %m", filename)));
+			err_msg = psprintf("could not read file \"%s\": %s",
+							   filename, strerror(save_errno));
+			rawline[0] = '\0';
+		}
+		if (strlen(rawline) == MAX_LINE - 1)
+		{
+			/* Line too long! */
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("authentication file line too long"),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_number, filename)));
+			err_msg = "authentication file line too long";
 		}
-		else
-			trailing_comma = false;
 
-		/* Is this referencing a file? */
-		if (buf[0] == '@')
-			incbuf = tokenize_inc_file(filename, buf + 1);
-		else
+		/* Strip trailing linebreak from rawline */
+		lineptr = rawline + strlen(rawline) - 1;
+		while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r'))
+			*lineptr-- = '\0';
+
+		/* Parse fields */
+		lineptr = rawline;
+		while (*lineptr && err_msg == NULL)
+		{
+			List	   *current_field;
+
+			current_field = next_field_expand(filename, &lineptr,
+											  elevel, &err_msg);
+			/* add field to line, unless we are at EOL or comment start */
+			if (current_field != NIL)
+				current_line = lappend(current_line, current_field);
+		}
+
+		/* Reached EOL; emit line to TokenizedLine list unless it's boring */
+		if (current_line != NIL || err_msg != NULL)
 		{
-			incbuf = pstrdup(buf);
+			TokenizedLine *tok_line;
+
+			tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine));
+			tok_line->fields = current_line;
+			tok_line->line_num = line_number;
+			tok_line->raw_line = pstrdup(rawline);
+			tok_line->err_msg = err_msg;
+			*tok_lines = lappend(*tok_lines, tok_line);
 		}
 
-		needed = strlen(comma_str) + strlen(incbuf) + 1;
-		if (trailing_comma)
-			needed++;
-		comma_str = repalloc(comma_str, needed);
-		strcat(comma_str, incbuf);
-		if (trailing_comma)
-			strcat(comma_str, MULTI_VALUE_SEP);
-		pfree(incbuf);
-	} while (trailing_comma);
-
-	return comma_str;
+		line_number++;
+	}
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return linecxt;
 }
 
 
 /*
- * Grab one token out of fp. Tokens are strings of non-blank
- * characters bounded by blank characters, beginning of line, and
- * end of line. Blank means space or tab. Return the token as
- * *buf. Leave file positioned at the character immediately after the
- * token or EOF, whichever comes first. If no more tokens on line,
- * return empty string as *buf and position the file to the beginning
- * of the next line or EOF, whichever comes first. Allow spaces in
- * quoted strings. Terminate on unquoted commas. Handle
- * comments. Treat unquoted keywords that might be user names or
- * database names specially, by appending a newline to them.
+ * Tokenize one HBA field from a line, handling file inclusion and comma lists.
+ *
+ * filename: current file's pathname (needed to resolve relative pathnames)
+ * *lineptr: current line pointer, which will be advanced past field
+ *
+ * In event of an error, log a message at ereport level elevel, and also
+ * set *err_msg to a string describing the error.  Note that the result
+ * may be non-NIL anyway, so *err_msg must be tested to determine whether
+ * there was an error.
+ *
+ * The result is a List of HbaToken structs, one for each token in the field,
+ * or NIL if we reached EOL.
  */
-static void next_token(FILE *fp, char *buf, int bufsz)
+static List *
+next_field_expand(const char *filename, char **lineptr,
+				  int elevel, char **err_msg)
 {
-	int c;
-	char *start_buf = buf;
-	char *end_buf = buf + (bufsz - 2);
-	bool in_quote = false;
-	bool was_quote = false;
-	bool saw_quote = false;
+	char		buf[MAX_TOKEN];
+	bool		trailing_comma;
+	bool		initial_quote;
+	List	   *tokens = NIL;
 
-	/*Assert(end_buf > start_buf);*/
+	do
+	{
+		if (!next_token(lineptr, buf, sizeof(buf),
+						&initial_quote, &trailing_comma,
+						elevel, err_msg))
+			break;
 
-	/* Move over initial whitespace and commas */
-	while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))
-		;
+		/* Is this referencing a file? */
+		if (!initial_quote && buf[0] == '@' && buf[1] != '\0')
+			tokens = tokenize_inc_file(tokens, filename, buf + 1,
+									   elevel, err_msg);
+		else
+			tokens = lappend(tokens, make_hba_token(buf, initial_quote));
+	} while (trailing_comma && (*err_msg == NULL));
 
-	if (c == EOF || c == '\n')
-	{
-		*buf = '\0';
-		return;
-	}
+	return tokens;
+}
+
+/*
+ * Grab one token out of the string pointed to by *lineptr.
+ *
+ * Tokens are strings of non-blank
+ * characters bounded by blank characters, commas, beginning of line, and
+ * end of line. Blank means space or tab. Tokens can be delimited by
+ * double quotes (this allows the inclusion of blanks, but not newlines).
+ * Comments (started by an unquoted '#') are skipped.
+ *
+ * The token, if any, is returned at *buf (a buffer of size bufsz), and
+ * *lineptr is advanced past the token.
+ *
+ * Also, we set *initial_quote to indicate whether there was quoting before
+ * the first character.  (We use that to prevent "@x" from being treated
+ * as a file inclusion request.  Note that @"x" should be so treated;
+ * we want to allow that to support embedded spaces in file paths.)
+ *
+ * We set *terminating_comma to indicate whether the token is terminated by a
+ * comma (which is not returned).
+ *
+ * In event of an error, log a message at ereport level elevel, and also
+ * set *err_msg to a string describing the error.  Currently the only
+ * possible error is token too long for buf.
+ *
+ * If successful: store null-terminated token at *buf and return TRUE.
+ * If no more tokens on line: set *buf = '\0' and return FALSE.
+ * If error: fill buf with truncated or misformatted token and return FALSE.
+ */
+static bool
+next_token(char **lineptr, char *buf, int bufsz,
+		   bool *initial_quote, bool *terminating_comma,
+		   int elevel, char **err_msg)
+{
+	int			c;
+	char	   *start_buf = buf;
+	char	   *end_buf = buf + (bufsz - 1);
+	bool		in_quote = false;
+	bool		was_quote = false;
+	bool		saw_quote = false;
+
+	Assert(end_buf > start_buf);
+
+	*initial_quote = false;
+	*terminating_comma = false;
+
+	/* Move over any whitespace and commas preceding the next token */
+	while ((c = (*(*lineptr)++)) != '\0' && (pg_isblank(c) || c == ','))
+		;
 
 	/*
-	 * Build a token in buf of next characters up to EOF, EOL, unquoted
-	 * comma, or unquoted whitespace.
+	 * Build a token in buf of next characters up to EOL, unquoted comma, or
+	 * unquoted whitespace.
 	 */
-	while (c != EOF && c != '\n' &&
-		   (!pg_isblank(c) || in_quote == true))
+	while (c != '\0' &&
+		   (!pg_isblank(c) || in_quote))
 	{
 		/* skip comments to EOL */
 		if (c == '#' && !in_quote)
 		{
-			while ((c = getc(fp)) != EOF && c != '\n')
+			while ((c = (*(*lineptr)++)) != '\0')
 				;
-			/* If only comment, consume EOL too; return EOL */
-			if (c != EOF && buf == start_buf)
-				c = getc(fp);
 			break;
 		}
 
 		if (buf >= end_buf)
 		{
 			*buf = '\0';
-			ereport(LOG,
-				(errmsg("parsing pool hba configuration file"),
-					 errdetail("authentication file token too long, skipping: \"%s\"", start_buf)));
-
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("authentication file token too long, skipping: \"%s\"",
+							start_buf)));
+			*err_msg = "authentication file token too long";
 			/* Discard remainder of line */
-			while ((c = getc(fp)) != EOF && c != '\n')
+			while ((c = (*(*lineptr)++)) != '\0')
 				;
+			/* Un-eat the '\0', in case we're called again */
+			(*lineptr)--;
+			return false;
+		}
+
+		/* we do not pass back a terminating comma in the token */
+		if (c == ',' && !in_quote)
+		{
+			*terminating_comma = true;
 			break;
 		}
 
-		if (c != '"' || (c == '"' && was_quote))
+		if (c != '"' || was_quote)
 			*buf++ = c;
 
-		/* We pass back the comma so the caller knows there is more */
-		if ((pg_isblank(c) || c == ',') && !in_quote)
-			break;
-
 		/* Literal double-quote is two double-quotes */
 		if (in_quote && c == '"')
 			was_quote = !was_quote;
@@ -1141,64 +1762,54 @@ static void next_token(FILE *fp, char *buf, int bufsz)
 		{
 			in_quote = !in_quote;
 			saw_quote = true;
+			if (buf == start_buf)
+				*initial_quote = true;
 		}
 
-		c = getc(fp);
+		c = *(*lineptr)++;
 	}
 
 	/*
-	 * Put back the char right after the token (critical in case it is
-	 * EOL, since we need to detect end-of-line at next call).
+	 * Un-eat the char right after the token (critical in case it is '\0',
+	 * else next call will read past end of string).
 	 */
-	if (c != EOF)
-		ungetc(c, fp);
+	(*lineptr)--;
 
 	*buf = '\0';
 
-	if (!saw_quote &&
-		(strcmp(start_buf, "all") == 0 ||
-		 strcmp(start_buf, "sameuser") == 0 ||
-		 strcmp(start_buf, "samegroup") == 0))
-	{
-		/* append newline to a magical keyword */
-		*buf++ = '\n';
-		*buf = '\0';
-	}
+	return (saw_quote || buf > start_buf);
 }
 
-
 /*
- * free memory used by lines and tokens built by tokenize_file()
+ * Construct a palloc'd HbaToken struct, copying the given string.
  */
-static void free_lines(List **lines, List **line_nums)
+static HbaToken *
+make_hba_token(const char *token, bool quoted)
 {
-	if (*lines)
-	{
-		ListCell *line;
-
-		foreach(line, *lines)
-		{
-			List *ln = lfirst(line);
-			ListCell *token;
+	HbaToken   *hbatoken;
+	int			toklen;
 
-			foreach(token, ln)
-				pfree(lfirst(token));
+	toklen = strlen(token);
+	/* we copy string into same palloc block as the struct */
+	hbatoken = (HbaToken *) palloc(sizeof(HbaToken) + toklen + 1);
+	hbatoken->string = (char *) hbatoken + sizeof(HbaToken);
+	hbatoken->quoted = quoted;
+	memcpy(hbatoken->string, token, toklen + 1);
 
-			list_free(ln);
-		}
+	return hbatoken;
+}
 
-		list_free(*lines);
-		*lines = NIL;
-	}
+/*
+ * Copy a HbaToken struct into freshly palloc'd memory.
+ */
+static HbaToken *
+copy_hba_token(HbaToken *in)
+{
+	HbaToken   *out = make_hba_token(in->string, in->quoted);
 
-	if (*line_nums)
-	{
-		list_free(*line_nums);
-		*line_nums = NIL;
-	}
+	return out;
 }
 
-
 #ifdef USE_PAM
 
 /*
@@ -1297,8 +1908,8 @@ static POOL_STATUS CheckPAMAuth(POOL_CONNECTION *frontend, char *user, char *pas
 													 * not allocated */
 
 	/* Optionally, one can set the service name in pool_hba.conf */
-	if (frontend->auth_arg && frontend->auth_arg[0] != '\0')
-		retval = pam_start(frontend->auth_arg, "pgpool@",
+	if (frontend->pool_hba->pamservice && frontend->pool_hba->pamservice[0] != '\0')
+		retval = pam_start(frontend->pool_hba->pamservice, "pgpool@",
 						   &pam_passw_conv, &pamh);
 	else
 		retval = pam_start(PGPOOL_PAM_SERVICE, "pgpool@",
diff --git a/src/include/pool.h b/src/include/pool.h
index b2f10ca6..d4b0fd26 100644
--- a/src/include/pool.h
+++ b/src/include/pool.h
@@ -31,7 +31,6 @@
 #include "pcp/libpcp_ext.h"
 #include "utils/pool_signal.h"
 #include "parser/nodes.h"
-
 #include <stdio.h>
 #include <time.h>
 #include <sys/time.h>
@@ -166,6 +165,12 @@ typedef struct {
 } ParamStatus;
 
 /*
+ * HbaLines is declared in pool_hba.h
+ * we use forward declaration here
+ */
+typedef struct HbaLine HbaLine;
+
+/*
  * stream connection structure
  */
 typedef struct {
@@ -232,10 +237,11 @@ typedef struct {
 	 */
 	int protoVersion;
 	SockAddr raddr;
-	UserAuth auth_method;
-	char *auth_arg;
+	HbaLine *pool_hba;
 	char *database;
 	char *username;
+	char *remote_hostname;
+	int remote_hostname_resolv;
 	ConnectionInfo *con_info; /* shared memory coninfo used
 						   * for handling the query containing
 						   * pg_terminate_backend*/
@@ -632,10 +638,6 @@ extern void discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT *cp);
 /* define pool_system.c */
 extern void pool_close_libpq_connection(void);
 
-/* pool_hba.c */
-extern int load_hba(char *hbapath);
-extern void ClientAuthentication(POOL_CONNECTION *frontend);
-
 /* pool_ip.c */
 extern void pool_getnameinfo_all(SockAddr *saddr, char *remote_host, char *remote_port);
 
diff --git a/src/include/pool_type.h b/src/include/pool_type.h
index 84013e6e..d0fcb87e 100644
--- a/src/include/pool_type.h
+++ b/src/include/pool_type.h
@@ -177,22 +177,6 @@ typedef struct
 }
 SockAddr;
 
-/* UserAuth type used for HBA which indicates the authentication method */
-typedef enum UserAuth
-{
-	uaReject,
-	/*  uaKrb4, */
-	/*  uaKrb5, */
-	uaTrust,
-	/*  uaIdent, */
-	/*  uaPassword, */
-	/*  uaCrypt, */
-	uaMD5
-#ifdef USE_PAM
-	,uaPAM
-#endif /* USE_PAM */
-}
-UserAuth;
 
 #define AUTH_REQ_OK         0   /* User is authenticated  */
 #define AUTH_REQ_KRB4       1   /* Kerberos V4 */
diff --git a/src/include/utils/pool_ip.h b/src/include/utils/pool_ip.h
index e2fc4fc3..94fa2100 100644
--- a/src/include/utils/pool_ip.h
+++ b/src/include/utils/pool_ip.h
@@ -32,6 +32,11 @@
 
 #include "pool_type.h"
 
+extern int SockAddr_cidr_mask(struct sockaddr_storage * mask,
+							  char *numbits, int family);
+
+typedef void (*PgIfAddrCallback) (struct sockaddr *addr, struct sockaddr *netmask, void *cb_data);
+
 extern int getaddrinfo_all(const char *hostname, const char *servname,
 				const struct addrinfo * hintp,
 				struct addrinfo ** result);
@@ -46,8 +51,6 @@ extern int rangeSockAddr(const struct sockaddr_storage * addr,
 			  const struct sockaddr_storage * netaddr,
 			  const struct sockaddr_storage * netmask);
 
-extern int SockAddr_cidr_mask(struct sockaddr_storage * mask,
-				   char *numbits, int family);
 
 /* imported from PostgreSQL getaddrinfo.c */
 #ifndef HAVE_GAI_STRERROR
@@ -57,6 +60,8 @@ extern const char * gai_strerror(int errcode);
 extern void promote_v4_to_v6_addr(struct sockaddr_storage * addr);
 extern void promote_v4_to_v6_mask(struct sockaddr_storage * addr);
 
+extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data);
+
 #define IS_AF_INET(fam) ((fam) == AF_INET)
 #define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
 
diff --git a/src/main/health_check.c b/src/main/health_check.c
index 14468f24..c8c71284 100644
--- a/src/main/health_check.c
+++ b/src/main/health_check.c
@@ -59,6 +59,7 @@
 #include "pool_config.h"
 #include "utils/pool_ip.h"
 #include "auth/md5.h"
+#include "auth/pool_hba.h"
 #include "utils/pool_stream.h"
 
 char remote_ps_data[NI_MAXHOST];		/* used for set_ps_display */
diff --git a/src/main/main.c b/src/main/main.c
index 9dd0cc30..27568ce1 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -42,6 +42,7 @@
 
 #include "version.h"
 #include "auth/pool_passwd.h"
+#include "auth/pool_hba.h"
 #include "query_cache/pool_memqcache.h"
 #include "watchdog/wd_utils.h"
 #include "pool_config_variables.h"
diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c
index 8b45c9e2..95a45a88 100644
--- a/src/main/pgpool_main.c
+++ b/src/main/pgpool_main.c
@@ -56,6 +56,7 @@
 #include "version.h"
 #include "parser/pool_string.h"
 #include "auth/pool_passwd.h"
+#include "auth/pool_hba.h"
 #include "query_cache/pool_memqcache.h"
 #include "watchdog/wd_ipc_commands.h"
 #include "watchdog/wd_lifecheck.h"
diff --git a/src/protocol/child.c b/src/protocol/child.c
index 2e761d5c..9cf1544a 100644
--- a/src/protocol/child.c
+++ b/src/protocol/child.c
@@ -60,6 +60,8 @@
 #include "utils/elog.h"
 #include "auth/md5.h"
 #include "auth/pool_passwd.h"
+#include "auth/pool_hba.h"
+
 
 static StartupPacket *read_startup_packet(POOL_CONNECTION *cp);
 static POOL_CONNECTION_POOL *connect_backend(StartupPacket *sp, POOL_CONNECTION *frontend);
diff --git a/src/protocol/pool_process_query.c b/src/protocol/pool_process_query.c
index 9631a5fb..9caa3a20 100644
--- a/src/protocol/pool_process_query.c
+++ b/src/protocol/pool_process_query.c
@@ -50,6 +50,7 @@
 #include "utils/palloc.h"
 #include "utils/memutils.h"
 #include "utils/elog.h"
+#include "auth/pool_hba.h"
 #include "utils/pool_relcache.h"
 #include "utils/pool_stream.h"
 #include "context/pool_session_context.h"
diff --git a/src/streaming_replication/pool_worker_child.c b/src/streaming_replication/pool_worker_child.c
index 3c11fa69..77158760 100644
--- a/src/streaming_replication/pool_worker_child.c
+++ b/src/streaming_replication/pool_worker_child.c
@@ -60,6 +60,7 @@
 #include "pool_config.h"
 #include "utils/pool_ip.h"
 #include "auth/md5.h"
+#include "auth/pool_hba.h"
 #include "utils/pool_stream.h"
 
 char remote_ps_data[NI_MAXHOST];		/* used for set_ps_display */
diff --git a/src/utils/pool_ip.c b/src/utils/pool_ip.c
index a33663aa..74d43c40 100644
--- a/src/utils/pool_ip.c
+++ b/src/utils/pool_ip.c
@@ -42,7 +42,7 @@
 #include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
-
+#include <ifaddrs.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -365,29 +365,6 @@ getnameinfo_unix(const struct sockaddr_un * sa, int salen,
 }
 
 
-/*
- * rangeSockAddr - is addr within the subnet specified by netaddr/netmask ?
- *
- * Note: caller must already have verified that all three addresses are
- * in the same address family; and AF_UNIX addresses are not supported.
- */
-int
-rangeSockAddr(const struct sockaddr_storage * addr,
-			  const struct sockaddr_storage * netaddr,
-			  const struct sockaddr_storage * netmask)
-{
-	if (addr->ss_family == AF_INET)
-		return rangeSockAddrAF_INET((struct sockaddr_in *) addr,
-									(struct sockaddr_in *) netaddr,
-									(struct sockaddr_in *) netmask);
-	else if (addr->ss_family == AF_INET6)
-		return rangeSockAddrAF_INET6((struct sockaddr_in6 *) addr,
-									 (struct sockaddr_in6 *) netaddr,
-									 (struct sockaddr_in6 *) netmask);
-	else
-		return 0;
-}
-
 static int
 rangeSockAddrAF_INET(const struct sockaddr_in * addr,
 					 const struct sockaddr_in * netaddr,
@@ -560,3 +537,91 @@ promote_v4_to_v6_mask(struct sockaddr_storage * addr)
 
 	memcpy(addr, &addr6, sizeof(addr6));
 }
+
+/*
+ * range_sockaddr - is addr within the subnet specified by netaddr/netmask ?
+ *
+ * Note: caller must already have verified that all three addresses are
+ * in the same address family; and AF_UNIX addresses are not supported.
+ */
+int
+rangeSockAddr(const struct sockaddr_storage *addr,
+				  const struct sockaddr_storage *netaddr,
+				  const struct sockaddr_storage *netmask)
+{
+	if (addr->ss_family == AF_INET)
+		return rangeSockAddrAF_INET((const struct sockaddr_in *) addr,
+									  (const struct sockaddr_in *) netaddr,
+									  (const struct sockaddr_in *) netmask);
+	else if (addr->ss_family == AF_INET6)
+		return rangeSockAddrAF_INET6((const struct sockaddr_in6 *) addr,
+									   (const struct sockaddr_in6 *) netaddr,
+									   (const struct sockaddr_in6 *) netmask);
+	else
+		return 0;
+}
+
+/*
+ * Run the callback function for the addr/mask, after making sure the
+ * mask is sane for the addr.
+ */
+static void
+run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data,
+					struct sockaddr *addr, struct sockaddr *mask)
+{
+	struct sockaddr_storage fullmask;
+
+	if (!addr)
+		return;
+
+	/* Check that the mask is valid */
+	if (mask)
+	{
+		if (mask->sa_family != addr->sa_family)
+		{
+			mask = NULL;
+		}
+		else if (mask->sa_family == AF_INET)
+		{
+			if (((struct sockaddr_in *) mask)->sin_addr.s_addr == INADDR_ANY)
+				mask = NULL;
+		}
+		else if (mask->sa_family == AF_INET6)
+		{
+			if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) mask)->sin6_addr))
+				mask = NULL;
+		}
+	}
+
+	/* If mask is invalid, generate our own fully-set mask */
+	if (!mask)
+	{
+		SockAddr_cidr_mask(&fullmask, NULL, addr->sa_family);
+		mask = (struct sockaddr *) &fullmask;
+	}
+
+	(*callback) (addr, mask, cb_data);
+}
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one.  Returns 0 if successful, -1 if trouble.
+ *
+ * This version uses the getifaddrs() interface, which is available on
+ * BSDs, AIX, and modern Linux.
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+	struct ifaddrs *ifa,
+	*l;
+
+	if (getifaddrs(&ifa) < 0)
+		return -1;
+
+	for (l = ifa; l; l = l->ifa_next)
+		run_ifaddr_callback(callback, cb_data,
+							l->ifa_addr, l->ifa_netmask);
+
+	freeifaddrs(ifa);
+	return 0;
+}


More information about the pgpool-hackers mailing list