Merge pull request #3066 from alexanderkyte/pedump_sgen
[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         MonoError error;
25         size_t needed;
26         in_addr_t in;
27         int mib[6];
28         int num_gws=0, gwnum=0;
29         unsigned int ifindex = 0;
30         char *buf, *next, *lim, *ifacename;
31         struct rt_msghdr *rtm;
32
33         MonoDomain *domain = mono_domain_get ();
34
35         ifacename = mono_string_to_utf8_checked(iface, &error);
36         if (mono_error_set_pending_exception (&error))
37                 return FALSE;
38
39         if ((ifindex = if_nametoindex(ifacename)) == 0)
40                 return FALSE;
41         g_free(ifacename);
42
43         // MIB array defining data to read from sysctl
44         mib[0] = CTL_NET;       // Networking
45         mib[1] = PF_ROUTE;      // Routing messages
46         mib[2] = 0;             // Protocol number (always zero)
47         mib[3] = AF_INET;       // Address family (IPv4)
48         mib[4] = NET_RT_DUMP;   // Dump routing table
49         mib[5] = 0;             //
50
51         // First sysctl call with oldp set to NULL to determine size of available data
52         if (sysctl(mib, G_N_ELEMENTS(mib), NULL, &needed, NULL, 0) < 0)
53                 return FALSE;
54
55         // Allocate suffcient memory for available data based on the previous sysctl call
56         if ((buf = malloc(needed)) == NULL)
57                 return FALSE;
58
59         // Second sysctl call to retrieve data into appropriately sized buffer
60         if (sysctl(mib, G_N_ELEMENTS(mib), buf, &needed, NULL, 0) < 0)
61                 return FALSE;
62
63         lim = buf + needed;
64         for (next = buf; next < lim; next += rtm->rtm_msglen) {
65                 rtm = (struct rt_msghdr *)next;
66                 if (rtm->rtm_version != RTM_VERSION)
67                         continue;
68                 if (rtm->rtm_index != ifindex)
69                         continue;
70                 if((in = gateway_from_rtm(rtm)) == 0)
71                         continue;
72                 num_gws++;
73         }
74
75         *gw_addr_list = mono_array_new(domain, mono_get_string_class (), num_gws);
76
77         for (next = buf; next < lim; next += rtm->rtm_msglen) {
78                 rtm = (struct rt_msghdr *)next;
79                 if (rtm->rtm_version != RTM_VERSION)
80                         continue;
81                 if (rtm->rtm_index != ifindex)
82                         continue;
83                 if ((in = gateway_from_rtm(rtm)) == 0)
84                         continue;
85
86                 MonoString *addr_string;
87                 char addr [16], *ptr;
88                 int len;
89
90                 ptr = (char *) &in;
91                 len = snprintf(addr, sizeof(addr), "%u.%u.%u.%u",
92                         (unsigned char) ptr [0],
93                         (unsigned char) ptr [1],
94                         (unsigned char) ptr [2],
95                         (unsigned char) ptr [3]);
96
97                 if ((len >= sizeof(addr)) || (len < 0))
98                         // snprintf output truncated
99                         continue;
100
101                 addr_string = mono_string_new (domain, addr);
102                 mono_array_setref (*gw_addr_list, gwnum, addr_string);
103                 gwnum++;
104         }
105         free(buf);
106         return TRUE;
107 }
108
109 in_addr_t gateway_from_rtm(struct rt_msghdr *rtm)
110 {
111         struct sockaddr *gw;
112         unsigned int l;
113
114         struct sockaddr *addr = (struct sockaddr *)(rtm + 1);
115         l = roundup(addr->sa_len, sizeof(long)); \
116         gw = (struct sockaddr *)((char *) addr + l); \
117
118         if (rtm->rtm_addrs & RTA_GATEWAY) {
119                 if(gw->sa_family == AF_INET) {
120                         struct sockaddr_in *sockin = (struct sockaddr_in *)gw;
121                         return(sockin->sin_addr.s_addr);
122                 }
123         }
124
125         return 0;
126 }
127
128 #endif /* #if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD) */