Fix linked list of ifaddrs in implementations of rep_getifaddrs.
[kai/samba.git] / source3 / lib / 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 #include "replace.h"
23 #include "system/network.h"
24
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32
33 #ifndef SIOCGIFCONF
34 #ifdef HAVE_SYS_SOCKIO_H
35 #include <sys/sockio.h>
36 #endif
37 #endif
38
39 #ifdef HAVE_IFACE_GETIFADDRS
40 #define _FOUND_IFACE_ANY
41 #else
42
43 void rep_freeifaddrs(struct ifaddrs *ifp)
44 {
45         free(ifp->ifa_name);
46         free(ifp->ifa_addr);
47         free(ifp->ifa_netmask);
48         free(ifp->ifa_dstaddr);
49         if (ifp->ifa_next != NULL)
50                 freeifaddrs(ifp->ifa_next);
51         free(ifp);
52 }
53
54 static struct sockaddr *sockaddr_dup(struct sockaddr *sa)
55 {
56         struct sockaddr *ret;
57         socklen_t socklen;
58 #ifdef HAVE_SOCKADDR_SA_LEN
59         socklen = sa->sa_len;
60 #else
61         socklen = sizeof(struct sockaddr_storage);
62 #endif
63         ret = calloc(1, socklen);
64         if (ret == NULL)
65                 return NULL;
66         memcpy(ret, sa, socklen);
67         return ret;
68 }
69 #endif
70
71 #if HAVE_IFACE_IFCONF
72
73 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
74    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
75
76    It probably also works on any BSD style system.  */
77
78 int rep_getifaddrs(struct ifaddrs **ifap)
79 {
80         struct ifconf ifc;
81         char buff[8192];
82         int fd, i, n;
83         struct ifreq *ifr=NULL;
84         struct in_addr ipaddr;
85         struct in_addr nmask;
86         char *iname;
87         struct ifaddrs *curif;
88         struct ifaddrs *lastif = NULL;
89
90         *ifap = NULL;
91
92         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
93                 return -1;
94         }
95   
96         ifc.ifc_len = sizeof(buff);
97         ifc.ifc_buf = buff;
98
99         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
100                 close(fd);
101                 return -1;
102         } 
103
104         ifr = ifc.ifc_req;
105   
106         n = ifc.ifc_len / sizeof(struct ifreq);
107
108         /* Loop through interfaces, looking for given IP address */
109         for (i=n-1; i>=0; i--) {
110                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
111                         freeifaddrs(*ifap);
112                 }
113
114                 curif = calloc(1, sizeof(struct ifaddrs));
115                 if (lastif == NULL) {
116                         *ifap = curif;
117                 } else {
118                         lastif->ifa_next = curif;
119                 }
120
121                 curif->ifa_name = strdup(ifr[i].ifr_name);
122                 curif->ifa_addr = sockaddr_dup(&ifr[i].ifr_addr);
123                 curif->ifa_dstaddr = NULL;
124                 curif->ifa_data = NULL;
125                 curif->ifa_next = NULL;
126                 curif->ifa_netmask = NULL;
127
128                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
129                         freeifaddrs(*ifap);
130                         return -1;
131                 }  
132
133                 curif->ifa_flags = ifr[i].ifr_flags;
134
135                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
136                         freeifaddrs(*ifap);
137                         return -1;
138                 }  
139
140                 curif->ifa_netmask = sockaddr_dup(&ifr[i].ifr_addr);
141
142                 lastif = curif;
143         }
144
145         close(fd);
146
147         return 0;
148 }  
149
150 #define _FOUND_IFACE_ANY
151 #endif /* HAVE_IFACE_IFCONF */
152 #ifdef HAVE_IFACE_IFREQ
153
154 #ifndef I_STR
155 #include <sys/stropts.h>
156 #endif
157
158 /****************************************************************************
159 this should cover most of the streams based systems
160 Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
161 ****************************************************************************/
162 int rep_getifaddrs(struct ifaddrs **ifap)
163 {
164         struct ifreq ifreq;
165         struct strioctl strioctl;
166         char buff[8192];
167         int fd, i, n;
168         struct ifreq *ifr=NULL;
169         struct in_addr ipaddr;
170         struct in_addr nmask;
171         char *iname;
172         struct ifaddrs *curif;
173         struct ifaddrs *lastif = NULL;
174
175         *ifap = NULL;
176
177         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
178                 return -1;
179         }
180   
181         strioctl.ic_cmd = SIOCGIFCONF;
182         strioctl.ic_dp  = buff;
183         strioctl.ic_len = sizeof(buff);
184         if (ioctl(fd, I_STR, &strioctl) < 0) {
185                 close(fd);
186                 return -1;
187         } 
188
189         /* we can ignore the possible sizeof(int) here as the resulting
190            number of interface structures won't change */
191         n = strioctl.ic_len / sizeof(struct ifreq);
192
193         /* we will assume that the kernel returns the length as an int
194            at the start of the buffer if the offered size is a
195            multiple of the structure size plus an int */
196         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
197                 ifr = (struct ifreq *)(buff + sizeof(int));  
198         } else {
199                 ifr = (struct ifreq *)buff;  
200         }
201
202         /* Loop through interfaces */
203
204         for (i = 0; i<n; i++) {
205                 ifreq = ifr[i];
206   
207                 curif = calloc(1, sizeof(struct ifaddrs));
208                 if (lastif == NULL) {
209                         *ifap = curif;
210                 } else {
211                         lastif->ifa_next = curif;
212                 }
213
214                 strioctl.ic_cmd = SIOCGIFFLAGS;
215                 strioctl.ic_dp  = (char *)&ifreq;
216                 strioctl.ic_len = sizeof(struct ifreq);
217                 if (ioctl(fd, I_STR, &strioctl) != 0) {
218                         freeifaddrs(*ifap);
219                         return -1;
220                 }
221
222                 curif->ifa_flags = ifreq.ifr_flags;
223                 
224                 strioctl.ic_cmd = SIOCGIFADDR;
225                 strioctl.ic_dp  = (char *)&ifreq;
226                 strioctl.ic_len = sizeof(struct ifreq);
227                 if (ioctl(fd, I_STR, &strioctl) != 0) {
228                         freeifaddrs(*ifap);
229                         return -1;
230                 }
231
232                 curif->ifa_name = strdup(ifreq.ifr_name);
233                 curif->ifa_addr = sockaddr_dup(&ifreq.ifr_addr);
234                 curif->ifa_dstaddr = NULL;
235                 curif->ifa_data = NULL;
236                 curif->ifa_next = NULL;
237                 curif->ifa_netmask = NULL;
238
239                 strioctl.ic_cmd = SIOCGIFNETMASK;
240                 strioctl.ic_dp  = (char *)&ifreq;
241                 strioctl.ic_len = sizeof(struct ifreq);
242                 if (ioctl(fd, I_STR, &strioctl) != 0) {
243                         freeifaddrs(*ifap);
244                         return -1;
245                 }
246
247                 curif->ifa_netmask = sockaddr_dup(&ifreq.ifr_addr);
248
249                 lastif = curif;
250         }
251
252         close(fd);
253
254         return 0;
255 }
256
257 #define _FOUND_IFACE_ANY
258 #endif /* HAVE_IFACE_IFREQ */
259 #ifdef HAVE_IFACE_AIX
260
261 /****************************************************************************
262 this one is for AIX (tested on 4.2)
263 ****************************************************************************/
264 int rep_getifaddrs(struct ifaddrs **ifap)
265 {
266         char buff[8192];
267         int fd, i;
268         struct ifconf ifc;
269         struct ifreq *ifr=NULL;
270         struct in_addr ipaddr;
271         struct in_addr nmask;
272         char *iname;
273         struct ifaddrs *curif;
274         struct ifaddrs *lastif = NULL;
275
276         *ifap = NULL;
277
278         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
279                 return -1;
280         }
281
282         ifc.ifc_len = sizeof(buff);
283         ifc.ifc_buf = buff;
284
285         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
286                 close(fd);
287                 return -1;
288         }
289
290         ifr = ifc.ifc_req;
291
292         /* Loop through interfaces */
293         i = ifc.ifc_len;
294
295         while (i > 0) {
296                 uint_t inc;
297
298                 inc = ifr->ifr_addr.sa_len;
299
300                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
301                         freeaddrinfo(*ifap);
302                         return -1;
303                 }
304
305                 curif = calloc(1, sizeof(struct ifaddrs));
306                 if (lastif == NULL) {
307                         *ifap = curif;
308                 } else {
309                         lastif->ifa_next = curif;
310                 }
311
312                 curif->ifa_name = strdup(ifr->ifr_name);
313                 curif->ifa_addr = sockaddr_dup(&ifr->ifr_addr);
314                 curif->ifa_dstaddr = NULL;
315                 curif->ifa_data = NULL;
316                 curif->ifa_netmask = NULL;
317                 curif->ifa_next = NULL;
318
319                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
320                         freeaddrinfo(*ifap);
321                         return -1;
322                 }
323
324                 curif->ifa_flags = ifr->ifr_flags;
325
326                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
327                         freeaddrinfo(*ifap);
328                         return -1;
329                 }
330
331                 curif->ifa_netmask = sockaddr_dup(&ifr->ifr_addr);
332
333                 lastif = curif;
334
335         next:
336                 /*
337                  * Patch from Archie Cobbs (archie@whistle.com).  The
338                  * addresses in the SIOCGIFCONF interface list have a
339                  * minimum size. Usually this doesn't matter, but if
340                  * your machine has tunnel interfaces, etc. that have
341                  * a zero length "link address", this does matter.  */
342
343                 if (inc < sizeof(ifr->ifr_addr))
344                         inc = sizeof(ifr->ifr_addr);
345                 inc += IFNAMSIZ;
346
347                 ifr = (struct ifreq*) (((char*) ifr) + inc);
348                 i -= inc;
349         }
350
351         close(fd);
352         return 0;
353 }
354
355 #define _FOUND_IFACE_ANY
356 #endif /* HAVE_IFACE_AIX */
357 #ifndef _FOUND_IFACE_ANY
358 int rep_getifaddrs(struct ifaddrs **ifap)
359 {
360         errno = ENOSYS;
361         return -1;
362 }
363 #endif
364
365 #ifdef AUTOCONF_TEST
366 /* this is the autoconf driver to test get_interfaces() */
367
368  int main()
369 {
370         struct ifaddrs *ifs = NULL;
371         int ret;
372         
373         ret = getifaddrs(&ifs);
374         if (ret != 0) {
375                 perror("getifaddrs() failed");
376                 return 1;
377         }
378
379         while (ifs) {
380                 printf("%-10s ", ifs->ifa_name);
381                 if (ifs->ifa_addr != NULL && 
382                     ifs->ifa_addr->sa_family == AF_INET) {
383                         printf("IP=%s ", inet_ntoa(((struct sockaddr_in *)ifs->ifa_addr)->sin_addr));
384                         if (ifs->ifa_netmask != NULL)
385                                 printf("NETMASK=%s", inet_ntoa(((struct sockaddr_in *)ifs->ifa_netmask)->sin_addr));
386                 }
387                 printf("\n");
388                 ifs = ifs->ifa_next;
389         }
390         return 0;
391 }
392 #endif