aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/os_network.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/os_network.c')
-rw-r--r--src/lib/util/os_network.c444
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 */
51static int
52try_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 */
244static int
245try_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 */
396void
397GNUNET_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 */