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