Upgrade file(1) and libmagic. 1/2
[dragonfly.git] / lib / libc / net / getifaddrs.c
1 /*      $FreeBSD: src/lib/libc/net/getifaddrs.c,v 1.6 2002/07/25 08:08:30 ume Exp $     */
2 /*      $KAME: getifaddrs.c,v 1.9 2001/08/20 02:31:20 itojun Exp $      */
3
4 /*
5  * Copyright (c) 1995, 1999
6  *      Berkeley Software Design, Inc.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      BSDI getifaddrs.c,v 2.12 2000/02/23 14:51:59 dab Exp
27  */
28 /*
29  * NOTE: SIOCGIFCONF case is not LP64 friendly.  it also does not perform
30  * try-and-error for region size.
31  */
32 #include "namespace.h"
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <net/if.h>
37 #include <sys/param.h>
38 #include <net/route.h>
39 #include <sys/sysctl.h>
40 #include <net/if_dl.h>
41
42 #include <errno.h>
43 #include <ifaddrs.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include "un-namespace.h"
47
48 #if !defined(AF_LINK)
49 #define SA_LEN(sa)      sizeof(struct sockaddr)
50 #endif
51
52 #if !defined(SA_LEN)
53 #define SA_LEN(sa)      (sa)->sa_len
54 #endif
55
56 #define SA_RLEN(sa)     RT_ROUNDUP((sa)->sa_len)
57
58 #ifndef ALIGNBYTES
59 /*
60  * On systems with a routing socket, ALIGNBYTES should match the value
61  * that the kernel uses when building the messages.
62  */
63 #define ALIGNBYTES      XXX
64 #endif
65 #ifndef ALIGN
66 #define ALIGN(p)        (((u_long)(p) + ALIGNBYTES) &~ ALIGNBYTES)
67 #endif
68
69 #define MAX_SYSCTL_TRY 5
70
71 int
72 getifaddrs(struct ifaddrs **pif)
73 {
74         int icnt = 1;
75         int dcnt = 0;
76         int ncnt = 0;
77         int ntry = 0;
78         int mib[6];
79         size_t needed;
80         char *buf;
81         char *next;
82         struct ifaddrs *cif = NULL;
83         char *p, *p0;
84         struct rt_msghdr *rtm;
85         struct if_msghdr *ifm;
86         struct ifa_msghdr *ifam;
87         struct sockaddr_dl *dl;
88         struct sockaddr *sa;
89         struct ifaddrs *ifa, *ift;
90         u_short idx = 0;
91         int i;
92         size_t len, alen;
93         char *data;
94         char *names;
95
96         mib[0] = CTL_NET;
97         mib[1] = PF_ROUTE;
98         mib[2] = 0;             /* protocol */
99         mib[3] = 0;             /* wildcard address family */
100         mib[4] = NET_RT_IFLIST;
101         mib[5] = 0;             /* no flags */
102         do {
103                 /*
104                  * We'll try to get addresses several times in case that
105                  * the number of addresses is unexpectedly increased during
106                  * the two sysctl calls.  This should rarely happen, but we'll
107                  * try to do our best for applications that assume success of
108                  * this library (which should usually be the case).
109                  * Portability note: since FreeBSD does not add margin of
110                  * memory at the first sysctl, the possibility of failure on
111                  * the second sysctl call is a bit higher.
112                  */
113
114                 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
115                         return (-1);
116                 if ((buf = malloc(needed)) == NULL)
117                         return (-1);
118                 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
119                         if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
120                                 free(buf);
121                                 return (-1);
122                         }
123                         free(buf);
124                         buf = NULL;
125                 } 
126         } while (buf == NULL);
127
128         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
129                 rtm = (struct rt_msghdr *)(void *)next;
130                 if (rtm->rtm_version != RTM_VERSION)
131                         continue;
132                 switch (rtm->rtm_type) {
133                 case RTM_IFINFO:
134                         ifm = (struct if_msghdr *)(void *)rtm;
135                         if (ifm->ifm_addrs & RTA_IFP) {
136                                 idx = ifm->ifm_index;
137                                 ++icnt;
138                                 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
139                                 dcnt += SA_RLEN((struct sockaddr *)(void*)dl) +
140                                     ALIGNBYTES;
141                                 dcnt += sizeof(ifm->ifm_data);
142                                 ncnt += dl->sdl_nlen + 1;
143                         } else
144                                 idx = 0;
145                         break;
146
147                 case RTM_NEWADDR:
148                         ifam = (struct ifa_msghdr *)(void *)rtm;
149                         if (idx && ifam->ifam_index != idx)
150                                 abort();        /* this cannot happen */
151
152 #define RTA_MASKS       (RTA_NETMASK | RTA_IFA | RTA_BRD)
153                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
154                                 break;
155                         p = (char *)(void *)(ifam + 1);
156                         ++icnt;
157                         /* Scan to look for length of address */
158                         alen = 0;
159                         for (p0 = p, i = 0; i < RTAX_MAX; i++) {
160                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
161                                     == 0)
162                                         continue;
163                                 sa = (struct sockaddr *)(void *)p;
164                                 len = SA_RLEN(sa);
165                                 if (i == RTAX_IFA) {
166                                         alen = len;
167                                         break;
168                                 }
169                                 p += len;
170                         }
171                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
172                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
173                                     == 0)
174                                         continue;
175                                 sa = (struct sockaddr *)(void *)p;
176                                 len = SA_RLEN(sa);
177                                 if (i == RTAX_NETMASK && SA_LEN(sa) == 0)
178                                         dcnt += alen;
179                                 else
180                                         dcnt += len;
181                                 p += len;
182                         }
183                         break;
184                 }
185         }
186
187         if (icnt + dcnt + ncnt == 1) {
188                 *pif = NULL;
189                 free(buf);
190                 return (0);
191         }
192         data = malloc(sizeof(struct ifaddrs) * icnt + dcnt + ncnt);
193         if (data == NULL) {
194                 free(buf);
195                 return(-1);
196         }
197
198         ifa = (struct ifaddrs *)(void *)data;
199         data += sizeof(struct ifaddrs) * icnt;
200         names = data + dcnt;
201
202         memset(ifa, 0, sizeof(struct ifaddrs) * icnt);
203         ift = ifa;
204
205         idx = 0;
206         for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
207                 rtm = (struct rt_msghdr *)(void *)next;
208                 if (rtm->rtm_version != RTM_VERSION)
209                         continue;
210                 switch (rtm->rtm_type) {
211                 case RTM_IFINFO:
212                         ifm = (struct if_msghdr *)(void *)rtm;
213                         if (ifm->ifm_addrs & RTA_IFP) {
214                                 idx = ifm->ifm_index;
215                                 dl = (struct sockaddr_dl *)(void *)(ifm + 1);
216
217                                 cif = ift;
218                                 ift->ifa_name = names;
219                                 ift->ifa_flags = (int)ifm->ifm_flags;
220                                 memcpy(names, dl->sdl_data,
221                                     (size_t)dl->sdl_nlen);
222                                 names[dl->sdl_nlen] = 0;
223                                 names += dl->sdl_nlen + 1;
224
225                                 ift->ifa_addr = (struct sockaddr *)(void *)data;
226                                 memcpy(data, dl,
227                                     (size_t)SA_LEN((struct sockaddr *)
228                                     (void *)dl));
229                                 data += SA_RLEN((struct sockaddr *)(void *)dl);
230
231                                 /* ifm_data needs to be aligned */
232                                 ift->ifa_data = data = (void *)ALIGN(data);
233                                 memcpy(data, &ifm->ifm_data, sizeof(ifm->ifm_data));
234                                 data += sizeof(ifm->ifm_data);
235                                 ift = (ift->ifa_next = ift + 1);
236                         } else
237                                 idx = 0;
238                         break;
239
240                 case RTM_NEWADDR:
241                         ifam = (struct ifa_msghdr *)(void *)rtm;
242                         if (idx && ifam->ifam_index != idx)
243                                 abort();        /* this cannot happen */
244
245                         if (idx == 0 || (ifam->ifam_addrs & RTA_MASKS) == 0)
246                                 break;
247                         ift->ifa_name = cif->ifa_name;
248                         ift->ifa_flags = cif->ifa_flags;
249                         ift->ifa_data = NULL;
250                         ift->ifa_addrflags = ifam->ifam_addrflags;
251                         p = (char *)(void *)(ifam + 1);
252                         /* Scan to look for length of address */
253                         alen = 0;
254                         for (p0 = p, i = 0; i < RTAX_MAX; i++) {
255                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
256                                     == 0)
257                                         continue;
258                                 sa = (struct sockaddr *)(void *)p;
259                                 len = SA_RLEN(sa);
260                                 if (i == RTAX_IFA) {
261                                         alen = len;
262                                         break;
263                                 }
264                                 p += len;
265                         }
266                         for (p = p0, i = 0; i < RTAX_MAX; i++) {
267                                 if ((RTA_MASKS & ifam->ifam_addrs & (1 << i))
268                                     == 0)
269                                         continue;
270                                 sa = (struct sockaddr *)(void *)p;
271                                 len = SA_RLEN(sa);
272                                 switch (i) {
273                                 case RTAX_IFA:
274                                         ift->ifa_addr =
275                                             (struct sockaddr *)(void *)data;
276                                         memcpy(data, p, len);
277                                         data += len;
278                                         break;
279
280                                 case RTAX_NETMASK:
281                                         ift->ifa_netmask =
282                                             (struct sockaddr *)(void *)data;
283                                         if (SA_LEN(sa) == 0) {
284                                                 memset(data, 0, alen);
285                                                 data += alen;
286                                                 break;
287                                         }
288                                         memcpy(data, p, len);
289                                         data += len;
290                                         break;
291
292                                 case RTAX_BRD:
293                                         ift->ifa_broadaddr =
294                                             (struct sockaddr *)(void *)data;
295                                         memcpy(data, p, len);
296                                         data += len;
297                                         break;
298                                 }
299                                 p += len;
300                         }
301
302                         ift = (ift->ifa_next = ift + 1);
303                         break;
304                 }
305         }
306
307         free(buf);
308         if (--ift >= ifa) {
309                 ift->ifa_next = NULL;
310                 *pif = ifa;
311         } else {
312                 *pif = NULL;
313                 free(ifa);
314         }
315         return (0);
316 }
317
318 void
319 freeifaddrs(struct ifaddrs *ifp)
320 {
321
322         free(ifp);
323 }