diff options
Diffstat (limited to 'src/lib/util/os_network.c')
-rw-r--r-- | src/lib/util/os_network.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/lib/util/os_network.c b/src/lib/util/os_network.c new file mode 100644 index 000000000..ac9fa691f --- /dev/null +++ b/src/lib/util/os_network.c | |||
@@ -0,0 +1,444 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2004, 2005, 2006, 2015 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | |||
20 | */ | ||
21 | /** | ||
22 | * @file util/os_network.c | ||
23 | * @brief function to determine available network interfaces | ||
24 | * @author Nils Durner | ||
25 | * @author Heikki Lindholm | ||
26 | * @author Jake Dust | ||
27 | * @author LRN | ||
28 | * @author Christian Grothoff | ||
29 | */ | ||
30 | |||
31 | #include "platform.h" | ||
32 | #include "gnunet_util_lib.h" | ||
33 | |||
34 | |||
35 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-os-network", __VA_ARGS__) | ||
36 | #define LOG_STRERROR_FILE(kind, syscall, \ | ||
37 | filename) GNUNET_log_from_strerror_file (kind, \ | ||
38 | "util-os-network", \ | ||
39 | syscall, \ | ||
40 | filename) | ||
41 | |||
42 | |||
43 | #if ! (HAVE_GETIFADDRS && HAVE_FREEIFADDRS) | ||
44 | /** | ||
45 | * Try to enumerate all network interfaces using 'ifconfig'. | ||
46 | * | ||
47 | * @param proc the callback function | ||
48 | * @param proc_cls closure for @a proc | ||
49 | * @return #GNUNET_OK if it worked | ||
50 | */ | ||
51 | static int | ||
52 | try_ifconfig (GNUNET_OS_NetworkInterfaceProcessor proc, | ||
53 | void *proc_cls) | ||
54 | { | ||
55 | int i; | ||
56 | char line[1024]; | ||
57 | char *replace; | ||
58 | const char *start; | ||
59 | char ifc[12]; | ||
60 | char addrstr[128]; | ||
61 | char bcstr[128]; | ||
62 | char netmaskstr[128]; | ||
63 | FILE *f; | ||
64 | int have_ifc; | ||
65 | struct sockaddr_in a4; | ||
66 | struct sockaddr_in6 a6; | ||
67 | struct in_addr v4; | ||
68 | struct in6_addr v6; | ||
69 | struct sockaddr_in bcaddr; | ||
70 | struct sockaddr_in netmask; | ||
71 | struct sockaddr_in6 netmask6; | ||
72 | struct sockaddr *pass_bcaddr; | ||
73 | struct sockaddr *pass_netmask; | ||
74 | int prefixlen; | ||
75 | static char *pcall; | ||
76 | |||
77 | if (NULL == pcall) | ||
78 | { | ||
79 | const char *sbin_ifconfig; | ||
80 | |||
81 | #ifdef IFCONFIG | ||
82 | if (0 == access (IFCONFIG, X_OK)) | ||
83 | sbin_ifconfig = IFCONFIG; | ||
84 | else | ||
85 | #endif | ||
86 | if (0 == access ("/sbin/ifconfig", X_OK)) | ||
87 | sbin_ifconfig = "/sbin/ifconfig"; | ||
88 | else if (0 == access ("/usr/sbin/ifconfig", X_OK)) | ||
89 | sbin_ifconfig = "/usr/sbin/ifconfig"; | ||
90 | else | ||
91 | sbin_ifconfig = "ifconfig"; | ||
92 | GNUNET_asprintf (&pcall, | ||
93 | "%s -a 2> /dev/null", | ||
94 | sbin_ifconfig); | ||
95 | } | ||
96 | f = popen (pcall, "r"); | ||
97 | if (NULL == f) | ||
98 | { | ||
99 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, | ||
100 | "popen", | ||
101 | "ifconfig"); | ||
102 | |||
103 | return GNUNET_SYSERR; | ||
104 | } | ||
105 | |||
106 | have_ifc = GNUNET_NO; | ||
107 | ifc[11] = '\0'; | ||
108 | while (NULL != fgets (line, sizeof(line), f)) | ||
109 | { | ||
110 | if (strlen (line) == 0) | ||
111 | { | ||
112 | have_ifc = GNUNET_NO; | ||
113 | continue; | ||
114 | } | ||
115 | if (! isspace (line[0])) | ||
116 | { | ||
117 | have_ifc = (1 == sscanf (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO; | ||
118 | /* would end with ':' on OSX, fix it! */ | ||
119 | if (ifc[strlen (ifc) - 1] == ':') | ||
120 | ifc[strlen (ifc) - 1] = '\0'; | ||
121 | continue; | ||
122 | } | ||
123 | if (! have_ifc) | ||
124 | continue; /* strange input, hope for the best */ | ||
125 | |||
126 | /* make parsing of ipv6 addresses easier */ | ||
127 | for (replace = line; *replace != '\0'; replace++) | ||
128 | { | ||
129 | if (*replace == '/') | ||
130 | *replace = ' '; | ||
131 | } | ||
132 | prefixlen = -1; | ||
133 | |||
134 | start = line; | ||
135 | while (('\0' != *start) && (isspace (*start))) | ||
136 | start++; | ||
137 | |||
138 | /* Zero-out stack allocated values */ | ||
139 | memset (addrstr, 0, 128); | ||
140 | memset (netmaskstr, 0, 128); | ||
141 | memset (bcstr, 0, 128); | ||
142 | prefixlen = 0; | ||
143 | |||
144 | if ( /* Linux */ | ||
145 | (3 == sscanf (start, "inet addr:%127s Bcast:%127s Mask:%127s", addrstr, | ||
146 | bcstr, netmaskstr)) || | ||
147 | (2 == sscanf (start, "inet addr:%127s Mask:%127s", addrstr, | ||
148 | netmaskstr)) || | ||
149 | (2 == sscanf (start, "inet6 addr:%127s %d", addrstr, &prefixlen)) || | ||
150 | /* Solaris, OS X */ | ||
151 | (1 == sscanf (start, "inet %127s", addrstr)) || | ||
152 | (1 == sscanf (start, "inet6 %127s", addrstr))) | ||
153 | { | ||
154 | /* IPv4 */ | ||
155 | if (1 == inet_pton (AF_INET, addrstr, &v4)) | ||
156 | { | ||
157 | memset (&a4, 0, sizeof(a4)); | ||
158 | a4.sin_family = AF_INET; | ||
159 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
160 | a4.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
161 | #endif | ||
162 | a4.sin_addr = v4; | ||
163 | |||
164 | pass_bcaddr = NULL; | ||
165 | pass_netmask = NULL; | ||
166 | if (1 == inet_pton (AF_INET, bcstr, &v4)) | ||
167 | { | ||
168 | memset (&bcaddr, 0, sizeof(bcaddr)); | ||
169 | bcaddr.sin_family = AF_INET; | ||
170 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
171 | bcaddr.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
172 | #endif | ||
173 | bcaddr.sin_addr = v4; | ||
174 | pass_bcaddr = (struct sockaddr *) &bcaddr; | ||
175 | } | ||
176 | if (1 == inet_pton (AF_INET, netmaskstr, &v4)) | ||
177 | { | ||
178 | memset (&netmask, 0, sizeof(netmask)); | ||
179 | netmask.sin_family = AF_INET; | ||
180 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
181 | netmask.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
182 | #endif | ||
183 | netmask.sin_addr = v4; | ||
184 | pass_netmask = (struct sockaddr *) &netmask; | ||
185 | } | ||
186 | |||
187 | |||
188 | if (GNUNET_OK != | ||
189 | proc (proc_cls, ifc,(0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE)), | ||
190 | (const struct sockaddr *) &a4, | ||
191 | pass_bcaddr, pass_netmask, sizeof(a4))) | ||
192 | break; | ||
193 | continue; | ||
194 | } | ||
195 | /* IPv6 */ | ||
196 | if (1 == inet_pton (AF_INET6, addrstr, &v6)) | ||
197 | { | ||
198 | memset (&a6, 0, sizeof(a6)); | ||
199 | a6.sin6_family = AF_INET6; | ||
200 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
201 | a6.sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
202 | #endif | ||
203 | a6.sin6_addr = v6; | ||
204 | |||
205 | pass_netmask = NULL; | ||
206 | if (prefixlen != -1) | ||
207 | { | ||
208 | memset (v6.s6_addr, 0, sizeof(v6.s6_addr)); | ||
209 | for (i = 0; i < prefixlen; i++) | ||
210 | { | ||
211 | v6.s6_addr[i >> 3] |= 1 << (i & 7); | ||
212 | } | ||
213 | memset (&netmask6, 0, sizeof(netmask6)); | ||
214 | netmask6.sin6_family = AF_INET6; | ||
215 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
216 | netmask6.sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
217 | #endif | ||
218 | netmask6.sin6_addr = v6; | ||
219 | |||
220 | pass_netmask = (struct sockaddr *) &netmask6; | ||
221 | } | ||
222 | |||
223 | if (GNUNET_OK != | ||
224 | proc (proc_cls, ifc,(0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE)), | ||
225 | (const struct sockaddr *) &a6, | ||
226 | NULL, pass_netmask, sizeof(a6))) | ||
227 | break; | ||
228 | continue; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | pclose (f); | ||
233 | return GNUNET_OK; | ||
234 | } | ||
235 | |||
236 | |||
237 | /** | ||
238 | * Try to enumerate all network interfaces using 'ip'. | ||
239 | * | ||
240 | * @param proc the callback function | ||
241 | * @param proc_cls closure for @a proc | ||
242 | * @return #GNUNET_OK if it worked | ||
243 | */ | ||
244 | static int | ||
245 | try_ip (GNUNET_OS_NetworkInterfaceProcessor proc, | ||
246 | void *proc_cls) | ||
247 | { | ||
248 | char line[1024]; | ||
249 | char *replace; | ||
250 | char ifname[64]; | ||
251 | char afstr[6]; | ||
252 | char addrstr[128]; | ||
253 | FILE *f; | ||
254 | struct sockaddr_in a4; | ||
255 | struct sockaddr_in6 a6; | ||
256 | struct in_addr v4; | ||
257 | struct in6_addr v6; | ||
258 | struct sockaddr_in netmask; | ||
259 | struct sockaddr_in6 netmask6; | ||
260 | unsigned int i; | ||
261 | unsigned int prefixlen; | ||
262 | static char *pcall; | ||
263 | |||
264 | if (NULL == pcall) | ||
265 | { | ||
266 | const char *sbin_ip; | ||
267 | |||
268 | #ifdef PATH_TO_IP | ||
269 | if (0 == access (PATH_TO_IP, X_OK)) | ||
270 | sbin_ip = PATH_TO_IP; | ||
271 | else | ||
272 | #endif | ||
273 | if (0 == access ("/sbin/ip", X_OK)) | ||
274 | sbin_ip = "/sbin/ip"; | ||
275 | else if (0 == access ("/usr/sbin/ip", X_OK)) | ||
276 | sbin_ip = "/usr/sbin/ip"; | ||
277 | else | ||
278 | sbin_ip = "if"; | ||
279 | GNUNET_asprintf (&pcall, | ||
280 | "%s -o add 2> /dev/null", | ||
281 | sbin_ip); | ||
282 | } | ||
283 | f = popen (pcall, "r"); | ||
284 | if (! f) | ||
285 | { | ||
286 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, | ||
287 | "popen", | ||
288 | "ip"); | ||
289 | return GNUNET_SYSERR; | ||
290 | } | ||
291 | |||
292 | while (NULL != fgets (line, sizeof(line), f)) | ||
293 | { | ||
294 | /* make parsing easier */ | ||
295 | for (replace = line; *replace != '\0'; replace++) | ||
296 | { | ||
297 | if (*replace == '/') | ||
298 | *replace = ' '; | ||
299 | } | ||
300 | /* Zero-out stack allocated values */ | ||
301 | memset (ifname, 0, 64); | ||
302 | memset (afstr, 0, 6); | ||
303 | memset (addrstr, 0, 128); | ||
304 | if (4 != sscanf (line, | ||
305 | "%*u: %63s %5s %127s %6u", | ||
306 | ifname, | ||
307 | afstr, | ||
308 | addrstr, | ||
309 | &prefixlen)) | ||
310 | continue; | ||
311 | /* IPv4 */ | ||
312 | if ((0 == strcasecmp ("inet", | ||
313 | afstr)) && | ||
314 | (1 == inet_pton (AF_INET, | ||
315 | addrstr, | ||
316 | &v4))) | ||
317 | { | ||
318 | memset (&a4, 0, sizeof(a4)); | ||
319 | a4.sin_family = AF_INET; | ||
320 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
321 | a4.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
322 | #endif | ||
323 | a4.sin_addr = v4; | ||
324 | |||
325 | memset (&v4.s_addr, 0, sizeof(v4.s_addr)); | ||
326 | for (i = 0; i < prefixlen; i++) | ||
327 | v4.s_addr |= 1 << (i & 7); | ||
328 | memset (&netmask, 0, sizeof(netmask)); | ||
329 | netmask.sin_family = AF_INET; | ||
330 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
331 | netmask.sin_len = (u_char) sizeof(struct sockaddr_in); | ||
332 | #endif | ||
333 | netmask.sin_addr = v4; | ||
334 | |||
335 | if (GNUNET_OK != | ||
336 | proc (proc_cls, | ||
337 | ifname, | ||
338 | (0 == strcmp (ifname, | ||
339 | GNUNET_DEFAULT_INTERFACE)), | ||
340 | (const struct sockaddr *) &a4, | ||
341 | NULL, | ||
342 | (const struct sockaddr *) &netmask, | ||
343 | sizeof(a4))) | ||
344 | break; | ||
345 | } | ||
346 | /* IPv6 */ | ||
347 | if ((0 == strcasecmp ("inet6", | ||
348 | afstr)) && | ||
349 | (1 == inet_pton (AF_INET6, | ||
350 | addrstr, | ||
351 | &v6))) | ||
352 | { | ||
353 | memset (&a6, 0, sizeof(a6)); | ||
354 | a6.sin6_family = AF_INET6; | ||
355 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
356 | a6.sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
357 | #endif | ||
358 | a6.sin6_addr = v6; | ||
359 | |||
360 | memset (v6.s6_addr, 0, sizeof(v6.s6_addr)); | ||
361 | for (i = 0; i < prefixlen; i++) | ||
362 | v6.s6_addr[i >> 3] |= 1 << (i & 7); | ||
363 | memset (&netmask6, 0, sizeof(netmask6)); | ||
364 | netmask6.sin6_family = AF_INET6; | ||
365 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
366 | netmask6.sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
367 | #endif | ||
368 | netmask6.sin6_addr = v6; | ||
369 | |||
370 | if (GNUNET_OK != | ||
371 | proc (proc_cls, | ||
372 | ifname, | ||
373 | (0 == strcmp (ifname, | ||
374 | GNUNET_DEFAULT_INTERFACE)), | ||
375 | (const struct sockaddr *) &a6, | ||
376 | NULL, | ||
377 | (const struct sockaddr *) &netmask6, | ||
378 | sizeof(a6))) | ||
379 | break; | ||
380 | } | ||
381 | } | ||
382 | pclose (f); | ||
383 | return GNUNET_OK; | ||
384 | } | ||
385 | |||
386 | |||
387 | #endif | ||
388 | |||
389 | |||
390 | /** | ||
391 | * @brief Enumerate all network interfaces | ||
392 | * | ||
393 | * @param proc the callback function | ||
394 | * @param proc_cls closure for @a proc | ||
395 | */ | ||
396 | void | ||
397 | GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc, | ||
398 | void *proc_cls) | ||
399 | { | ||
400 | #if HAVE_GETIFADDRS && HAVE_FREEIFADDRS | ||
401 | struct ifaddrs *ifa_first; | ||
402 | struct ifaddrs *ifa_ptr; | ||
403 | socklen_t alen; | ||
404 | |||
405 | if (getifaddrs (&ifa_first) == 0) | ||
406 | { | ||
407 | for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next) | ||
408 | { | ||
409 | if ((ifa_ptr->ifa_name != NULL) && (ifa_ptr->ifa_addr != NULL) && | ||
410 | ( (ifa_ptr->ifa_flags & IFF_UP) != 0) ) | ||
411 | { | ||
412 | if ((ifa_ptr->ifa_addr->sa_family != AF_INET) && | ||
413 | (ifa_ptr->ifa_addr->sa_family != AF_INET6)) | ||
414 | continue; | ||
415 | if (ifa_ptr->ifa_addr->sa_family == AF_INET) | ||
416 | alen = sizeof(struct sockaddr_in); | ||
417 | else | ||
418 | alen = sizeof(struct sockaddr_in6); | ||
419 | if (GNUNET_OK != | ||
420 | proc (proc_cls, ifa_ptr->ifa_name, | ||
421 | (0 == strcmp (ifa_ptr->ifa_name, GNUNET_DEFAULT_INTERFACE)), | ||
422 | ifa_ptr->ifa_addr, ifa_ptr->ifa_broadaddr, | ||
423 | ifa_ptr->ifa_netmask, alen)) | ||
424 | break; | ||
425 | } | ||
426 | } | ||
427 | freeifaddrs (ifa_first); | ||
428 | } | ||
429 | #else | ||
430 | if (GNUNET_OK == | ||
431 | try_ip (proc, | ||
432 | proc_cls)) | ||
433 | return; | ||
434 | if (GNUNET_OK == | ||
435 | try_ifconfig (proc, | ||
436 | proc_cls)) | ||
437 | return; | ||
438 | LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, | ||
439 | "Failed to enumerate network interfaces\n"); | ||
440 | #endif | ||
441 | } | ||
442 | |||
443 | |||
444 | /* end of os_network.c */ | ||