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 module ocean.sys.GetIfAddrs; 17 18 19 import ocean.transition; 20 21 import core.stdc.errno; 22 import core.stdc.string; 23 import ocean.stdc.posix.arpa.inet; 24 import ocean.stdc.posix.netinet.in_: sockaddr_in, sockaddr_in6; 25 import ocean.stdc.posix.sys.socket; 26 27 static if (__VERSION__ >= 2000 && __VERSION__ < 2073) 28 { 29 extern (C) 30 { 31 struct ifaddrs 32 { 33 /// Next item in the list 34 ifaddrs* ifa_next; 35 /// Name of the interface 36 char* ifa_name; 37 /// Flags from SIOCGIFFLAGS 38 uint ifa_flags; 39 /// Address of interface 40 sockaddr* ifa_addr; 41 /// Netmask of interface 42 sockaddr* ifa_netmask; 43 44 union 45 { 46 /// Broadcast address of the interface 47 sockaddr* ifu_broadaddr; 48 /// Point-to-point destination addresss 49 sockaddr* if_dstaddr; 50 } 51 52 /// Address specific data 53 void* ifa_data; 54 }; 55 56 int getifaddrs (ifaddrs**); 57 58 void freeifaddrs (ifaddrs*); 59 } 60 } 61 else 62 import core.sys.linux.ifaddrs; 63 64 import core.sys.posix.sys.socket: AF_INET, AF_INET6; 65 66 import ocean.core.Test; 67 import ocean.core.TypeConvert; 68 import ocean.sys.ErrnoException; 69 import ocean.text.util.StringC; 70 71 /******************************************************************************* 72 73 Exception type to be thrown when fetching the IP address(es) for the 74 interface fails. 75 76 *******************************************************************************/ 77 78 class ResolveIPException : ErrnoException 79 { 80 } 81 82 /******************************************************************************* 83 84 Returns IP addresses for the network interface. 85 86 Params: 87 interface_name = Name of the interface (e.g. eth0) 88 ipv6 = true: fetch IPv6 addresses, false: IPv4 89 90 Returns: 91 IP addresses of the interface for the given family as strings, 92 if they could be resolved, otherwise an empty array. 93 94 *******************************************************************************/ 95 96 istring[] getAddrsForInterface( cstring interface_name, bool ipv6 = false ) 97 { 98 istring[] addresses; 99 bool delegate_called = false; 100 101 auto ret = getAddrsForInterface(interface_name, ipv6, 102 (cstring address, int getnameinfo_status) 103 { 104 delegate_called = true; 105 106 if (getnameinfo_status != 0) 107 { 108 throw (new ResolveIPException).set(getnameinfo_status, 109 "getnameinfo failed"); 110 } 111 112 if (address.length) 113 { 114 addresses ~= idup(address); 115 } 116 117 return false; 118 }); 119 120 if (ret && !delegate_called) 121 { 122 throw (new ResolveIPException).useGlobalErrno("getifaddrs"); 123 } 124 125 return addresses; 126 } 127 128 129 /******************************************************************************* 130 131 Iterates over IP addresses for the network interface. 132 133 Obtains the network address of the local system from getifaddrs() and calls 134 dg with a host and service name string for each of these addresses. If host 135 and service name string formatting failed for an address, dg is called with 136 a null address and the status code of the conversion function, 137 getnameinfo(). See the manpage of getnameinfo() for its status codes. 138 139 dg should return false to continue or true to stop iteration. 140 141 If dg isn't called and return value is true, getifaddrs() has failed; 142 in this case check errno and see the getnameinfo() manpage. 143 144 Params: 145 interface_name = Name of the interface (e.g. eth0) 146 ipv6 = true: fetch IPv6 addresses, false: IPv4 147 dg = iteration delegate 148 149 Returns: 150 true if either dg returned true to stop the iteration or getifaddrs() 151 failed or false if the iteration finished normally. 152 153 *******************************************************************************/ 154 155 bool getAddrsForInterface( cstring interface_name, bool ipv6, 156 scope bool delegate ( cstring address, 157 int getnameinfo_status ) dg ) 158 { 159 ifaddrs* ifaddr; 160 161 // Try to fetch a linked list of interfaces and their adresses 162 if (getifaddrs(&ifaddr) == -1) 163 { 164 return true; 165 } 166 167 // ifaddr is allocated, and it needs to be freed! 168 scope(exit) freeifaddrs(ifaddr); 169 170 auto salen = ipv6? sockaddr_in6.sizeof : sockaddr_in.sizeof, 171 family = ipv6? AF_INET6 : AF_INET; 172 173 // Iterate through each interface and check if the interface 174 // is the one that we're looking for. 175 176 for (auto ifa = ifaddr; ifa !is null; ifa = ifa.ifa_next) 177 { 178 /*********************************************************************** 179 180 From the `getifaddrs` man page: 181 The ifa_addr field points to a structure containing the 182 interface address. (The sa_family subfield should be consulted 183 to determine the format of the address structure.) This field 184 may contain a null pointer. 185 186 ***********************************************************************/ 187 188 if(!ifa.ifa_addr) 189 { 190 continue; 191 } 192 193 if (interface_name != StringC.toDString(ifa.ifa_name)) 194 { 195 continue; 196 } 197 198 if (ifa.ifa_addr.sa_family != family) 199 { 200 continue; 201 } 202 203 char[NI_MAXHOST] buffer; 204 205 // Use getnameinfo to get the interface address 206 207 auto result = getnameinfo(ifa.ifa_addr, 208 castFrom!(size_t).to!(uint)(salen), 209 buffer.ptr, 210 buffer.length, 211 null, 212 0, 213 NI_NUMERICHOST); 214 215 // Check the result code and invoke the iteration delegate 216 if (dg(result? null : StringC.toDString(buffer.ptr), result)) 217 { 218 return true; 219 } 220 } 221 222 return false; 223 }