f6f0ec080c2e48ef085635c04c6af7d87c61753d
[samba.git] / replace / getifaddrs.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Jeremy Allison 2007
6    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #define SOCKET_WRAPPER_NOT_REPLACE
23
24 #include "replace.h"
25 #include "system/network.h"
26
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30
31 #ifdef HAVE_SYS_TIME_H
32 #include <sys/time.h>
33 #endif
34
35 #ifndef SIOCGIFCONF
36 #ifdef HAVE_SYS_SOCKIO_H
37 #include <sys/sockio.h>
38 #endif
39 #endif
40
41 #ifdef HAVE_IFACE_GETIFADDRS
42 #define _FOUND_IFACE_ANY
43 #else
44
45 void rep_freeifaddrs(struct ifaddrs *ifp)
46 {
47         if (ifp != NULL) {
48                 free(ifp->ifa_name);
49                 free(ifp->ifa_addr);
50                 free(ifp->ifa_netmask);
51                 free(ifp->ifa_dstaddr);
52                 freeifaddrs(ifp->ifa_next);
53                 free(ifp);
54         }
55 }
56
57 static struct sockaddr *sockaddr_dup(struct sockaddr *sa)
58 {
59         struct sockaddr *ret;
60         socklen_t socklen;
61 #ifdef HAVE_SOCKADDR_SA_LEN
62         socklen = sa->sa_len;
63 #else
64         socklen = sizeof(struct sockaddr_storage);
65 #endif
66         ret = calloc(1, socklen);
67         if (ret == NULL)
68                 return NULL;
69         memcpy(ret, sa, socklen);
70         return ret;
71 }
72 #endif
73
74 #if HAVE_IFACE_IFCONF
75
76 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
77    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
78
79    It probably also works on any BSD style system.  */
80
81 int rep_getifaddrs(struct ifaddrs **ifap)
82 {
83         struct ifconf ifc;
84         char buff[8192];
85         int fd, i, n;
86         struct ifreq *ifr=NULL;
87         struct in_addr ipaddr;
88         struct in_addr nmask;
89         char *iname;
90         struct ifaddrs *curif;
91         struct ifaddrs *lastif = NULL;
92
93         *ifap = NULL;
94
95         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
96                 return -1;
97         }
98   
99         ifc.ifc_len = sizeof(buff);
100         ifc.ifc_buf = buff;
101
102         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
103                 close(fd);
104                 return -1;
105         } 
106
107         ifr = ifc.ifc_req;
108   
109         n = ifc.ifc_len / sizeof(struct ifreq);
110
111         /* Loop through interfaces, looking for given IP address */
112         for (i=n-1; i>=0; i--) {
113                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) == -1) {
114                         freeifaddrs(*ifap);
115                         return -1;
116                 }
117
118                 curif = calloc(1, sizeof(struct ifaddrs));
119                 curif->ifa_name = strdup(ifr[i].ifr_name);
120                 curif->ifa_flags = ifr[i].ifr_flags;
121                 curif->ifa_dstaddr = NULL;
122                 curif->ifa_data = NULL;
123                 curif->ifa_next = NULL;
124
125                 curif->ifa_addr = NULL;
126                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != -1) {
127                         curif->ifa_addr = sockaddr_dup(&ifr[i].ifr_addr);
128                 }
129
130                 curif->ifa_netmask = NULL;
131                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != -1) {
132                         curif->ifa_netmask = sockaddr_dup(&ifr[i].ifr_addr);
133                 }
134
135                 if (lastif == NULL) {
136                         *ifap = curif;
137                 } else {
138                         lastif->ifa_next = curif;
139                 }
140                 lastif = curif;
141         }
142
143         close(fd);
144
145         return 0;
146 }  
147
148 #define _FOUND_IFACE_ANY
149 #endif /* HAVE_IFACE_IFCONF */
150 #ifdef HAVE_IFACE_IFREQ
151
152 #ifndef I_STR
153 #include <sys/stropts.h>
154 #endif
155
156 /****************************************************************************
157 this should cover most of the streams based systems
158 Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
159 ****************************************************************************/
160 int rep_getifaddrs(struct ifaddrs **ifap)
161 {
162         struct ifreq ifreq;
163         struct strioctl strioctl;
164         char buff[8192];
165         int fd, i, n;
166         struct ifreq *ifr=NULL;
167         struct in_addr ipaddr;
168         struct in_addr nmask;
169         char *iname;
170         struct ifaddrs *curif;
171         struct ifaddrs *lastif = NULL;
172
173         *ifap = NULL;
174
175         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
176                 return -1;
177         }
178   
179         strioctl.ic_cmd = SIOCGIFCONF;
180         strioctl.ic_dp  = buff;
181         strioctl.ic_len = sizeof(buff);
182         if (ioctl(fd, I_STR, &strioctl) < 0) {
183                 close(fd);
184                 return -1;
185         } 
186
187         /* we can ignore the possible sizeof(int) here as the resulting
188            number of interface structures won't change */
189         n = strioctl.ic_len / sizeof(struct ifreq);
190
191         /* we will assume that the kernel returns the length as an int
192            at the start of the buffer if the offered size is a
193            multiple of the structure size plus an int */
194         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
195                 ifr = (struct ifreq *)(buff + sizeof(int));  
196         } else {
197                 ifr = (struct ifreq *)buff;  
198         }
199
200         /* Loop through interfaces */
201
202         for (i = 0; i<n; i++) {
203                 ifreq = ifr[i];
204   
205                 curif = calloc(1, sizeof(struct ifaddrs));
206                 if (lastif == NULL) {
207                         *ifap = curif;
208                 } else {
209                         lastif->ifa_next = curif;
210                 }
211
212                 strioctl.ic_cmd = SIOCGIFFLAGS;
213                 strioctl.ic_dp  = (char *)&ifreq;
214                 strioctl.ic_len = sizeof(struct ifreq);
215                 if (ioctl(fd, I_STR, &strioctl) != 0) {
216                         freeifaddrs(*ifap);
217                         return -1;
218                 }
219
220                 curif->ifa_flags = ifreq.ifr_flags;
221                 
222                 strioctl.ic_cmd = SIOCGIFADDR;
223                 strioctl.ic_dp  = (char *)&ifreq;
224                 strioctl.ic_len = sizeof(struct ifreq);
225                 if (ioctl(fd, I_STR, &strioctl) != 0) {
226                         freeifaddrs(*ifap);
227                         return -1;
228                 }
229
230                 curif->ifa_name = strdup(ifreq.ifr_name);
231                 curif->ifa_addr = sockaddr_dup(&ifreq.ifr_addr);
232                 curif->ifa_dstaddr = NULL;
233                 curif->ifa_data = NULL;
234                 curif->ifa_next = NULL;
235                 curif->ifa_netmask = NULL;
236
237                 strioctl.ic_cmd = SIOCGIFNETMASK;
238                 strioctl.ic_dp  = (char *)&ifreq;
239                 strioctl.ic_len = sizeof(struct ifreq);
240                 if (ioctl(fd, I_STR, &strioctl) != 0) {
241                         freeifaddrs(*ifap);
242                         return -1;
243                 }
244
245                 curif->ifa_netmask = sockaddr_dup(&ifreq.ifr_addr);
246
247                 lastif = curif;
248         }
249
250         close(fd);
251
252         return 0;
253 }
254
255 #define _FOUND_IFACE_ANY
256 #endif /* HAVE_IFACE_IFREQ */
257 #ifdef HAVE_IFACE_AIX
258
259 /****************************************************************************
260 this one is for AIX (tested on 4.2)
261 ****************************************************************************/
262 int rep_getifaddrs(struct ifaddrs **ifap)
263 {
264         char buff[8192];
265         int fd, i;
266         struct ifconf ifc;
267         struct ifreq *ifr=NULL;
268         struct in_addr ipaddr;
269         struct in_addr nmask;
270         char *iname;
271         struct ifaddrs *curif;
272         struct ifaddrs *lastif = NULL;
273
274         *ifap = NULL;
275
276         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
277                 return -1;
278         }
279
280         ifc.ifc_len = sizeof(buff);
281         ifc.ifc_buf = buff;
282
283         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
284                 close(fd);
285                 return -1;
286         }
287
288         ifr = ifc.ifc_req;
289
290         /* Loop through interfaces */
291         i = ifc.ifc_len;
292
293         while (i > 0) {
294                 uint_t inc;
295
296                 inc = ifr->ifr_addr.sa_len;
297
298                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
299                         freeaddrinfo(*ifap);
300                         return -1;
301                 }
302
303                 curif = calloc(1, sizeof(struct ifaddrs));
304                 if (lastif == NULL) {
305                         *ifap = curif;
306                 } else {
307                         lastif->ifa_next = curif;
308                 }
309
310                 curif->ifa_name = strdup(ifr->ifr_name);
311                 curif->ifa_addr = sockaddr_dup(&ifr->ifr_addr);
312                 curif->ifa_dstaddr = NULL;
313                 curif->ifa_data = NULL;
314                 curif->ifa_netmask = NULL;
315                 curif->ifa_next = NULL;
316
317                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
318                         freeaddrinfo(*ifap);
319                         return -1;
320                 }
321
322                 curif->ifa_flags = ifr->ifr_flags;
323
324                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
325                         freeaddrinfo(*ifap);
326                         return -1;
327                 }
328
329                 curif->ifa_netmask = sockaddr_dup(&ifr->ifr_addr);
330
331                 lastif = curif;
332
333         next:
334                 /*
335                  * Patch from Archie Cobbs (archie@whistle.com).  The
336                  * addresses in the SIOCGIFCONF interface list have a
337                  * minimum size. Usually this doesn't matter, but if
338                  * your machine has tunnel interfaces, etc. that have
339                  * a zero length "link address", this does matter.  */
340
341                 if (inc < sizeof(ifr->ifr_addr))
342                         inc = sizeof(ifr->ifr_addr);
343                 inc += IFNAMSIZ;
344
345                 ifr = (struct ifreq*) (((char*) ifr) + inc);
346                 i -= inc;
347         }
348
349         close(fd);
350         return 0;
351 }
352
353 #define _FOUND_IFACE_ANY
354 #endif /* HAVE_IFACE_AIX */
355 #ifndef _FOUND_IFACE_ANY
356 int rep_getifaddrs(struct ifaddrs **ifap)
357 {
358         errno = ENOSYS;
359         return -1;
360 }
361 #endif