diff --git a/doc/README.IPv6 b/doc/README.IPv6 index 04ee9ae630..86d533b26c 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 d23d50724b..99e4753df2 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 94028b7dcb..ac841caf8e 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 40f43bbfd9..7191db3d9c 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*, - 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, - 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) + RefPtr* 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, - 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 af00e8fa5d..6aeebace22 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*, const Firebird::PathName*); + bool, Firebird::ClumpletReader&, Firebird::RefPtr*, + const Firebird::PathName*, int af = AF_UNSPEC); rem_port* INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*, - Firebird::RefPtr*); + Firebird::RefPtr*, int af = AF_UNSPEC); rem_port* INET_reconnect(SOCKET); rem_port* INET_server(SOCKET); void setStopMainThread(FPTR_INT func);