File Allow-to-enforce-IPv4-or-IPv6-in-URL-like-connection.patch of Package firebird30

From: Michal Kubecek <mkubecek@suse.cz>
Date: Wed, 4 May 2016 21:38:36 +0200
Subject: Allow to enforce IPv4 or IPv6 in URL-like connection string.
Patch-mainline: submitted
Git-commit: 13aa5420f24546b99c25a8651d135133d18583f2

---
 doc/README.IPv6                 | 13 +++++++++++++
 doc/README.connection_strings   |  7 +++++++
 src/remote/client/interface.cpp | 12 ++++++++++--
 src/remote/inet.cpp             | 26 +++++++++++++++++---------
 src/remote/inet_proto.h         |  5 +++--
 5 files changed, 50 insertions(+), 13 deletions(-)

diff --git a/doc/README.IPv6 b/doc/README.IPv6
index 04ee9ae63060..86d533b26c95 100644
--- a/doc/README.IPv6
+++ b/doc/README.IPv6
@@ -22,6 +22,19 @@ If a domain name is used in connection string, all addresses (IPv4 and IPv6)
 are tried in the order returned by resolver until a connection is established.
 Only if all attempts fail, the client fails to connect.
 
+New URL-style connection string format (see README.connection_strings) allows
+to restrict name lookup to only IPv4 or IPv6 addresses:
+
+  connect 'inet://server.example.org/test';
+  connect 'inet4://server.example.org/test';
+  connect 'inet6://server.example.org/test';
+
+First example tries all addresses, second only IPv4 ones, third only IPv6
+ones. This can be used to avoid connection delays on systems where name lookup
+returns IPv6 addresses for some host names but attempts to connect to them
+time out rather than failing immediatelly (as reported, this can happen even
+for name "localhost" on some systems).
+
 
 Server
 ------
diff --git a/doc/README.connection_strings b/doc/README.connection_strings
index d23d50724b04..99e4753df2e7 100644
--- a/doc/README.connection_strings
+++ b/doc/README.connection_strings
@@ -107,6 +107,13 @@ Examples:
     inet://myserver:fb_db/mydb
     inet://localhost:fb_db/mydb
 
+  The "inet" protocol can be replaced by "inet4" or "inet6" to restrict client
+  to IPv4 or IPv6 addresses corresponding to supplied name ("inet" protocol
+  tries all addresses in the order determined by OS):
+
+    inet4://myserver/mydb
+    inet6://myserver/mydb
+
   Connect via named pipes:
 
     wnet://myserver/C:\db\mydb.fdb
diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp
index bdfcedf3bb10..29ddb708398d 100644
--- a/src/remote/client/interface.cpp
+++ b/src/remote/client/interface.cpp
@@ -92,6 +92,8 @@
 
 
 const char* const PROTOCOL_INET = "inet";
+const char* const PROTOCOL_INET4 = "inet4";
+const char* const PROTOCOL_INET6 = "inet6";
 const char* const PROTOCOL_WNET = "wnet";
 const char* const PROTOCOL_XNET = "xnet";
 
@@ -5419,6 +5421,7 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned
  **************************************/
 
 	rem_port* port = NULL;
+	int inet_af = AF_UNSPEC;
 
 	cBlock.loadClnt(pb, &parSet);
 	authenticateStep0(cBlock);
@@ -5443,7 +5446,12 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned
 	else
 #endif
 
-	if (ISC_analyze_protocol(PROTOCOL_INET, attach_name, node_name, INET_SEPARATOR) ||
+	if (ISC_analyze_protocol(PROTOCOL_INET4, attach_name, node_name, INET_SEPARATOR))
+		inet_af = AF_INET;
+	else if (ISC_analyze_protocol(PROTOCOL_INET6, attach_name, node_name, INET_SEPARATOR))
+		inet_af = AF_INET6;
+	if (inet_af != AF_UNSPEC ||
+		ISC_analyze_protocol(PROTOCOL_INET, attach_name, node_name, INET_SEPARATOR) ||
 		ISC_analyze_tcp(attach_name, node_name))
 	{
 		if (node_name.isEmpty())
@@ -5455,7 +5463,7 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned
 		}
 
 		port = INET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_UV, pb,
-			cBlock.getConfig(), ref_db_name);
+			cBlock.getConfig(), ref_db_name, inet_af);
 	}
 
 	// We have a local connection string. If it's a file on a network share,
diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp
index 40f43bbfd9c0..7191db3d9c1f 100644
--- a/src/remote/inet.cpp
+++ b/src/remote/inet.cpp
@@ -453,7 +453,8 @@ static rem_port*		inet_try_connect(	PACKET*,
 									const TEXT*,
 									ClumpletReader&,
 									RefPtr<Config>*,
-									const PathName*);
+									const PathName*,
+									int);
 static bool		inet_write(XDR*);
 static rem_port* listener_socket(rem_port* port, USHORT flag, const addrinfo* pai);
 
@@ -532,7 +533,8 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock,
 					   bool uv_flag,
 					   ClumpletReader &dpb,
 					   RefPtr<Config>* config,
-					   const PathName* ref_db_name)
+					   const PathName* ref_db_name,
+					   int af)
 {
 /**************************************
  *
@@ -624,7 +626,7 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock,
 		}
 	}
 
-	rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config, ref_db_name);
+	rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config, ref_db_name, af);
 
 	P_ACPT* accept = NULL;
 	switch (packet->p_operation)
@@ -708,7 +710,8 @@ rem_port* INET_connect(const TEXT* name,
 					   PACKET* packet,
 					   USHORT flag,
 					   ClumpletReader* dpb,
-					   RefPtr<Config>* config)
+					   RefPtr<Config>* config,
+					   int af)
 {
 /**************************************
  *
@@ -800,7 +803,10 @@ rem_port* INET_connect(const TEXT* name,
 
 	struct addrinfo gai_hints;
 	memset(&gai_hints, 0, sizeof(gai_hints));
-	gai_hints.ai_family = ((packet || host.hasData() || !ipv6) ? AF_UNSPEC : AF_INET6);
+	if (packet)
+		gai_hints.ai_family = af;
+	else
+		gai_hints.ai_family = ((host.hasData() || !ipv6) ? AF_UNSPEC : AF_INET6);
 	gai_hints.ai_socktype = SOCK_STREAM;
 
 #if !defined(WIN_NT) && !defined(__clang__)
@@ -811,7 +817,7 @@ rem_port* INET_connect(const TEXT* name,
 
 	gai_hints.ai_flags =
 #ifndef ANDROID
-		AI_V4MAPPED |
+		((af == AF_UNSPEC) ? AI_V4MAPPED : 0) |
 #endif
 			AI_ADDRCONFIG | (packet ? 0 : AI_PASSIVE);
 
@@ -825,7 +831,8 @@ rem_port* INET_connect(const TEXT* name,
 		retry_gai = false;
 		n = getaddrinfo(host_str, protocol.c_str(), &gai_hints, &gai_result);
 
-		if ((n == EAI_FAMILY || (!host_str && n == EAI_NONAME)) && (gai_hints.ai_family == AF_INET6))
+		if ((n == EAI_FAMILY || (!host_str && n == EAI_NONAME)) &&
+			(gai_hints.ai_family == AF_INET6) && (af != AF_INET6))
 		{
 			// May be on a system without IPv6 support, try IPv4
 			gai_hints.ai_family = AF_UNSPEC;
@@ -2639,7 +2646,8 @@ static rem_port* inet_try_connect(PACKET* packet,
 								  const TEXT* node_name,
 								  ClumpletReader& dpb,
 								  RefPtr<Config>* config,
-								  const PathName* ref_db_name)
+								  const PathName* ref_db_name,
+								  int af)
 {
 /**************************************
  *
@@ -2671,7 +2679,7 @@ static rem_port* inet_try_connect(PACKET* packet,
 	rem_port* port = NULL;
 	try
 	{
-		port = INET_connect(node_name, packet, false, &dpb, config);
+		port = INET_connect(node_name, packet, false, &dpb, config, af);
 	}
 	catch (const Exception&)
 	{
diff --git a/src/remote/inet_proto.h b/src/remote/inet_proto.h
index af00e8fa5ddb..6aeebace22bd 100644
--- a/src/remote/inet_proto.h
+++ b/src/remote/inet_proto.h
@@ -34,9 +34,10 @@ namespace Firebird
 }
 
 rem_port*	INET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*,
-						 bool, Firebird::ClumpletReader&, Firebird::RefPtr<Config>*, const Firebird::PathName*);
+						 bool, Firebird::ClumpletReader&, Firebird::RefPtr<Config>*,
+						 const Firebird::PathName*, int af = AF_UNSPEC);
 rem_port*	INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*,
-						 Firebird::RefPtr<Config>*);
+						 Firebird::RefPtr<Config>*, int af = AF_UNSPEC);
 rem_port*	INET_reconnect(SOCKET);
 rem_port*	INET_server(SOCKET);
 void		setStopMainThread(FPTR_INT func);
-- 
2.8.2

openSUSE Build Service is sponsored by