1 /*******************************************************************************
2 
3     Internet address handling and conversion heper.
4 
5     Wraps a sockaddr_in or sockaddr_in6 struct instance together with the IP
6     address network <-> presentation and port number network <-> host conversion
7     functions.
8 
9     Aliases the following types and constants from <netinet/in.h>:
10 
11         Type                IPv4 name           IPv6 name           Alias name
12 
13         struct              sockaddr_in         sockaddr_in6        Addr
14         const int           INET_ADDRSTRLEN     INET6_ADDRSTRLEN    addrstrlen
15         const sa_family_t   AF_INET             AF_INET6            family
16 
17     Uses the following functions from <arpa/inet.h>:
18         - inet_ntop, inet_pton (IP address network <-> presentation conversion),
19         - htons, ntohs, htonl  (port number network <-> host conversion).
20 
21     For the whole misery see
22 
23     http://pubs.opengroup.org/onlinepubs/009604499/basedefs/netinet/in.h.html
24     http://pubs.opengroup.org/onlinepubs/009604499/functions/inet_ntop.html
25     http://pubs.opengroup.org/onlinepubs/009604499/functions/ntohs.html
26     http://www.openisbn.com/isbn/0131411551/
27 
28     Important note: To reinitialise the address, use clear() (do not assign
29                     .init).
30 
31     Copyright:
32         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
33         All rights reserved.
34 
35     License:
36         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
37         Alternatively, this file may be distributed under the terms of the Tango
38         3-Clause BSD License (see LICENSE_BSD.txt for details).
39 
40 *******************************************************************************/
41 
42 module ocean.sys.socket.InetAddress;
43 
44 
45 
46 import ocean.transition;
47 
48 import ocean.stdc.posix.sys.socket: sockaddr;
49 
50 import core.sys.posix.netinet.in_ :
51            sockaddr_in,  AF_INET,  INET_ADDRSTRLEN,  INADDR_ANY,
52            sockaddr_in6, AF_INET6, INET6_ADDRSTRLEN;
53 
54 import core.sys.posix.netdb;
55 
56 import core.sys.posix.arpa.inet: inet_ntop, inet_pton, ntohs, htons, htonl;
57 
58 import core.stdc.string: strlen;
59 
60 import core.stdc.errno: errno, EINVAL;
61 
62 import ocean.core.TypeConvert;
63 
64 import ocean.core.Verify;
65 
66 /******************************************************************************
67 
68     Flags supported by getnameinfo().
69 
70  ******************************************************************************/
71 
72 public enum GetNameInfoFlags : int
73 {
74     None = 0,
75     NI_NUMERICHOST              = 1 << 0, /// Don't try to look up hostname.
76     NI_NUMERICSERV              = 1 << 1, /// Don't convert port number to name.
77     NI_NOFQDN                   = 1 << 2, /// Only return nodename portion.
78     NI_NAMEREQD                 = 1 << 3, /// Don't return numeric addresses.
79     NI_DGRAM                    = 1 << 4, /// Look up UDP service rather than TCP.
80     NI_IDN                      = 1 << 5, /// Convert name from IDN format.
81     NI_IDN_ALLOW_UNASSIGNED     = 1 << 6, /// Don't reject unassigned Unicode code points.
82     NI_IDN_USE_STD3_ASCII_RULES = 1 << 7  /// Validate strings according to STD3 rules.
83 }
84 
85 /******************************************************************************/
86 
87 struct InetAddress ( bool IPv6 = false )
88 {
89     /**************************************************************************
90 
91         Constants and aliases.
92 
93             - Addr aliases the "sin" internet address struct type, sockaddr_in
94               for IPv4 or sockaddr_in6 for IPv6, respectively.
95             - addrstrlen is the maximum length of a presentation address string
96               plus one (for the NUL-terminator).
97             - family is the address family identifier.
98             - addr_init is the initial value of an Addr instance with the family
99               field set appropriately.
100 
101      **************************************************************************/
102 
103     static if (IPv6)
104     {
105         alias sockaddr_in6 Addr;
106 
107         enum addrstrlen = INET6_ADDRSTRLEN,
108               family     = AF_INET6;
109 
110         enum Addr addr_init = {sin6_family: family};
111     }
112     else
113     {
114         alias sockaddr_in Addr;
115 
116         enum addrstrlen = INET_ADDRSTRLEN,
117               family     = AF_INET;
118 
119         enum Addr addr_init = {sin_family: family};
120     }
121 
122     /**************************************************************************
123 
124         Internet address struct instance.
125         The address and port fields should be accessed by port() and
126         inet_pton()/inet_ntop() unless they are copied from another instance.
127 
128      **************************************************************************/
129 
130     Addr addr = addr_init;
131 
132     /**************************************************************************
133 
134         Gets the port number from this.addr.
135 
136         Returns:
137             the port number.
138 
139      **************************************************************************/
140 
141     ushort port ( )
142     {
143         static if (IPv6)
144         {
145             return .ntohs((&this).addr.sin6_port);
146         }
147         else
148         {
149             return .ntohs((&this).addr.sin_port);
150         }
151     }
152 
153     /**************************************************************************
154 
155         Sets the port field (sin_port/sin6_port) of this.addr to p.
156 
157         Params:
158             p = port
159 
160         Returns:
161             p.
162 
163      **************************************************************************/
164 
165     ushort port ( ushort p )
166     {
167         static if (IPv6)
168         {
169             (&this).addr.sin6_port = .htons(p);
170 
171         }
172         else
173         {
174             (&this).addr.sin_port = .htons(p);
175         }
176 
177         return p;
178     }
179 
180     /**************************************************************************
181 
182         Sets the address field (sin_addr/sin6_addr) of this.addr to the address
183         represented by the string in src. src is expected to contain a valid IP
184         address.
185 
186         Params:
187             ip_address_str = input IP address
188 
189         Returns:
190             1 on success or 0 if src does not contain a valid IP address.
191 
192      **************************************************************************/
193 
194     int inet_pton ( cstring ip_address_str )
195     {
196         if (ip_address_str.length < (&this).addrstrlen)
197         {
198             char[(&this).addrstrlen] nultermbuf;
199 
200             nultermbuf[0 .. ip_address_str.length] = ip_address_str[];
201             nultermbuf[ip_address_str.length]      = '\0';
202 
203             return (&this).inet_pton(nultermbuf.ptr);
204         }
205         else
206         {
207             return 0;
208         }
209 
210     }
211 
212     /**************************************************************************
213 
214         Sets the address field (sin_addr/sin6_addr) of this.addr to the address
215         in ip_address_str. ip_address_str is expected be a NUL-terminated
216         string.
217 
218         Params:
219             ip_address_str = input IP address string
220 
221         Returns:
222             1 on success or 0 if src does not contain a valid IP address.
223 
224         (Note: The inet_pton() specs say it can return -1 with
225         errno = EAFNOSUPPORT; this is not possible unless there is a bug in this
226         struct template ;)
227 
228      **************************************************************************/
229 
230     int inet_pton ( in char* ip_address_str )
231     {
232         return .inet_pton((&this).family, ip_address_str, (&this).address_n.ptr);
233     }
234 
235     /**************************************************************************
236 
237         Sets this.addr to the wildcard "any" IP address.
238 
239         Returns:
240 
241 
242      **************************************************************************/
243 
244     sockaddr* setAddressAny ( )
245     {
246         static if (IPv6)
247         {
248             (&this).addr.sin6_addr = (&this).addr.sin6_addr.init;
249         }
250         else
251         {
252             (&this).addr.sin_addr.s_addr = htonl(INADDR_ANY);
253         }
254 
255         return cast (sockaddr*) &(&this).addr;
256     }
257 
258     /**************************************************************************
259 
260         Renders the current address of this.addr as an IP address string,
261         writing to dst. dst.length is expected to be at least this.addstrlength.
262 
263         Params:
264             dst = destination string buffer
265 
266         Returns:
267             a slice to valid data in dst on success or null on failure.
268 
269         Errors:
270             ENOSPC - dst is too short.
271 
272         (Note: The inet_ntop() specs say it can fail with errno = EAFNOSUPPORT;
273         this is not possible unless there is a bug in this struct template ;)
274 
275      **************************************************************************/
276 
277     mstring inet_ntop ( mstring dst )
278     {
279         verify(
280             dst.length >= (&this).addrstrlen,
281             "dst.length expected to be at least addrstrlen"
282         );
283 
284         auto address_p = .inet_ntop((&this).family, (&this).address_n.ptr, dst.ptr,
285             castFrom!(size_t).to!(int)(dst.length));
286 
287         return address_p? dst.ptr[0 .. strlen(dst.ptr)] : null;
288     }
289 
290     /**************************************************************************
291 
292         Sets this.addr to the IP address in ip_address_str and the port to port.
293 
294         Params:
295             ip_address_str = IP address string
296             port           = port
297 
298         Returns:
299             a sockaddr pointer to this.addr on success.
300             If ip_address_str does not contain a valid IP address, null is
301             returned and errno set to EINVAL (this error is reported by this
302             wrapper, not the underlying POSIX function).
303 
304      **************************************************************************/
305 
306     public sockaddr* opCall ( cstring ip_address_str, ushort port = 0 )
307     {
308         if ((&this).inet_pton(ip_address_str) == 1)
309         {
310             (&this).port = port;
311             return cast (sockaddr*) &(&this).addr;
312         }
313         else
314         {
315             .errno = EINVAL;
316             return null;
317         }
318     }
319 
320     /**************************************************************************
321 
322         Sets this.addr to the wildcard "any" IP address and the port to port.
323 
324         Params:
325             port = input port
326 
327         Returns:
328             a sockaddr pointer to this.addr.
329 
330      **************************************************************************/
331 
332     public sockaddr* opCall ( ushort port )
333     {
334         (&this).port = port;
335 
336         return (&this).setAddressAny();
337     }
338 
339     /**************************************************************************
340 
341         Copies addr to this.addr.
342 
343         Params:
344             addr = input address
345 
346         Returns:
347             a sockaddr pointer to this.addr.
348 
349      **************************************************************************/
350 
351     public sockaddr* opAssign ( Addr addr )
352     {
353         (&this).addr = addr;
354 
355         return cast (sockaddr*) &(&this).addr;
356     }
357 
358     /**************************************************************************
359 
360         Copies *addr to this.addr.
361 
362         Params:
363             addr = input address
364 
365         Returns:
366             a sockaddr pointer to this.addr.
367 
368         In:
369             addr must not be null.
370 
371      **************************************************************************/
372 
373     public sockaddr* opAssign ( Addr* addr )
374     {
375         verify(addr !is null);
376         (&this).addr = *addr;
377 
378         return cast (sockaddr*) &(&this).addr;
379     }
380 
381     /**************************************************************************
382 
383         Clears this.addr.
384 
385      **************************************************************************/
386 
387     public void clear ( )
388     {
389         (&this).addr = (&this).addr_init;
390     }
391 
392     /**************************************************************************
393 
394         Obtains a slice to the binary address in from this.addr, that is,
395         for IPv4 the sin_addr or for IPv6 the sin6_addr field, respectively.
396 
397         Returns:
398             a slice to the binary address data from this.addr.
399 
400      **************************************************************************/
401 
402     public void[] address_n ( )
403     {
404         with ((&this).addr) static if (IPv6)
405         {
406             return (cast (void*) &sin6_addr)[0 .. sin6_addr.sizeof];
407         }
408         else
409         {
410             return (cast (void*) &sin_addr)[0 .. sin_addr.sizeof];
411         }
412     }
413 
414     // TODO
415 
416     public int getnameinfo(mstring host, mstring serv,
417                            GetNameInfoFlags flags = GetNameInfoFlags.None)
418     {
419         return core.sys.posix.netdb.getnameinfo(
420             cast (sockaddr*) &(&this).addr, (&this).addr.sizeof,
421             host.ptr, castFrom!(size_t).to!(int)(host.length), serv.ptr,
422             castFrom!(size_t).to!(int)(serv.length), flags);
423     }
424 }