uncrustify as demanded.
[oweals/gnunet.git] / src / util / os_network.c
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 #include "platform.h"
31 #include "gnunet_util_lib.h"
32
33
34 #define LOG(kind, ...) GNUNET_log_from(kind, "util-os-network", __VA_ARGS__)
35 #define LOG_STRERROR_FILE(kind, syscall, filename) GNUNET_log_from_strerror_file(kind, "util-os-network", syscall, filename)
36
37
38 #if !(HAVE_GETIFADDRS && HAVE_FREEIFADDRS) && !MINGW
39 /**
40  * Try to enumerate all network interfaces using 'ifconfig'.
41  *
42  * @param proc the callback function
43  * @param proc_cls closure for @a proc
44  * @return #GNUNET_OK if it worked
45  */
46 static int
47 try_ifconfig(GNUNET_OS_NetworkInterfaceProcessor proc,
48              void *proc_cls)
49 {
50   int i;
51   char line[1024];
52   char *replace;
53   const char *start;
54   char ifc[12];
55   char addrstr[128];
56   char bcstr[128];
57   char netmaskstr[128];
58   FILE *f;
59   int have_ifc;
60   struct sockaddr_in a4;
61   struct sockaddr_in6 a6;
62   struct in_addr v4;
63   struct in6_addr v6;
64   struct sockaddr_in bcaddr;
65   struct sockaddr_in netmask;
66   struct sockaddr_in6 netmask6;
67   struct sockaddr *pass_bcaddr;
68   struct sockaddr *pass_netmask;
69   int prefixlen;
70   static char *pcall;
71
72   if (NULL == pcall)
73     {
74       const char *sbin_ifconfig;
75
76 #ifdef IFCONFIG
77       if (0 == access(IFCONFIG, X_OK))
78         sbin_ifconfig = IFCONFIG;
79       else
80 #endif
81       if (0 == access("/sbin/ifconfig", X_OK))
82         sbin_ifconfig = "/sbin/ifconfig";
83       else if (0 == access("/usr/sbin/ifconfig", X_OK))
84         sbin_ifconfig = "/usr/sbin/ifconfig";
85       else
86         sbin_ifconfig = "ifconfig";
87       GNUNET_asprintf(&pcall,
88                       "%s -a 2> /dev/null",
89                       sbin_ifconfig);
90     }
91   f = popen(pcall, "r");
92   if (NULL == f)
93     {
94       LOG_STRERROR_FILE(GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
95                         "popen",
96                         "ifconfig");
97
98       return GNUNET_SYSERR;
99     }
100
101   have_ifc = GNUNET_NO;
102   ifc[11] = '\0';
103   while (NULL != fgets(line, sizeof(line), f))
104     {
105       if (strlen(line) == 0)
106         {
107           have_ifc = GNUNET_NO;
108           continue;
109         }
110       if (!isspace(line[0]))
111         {
112           have_ifc = (1 == SSCANF(line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO;
113           /* would end with ':' on OSX, fix it! */
114           if (ifc[strlen(ifc) - 1] == ':')
115             ifc[strlen(ifc) - 1] = '\0';
116           continue;
117         }
118       if (!have_ifc)
119         continue;               /* strange input, hope for the best */
120
121       /* make parsing of ipv6 addresses easier */
122       for (replace = line; *replace != '\0'; replace++)
123         {
124           if (*replace == '/')
125             *replace = ' ';
126         }
127       prefixlen = -1;
128
129       start = line;
130       while (('\0' != *start) && (isspace(*start)))
131         start++;
132
133       /* Zero-out stack allocated values */
134       memset(addrstr, 0, 128);
135       memset(netmaskstr, 0, 128);
136       memset(bcstr, 0, 128);
137       prefixlen = 0;
138
139       if ( /* Linux */
140         (3 == SSCANF(start, "inet addr:%127s Bcast:%127s Mask:%127s", addrstr, bcstr, netmaskstr)) ||
141         (2 == SSCANF(start, "inet addr:%127s Mask:%127s", addrstr, netmaskstr)) ||
142         (2 == SSCANF(start, "inet6 addr:%127s %d", addrstr, &prefixlen)) ||
143         /* Solaris, OS X */
144         (1 == SSCANF(start, "inet %127s", addrstr)) ||
145         (1 == SSCANF(start, "inet6 %127s", addrstr)))
146         {
147           /* IPv4 */
148           if (1 == inet_pton(AF_INET, addrstr, &v4))
149             {
150               memset(&a4, 0, sizeof(a4));
151               a4.sin_family = AF_INET;
152 #if HAVE_SOCKADDR_IN_SIN_LEN
153               a4.sin_len = (u_char)sizeof(struct sockaddr_in);
154 #endif
155               a4.sin_addr = v4;
156
157               pass_bcaddr = NULL;
158               pass_netmask = NULL;
159               if (1 == inet_pton(AF_INET, bcstr, &v4))
160                 {
161                   memset(&bcaddr, 0, sizeof(bcaddr));
162                   bcaddr.sin_family = AF_INET;
163 #if HAVE_SOCKADDR_IN_SIN_LEN
164                   bcaddr.sin_len = (u_char)sizeof(struct sockaddr_in);
165 #endif
166                   bcaddr.sin_addr = v4;
167                   pass_bcaddr = (struct sockaddr *)&bcaddr;
168                 }
169               if (1 == inet_pton(AF_INET, netmaskstr, &v4))
170                 {
171                   memset(&netmask, 0, sizeof(netmask));
172                   netmask.sin_family = AF_INET;
173 #if HAVE_SOCKADDR_IN_SIN_LEN
174                   netmask.sin_len = (u_char)sizeof(struct sockaddr_in);
175 #endif
176                   netmask.sin_addr = v4;
177                   pass_netmask = (struct sockaddr *)&netmask;
178                 }
179
180
181               if (GNUNET_OK !=
182                   proc(proc_cls, ifc, 0 == strcmp(ifc, GNUNET_DEFAULT_INTERFACE),
183                        (const struct sockaddr *)&a4,
184                        pass_bcaddr, pass_netmask, sizeof(a4)))
185                 break;
186               continue;
187             }
188           /* IPv6 */
189           if (1 == inet_pton(AF_INET6, addrstr, &v6))
190             {
191               memset(&a6, 0, sizeof(a6));
192               a6.sin6_family = AF_INET6;
193 #if HAVE_SOCKADDR_IN_SIN_LEN
194               a6.sin6_len = (u_char)sizeof(struct sockaddr_in6);
195 #endif
196               a6.sin6_addr = v6;
197
198               pass_netmask = NULL;
199               if (prefixlen != -1)
200                 {
201                   memset(v6.s6_addr, 0, sizeof(v6.s6_addr));
202                   for (i = 0; i < prefixlen; i++)
203                     {
204                       v6.s6_addr[i >> 3] |= 1 << (i & 7);
205                     }
206                   memset(&netmask6, 0, sizeof(netmask6));
207                   netmask6.sin6_family = AF_INET6;
208 #if HAVE_SOCKADDR_IN_SIN_LEN
209                   netmask6.sin6_len = (u_char)sizeof(struct sockaddr_in6);
210 #endif
211                   netmask6.sin6_addr = v6;
212
213                   pass_netmask = (struct sockaddr *)&netmask6;
214                 }
215
216               if (GNUNET_OK !=
217                   proc(proc_cls, ifc, 0 == strcmp(ifc, GNUNET_DEFAULT_INTERFACE),
218                        (const struct sockaddr *)&a6,
219                        NULL, pass_netmask, sizeof(a6)))
220                 break;
221               continue;
222             }
223         }
224     }
225   pclose(f);
226   return GNUNET_OK;
227 }
228
229
230 /**
231  * Try to enumerate all network interfaces using 'ip'.
232  *
233  * @param proc the callback function
234  * @param proc_cls closure for @a proc
235  * @return #GNUNET_OK if it worked
236  */
237 static int
238 try_ip(GNUNET_OS_NetworkInterfaceProcessor proc,
239        void *proc_cls)
240 {
241   char line[1024];
242   char *replace;
243   char ifname[64];
244   char afstr[6];
245   char addrstr[128];
246   FILE *f;
247   struct sockaddr_in a4;
248   struct sockaddr_in6 a6;
249   struct in_addr v4;
250   struct in6_addr v6;
251   struct sockaddr_in netmask;
252   struct sockaddr_in6 netmask6;
253   unsigned int i;
254   unsigned int prefixlen;
255   static char *pcall;
256
257   if (NULL == pcall)
258     {
259       const char *sbin_ip;
260
261 #ifdef IFCONFIG
262       if (0 == access(PATH_TO_IP, X_OK))
263         sbin_ip = PATH_TO_IP;
264       else
265 #endif
266       if (0 == access("/sbin/ip", X_OK))
267         sbin_ip = "/sbin/ip";
268       else if (0 == access("/usr/sbin/ip", X_OK))
269         sbin_ip = "/usr/sbin/ip";
270       else
271         sbin_ip = "if";
272       GNUNET_asprintf(&pcall,
273                       "%s -o add 2> /dev/null",
274                       sbin_ip);
275     }
276   f = popen(pcall, "r");
277   if (!f)
278     {
279       LOG_STRERROR_FILE(GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
280                         "popen",
281                         "ip");
282       return GNUNET_SYSERR;
283     }
284
285   while (NULL != fgets(line, sizeof(line), f))
286     {
287       /* make parsing easier */
288       for (replace = line; *replace != '\0'; replace++)
289         {
290           if (*replace == '/')
291             *replace = ' ';
292         }
293       /* Zero-out stack allocated values */
294       memset(ifname, 0, 64);
295       memset(afstr, 0, 6);
296       memset(addrstr, 0, 128);
297       if (4 != SSCANF(line,
298                       "%*u: %63s %5s %127s %6u",
299                       ifname,
300                       afstr,
301                       addrstr,
302                       &prefixlen))
303         continue;
304       /* IPv4 */
305       if ((0 == strcasecmp("inet",
306                            afstr)) &&
307           (1 == inet_pton(AF_INET,
308                           addrstr,
309                           &v4)))
310         {
311           memset(&a4, 0, sizeof(a4));
312           a4.sin_family = AF_INET;
313 #if HAVE_SOCKADDR_IN_SIN_LEN
314           a4.sin_len = (u_char)sizeof(struct sockaddr_in);
315 #endif
316           a4.sin_addr = v4;
317
318           memset(&v4.s_addr, 0, sizeof(v4.s_addr));
319           for (i = 0; i < prefixlen; i++)
320             v4.s_addr |= 1 << (i & 7);
321           memset(&netmask, 0, sizeof(netmask));
322           netmask.sin_family = AF_INET;
323 #if HAVE_SOCKADDR_IN_SIN_LEN
324           netmask.sin_len = (u_char)sizeof(struct sockaddr_in);
325 #endif
326           netmask.sin_addr = v4;
327
328           if (GNUNET_OK !=
329               proc(proc_cls,
330                    ifname,
331                    (0 == strcmp(ifname,
332                                 GNUNET_DEFAULT_INTERFACE)),
333                    (const struct sockaddr *)&a4,
334                    NULL,
335                    (const struct sockaddr *)&netmask,
336                    sizeof(a4)))
337             break;
338         }
339       /* IPv6 */
340       if ((0 == strcasecmp("inet6",
341                            afstr)) &&
342           (1 == inet_pton(AF_INET6,
343                           addrstr,
344                           &v6)))
345         {
346           memset(&a6, 0, sizeof(a6));
347           a6.sin6_family = AF_INET6;
348 #if HAVE_SOCKADDR_IN_SIN_LEN
349           a6.sin6_len = (u_char)sizeof(struct sockaddr_in6);
350 #endif
351           a6.sin6_addr = v6;
352
353           memset(v6.s6_addr, 0, sizeof(v6.s6_addr));
354           for (i = 0; i < prefixlen; i++)
355             v6.s6_addr[i >> 3] |= 1 << (i & 7);
356           memset(&netmask6, 0, sizeof(netmask6));
357           netmask6.sin6_family = AF_INET6;
358 #if HAVE_SOCKADDR_IN_SIN_LEN
359           netmask6.sin6_len = (u_char)sizeof(struct sockaddr_in6);
360 #endif
361           netmask6.sin6_addr = v6;
362
363           if (GNUNET_OK !=
364               proc(proc_cls,
365                    ifname,
366                    (0 == strcmp(ifname,
367                                 GNUNET_DEFAULT_INTERFACE)),
368                    (const struct sockaddr *)&a6,
369                    NULL,
370                    (const struct sockaddr *)&netmask6,
371                    sizeof(a6)))
372             break;
373         }
374     }
375   pclose(f);
376   return GNUNET_OK;
377 }
378 #endif
379
380
381 /**
382  * @brief Enumerate all network interfaces
383  *
384  * @param proc the callback function
385  * @param proc_cls closure for @a proc
386  */
387 void
388 GNUNET_OS_network_interfaces_list(GNUNET_OS_NetworkInterfaceProcessor proc,
389                                   void *proc_cls)
390 {
391 #ifdef MINGW
392   int r;
393   int i;
394   struct EnumNICs3_results *results = NULL;
395   int results_count;
396
397   r = EnumNICs3(&results, &results_count);
398   if (r != GNUNET_OK)
399     return;
400
401   for (i = 0; i < results_count; i++)
402     {
403       if (GNUNET_OK !=
404           proc(proc_cls, results[i].pretty_name, results[i].is_default,
405                (const struct sockaddr *)&results[i].address,
406                results[i].
407                flags & ENUMNICS3_BCAST_OK ?
408                (const struct sockaddr *)&results[i].broadcast : NULL,
409                results[i].flags & ENUMNICS3_MASK_OK ?
410                (const struct sockaddr *)&results[i].mask : NULL,
411                results[i].addr_size))
412         break;
413     }
414   EnumNICs3_free(results);
415   return;
416 #elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS
417   struct ifaddrs *ifa_first;
418   struct ifaddrs *ifa_ptr;
419   socklen_t alen;
420
421   if (getifaddrs(&ifa_first) == 0)
422     {
423       for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next)
424         {
425           if (ifa_ptr->ifa_name != NULL && ifa_ptr->ifa_addr != NULL &&
426               (ifa_ptr->ifa_flags & IFF_UP) != 0)
427             {
428               if ((ifa_ptr->ifa_addr->sa_family != AF_INET) &&
429                   (ifa_ptr->ifa_addr->sa_family != AF_INET6))
430                 continue;
431               if (ifa_ptr->ifa_addr->sa_family == AF_INET)
432                 alen = sizeof(struct sockaddr_in);
433               else
434                 alen = sizeof(struct sockaddr_in6);
435               if (GNUNET_OK !=
436                   proc(proc_cls, ifa_ptr->ifa_name,
437                        0 == strcmp(ifa_ptr->ifa_name, GNUNET_DEFAULT_INTERFACE),
438                        ifa_ptr->ifa_addr, ifa_ptr->ifa_broadaddr,
439                        ifa_ptr->ifa_netmask, alen))
440                 break;
441             }
442         }
443       freeifaddrs(ifa_first);
444     }
445 #else
446   if (GNUNET_OK ==
447       try_ip(proc,
448              proc_cls))
449     return;
450   if (GNUNET_OK ==
451       try_ifconfig(proc,
452                    proc_cls))
453     return;
454   LOG(GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
455       "Failed to enumerate network interfaces\n");
456 #endif
457 }
458
459
460 /* end of os_network.c */