[utils] Add abstraction to retrive the local interface addresses.
[mono-project.git] / mono / utils / networking-posix.c
blob6bd4ecaad5eab48a95853bd65c6459773be1b0b9
1 /*
2 * networking-posix.c: Modern posix networking code
4 * Author:
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * (C) 2015 Xamarin
8 */
10 #include <config.h>
11 #include <mono/utils/networking.h>
12 #include <glib.h>
14 #ifdef HAVE_NETDB_H
15 #include <netdb.h>
16 #endif
17 #ifdef HAVE_SYS_IOCTL_H
18 #include <sys/ioctl.h>
19 #endif
20 #ifdef HAVE_NET_IF_H
21 #include <net/if.h>
22 #endif
23 #ifdef HAVE_SYS_SOCKIO_H
24 #include <sys/sockio.h>
25 #endif
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_GETIFADDRS
30 #include <ifaddrs.h>
31 #endif
34 static int
35 get_address_size_by_family (int family)
37 switch (family) {
38 case AF_INET:
39 return sizeof (struct in_addr);
40 case AF_INET6:
41 return sizeof (struct in6_addr);
43 return 0;
46 static void*
47 get_address_from_sockaddr (struct sockaddr *sa)
49 switch (sa->sa_family) {
50 case AF_INET:
51 return &((struct sockaddr_in*)sa)->sin_addr;
52 case AF_INET6:
53 return &((struct sockaddr_in6*)sa)->sin6_addr;
55 return NULL;
58 #ifdef HAVE_GETADDRINFO
60 int
61 mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInfo **result)
63 char service_name [16];
64 struct addrinfo hints, *res = NULL, *info;
65 MonoAddressEntry *cur = NULL, *prev = NULL;
66 MonoAddressInfo *addr_info;
68 memset (&hints, 0, sizeof (struct addrinfo));
69 *result = NULL;
71 hints.ai_family = PF_UNSPEC;
72 if (flags & MONO_HINT_IPV4)
73 hints.ai_family = PF_INET;
74 else if (flags & MONO_HINT_IPV6)
75 hints.ai_family = PF_INET6;
77 hints.ai_socktype = SOCK_STREAM;
79 if (flags & MONO_HINT_CANONICAL_NAME)
80 hints.ai_flags = AI_CANONNAME;
82 /* Some ancient libc don't define AI_ADDRCONFIG */
83 #ifdef AI_ADDRCONFIG
84 if (flags & MONO_HINT_CONFIGURED_ONLY)
85 hints.ai_flags = AI_ADDRCONFIG;
86 #endif
87 sprintf (service_name, "%d", port);
88 if (getaddrinfo (hostname, service_name, &hints, &info))
89 return 1; /* FIXME propagate the error */
91 res = info;
92 *result = addr_info = g_new0 (MonoAddressInfo, 1);
94 while (res) {
95 cur = g_new0 (MonoAddressEntry, 1);
96 if (prev)
97 prev->next = cur;
98 else
99 addr_info->entries = cur;
101 cur->family = res->ai_family;
102 cur->socktype = res->ai_socktype;
103 cur->protocol = res->ai_protocol;
104 if (cur->family == PF_INET) {
105 cur->address_len = sizeof (struct in_addr);
106 cur->address.v4 = ((struct sockaddr_in*)res->ai_addr)->sin_addr;
107 } else if (cur->family == PF_INET6) {
108 cur->address_len = sizeof (struct in6_addr);
109 cur->address.v6 = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr;
110 } else {
111 g_error ("Cannot handle address family %d", cur->family);
114 if (res->ai_canonname)
115 cur->canonical_name = g_strdup (res->ai_canonname);
117 prev = cur;
118 res = res->ai_next;
121 freeaddrinfo (info);
122 return 0;
125 #endif
127 #ifdef HAVE_GETPROTOBYNAME
129 static int
130 fetch_protocol (const char *proto_name, int *cache, int *proto, int default_val)
132 if (!*cache) {
133 struct protoent *pent;
135 pent = getprotobyname (proto_name);
136 *proto = pent ? pent->p_proto : default_val;
137 *cache = 1;
139 return *proto;
143 mono_networking_get_tcp_protocol (void)
145 static int cache, proto;
146 return fetch_protocol ("tcp", &cache, &proto, 6); //6 is SOL_TCP on linux
150 mono_networking_get_ip_protocol (void)
152 static int cache, proto;
153 return fetch_protocol ("ip", &cache, &proto, 0); //0 is SOL_IP on linux
157 mono_networking_get_ipv6_protocol (void)
159 static int cache, proto;
160 return fetch_protocol ("ipv6", &cache, &proto, 41); //41 is SOL_IPV6 on linux
163 #endif
165 #if defined (HAVE_SIOCGIFCONF)
167 #define IFCONF_BUFF_SIZE 1024
168 #ifndef _SIZEOF_ADDR_IFREQ
169 #define _SIZEOF_ADDR_IFREQ(ifr) (sizeof (struct ifreq))
170 #endif
172 #define FOREACH_IFR(IFR, IFC) \
173 for (IFR = (IFC).ifc_req; \
174 ifr < (struct ifreq*)((char*)(IFC).ifc_req + (IFC).ifc_len); \
175 ifr = (struct ifreq*)((char*)(IFR) + _SIZEOF_ADDR_IFREQ (*(IFR))))
177 void *
178 mono_get_local_interfaces (int family, int *interface_count)
180 int fd;
181 struct ifconf ifc;
182 struct ifreq *ifr;
183 int if_count = 0;
184 gboolean ignore_loopback = FALSE;
185 void *result = NULL;
186 char *result_ptr;
188 *interface_count = 0;
190 if (!get_address_size_by_family (family))
191 return NULL;
193 fd = socket (family, SOCK_STREAM, 0);
194 if (fd == -1)
195 return NULL;
197 memset (&ifc, 0, sizeof (ifc));
198 ifc.ifc_len = IFCONF_BUFF_SIZE;
199 ifc.ifc_buf = g_malloc (IFCONF_BUFF_SIZE); /* We can't have such huge buffers on the stack. */
200 if (ioctl (fd, SIOCGIFCONF, &ifc) < 0)
201 goto done;
203 FOREACH_IFR (ifr, ifc) {
204 struct ifreq iflags;
206 //only return addresses of the same type as @family
207 if (ifr->ifr_addr.sa_family != family) {
208 ifr->ifr_name [0] = '\0';
209 continue;
212 strcpy (iflags.ifr_name, ifr->ifr_name);
214 //ignore interfaces we can't get props for
215 if (ioctl (fd, SIOCGIFFLAGS, &iflags) < 0) {
216 ifr->ifr_name [0] = '\0';
217 continue;
220 //ignore interfaces that are down
221 if ((iflags.ifr_flags & IFF_UP) == 0) {
222 ifr->ifr_name [0] = '\0';
223 continue;
226 //If we have a non-loopback iface, don't return any loopback
227 if ((iflags.ifr_flags & IFF_LOOPBACK) == 0) {
228 ignore_loopback = TRUE;
229 ifr->ifr_name [0] = 1;//1 means non-loopback
230 } else {
231 ifr->ifr_name [0] = 2; //2 means loopback
233 ++if_count;
236 result_ptr = result = g_malloc (if_count * get_address_size_by_family (family));
237 FOREACH_IFR (ifr, ifc) {
238 if (ifr->ifr_name [0] == '\0')
239 continue;
241 if (ignore_loopback && ifr->ifr_name [0] == 2) {
242 --if_count;
243 continue;
246 memcpy (result_ptr, get_address_from_sockaddr (&ifr->ifr_addr), get_address_size_by_family (family));
247 result_ptr += get_address_size_by_family (family);
249 g_assert (result_ptr <= (char*)result + if_count * get_address_size_by_family (family));
251 done:
252 *interface_count = if_count;
253 g_free (ifc.ifc_buf);
254 close (fd);
255 return result;
258 #elif defined(HAVE_GETIFADDRS)
260 void *
261 mono_get_local_interfaces (int family, int *interface_count)
263 struct ifaddrs *ifap = NULL, *cur;
264 int if_count = 0;
265 gboolean ignore_loopback = FALSE;
266 void *result;
267 char *result_ptr;
269 *interface_count = 0;
271 if (!get_address_size_by_family (family))
272 return NULL;
274 if (getifaddrs (&ifap))
275 return NULL;
277 for (cur = ifap; cur; cur = cur->ifa_next) {
278 //ignore interfaces with no address assigned
279 if (!cur->ifa_addr)
280 continue;
282 //ignore interfaces that don't belong to @family
283 if (cur->ifa_addr->sa_family != family)
284 continue;
286 //ignore interfaces that are down
287 if ((cur->ifa_flags & IFF_UP) == 0)
288 continue;
290 //If we have a non-loopback iface, don't return any loopback
291 if ((cur->ifa_flags & IFF_LOOPBACK) == 0)
292 ignore_loopback = TRUE;
294 if_count++;
297 result_ptr = result = g_malloc (if_count * get_address_size_by_family (family));
298 for (cur = ifap; cur; cur = cur->ifa_next) {
299 if (!cur->ifa_addr)
300 continue;
301 if (cur->ifa_addr->sa_family != family)
302 continue;
303 if ((cur->ifa_flags & IFF_UP) == 0)
304 continue;
306 //we decrement if_count because it did not on the previous loop.
307 if (ignore_loopback && (cur->ifa_flags & IFF_LOOPBACK)) {
308 --if_count;
309 continue;
312 memcpy (result_ptr, get_address_from_sockaddr (cur->ifa_addr), get_address_size_by_family (family));
313 result_ptr += get_address_size_by_family (family);
315 g_assert (result_ptr <= (char*)result + if_count * get_address_size_by_family (family));
317 freeifaddrs (ifap);
318 *interface_count = if_count;
319 return result;
322 #endif