1 /*******************************************************************************
2 
3     Declaration of and wrappers for the addrinfo address lookup API.
4 
5     Copyright:
6         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module ocean.sys.socket.AddrInfo;
17 
18 import ocean.core.Array: concat;
19 import ocean.core.TypeConvert;
20 import ocean.core.Verify;
21 import ocean.meta.types.Qualifiers;
22 
23 import core.stdc.errno: errno, EAFNOSUPPORT;
24 import core.stdc.string: strlen;
25 import core.sys.posix.arpa.inet: inet_ntop, inet_pton, ntohs, htons, htonl;
26 import core.sys.posix.netinet.in_: sockaddr, socklen_t,
27                                    sockaddr_in,  AF_INET,  INET_ADDRSTRLEN,
28                                    sockaddr_in6, AF_INET6, INET6_ADDRSTRLEN,
29                                    SOCK_STREAM, IPPROTO_TCP;
30 
31 
32 /*******************************************************************************
33 
34     Address information struct as returned by getaddrinfo().
35 
36 *******************************************************************************/
37 
38 struct addrinfo
39 {
40     /***************************************************************************
41 
42         getaddrinfo() flags.
43 
44     ***************************************************************************/
45 
46     enum Flags
47     {
48         None = 0,
49         AI_PASSIVE                  = 1 << 0, /// Socket address is intended for `bind`.
50         AI_CANONNAME                = 1 << 1, /// Request for canonical name.
51         AI_NUMERICHOST              = 1 << 2, /// Don't use name resolution.
52         AI_V4MAPPED                 = 1 << 3, /// IPv4 mapped addresses are acceptable.
53         AI_ALL                      = 1 << 4, /// Return IPv4 mapped and IPv6 addresses.
54         AI_ADDRCONFIG               = 1 << 5, /// Use configuration of this host to choose returned address type.
55         AI_IDN                      = 1 << 6, /// IDN encode input (assuming it is encoded in the current locale's character set) before looking it up.
56         AI_CANONIDN                 = 1 << 7, /// Translate canonical name from IDN format.
57         AI_IDN_ALLOW_UNASSIGNED     = 1 << 8, /// Don't reject unassigned Unicode code points.
58         AI_IDN_USE_STD3_ASCII_RULES = 1 << 9  /// Validate strings according to STD3 rules.
59 
60     }
61 
62     /***************************************************************************
63 
64         Error codes returned by getaddrinfo() (not passed via errno).
65 
66     ***************************************************************************/
67 
68     enum ErrorCode
69     {
70         Success = 0,
71         EAI_BADFLAGS    = -1,     /// Invalid value for ai_flags field.
72         EAI_NONAME      = -2,     /// NAME or SERVICE is unknown.
73         EAI_AGAIN       = -3,     /// Temporary failure in name resolution.
74         EAI_FAIL        = -4,     /// Non-recoverable failure in name res.
75         EAI_FAMILY      = -6,     /// `ai_family` not supported.
76         EAI_SOCKTYPE    = -7,     /// `ai_socktype` not supported.
77         EAI_SERVICE     = -8,     /// SERVICE not supported for `ai_socktype`.
78         EAI_MEMORY      = -10,    /// Memory allocation failure.
79         EAI_SYSTEM      = -11,    /// System error returned in `errno`.
80         EAI_OVERFLOW    = -12,    /// Argument buffer overflow.
81         EAI_NODATA      = -5,     /// No address associated with NAME.
82         EAI_ADDRFAMILY  = -9,     /// Address family for NAME not supported.
83         EAI_INPROGRESS  = -100,   /// Processing request in progress.
84         EAI_CANCELED    = -101,   /// Request canceled.
85         EAI_NOTCANCELED = -102,   /// Request not canceled.
86         EAI_ALLDONE     = -103,   /// All requests done.
87         EAI_INTR        = -104,   /// Interrupted by a signal.
88         EAI_IDN_ENCODE  = -105,   /// IDN encoding failed.
89     }
90 
91     /***************************************************************************
92 
93         Data fields.
94 
95     ***************************************************************************/
96 
97     Flags           ai_flags;
98     int             ai_family,
99                     ai_socktype,
100                     ai_protocol;
101     socklen_t       ai_addrlen;               // The manpage says size_t: WRONG!
102     sockaddr*       ai_addr;
103     char*           ai_canonname;
104     typeof(&this)   ai_next;
105 
106     alias .INET6_ADDRSTRLEN INET6_ADDRSTRLEN;
107     alias .INET_ADDRSTRLEN  INET_ADDRSTRLEN;
108 
109     /***************************************************************************
110 
111         Obtains the current IP address in standard notation.
112 
113         Params:
114             dst = destination buffer
115 
116         Returns:
117             a slice to the resulting IP address string in dst on success or null
118             on error. On error errno is set appropriately.
119 
120         Errors:
121             EAFNOSUPPORT: The address family is not supported (AF_INET/IPv4 or
122                           AF_INET6/IPv6).
123 
124         In:
125             - this.ai_addr must not be null: this instance should have been
126               obtained by getaddrinfo() or manually initialised.
127             - dst.length must be at least the required address length for the
128               address family, INET_ADDRSTRLEN for IPv4 or INET6_ADDRSTRLEN for
129               IPv6.
130 
131         Out:
132             If the resulting slice is not null, it slices dst from the
133             beginning.
134 
135     ***************************************************************************/
136 
137     mstring ipAddress ( mstring dst )
138     out (result)
139     {
140         if (result.length) assert (result.ptr is dst.ptr);
141     }
142     do
143     {
144         void sanity_check ( )
145         {
146             verify(this.ai_addr !is null);
147 
148             switch (this.ai_family)
149             {
150                 case AF_INET:
151                     verify(
152                         dst.length >= INET_ADDRSTRLEN,
153                         "dst.length expected to be at least "
154                             ~ INET_ADDRSTRLEN.stringof
155                     );
156                     break;
157 
158                 case AF_INET6:
159                     verify(
160                         dst.length >= INET6_ADDRSTRLEN,
161                         "dst.length expected to be at least "
162                             ~ INET6_ADDRSTRLEN.stringof
163                     );
164                     break;
165 
166                 default: // will fail with EAFNOSUPPORT anyway
167             }
168         }
169 
170         sanity_check();
171 
172         void* addr;
173 
174         switch (this.ai_family)
175         {
176             case AF_INET:
177                 addr = &(*cast (sockaddr_in*) this.ai_addr).sin_addr;
178                 break;
179 
180             case AF_INET6:
181                 addr = &(*cast (sockaddr_in6*) this.ai_addr).sin6_addr;
182                 break;
183 
184             default:
185                 .errno = EAFNOSUPPORT; // inet_ntop() would do the same
186                 return null;
187         }
188 
189         auto address_p = .inet_ntop(this.ai_family, addr, dst.ptr,
190             castFrom!(size_t).to!(int)(dst.length));
191         // inet_ntop returns const pointer even if spec says it will always
192         // use `dst` memory. Using `dst` directly to avoid casts.
193         return address_p ? dst.ptr[0 .. strlen(dst.ptr)] : null;
194     }
195 
196     /**************************************************************************
197 
198         Obtains the current port number.
199 
200         Returns:
201             the current port number.
202 
203         Errors:
204             EAFNOSUPPORT: The address family is not supported (AF_INET/IPv4 or
205                           AF_INET6/IPv6).
206 
207         In:
208             this.ai_addr must not be null: this instance should have been
209             obtained by getaddrinfo() or manually initialised.
210 
211      **************************************************************************/
212 
213     ushort port ( )
214     {
215         verify(this.ai_addr !is null);
216         .errno = 0;
217 
218         switch (this.ai_family)
219         {
220             case AF_INET:
221                 return .ntohs((cast (sockaddr_in*) this.ai_addr).sin_port);
222 
223             case AF_INET6:
224                 return .ntohs((cast (sockaddr_in6*) this.ai_addr).sin6_port);
225 
226             default:
227                 .errno = EAFNOSUPPORT;
228                 return 0;
229         }
230     }
231 
232     /**************************************************************************
233 
234         Obtains the current canonical name.
235 
236         Returns:
237             the current canonical name or null.
238 
239      **************************************************************************/
240 
241     char[] canonname ( )
242     {
243         return this.ai_canonname? this.ai_canonname[0 .. strlen(this.ai_canonname)] : null;
244     }
245 
246     /**************************************************************************
247 
248         'foreach' iteration over the linked list of instances of this struct;
249         starting with this instance.
250 
251         Do not change any of the pointer struct members.
252 
253      **************************************************************************/
254 
255     int opApply ( scope int delegate ( ref typeof (this) info ) dg )
256     {
257         int result = 0;
258 
259         for (typeof(&this) info = &this; info && !result; info = info.ai_next)
260         {
261             result = dg(*info);
262         }
263 
264         return result;
265     }
266 }
267 
268 extern (C)
269 {
270     /**************************************************************************
271 
272         Obtains the error message for errcode.
273 
274         Params:
275             errcode = error code returned by getaddrinfo()
276 
277         Returns:
278             the error message for errcode.
279 
280      **************************************************************************/
281 
282     public char* gai_strerror(addrinfo.ErrorCode errcode);
283 
284     /**************************************************************************
285 
286         Given node and service, which identify an Internet host and  a  service,
287         getaddrinfo()  returns  one  or  more addrinfo structures, each of which
288         contains an Internet address that can be specified in a call to  bind(2)
289         or  connect(2).   The  getaddrinfo() function combines the functionality
290         provided by the getservbyname(3) and getservbyport(3) functions  into  a
291         single  interface,  but  unlike  the  latter functions, getaddrinfo() is
292         reentrant and allows programs to  eliminate  IPv4-versus-IPv6  dependen‐
293         cies.
294 
295         The  addrinfo  structure  used  by  getaddrinfo() contains the following
296         fields:
297 
298             struct addrinfo {
299                 int              ai_flags;
300                 int              ai_family;
301                 int              ai_socktype;
302                 int              ai_protocol;
303                 size_t           ai_addrlen;
304                 struct sockaddr *ai_addr;
305                 char            *ai_canonname;
306                 struct addrinfo *ai_next;
307             };
308 
309         The hints argument points to an addrinfo structure that specifies crite‐
310         ria  for  selecting  the  socket address structures returned in the list
311         pointed to by res.  If hints is not NULL it points to an addrinfo struc‐
312         ture whose ai_family, ai_socktype, and ai_protocol specify criteria that
313         limit the set of socket addresses returned by getaddrinfo(), as follows:
314 
315         ai_family   This field specifies the  desired  address  family  for  the
316                     returned  addresses.   Valid  values  for this field include
317                     AF_INET and AF_INET6.  The value  AF_UNSPEC  indicates  that
318                     getaddrinfo() should return socket addresses for any address
319                     family (either IPv4 or IPv6, for example) that can  be  used
320                     with node and service.
321 
322         ai_socktype This  field specifies the preferred socket type, for example
323                     SOCK_STREAM or SOCK_DGRAM.  Specifying 0 in this field indi‐
324                     cates  that  socket addresses of any type can be returned by
325                     getaddrinfo().
326 
327         ai_protocol This field specifies the protocol for  the  returned  socket
328                     addresses.  Specifying 0 in this field indicates that socket
329                     addresses with  any  protocol  can  be  returned  by  getad‐
330                     drinfo().
331 
332         ai_flags    This  field  specifies  additional options, described below.
333                     Multiple  flags  are  specified  by  bitwise   OR-ing   them
334                     together.
335 
336         All  the  other fields in the structure pointed to by hints must contain
337         either 0 or a null pointer, as appropriate.  Specifying hints as NULL is
338         equivalent  to  setting  ai_socktype  and ai_protocol to 0; ai_family to
339         AF_UNSPEC; and ai_flags to (AI_V4MAPPED | AI_ADDRCONFIG).
340 
341         node specifies either a numerical network address  (for  IPv4,  numbers-
342         and-dots  notation  as  supported by inet_aton(3); for IPv6, hexadecimal
343         string format as supported by  inet_pton(3)),  or  a  network  hostname,
344         contains the AI_NUMERICHOST flag then node must be a  numerical  network
345         address.   The  AI_NUMERICHOST  flag  suppresses any potentially lengthy
346         network host address lookups.
347 
348         If the AI_PASSIVE flag is specified in hints.ai_flags, and node is NULL,
349         then  the  returned  socket  addresses will be suitable for bind(2)ing a
350         socket that will accept(2) connections.   The  returned  socket  address
351         will  contain  the  "wildcard  address"  (INADDR_ANY for IPv4 addresses,
352         IN6ADDR_ANY_INIT for IPv6 address).  The wildcard  address  is  used  by
353         applications  (typically  servers)  that intend to accept connections on
354         any of the hosts's network addresses.  If node is  not  NULL,  then  the
355         AI_PASSIVE flag is ignored.
356 
357         If  the  AI_PASSIVE flag is not set in hints.ai_flags, then the returned
358         socket addresses will be suitable for use with connect(2), sendto(2), or
359         sendmsg(2).   If  node  is NULL, then the network address will be set to
360         the loopback interface  address  (INADDR_LOOPBACK  for  IPv4  addresses,
361         IN6ADDR_LOOPBACK_INIT  for  IPv6  address); this is used by applications
362         that intend to communicate with peers running on the same host.
363 
364         service sets the port in each returned address structure.  If this argu‐
365         ment is a service name (see services(5)), it is translated to the corre‐
366         sponding port number.  This argument can also be specified as a  decimal
367         number,  which  is simply converted to binary.  If service is NULL, then
368         the port number of the returned socket addresses will be left uninitial‐
369         ized.   If  AI_NUMERICSERV is specified in hints.ai_flags and service is
370         not NULL, then service must point to a string containing a numeric  port
371         number.   This  flag is used to inhibit the invocation of a name resolu‐
372         tion service in cases where it is known not to be required.
373 
374         Either node or service, but not both, may be NULL.
375 
376         The getaddrinfo() function allocates and initializes a  linked  list  of
377         addrinfo  structures, one for each network address that matches node and
378         service, subject to any restrictions imposed by  hints,  and  returns  a
379         pointer  to  the start of the list in res.  The items in the linked list
380         are linked by the ai_next field.
381 
382         There are several reasons why the linked list may  have  more  than  one
383         addrinfo  structure, including: the network host is multihomed, accessi‐
384         ble over multiple protocols (e.g. both AF_INET  and  AF_INET6);  or  the
385         same  service  is  available from multiple socket types (one SOCK_STREAM
386         address and another SOCK_DGRAM address,  for  example).   Normally,  the
387         application  should  try  using the addresses in the order in which they
388         are returned.  The sorting function used within getaddrinfo() is defined
389         in RFC 3484; the order can be tweaked for a particular system by editing
390         /etc/gai.conf (available since glibc 2.5).
391 
392         If hints.ai_flags includes the AI_CANONNAME flag, then the  ai_canonname
393         field  of  the  first of the addrinfo structures in the returned list is
394         set to point to the official name of the host.
395 
396         The remaining fields of each returned addrinfo structure are initialized
397         as follows:
398 
399         * The  ai_family,  ai_socktype, and ai_protocol fields return the socket
400           creation parameters (i.e., these fields have the same meaning  as  the
401           corresponding  arguments  of socket(2)).  For example, ai_family might
402           return AF_INET or AF_INET6; ai_socktype  might  return  SOCK_DGRAM  or
403           SOCK_STREAM; and ai_protocol returns the protocol for the socket.
404 
405         * A  pointer  to  the socket address is placed in the ai_addr field, and
406           the length  of  the  socket  address,  in  bytes,  is  placed  in  the
407           ai_addrlen field.
408 
409         If  hints.ai_flags  includes the AI_ADDRCONFIG flag, then IPv4 addresses
410         are returned in the list pointed to by res only if the local system  has
411         at  least  one  IPv4  address  configured,  and  IPv6 addresses are only
412         returned if the local system has at least one IPv6 address configured.
413 
414         If hint.ai_flags specifies the AI_V4MAPPED flag, and hints.ai_family was
415         specified  as  AF_INET6,  and no matching IPv6 addresses could be found,
416         then return IPv4-mapped IPv6 addresses in the list pointed  to  by  res.
417         If  both  AI_V4MAPPED  and  AI_ALL are specified in hints.ai_flags, then
418         return both IPv6 and IPv4-mapped IPv6 addresses in the list  pointed  to
419         by res.  AI_ALL is ignored if AI_V4MAPPED is not also specified.
420 
421         The  freeaddrinfo() function frees the memory that was allocated for the
422         dynamically allocated linked list res.
423 
424         Extensions to getaddrinfo() for Internationalized Domain Names
425         Starting with glibc 2.3.4, getaddrinfo() has  been  extended  to  selec‐
426         tively  allow  the  incoming  and outgoing hostnames to be transparently
427         converted to and from the Internationalized  Domain  Name  (IDN)  format
428         (see  RFC 3490, Internationalizing Domain Names in Applications (IDNA)).
429         Four new flags are defined:
430 
431         AI_IDN If this flag is specified, then the node name given  in  node  is
432                converted  to  IDN  format  if necessary.  The source encoding is
433                that of the current locale.
434 
435                If the input name contains non-ASCII  characters,  then  the  IDN
436                encoding  is  used.   Those  parts of the node name (delimited by
437                dots) that contain non-ASCII characters are encoded  using  ASCII
438                Compatible Encoding (ACE) before being passed to the name resolu‐
439                tion functions.
440 
441         AI_CANONIDN
442                After a successful name lookup, and if the AI_CANONNAME flag  was
443                specified,  getaddrinfo()  will  return the canonical name of the
444                node corresponding to the addrinfo structure value  passed  back.
445                The  return  value  is an exact copy of the value returned by the
446                name resolution function.
447 
448                If the name is encoded using ACE, then it will contain  the  xn--
449                prefix  for one or more components of the name.  To convert these
450                components into a readable  form  the  AI_CANONIDN  flag  can  be
451                passed  in  addition  to  AI_CANONNAME.   The resulting string is
452                encoded using the current locale's encoding.
453 
454         AI_IDN_ALLOW_UNASSIGNED, AI_IDN_USE_STD3_ASCII_RULES
455                Setting these flags will enable the IDNA_ALLOW_UNASSIGNED  (allow
456                unassigned  Unicode  code  points)  and IDNA_USE_STD3_ASCII_RULES
457                (check output to make sure it  is  a  STD3  conforming  hostname)
458                flags respectively to be used in the IDNA handling.
459 
460 
461         getaddrinfo()  returns 0 if it succeeds, or one of the following nonzero
462         error codes:
463 
464         EAI_ADDRFAMILY
465                The specified network host does not have any network addresses in
466                the requested address family.
467 
468         EAI_AGAIN
469                The  name  server  returned  a temporary failure indication.  Try
470                again later.
471 
472         EAI_BADFLAGS
473                hints.ai_flags  contains  invalid   flags;   or,   hints.ai_flags
474                included AI_CANONNAME and name was NULL.
475 
476         EAI_FAIL
477                The name server returned a permanent failure indication.
478 
479         EAI_FAMILY
480                The requested address family is not supported.
481 
482         EAI_MEMORY
483                Out of memory.
484 
485         EAI_NODATA
486                The  specified network host exists, but does not have any network
487                addresses defined.
488 
489         EAI_NONAME
490                The node or service is not known; or both node  and  service  are
491                NULL;  or AI_NUMERICSERV was specified in hints.ai_flags and ser‐
492                vice was not a numeric port-number string.
493 
494         EAI_SERVICE
495                The requested service is not available for the  requested  socket
496                type.   It  may  be  available  through another socket type.  For
497                example, this error could occur if service was "shell" (a service
498                only  available  on stream sockets), and either hints.ai_protocol
499                was IPPROTO_UDP, or  hints.ai_socktype  was  SOCK_DGRAM;  or  the
500                error  could occur if service was not NULL, and hints.ai_socktype
501                was SOCK_RAW (a socket type that does not support the concept  of
502                services).
503 
504         EAI_SOCKTYPE
505                The  requested  socket  type is not supported.  This could occur,
506                for  example,  if  hints.ai_socktype  and  hints.ai_protocol  are
507                inconsistent (e.g., SOCK_DGRAM and IPPROTO_TCP, respectively).
508 
509         EAI_SYSTEM
510                Other system error, check errno for details.
511 
512         The  gai_strerror()  function  translates  these  error codes to a human
513         readable string, suitable for error reporting.
514 
515      **************************************************************************/
516 
517     private addrinfo.ErrorCode getaddrinfo(char* node, char* service,
518                                            addrinfo* hints, addrinfo** res);
519 
520     private void freeaddrinfo(addrinfo* res);
521 
522 }
523 
524 /******************************************************************************
525 
526     Wraps getaddrinfo()/freeaddrinfo() and manages an addrinfo instance.
527 
528  ******************************************************************************/
529 
530 class AddrInfo : AddrInfoC
531 {
532     /**************************************************************************
533 
534         String nul-termination buffers.
535 
536      **************************************************************************/
537 
538     private mstring node, service;
539 
540     /**************************************************************************
541 
542         Returns:
543             the current address info as most recently obtained.
544 
545      **************************************************************************/
546 
547     public override addrinfo* info ( )
548     {
549         return this.info_;
550     }
551 
552     /**************************************************************************
553 
554         Gets the address info for a TCP/IP node and/or service.
555 
556         Params:
557             node    = node name (may be null)
558             service = service name (may be null)
559             ipv6    = false: get the IPv4, true: get the IPv6 address
560             flags   = getaddrinfo() flags
561 
562         Returns:
563             0 on success or an error code on failure, see addrinfo.ErrorCode.
564 
565      **************************************************************************/
566 
567     public ErrorCode getTcpIp ( cstring node, cstring service, bool ipv6,
568                                 addrinfo.Flags flags = addrinfo.Flags.None )
569     {
570         return super.getTcpIp(this.node.toCstr(node),
571                               this.service.toCstr(service), ipv6, flags);
572     }
573 
574     /**************************************************************************
575 
576         Gets the address info for an IP node and/or service.
577 
578         Params:
579             node     = node name (may be null)
580             service  = service name (may be null)
581             ipv6     = false: get the IPv4, true: get the IPv6 address
582             type     = socket type (0 for any type)
583             protocol = socket protocol (0 for any protocol)
584             flags    = getaddrinfo() flags
585 
586         Returns:
587             0 on success or an error code on failure, see addrinfo.ErrorCode.
588 
589      **************************************************************************/
590 
591     public ErrorCode getIp ( cstring node, cstring service,
592                              bool ipv6, int type, int protocol,
593                              addrinfo.Flags flags = addrinfo.Flags.None )
594     {
595         return super.getIp(this.node.toCstr(node), this.service.toCstr(service),
596                            ipv6, type, protocol, flags);
597     }
598 
599     /**************************************************************************
600 
601         Gets the address info for a node and/or service.
602 
603         Params:
604             node     = node name (may be null)
605             service  = service name (may be null)
606             family   = socket family (0 for any family)
607             type     = socket type (0 for any type)
608             protocol = socket protocol (0 for any protocol)
609             flags    = getaddrinfo() flags
610 
611         Returns:
612             0 on success or an error code on failure, see addrinfo.ErrorCode.
613 
614      **************************************************************************/
615 
616     public ErrorCode get ( cstring node, cstring service,
617                            int family, int type, int protocol,
618                            addrinfo.Flags flags = addrinfo.Flags.None )
619     {
620         return super.get(this.node.toCstr(node), this.service.toCstr(service),
621                          family, type, protocol, flags);
622     }
623 
624     /**************************************************************************
625 
626         Gets the address info for a node and/or service.
627 
628         Params:
629             node    = node name (may be null)
630             service = service name (may be null)
631             hints   = addrinfo instance specifying the socket family, type,
632                       protocol and flags or null to get all available addresses
633 
634         Returns:
635             0 on success or an error code on failure, see addrinfo.ErrorCode.
636 
637      **************************************************************************/
638 
639     public ErrorCode get ( cstring node, cstring service, addrinfo* hints = null )
640     {
641         return super.get(this.node.toCstr(node), this.service.toCstr(service),
642                          hints);
643     }
644 }
645 
646 /**************************************************************************
647 
648     Appends a nul-terminator to src, storing the result in dst.
649 
650     Params:
651         dst = destination string buffer
652         src = string to nul-terminate
653 
654     Returns:
655         dst.ptr or null if src is empty.
656 
657  **************************************************************************/
658 
659 private char* toCstr ( ref mstring dst, cstring src )
660 {
661     return src.length? dst.concat(src, "\0"[]).ptr : null;
662 }
663 
664 /******************************************************************************
665 
666     Wraps getaddrinfo()/freeaddrinfo() and manages an addrinfo instance; uses
667     C strings as arguments. This class is memory-friendly when used with 'scope'
668     instances.
669 
670  ******************************************************************************/
671 
672 class AddrInfoC
673 {
674     alias addrinfo.Flags     Flags;
675     alias addrinfo.ErrorCode ErrorCode;
676 
677     /**************************************************************************
678 
679         addrinfo instance.
680 
681      **************************************************************************/
682 
683     private addrinfo* info_ = null;
684 
685     /**************************************************************************
686 
687         IP address conversion buffer
688 
689      **************************************************************************/
690 
691     static assert (INET6_ADDRSTRLEN > INET_ADDRSTRLEN);
692 
693     char[INET6_ADDRSTRLEN] ip_address_buf;
694 
695     /**************************************************************************
696 
697         Destructor.
698 
699      **************************************************************************/
700 
701     ~this ( )
702     {
703         if (this.info_)
704         {
705             freeaddrinfo(this.info_);
706         }
707     }
708 
709     /**************************************************************************
710 
711         Gets the address info for a TCP/IP node and/or service.
712 
713         Params:
714             node    = node name (may be null)
715             service = service name (may be null)
716             ipv6    = false: get the IPv4, true: get the IPv6 address
717             flags   = getaddrinfo() flags
718 
719         Returns:
720             0 on success or an error code on failure, see addrinfo.ErrorCode.
721 
722      **************************************************************************/
723 
724     public ErrorCode getTcpIp ( char* node, char* service, bool ipv6,
725                                 addrinfo.Flags flags = addrinfo.Flags.None )
726     {
727         return this.getIp(node, service, ipv6, SOCK_STREAM, IPPROTO_TCP, flags);
728     }
729 
730     /**************************************************************************
731 
732         Gets the address info for an IP node and/or service.
733 
734         Params:
735             node     = node name (may be null)
736             service  = service name (may be null)
737             ipv6     = false: get the IPv4, true: get the IPv6 address
738             type     = socket type (0 for any type)
739             protocol = socket protocol (0 for any protocol)
740             flags    = getaddrinfo() flags
741 
742         Returns:
743             0 on success or an error code on failure, see addrinfo.ErrorCode.
744 
745      **************************************************************************/
746 
747     public ErrorCode getIp ( char* node, char* service,
748                              bool ipv6, int type, int protocol,
749                              addrinfo.Flags flags = addrinfo.Flags.None )
750     {
751         return this.get(node, service, ipv6? AF_INET6 : AF_INET, type, protocol);
752     }
753 
754 
755     /**************************************************************************
756 
757         Gets the address info for a node and/or service.
758 
759         Params:
760             node     = node name (may be null)
761             service  = service name (may be null)
762             family   = socket family (0 for any family)
763             type     = socket type (0 for any type)
764             protocol = socket protocol (0 for any protocol)
765             flags    = getaddrinfo() flags
766 
767         Returns:
768             0 on success or an error code on failure, see addrinfo.ErrorCode.
769 
770      **************************************************************************/
771 
772     public ErrorCode get ( char* node, char* service,
773                            int family, int type, int protocol,
774                            addrinfo.Flags flags = addrinfo.Flags.None )
775     {
776         auto hints = addrinfo(flags, family, type, protocol);
777 
778         return this.get(node, service, &hints);
779     }
780 
781     /**************************************************************************
782 
783         Gets the address info for a node and/or service.
784 
785         Params:
786             node    = node name (may be null)
787             service = service name (may be null)
788             hints   = addrinfo instance specifying the socket family, type,
789                       protocol and flags or null to get all available addresses
790 
791         Returns:
792             0 on success or an error code on failure, see addrinfo.ErrorCode.
793 
794      **************************************************************************/
795 
796     public ErrorCode get ( char* node, char* service, addrinfo* hints = null )
797     {
798         if (this.info_)
799         {
800             freeaddrinfo(this.info_);
801 
802             this.info_ = null;
803         }
804 
805         return .getaddrinfo(node, service, hints, &this.info_);
806     }
807 
808     /**************************************************************************
809 
810         Returns:
811             the current address info as most recently obtained or null if the
812             last get() failed or get() has not been called yet.
813 
814      **************************************************************************/
815 
816     public addrinfo* info ( )
817     {
818         return this.info_;
819     }
820 
821     /***************************************************************************
822 
823         Obtains the current IP address in standard notation.
824 
825         Returns:
826             a slice to the resulting IP address string in dst on success or null
827             either on error or if the last get() failed or get() has not been
828             called yet; errno is then 0. On success a nul-terminator follows the
829             sliced string so its .ptr is a C string. On error errno is set
830             appropriately.
831 
832         Errors:
833             EAFNOSUPPORT: The address family is not supported (AF_INET/IPv4 or
834                           AF_INET6/IPv6).
835 
836     ***************************************************************************/
837 
838     public char[] ip_address ( )
839     {
840         .errno = 0;
841 
842         return this.info_? this.info_.ipAddress(this.ip_address_buf) : null;
843     }
844 
845     /***************************************************************************
846 
847         Returns:
848             the current port or 0 if the last get() failed or get() has not been
849             called yet.
850 
851     ***************************************************************************/
852 
853     public ushort port ( )
854     {
855         return this.info_? this.info_.port : cast(ushort) 0;
856     }
857 
858     /***************************************************************************
859 
860         Returns:
861             the official host name or null if the last get() failed or get() has
862             not been called yet with Flags.AI_CANONNAME set. On success a
863             nul-terminator follows the sliced string so its .ptr is a C string.
864 
865     ***************************************************************************/
866 
867     public char[] canonname ( )
868     {
869         return this.info_? this.info_.canonname : null;
870     }
871 
872     /***************************************************************************
873 
874         Returns:
875             the official host name as a nul-terminated C string or null if the
876             last get() failed or get() has not been called yet with
877             Flags.AI_CANONNAME set.
878 
879     ***************************************************************************/
880 
881     public char* canonname_c ( )
882     {
883         return this.info_? this.info_.ai_canonname : null;
884     }
885 }