Merge pull request #3008 from lateralusX/jlorenss/win-x64-shutdown-crash
[mono.git] / mono / utils / networking-posix.c
1 /*
2  * networking-posix.c: Modern posix networking code
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2015 Xamarin
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #ifdef HAVE_NETDB_H
14 #include <netdb.h>
15 #endif
16 #ifdef HAVE_SYS_IOCTL_H
17 #include <sys/ioctl.h>
18 #endif
19 #ifdef HAVE_NET_IF_H
20 #include <net/if.h>
21 #endif
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #ifdef HAVE_GETIFADDRS
26 #include <ifaddrs.h>
27 #endif
28
29 #include <mono/utils/networking.h>
30 #include <mono/utils/mono-threads-coop.h>
31
32 static void*
33 get_address_from_sockaddr (struct sockaddr *sa)
34 {
35         switch (sa->sa_family) {
36         case AF_INET:
37                 return &((struct sockaddr_in*)sa)->sin_addr;
38         case AF_INET6:
39                 return &((struct sockaddr_in6*)sa)->sin6_addr;
40         }
41         return NULL;
42 }
43
44 #ifdef HAVE_GETADDRINFO
45
46 int
47 mono_get_address_info (const char *hostname, int port, int flags, MonoAddressInfo **result)
48 {
49         char service_name [16];
50         struct addrinfo hints, *res = NULL, *info;
51         MonoAddressEntry *cur = NULL, *prev = NULL;
52         MonoAddressInfo *addr_info;
53         int ret;
54
55         memset (&hints, 0, sizeof (struct addrinfo));
56         *result = NULL;
57
58         hints.ai_family = PF_UNSPEC;
59         if (flags & MONO_HINT_IPV4)
60                 hints.ai_family = PF_INET;
61         else if (flags & MONO_HINT_IPV6)
62                 hints.ai_family = PF_INET6;
63
64         hints.ai_socktype = SOCK_STREAM;
65
66         if (flags & MONO_HINT_CANONICAL_NAME)
67                 hints.ai_flags = AI_CANONNAME;
68
69 /* Some ancient libc don't define AI_ADDRCONFIG */
70 #ifdef AI_ADDRCONFIG
71         if (flags & MONO_HINT_CONFIGURED_ONLY)
72                 hints.ai_flags = AI_ADDRCONFIG;
73 #endif
74         sprintf (service_name, "%d", port);
75
76         MONO_ENTER_GC_SAFE;
77         ret = getaddrinfo (hostname, service_name, &hints, &info);
78         MONO_EXIT_GC_SAFE;
79
80         if (ret)
81                 return 1; /* FIXME propagate the error */
82
83         res = info;
84         *result = addr_info = g_new0 (MonoAddressInfo, 1);
85
86         while (res) {
87                 cur = g_new0 (MonoAddressEntry, 1);
88                 cur->family = res->ai_family;
89                 cur->socktype = res->ai_socktype;
90                 cur->protocol = res->ai_protocol;
91                 if (cur->family == PF_INET) {
92                         cur->address_len = sizeof (struct in_addr);
93                         cur->address.v4 = ((struct sockaddr_in*)res->ai_addr)->sin_addr;
94                 } else if (cur->family == PF_INET6) {
95                         cur->address_len = sizeof (struct in6_addr);
96                         cur->address.v6 = ((struct sockaddr_in6*)res->ai_addr)->sin6_addr;
97                 } else {
98                         g_warning ("Cannot handle address family %d", cur->family);
99                         res = res->ai_next;
100                         g_free (cur);
101                         continue;
102                 }
103
104                 if (res->ai_canonname)
105                         cur->canonical_name = g_strdup (res->ai_canonname);
106
107                 if (prev)
108                         prev->next = cur;
109                 else
110                         addr_info->entries = cur;
111                         
112                 prev = cur;
113                 res = res->ai_next;
114         }
115
116         freeaddrinfo (info);
117         return 0;
118 }
119
120 #endif
121
122 #ifdef HAVE_GETPROTOBYNAME
123
124 static int
125 fetch_protocol (const char *proto_name, int *cache, int *proto, int default_val)
126 {
127         if (!*cache) {
128                 struct protoent *pent;
129
130                 pent = getprotobyname (proto_name);
131                 *proto = pent ? pent->p_proto : default_val;
132                 *cache = 1;
133         }
134         return *proto;
135 }
136
137 int
138 mono_networking_get_tcp_protocol (void)
139 {
140         static int cache, proto;
141         return fetch_protocol ("tcp", &cache, &proto, 6); //6 is SOL_TCP on linux
142 }
143
144 int
145 mono_networking_get_ip_protocol (void)
146 {
147         static int cache, proto;
148         return fetch_protocol ("ip", &cache, &proto, 0); //0 is SOL_IP on linux
149 }
150
151 int
152 mono_networking_get_ipv6_protocol (void)
153 {
154         static int cache, proto;
155         return fetch_protocol ("ipv6", &cache, &proto, 41); //41 is SOL_IPV6 on linux
156 }
157
158 #endif
159
160 #if defined (HAVE_SIOCGIFCONF)
161
162 #define IFCONF_BUFF_SIZE 1024
163 #ifndef _SIZEOF_ADDR_IFREQ
164 #define _SIZEOF_ADDR_IFREQ(ifr) (sizeof (struct ifreq))
165 #endif
166
167 #define FOREACH_IFR(IFR, IFC) \
168         for (IFR = (IFC).ifc_req;       \
169         ifr < (struct ifreq*)((char*)(IFC).ifc_req + (IFC).ifc_len); \
170         ifr = (struct ifreq*)((char*)(IFR) + _SIZEOF_ADDR_IFREQ (*(IFR))))
171
172 void *
173 mono_get_local_interfaces (int family, int *interface_count)
174 {
175         int fd;
176         struct ifconf ifc;
177         struct ifreq *ifr;
178         int if_count = 0;
179         gboolean ignore_loopback = FALSE;
180         void *result = NULL;
181         char *result_ptr;
182
183         *interface_count = 0;
184
185         if (!mono_address_size_for_family (family))
186                 return NULL;
187
188         fd = socket (family, SOCK_STREAM, 0);
189         if (fd == -1)
190                 return NULL;
191
192         memset (&ifc, 0, sizeof (ifc));
193         ifc.ifc_len = IFCONF_BUFF_SIZE;
194         ifc.ifc_buf = (char *)g_malloc (IFCONF_BUFF_SIZE); /* We can't have such huge buffers on the stack. */
195         if (ioctl (fd, SIOCGIFCONF, &ifc) < 0)
196                 goto done;
197
198         FOREACH_IFR (ifr, ifc) {
199                 struct ifreq iflags;
200
201                 //only return addresses of the same type as @family
202                 if (ifr->ifr_addr.sa_family != family) {
203                         ifr->ifr_name [0] = '\0';
204                         continue;
205                 }
206
207                 strcpy (iflags.ifr_name, ifr->ifr_name);
208
209                 //ignore interfaces we can't get props for
210                 if (ioctl (fd, SIOCGIFFLAGS, &iflags) < 0) {
211                         ifr->ifr_name [0] = '\0';
212                         continue;
213                 }
214
215                 //ignore interfaces that are down
216                 if ((iflags.ifr_flags & IFF_UP) == 0) {
217                         ifr->ifr_name [0] = '\0';
218                         continue;
219                 }
220
221                 //If we have a non-loopback iface, don't return any loopback
222                 if ((iflags.ifr_flags & IFF_LOOPBACK) == 0) {
223                         ignore_loopback = TRUE;
224                         ifr->ifr_name [0] = 1;//1 means non-loopback
225                 } else {
226                         ifr->ifr_name [0] = 2; //2 means loopback
227                 }
228                 ++if_count;
229         }
230
231         result = (char *)g_malloc (if_count * mono_address_size_for_family (family));
232         result_ptr = (char *)result;
233         FOREACH_IFR (ifr, ifc) {
234                 if (ifr->ifr_name [0] == '\0')
235                         continue;
236
237                 if (ignore_loopback && ifr->ifr_name [0] == 2) {
238                         --if_count;
239                         continue;
240                 }
241
242                 memcpy (result_ptr, get_address_from_sockaddr (&ifr->ifr_addr), mono_address_size_for_family (family));
243                 result_ptr += mono_address_size_for_family (family);
244         }
245         g_assert (result_ptr <= (char*)result + if_count * mono_address_size_for_family (family));
246
247 done:
248         *interface_count = if_count;
249         g_free (ifc.ifc_buf);
250         close (fd);
251         return result;
252 }
253
254 #elif defined(HAVE_GETIFADDRS)
255
256 void *
257 mono_get_local_interfaces (int family, int *interface_count)
258 {
259         struct ifaddrs *ifap = NULL, *cur;
260         int if_count = 0;
261         gboolean ignore_loopback = FALSE;
262         void *result;
263         char *result_ptr;
264
265         *interface_count = 0;
266
267         if (!mono_address_size_for_family (family))
268                 return NULL;
269
270         if (getifaddrs (&ifap))
271                 return NULL;
272
273         for (cur = ifap; cur; cur = cur->ifa_next) {
274                 //ignore interfaces with no address assigned
275                 if (!cur->ifa_addr)
276                         continue;
277
278                 //ignore interfaces that don't belong to @family
279                 if (cur->ifa_addr->sa_family != family)
280                         continue;
281
282                 //ignore interfaces that are down
283                 if ((cur->ifa_flags & IFF_UP) == 0)
284                         continue;
285
286                 //If we have a non-loopback iface, don't return any loopback
287                 if ((cur->ifa_flags & IFF_LOOPBACK) == 0)
288                         ignore_loopback = TRUE;
289
290                 if_count++;
291         }
292
293         result_ptr = result = g_malloc (if_count * mono_address_size_for_family (family));
294         for (cur = ifap; cur; cur = cur->ifa_next) {
295                 if (!cur->ifa_addr)
296                         continue;
297                 if (cur->ifa_addr->sa_family != family)
298                         continue;
299                 if ((cur->ifa_flags & IFF_UP) == 0)
300                         continue;
301
302                 //we decrement if_count because it did not on the previous loop.
303                 if (ignore_loopback && (cur->ifa_flags & IFF_LOOPBACK)) {
304                         --if_count;
305                         continue;
306                 }
307
308                 memcpy (result_ptr, get_address_from_sockaddr (cur->ifa_addr), mono_address_size_for_family (family));
309                 result_ptr += mono_address_size_for_family (family);
310         }
311         g_assert (result_ptr <= (char*)result + if_count * mono_address_size_for_family (family));
312
313         freeifaddrs (ifap);
314         *interface_count = if_count;
315         return result;
316 }
317
318 #endif
319
320 #ifdef HAVE_GETNAMEINFO
321
322 gboolean
323 mono_networking_addr_to_str (MonoAddress *address, char *buffer, socklen_t buflen)
324 {
325         MonoSocketAddress saddr;
326         socklen_t len;
327         mono_socket_address_init (&saddr, &len, address->family, &address->addr, 0);
328
329         return getnameinfo (&saddr.addr, len, buffer, buflen, NULL, 0, NI_NUMERICHOST) == 0;
330 }
331
332 #elif HAVE_INET_NTOP
333
334 gboolean
335 mono_networking_addr_to_str (MonoAddress *address, char *buffer, socklen_t buflen)
336 {
337         return inet_ntop (address->family, &address->addr, buffer, buflen) != NULL;
338 }
339
340 #endif
341
342 #ifndef _WIN32
343 // These are already defined in networking-windows.c for Windows
344 void
345 mono_networking_init (void)
346 {
347         //nothing really
348 }
349
350 void
351 mono_networking_shutdown (void)
352 {
353         //nothing really
354 }
355 #endif