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 }