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.meta.types.Qualifiers; 20 21 import core.stdc.errno; 22 import core.stdc.string; 23 import core.sys.posix.arpa.inet; 24 import core.sys.posix.netdb : getnameinfo, NI_MAXHOST, NI_NUMERICHOST; 25 import core.sys.posix.netinet.in_: sockaddr_in, sockaddr_in6; 26 import core.sys.posix.sys.socket; 27 import core.sys.linux.ifaddrs; 28 import core.sys.posix.sys.socket: AF_INET, AF_INET6; 29 30 import ocean.core.Test; 31 import ocean.core.TypeConvert; 32 import ocean.sys.ErrnoException; 33 import ocean.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 class ResolveIPException : ErrnoException 43 { 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( cstring interface_name, bool ipv6 = false ) 61 { 62 istring[] addresses; 63 bool delegate_called = false; 64 65 auto ret = getAddrsForInterface(interface_name, ipv6, 66 (cstring address, int getnameinfo_status) 67 { 68 delegate_called = true; 69 70 if (getnameinfo_status != 0) 71 { 72 throw (new ResolveIPException).set(getnameinfo_status, 73 "getnameinfo failed"); 74 } 75 76 if (address.length) 77 { 78 addresses ~= idup(address); 79 } 80 81 return false; 82 }); 83 84 if (ret && !delegate_called) 85 { 86 throw (new ResolveIPException).useGlobalErrno("getifaddrs"); 87 } 88 89 return addresses; 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 bool getAddrsForInterface( cstring interface_name, bool ipv6, 120 scope bool delegate ( cstring address, 121 int getnameinfo_status ) dg ) 122 { 123 ifaddrs* ifaddr; 124 125 // Try to fetch a linked list of interfaces and their adresses 126 if (getifaddrs(&ifaddr) == -1) 127 { 128 return true; 129 } 130 131 // ifaddr is allocated, and it needs to be freed! 132 scope(exit) freeifaddrs(ifaddr); 133 134 auto salen = ipv6? sockaddr_in6.sizeof : sockaddr_in.sizeof, 135 family = ipv6? AF_INET6 : AF_INET; 136 137 // Iterate through each interface and check if the interface 138 // is the one that we're looking for. 139 140 for (auto ifa = ifaddr; ifa !is null; 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 address 170 171 auto result = 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 delegate 180 if (dg(result? null : StringC.toDString(buffer.ptr), result)) 181 { 182 return true; 183 } 184 } 185 186 return false; 187 }