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