1 /*******************************************************************************
2 3 Functions to get IP address from the given interface.
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 moduleocean.sys.GetIfAddrs;
17 18 19 importocean.meta.types.Qualifiers;
20 21 importcore.stdc.errno;
22 importcore.stdc.string;
23 importcore.sys.posix.arpa.inet;
24 importcore.sys.posix.netdb : getnameinfo, NI_MAXHOST, NI_NUMERICHOST;
25 importcore.sys.posix.netinet.in_: sockaddr_in, sockaddr_in6;
26 importcore.sys.posix.sys.socket;
27 importcore.sys.linux.ifaddrs;
28 importcore.sys.posix.sys.socket: AF_INET, AF_INET6;
29 30 importocean.core.Test;
31 importocean.core.TypeConvert;
32 importocean.sys.ErrnoException;
33 importocean.text.util.StringC;
34 35 /*******************************************************************************
36 37 Exception type to be thrown when fetching the IP address(es) for the
38 interface fails.
39 40 *******************************************************************************/41 42 classResolveIPException : ErrnoException43 {
44 }
45 46 /*******************************************************************************
47 48 Returns IP addresses for the network interface.
49 50 Params:
51 interface_name = Name of the interface (e.g. eth0)
52 ipv6 = true: fetch IPv6 addresses, false: IPv4
53 54 Returns:
55 IP addresses of the interface for the given family as strings,
56 if they could be resolved, otherwise an empty array.
57 58 *******************************************************************************/59 60 istring[] getAddrsForInterface( cstringinterface_name, boolipv6 = false )
61 {
62 istring[] addresses;
63 booldelegate_called = false;
64 65 autoret = getAddrsForInterface(interface_name, ipv6,
66 (cstringaddress, intgetnameinfo_status)
67 {
68 delegate_called = true;
69 70 if (getnameinfo_status != 0)
71 {
72 throw (newResolveIPException).set(getnameinfo_status,
73 "getnameinfo failed");
74 }
75 76 if (address.length)
77 {
78 addresses ~= idup(address);
79 }
80 81 returnfalse;
82 });
83 84 if (ret && !delegate_called)
85 {
86 throw (newResolveIPException).useGlobalErrno("getifaddrs");
87 }
88 89 returnaddresses;
90 }
91 92 93 /*******************************************************************************
94 95 Iterates over IP addresses for the network interface.
96 97 Obtains the network address of the local system from getifaddrs() and calls
98 dg with a host and service name string for each of these addresses. If host
99 and service name string formatting failed for an address, dg is called with
100 a null address and the status code of the conversion function,
101 getnameinfo(). See the manpage of getnameinfo() for its status codes.
102 103 dg should return false to continue or true to stop iteration.
104 105 If dg isn't called and return value is true, getifaddrs() has failed;
106 in this case check errno and see the getnameinfo() manpage.
107 108 Params:
109 interface_name = Name of the interface (e.g. eth0)
110 ipv6 = true: fetch IPv6 addresses, false: IPv4
111 dg = iteration delegate
112 113 Returns:
114 true if either dg returned true to stop the iteration or getifaddrs()
115 failed or false if the iteration finished normally.
116 117 *******************************************************************************/118 119 boolgetAddrsForInterface( cstringinterface_name, boolipv6,
120 scopebooldelegate ( cstringaddress,
121 intgetnameinfo_status ) dg )
122 {
123 ifaddrs* ifaddr;
124 125 // Try to fetch a linked list of interfaces and their adresses126 if (getifaddrs(&ifaddr) == -1)
127 {
128 returntrue;
129 }
130 131 // ifaddr is allocated, and it needs to be freed!132 scope(exit) freeifaddrs(ifaddr);
133 134 autosalen = ipv6? sockaddr_in6.sizeof : sockaddr_in.sizeof,
135 family = ipv6? AF_INET6 : AF_INET;
136 137 // Iterate through each interface and check if the interface138 // is the one that we're looking for.139 140 for (autoifa = ifaddr; ifa !isnull; ifa = ifa.ifa_next)
141 {
142 /***********************************************************************
143 144 From the `getifaddrs` man page:
145 The ifa_addr field points to a structure containing the
146 interface address. (The sa_family subfield should be consulted
147 to determine the format of the address structure.) This field
148 may contain a null pointer.
149 150 ***********************************************************************/151 152 if(!ifa.ifa_addr)
153 {
154 continue;
155 }
156 157 if (interface_name != StringC.toDString(ifa.ifa_name))
158 {
159 continue;
160 }
161 162 if (ifa.ifa_addr.sa_family != family)
163 {
164 continue;
165 }
166 167 char[NI_MAXHOST] buffer;
168 169 // Use getnameinfo to get the interface address170 171 autoresult = getnameinfo(ifa.ifa_addr,
172 castFrom!(size_t).to!(uint)(salen),
173 buffer.ptr,
174 buffer.length,
175 null,
176 0,
177 NI_NUMERICHOST);
178 179 // Check the result code and invoke the iteration delegate180 if (dg(result? null : StringC.toDString(buffer.ptr), result))
181 {
182 returntrue;
183 }
184 }
185 186 returnfalse;
187 }