"Fixing my FORTRAN". Ensure we always have some valid char converter for the
[tprouty/samba.git] / source / lib / interfaces.c
1 /* 
2    Unix SMB/CIFS implementation.
3    return a list of network interfaces
4    Copyright (C) Andrew Tridgell 1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 /* working out the interfaces for a OS is an incredibly non-portable
23    thing. We have several possible implementations below, and autoconf
24    tries each of them to see what works
25
26    Note that this file does _not_ include includes.h. That is so this code
27    can be called directly from the autoconf tests. That also means
28    this code cannot use any of the normal Samba debug stuff or defines.
29    This is standalone code.
30
31 */
32
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 #include <sys/ioctl.h>
41 #include <sys/time.h>
42 #include <net/if.h>
43
44 #ifdef AUTOCONF_TEST
45 struct iface_struct {
46         char name[16];
47         struct in_addr ip;
48         struct in_addr netmask;
49 };
50 #else
51 #include "config.h"
52 #include "interfaces.h"
53 #endif
54
55 #ifdef HAVE_SYS_TIME_H
56 #include <sys/time.h>
57 #endif
58
59 #ifndef SIOCGIFCONF
60 #ifdef HAVE_SYS_SOCKIO_H
61 #include <sys/sockio.h>
62 #endif
63 #endif
64
65 #ifdef HAVE_STDLIB_H
66 #include <stdlib.h>
67 #endif
68
69 #ifdef HAVE_STRING_H
70 #include <string.h>
71 #endif
72
73 #ifdef HAVE_STRINGS_H
74 #include <strings.h>
75 #endif
76
77 #ifdef __COMPAR_FN_T
78 #define QSORT_CAST (__compar_fn_t)
79 #endif
80
81 #ifndef QSORT_CAST
82 #define QSORT_CAST (int (*)(const void *, const void *))
83 #endif
84
85 #if HAVE_IFACE_IFCONF
86
87 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
88    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
89
90    It probably also works on any BSD style system.  */
91
92 /****************************************************************************
93   get the netmask address for a local interface
94 ****************************************************************************/
95 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
96 {  
97         struct ifconf ifc;
98         char buff[8192];
99         int fd, i, n;
100         struct ifreq *ifr=NULL;
101         int total = 0;
102         struct in_addr ipaddr;
103         struct in_addr nmask;
104         char *iname;
105
106         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
107                 return -1;
108         }
109   
110         ifc.ifc_len = sizeof(buff);
111         ifc.ifc_buf = buff;
112
113         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
114                 close(fd);
115                 return -1;
116         } 
117
118         ifr = ifc.ifc_req;
119   
120         n = ifc.ifc_len / sizeof(struct ifreq);
121
122         /* Loop through interfaces, looking for given IP address */
123         for (i=n-1;i>=0 && total < max_interfaces;i--) {
124                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
125                         continue;
126                 }
127
128                 iname = ifr[i].ifr_name;
129                 ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
130
131                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
132                         continue;
133                 }  
134
135                 if (!(ifr[i].ifr_flags & IFF_UP)) {
136                         continue;
137                 }
138
139                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
140                         continue;
141                 }  
142
143                 nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
144
145                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
146                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
147                 ifaces[total].ip = ipaddr;
148                 ifaces[total].netmask = nmask;
149                 total++;
150         }
151
152         close(fd);
153
154         return total;
155 }  
156
157 #elif HAVE_IFACE_IFREQ
158
159 #ifndef I_STR
160 #include <sys/stropts.h>
161 #endif
162
163 /****************************************************************************
164 this should cover most of the streams based systems
165 Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
166 ****************************************************************************/
167 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
168 {
169         struct ifreq ifreq;
170         struct strioctl strioctl;
171         char buff[8192];
172         int fd, i, n;
173         struct ifreq *ifr=NULL;
174         int total = 0;
175         struct in_addr ipaddr;
176         struct in_addr nmask;
177         char *iname;
178
179         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
180                 return -1;
181         }
182   
183         strioctl.ic_cmd = SIOCGIFCONF;
184         strioctl.ic_dp  = buff;
185         strioctl.ic_len = sizeof(buff);
186         if (ioctl(fd, I_STR, &strioctl) < 0) {
187                 close(fd);
188                 return -1;
189         } 
190
191         /* we can ignore the possible sizeof(int) here as the resulting
192            number of interface structures won't change */
193         n = strioctl.ic_len / sizeof(struct ifreq);
194
195         /* we will assume that the kernel returns the length as an int
196            at the start of the buffer if the offered size is a
197            multiple of the structure size plus an int */
198         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
199                 ifr = (struct ifreq *)(buff + sizeof(int));  
200         } else {
201                 ifr = (struct ifreq *)buff;  
202         }
203
204         /* Loop through interfaces */
205
206         for (i = 0; i<n && total < max_interfaces; i++) {
207                 ifreq = ifr[i];
208   
209                 strioctl.ic_cmd = SIOCGIFFLAGS;
210                 strioctl.ic_dp  = (char *)&ifreq;
211                 strioctl.ic_len = sizeof(struct ifreq);
212                 if (ioctl(fd, I_STR, &strioctl) != 0) {
213                         continue;
214                 }
215                 
216                 if (!(ifreq.ifr_flags & IFF_UP)) {
217                         continue;
218                 }
219
220                 strioctl.ic_cmd = SIOCGIFADDR;
221                 strioctl.ic_dp  = (char *)&ifreq;
222                 strioctl.ic_len = sizeof(struct ifreq);
223                 if (ioctl(fd, I_STR, &strioctl) != 0) {
224                         continue;
225                 }
226
227                 ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
228                 iname = ifreq.ifr_name;
229
230                 strioctl.ic_cmd = SIOCGIFNETMASK;
231                 strioctl.ic_dp  = (char *)&ifreq;
232                 strioctl.ic_len = sizeof(struct ifreq);
233                 if (ioctl(fd, I_STR, &strioctl) != 0) {
234                         continue;
235                 }
236
237                 nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
238
239                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
240                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
241                 ifaces[total].ip = ipaddr;
242                 ifaces[total].netmask = nmask;
243
244                 total++;
245         }
246
247         close(fd);
248
249         return total;
250 }
251
252 #elif HAVE_IFACE_AIX
253
254 /****************************************************************************
255 this one is for AIX (tested on 4.2)
256 ****************************************************************************/
257 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
258 {
259         char buff[8192];
260         int fd, i;
261         struct ifconf ifc;
262         struct ifreq *ifr=NULL;
263         struct in_addr ipaddr;
264         struct in_addr nmask;
265         char *iname;
266         int total = 0;
267
268         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
269                 return -1;
270         }
271
272
273         ifc.ifc_len = sizeof(buff);
274         ifc.ifc_buf = buff;
275
276         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
277                 close(fd);
278                 return -1;
279         }
280
281         ifr = ifc.ifc_req;
282
283         /* Loop through interfaces */
284         i = ifc.ifc_len;
285
286         while (i > 0 && total < max_interfaces) {
287                 unsigned inc;
288
289                 inc = ifr->ifr_addr.sa_len;
290
291                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
292                         goto next;
293                 }
294
295                 ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
296                 iname = ifr->ifr_name;
297
298                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
299                         goto next;
300                 }
301
302                 if (!(ifr->ifr_flags & IFF_UP)) {
303                         goto next;
304                 }
305
306                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
307                         goto next;
308                 }
309
310                 nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
311
312                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
313                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
314                 ifaces[total].ip = ipaddr;
315                 ifaces[total].netmask = nmask;
316
317                 total++;
318
319         next:
320                 /*
321                  * Patch from Archie Cobbs (archie@whistle.com).  The
322                  * addresses in the SIOCGIFCONF interface list have a
323                  * minimum size. Usually this doesn't matter, but if
324                  * your machine has tunnel interfaces, etc. that have
325                  * a zero length "link address", this does matter.  */
326
327                 if (inc < sizeof(ifr->ifr_addr))
328                         inc = sizeof(ifr->ifr_addr);
329                 inc += IFNAMSIZ;
330
331                 ifr = (struct ifreq*) (((char*) ifr) + inc);
332                 i -= inc;
333         }
334   
335
336         close(fd);
337         return total;
338 }
339
340 #else /* a dummy version */
341 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
342 {
343         return -1;
344 }
345 #endif
346
347
348 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
349 {
350         int r;
351         r = strcmp(i1->name, i2->name);
352         if (r) return r;
353         r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
354         if (r) return r;
355         r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
356         return r;
357 }
358
359 /* this wrapper is used to remove duplicates from the interface list generated
360    above */
361 int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
362 {
363         int total, i, j;
364
365         total = _get_interfaces(ifaces, max_interfaces);
366         if (total <= 0) return total;
367
368         /* now we need to remove duplicates */
369         qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
370
371         for (i=1;i<total;) {
372                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
373                         for (j=i-1;j<total-1;j++) {
374                                 ifaces[j] = ifaces[j+1];
375                         }
376                         total--;
377                 } else {
378                         i++;
379                 }
380         }
381
382         return total;
383 }
384
385
386 #ifdef AUTOCONF_TEST
387 /* this is the autoconf driver to test get_interfaces() */
388
389 #define MAX_INTERFACES 128
390
391  int main()
392 {
393         struct iface_struct ifaces[MAX_INTERFACES];
394         int total = get_interfaces(ifaces, MAX_INTERFACES);
395         int i;
396
397         printf("got %d interfaces:\n", total);
398         if (total <= 0) exit(1);
399
400         for (i=0;i<total;i++) {
401                 printf("%-10s ", ifaces[i].name);
402                 printf("IP=%s ", inet_ntoa(ifaces[i].ip));
403                 printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask));
404         }
405         return 0;
406 }
407 #endif