1 module more.net.dns; 2 3 import std.traits : hasMember; 4 import std.internal.cstring : tempCString; 5 6 import more.c : cint; 7 import more.sentinel : SentinelArray; 8 import more.net : SockResult; 9 10 version(Windows) 11 { 12 import more.os.windows.sock : 13 addrinfo, AI_NUMERICHOST, 14 getaddrinfoPointer, freeaddrinfoPointer; 15 } 16 else version(Posix) 17 { 18 import more.os.posix.sock : 19 addrinfo, getaddrinfo; 20 } 21 else 22 { 23 static assert(0, "unhandled platform"); 24 } 25 26 import more.net : sockaddr, inet_sockaddr, AddressFamily; 27 28 template isAddressInfo(T) 29 { 30 enum isAddressInfo = 31 hasMember!(T, "addressFamily") && 32 hasMember!(T, "assignTo"); 33 } 34 35 struct StandardAddressSelector 36 { 37 static struct IPv4ThenIPv6 38 { 39 enum hintsAIFamily = AddressFamily.unspec; 40 static SockResult selectAddress(T,U)(T* resolved, U addressList) 41 { 42 foreach(address; addressList) 43 { 44 if(address.addressFamily == AddressFamily.inet) 45 { 46 resolved.resolved(address); 47 return SockResult.pass; 48 } 49 } 50 foreach(address; addressList) 51 { 52 if(address.addressFamily == AddressFamily.inet6) 53 { 54 resolved.resolved(address); 55 return SockResult.pass; 56 } 57 } 58 resolved.noResolution(); 59 return SockResult.pass; 60 } 61 } 62 static struct IPv4Only 63 { 64 enum hintsAIFamily = AddressFamily.inet; 65 } 66 static struct IPv6Only 67 { 68 enum hintsAIFamily = AddressFamily.inet6; 69 } 70 } 71 72 SockResult getaddrinfo(const(char)* node, const(char)* service, 73 const(addrinfo)* hints, addrinfo** result) 74 { 75 version (Windows) 76 { 77 return getaddrinfoPointer(node, service, hints, result); 78 } 79 else static assert(0, "OS not supported"); 80 } 81 void freeaddrinfo(addrinfo* result) 82 { 83 version (Windows) 84 { 85 freeaddrinfoPointer(result); 86 } 87 else static assert(0, "OS not supported"); 88 } 89 90 // Returns: error code on failure 91 SockResult resolve(alias AddressSelector = StandardAddressSelector.IPv4ThenIPv6, T) 92 (T* resolved, SentinelArray!(const(char)) host) 93 { 94 addrinfo hints; 95 hints.ai_flags = AI_NUMERICHOST; 96 hints.ai_family = AddressSelector.hintsAIFamily; 97 98 addrinfo* addrList; 99 { 100 auto result = getaddrinfo(host.ptr, null, &hints, &addrList); 101 if(result.failed) 102 return result; 103 } 104 scope(exit) freeaddrinfo(addrList); 105 106 static struct AddressInfo 107 { 108 addrinfo* ptr; 109 alias ptr this; 110 @property auto addressFamily() const { return ai_family; } 111 void assignTo(sockaddr* dst) const 112 { 113 (cast(ubyte*)dst)[0..ptr.ai_addrlen] = (cast(ubyte*)ptr.ai_addr)[0..ptr.ai_addrlen]; 114 } 115 } 116 static struct AddressRange 117 { 118 AddressInfo next; 119 @property bool empty() { return next.ptr is null; } 120 @property auto front() { return next; } 121 void popFront() 122 { 123 next.ptr = next.ptr.ai_next; 124 } 125 } 126 127 return AddressSelector.selectAddress(resolved, AddressRange(AddressInfo(addrList))); 128 } 129 130 private void noResolution(inet_sockaddr* addr) 131 { 132 addr.family = AddressFamily.unspec; 133 } 134 private void resolved(T)(inet_sockaddr* addr, T addressInfo) if(isAddressInfo!T) 135 { 136 assert(addressInfo.addressFamily == AddressFamily.inet || 137 addressInfo.addressFamily == AddressFamily.inet6); 138 addressInfo.assignTo(&addr.sa); 139 }