[sgen] Exclusive write on binary protocol file
[mono.git] / mono / metadata / mono-route.c
1 /*
2  * mono-route.c: Read the network routing tables using sysctl(3) calls
3  *               Required for Unix-like systems that don't have Linux's /proc/net/route
4  *
5  * Author:
6  *   Ben Woods (woodsb02@gmail.com)
7  */
8
9 #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD)
10
11 #include <sys/socket.h>
12 #include <net/if.h>
13 #include <net/if_dl.h>
14 #include <netinet/in.h>
15 #include <sys/param.h>
16 #include <sys/sysctl.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <mono/metadata/object.h>
20 #include <mono/metadata/mono-route.h>
21
22 extern MonoBoolean ves_icall_System_Net_NetworkInformation_MacOsIPInterfaceProperties_ParseRouteInfo_internal(MonoString *iface, MonoArray **gw_addr_list)
23 {
24         size_t needed;
25         in_addr_t in;
26         int mib[6];
27         int num_gws=0, gwnum=0;
28         unsigned int ifindex = 0;
29         char *buf, *next, *lim, *ifacename;
30         struct rt_msghdr *rtm;
31
32         MonoDomain *domain = mono_domain_get ();
33
34         ifacename = mono_string_to_utf8(iface);
35         if ((ifindex = if_nametoindex(ifacename)) == 0)
36                 return FALSE;
37         g_free(ifacename);
38
39         // MIB array defining data to read from sysctl
40         mib[0] = CTL_NET;       // Networking
41         mib[1] = PF_ROUTE;      // Routing messages
42         mib[2] = 0;             // Protocol number (always zero)
43         mib[3] = AF_INET;       // Address family (IPv4)
44         mib[4] = NET_RT_DUMP;   // Dump routing table
45         mib[5] = 0;             //
46
47         // First sysctl call with oldp set to NULL to determine size of available data
48         if (sysctl(mib, G_N_ELEMENTS(mib), NULL, &needed, NULL, 0) < 0)
49                 return FALSE;
50
51         // Allocate suffcient memory for available data based on the previous sysctl call
52         if ((buf = malloc(needed)) == NULL)
53                 return FALSE;
54
55         // Second sysctl call to retrieve data into appropriately sized buffer
56         if (sysctl(mib, G_N_ELEMENTS(mib), buf, &needed, NULL, 0) < 0)
57                 return FALSE;
58
59         lim = buf + needed;
60         for (next = buf; next < lim; next += rtm->rtm_msglen) {
61                 rtm = (struct rt_msghdr *)next;
62                 if (rtm->rtm_version != RTM_VERSION)
63                         continue;
64                 if (rtm->rtm_index != ifindex)
65                         continue;
66                 if((in = gateway_from_rtm(rtm)) == 0)
67                         continue;
68                 num_gws++;
69         }
70
71         *gw_addr_list = mono_array_new(domain, mono_get_string_class (), num_gws);
72
73         for (next = buf; next < lim; next += rtm->rtm_msglen) {
74                 rtm = (struct rt_msghdr *)next;
75                 if (rtm->rtm_version != RTM_VERSION)
76                         continue;
77                 if (rtm->rtm_index != ifindex)
78                         continue;
79                 if ((in = gateway_from_rtm(rtm)) == 0)
80                         continue;
81
82                 MonoString *addr_string;
83                 char addr [16], *ptr;
84                 int len;
85
86                 ptr = (char *) &in;
87                 len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u",
88                         (unsigned char) ptr [0],
89                         (unsigned char) ptr [1],
90                         (unsigned char) ptr [2],
91                         (unsigned char) ptr [3]);
92
93                 if ((len >= sizeof(addr)) || (len < 0))
94                         // snprintf output truncated
95                         continue;
96
97                 addr_string = mono_string_new (domain, addr);
98                 mono_array_setref (*gw_addr_list, gwnum, addr_string);
99                 gwnum++;
100         }
101         free(buf);
102         return TRUE;
103 }
104
105 in_addr_t gateway_from_rtm(struct rt_msghdr *rtm)
106 {
107         struct sockaddr *gw;
108         unsigned int l;
109
110         struct sockaddr *addr = (struct sockaddr *)(rtm + 1);
111         l = roundup(addr->sa_len, sizeof(long)); \
112         gw = (struct sockaddr *)((char *) addr + l); \
113
114         if (rtm->rtm_addrs & RTA_GATEWAY) {
115                 if(gw->sa_family == AF_INET) {
116                         struct sockaddr_in *sockin = (struct sockaddr_in *)gw;
117                         return(sockin->sin_addr.s_addr);
118                 }
119         }
120
121         return 0;
122 }
123
124 #endif /* #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD) */