diff --git a/std/socket.d b/std/socket.d index e20395fcac5..68fd098e1ce 100644 --- a/std/socket.d +++ b/std/socket.d @@ -289,56 +289,96 @@ bool wouldHaveBlocked() nothrow @nogc assert(rec == -1 && wouldHaveBlocked()); } +// Returns true iff getnameinfo is supported on this platform. +version(Windows) private @property bool getnameinfoSupported() +{ + initialize; + return getnameinfoPointer !is null; +} +// Ditto (always supported on Posix). +version(Posix) private enum bool getnameinfoSupported = true; -private immutable +// Returns true iff getaddrinfo is supported on this platform. +version(Windows) private @property bool getaddrinfoSupported() { - typeof(&getnameinfo) getnameinfoPointer; - typeof(&getaddrinfo) getaddrinfoPointer; - typeof(&freeaddrinfo) freeaddrinfoPointer; + initialize; + assert((getaddrinfoPointer is null) == (freeaddrinfoPointer is null)); + return getaddrinfoPointer !is null; } +// Ditto (always supported on Posix). +version(Posix) private enum bool getaddrinfoSupported = true; -shared static this() @system +version(Windows) private { - version (Windows) - { - WSADATA wd; + import core.atomic; - // Winsock will still load if an older version is present. - // The version is just a request. - int val; - val = WSAStartup(0x2020, &wd); - if (val) // Request Winsock 2.2 for IPv6. - throw new SocketOSException("Unable to initialize socket library", val); + shared typeof(&getnameinfo) getnameinfoPointer; + shared typeof(&getaddrinfo) getaddrinfoPointer; + shared typeof(&freeaddrinfo) freeaddrinfoPointer; - // These functions may not be present on older Windows versions. - // See the comment in InternetAddress.toHostNameString() for details. - auto ws2Lib = GetModuleHandleA("ws2_32.dll"); - if (ws2Lib) - { - getnameinfoPointer = cast(typeof(getnameinfoPointer)) - GetProcAddress(ws2Lib, "getnameinfo"); - getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) - GetProcAddress(ws2Lib, "getaddrinfo"); - freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) - GetProcAddress(ws2Lib, "freeaddrinfo"); - } + auto our_getnameinfo(A...)(A a) @system + { + initialize; + return getnameinfoPointer(a); } - else version (Posix) + + auto our_getaddrinfo(A...)(A a) @system { - getnameinfoPointer = &getnameinfo; - getaddrinfoPointer = &getaddrinfo; - freeaddrinfoPointer = &freeaddrinfo; + initialize; + return getaddrinfoPointer(a); } -} + auto our_freeaddrinfo(A...)(A a) @system + { + initialize; + if (freeaddrinfoPointer) + // May be null if WSAStartup() failed, just do nothing + return; + return freeaddrinfoPointer(a); + } -shared static ~this() @system nothrow @nogc -{ - version (Windows) + void initialize() @trusted nothrow { - WSACleanup(); + static shared bool processInitialized; + if (atomicLoad(processInitialized)) return; + + import std.concurrency : initOnce; + + static bool perform() + { + // Winsock will still load if an older version is present. + // The version is just a request. + WSADATA wd; + if (!WSAStartup(0x2020, &wd)) // Request Winsock 2.2 for IPv6. + // No harm done, pointers to functions will be null (no supported functionality) + return true; + + static extern(C) void cleanup() { WSACleanup(); } + atexit(&cleanup); + + // These functions may not be present on older Windows versions. + // See the comment in InternetAddress.toHostNameString() for details.+ ; + if (auto ws2Lib = GetModuleHandleA("ws2_32.dll")) + { + getnameinfoPointer = cast(typeof(getnameinfoPointer)) + GetProcAddress(ws2Lib, "getnameinfo"); + getaddrinfoPointer = cast(typeof(getaddrinfoPointer)) + GetProcAddress(ws2Lib, "getaddrinfo"); + freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer)) + GetProcAddress(ws2Lib, "freeaddrinfo"); + } + return true; + } + + initOnce!processInitialized(perform()); } } +else version(Posix) +{ + alias our_getnameinfo = getnameinfo; + alias our_getaddrinfo = getaddrinfo; + alias our_freeaddrinfo = freeaddrinfo; +} /** * The communication domain used to resolve an address. @@ -963,16 +1003,16 @@ private AddressInfo[] getAddressInfoImpl(scope const(char)[] node, scope const(c { import std.array : appender; - if (getaddrinfoPointer && freeaddrinfoPointer) + if (getaddrinfoSupported) { addrinfo* ai_res; - int ret = getaddrinfoPointer( + int ret = our_getaddrinfo( node.tempCString(), service.tempCString(), hints, &ai_res); enforce(ret == 0, new SocketOSException("getaddrinfo error", ret, &formatGaiError)); - scope(exit) freeaddrinfoPointer(ai_res); + scope(exit) our_freeaddrinfo(ai_res); auto result = appender!(AddressInfo[])(); @@ -989,15 +1029,16 @@ private AddressInfo[] getAddressInfoImpl(scope const(char)[] node, scope const(c return result.data; } - throw new SocketFeatureException("Address info lookup is not available " ~ - "on this system."); + version(Windows) + throw new SocketFeatureException("Address info lookup is not available " ~ + "on this system."); } @safe unittest { softUnittest({ - if (getaddrinfoPointer) + if (getaddrinfoSupported) { // Roundtrip DNS resolution auto results = getAddressInfo("www.digitalmars.com"); @@ -1026,7 +1067,7 @@ private AddressInfo[] getAddressInfoImpl(scope const(char)[] node, scope const(c } }); - if (getaddrinfoPointer) + if (getaddrinfoSupported) { auto results = getAddressInfo(null, "1234", AddressInfoFlags.PASSIVE, SocketType.STREAM, ProtocolType.TCP, AddressFamily.INET); @@ -1074,7 +1115,7 @@ private ushort serviceToPort(scope const(char)[] service) */ Address[] getAddress(scope const(char)[] hostname, scope const(char)[] service = null) { - if (getaddrinfoPointer && freeaddrinfoPointer) + if (getaddrinfoSupported) { // use getAddressInfo auto infos = getAddressInfo(hostname, service); @@ -1091,7 +1132,7 @@ Address[] getAddress(scope const(char)[] hostname, scope const(char)[] service = /// ditto Address[] getAddress(scope const(char)[] hostname, ushort port) { - if (getaddrinfoPointer && freeaddrinfoPointer) + if (getaddrinfoSupported) return getAddress(hostname, to!string(port)); else { @@ -1109,13 +1150,13 @@ Address[] getAddress(scope const(char)[] hostname, ushort port) } -@safe unittest +version(Windows) @safe unittest { softUnittest({ auto addresses = getAddress("63.105.9.61"); assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61"); - if (getaddrinfoPointer) + if (getaddrinfoSupported) { // test via gethostbyname auto getaddrinfoPointerBackup = getaddrinfoPointer; @@ -1168,7 +1209,7 @@ Address[] getAddress(scope const(char)[] hostname, ushort port) */ Address parseAddress(scope const(char)[] hostaddr, scope const(char)[] service = null) { - if (getaddrinfoPointer && freeaddrinfoPointer) + if (getaddrinfoSupported) return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address; else return parseAddress(hostaddr, serviceToPort(service)); @@ -1177,7 +1218,7 @@ Address parseAddress(scope const(char)[] hostaddr, scope const(char)[] service = /// ditto Address parseAddress(scope const(char)[] hostaddr, ushort port) { - if (getaddrinfoPointer && freeaddrinfoPointer) + if (getaddrinfoSupported) return parseAddress(hostaddr, to!string(port)); else { @@ -1189,13 +1230,13 @@ Address parseAddress(scope const(char)[] hostaddr, ushort port) } -@safe unittest +version(Windows) @safe unittest { softUnittest({ auto address = parseAddress("63.105.9.61"); assert(address.toAddrString() == "63.105.9.61"); - if (getaddrinfoPointer) + if (getaddrinfoSupported) { // test via inet_addr auto getaddrinfoPointerBackup = getaddrinfoPointer; @@ -1286,10 +1327,10 @@ abstract class Address // deprecated, albeit commonly-available method when getnameinfo() // is not available. // http://technet.microsoft.com/en-us/library/aa450403.aspx - if (getnameinfoPointer) + if (getnameinfoSupported) { auto buf = new char[NI_MAXHOST]; - auto ret = getnameinfoPointer( + auto ret = our_getnameinfo( name, nameLen, buf.ptr, cast(uint) buf.length, null, 0, @@ -1309,18 +1350,20 @@ abstract class Address return assumeUnique(buf[0 .. strlen(buf.ptr)]); } - throw new SocketFeatureException((numeric ? "Host address" : "Host name") ~ - " lookup for this address family is not available on this system."); + version(Windows) + throw new SocketFeatureException( + (numeric ? "Host address" : "Host name") ~ + " lookup for this address family is not available on this system."); } // Common code for toPortString and toServiceNameString private string toServiceString(bool numeric) @trusted const { // See toHostNameString() for details about getnameinfo(). - if (getnameinfoPointer) + if (getnameinfoSupported) { auto buf = new char[NI_MAXSERV]; - enforce(getnameinfoPointer( + enforce(our_getnameinfo( name, nameLen, null, 0, buf.ptr, cast(uint) buf.length, @@ -1330,8 +1373,10 @@ abstract class Address return assumeUnique(buf[0 .. strlen(buf.ptr)]); } - throw new SocketFeatureException((numeric ? "Port number" : "Service name") ~ - " lookup for this address family is not available on this system."); + version(Windows) + throw new SocketFeatureException( + (numeric ? "Port number" : "Service name") ~ + " lookup for this address family is not available on this system."); } /** @@ -1613,7 +1658,7 @@ public: // deprecated getHostByAddr() if it could not be found. See also: // http://technet.microsoft.com/en-us/library/aa450403.aspx - if (getnameinfoPointer) + if (getnameinfoSupported) return super.toHostNameString(); else { @@ -1689,7 +1734,7 @@ public: assert(ia.toString() == "127.0.0.1:80"); }); - softUnittest({ + version(Windows) softUnittest({ // test reverse lookup auto ih = new InternetHost; if (ih.getHostByName("digitalmars.com")) @@ -1697,7 +1742,7 @@ public: const ia = new InternetAddress(ih.addrList[0], 80); assert(ia.toHostNameString() == "digitalmars.com"); - if (getnameinfoPointer) + if (getnameinfoSupported) { // test reverse lookup, via gethostbyaddr auto getnameinfoPointerBackup = getnameinfoPointer; @@ -1709,13 +1754,13 @@ public: } }); - version (SlowTests) + version(Windows) version (SlowTests) softUnittest({ // test failing reverse lookup const InternetAddress ia = new InternetAddress("127.114.111.120", 80); assert(ia.toHostNameString() is null); - if (getnameinfoPointer) + if (getnameinfoSupported) { // test failing reverse lookup, via gethostbyaddr auto getnameinfoPointerBackup = getnameinfoPointer;