Changed errno to SOCKET_ERRNO/SOCKET_SET_ERRNO for Mac OT SocketsLib
[krb5.git] / src / lib / krb5 / os / localaddr.c
1 /*
2  * lib/krb5/os/localaddr.c
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  * 
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  M.I.T. makes no representations about the suitability of
20  * this software for any purpose.  It is provided "as is" without express
21  * or implied warranty.
22  * 
23  *
24  * Return the protocol addresses supported by this host.
25  *
26  * XNS support is untested, but "Should just work".
27  */
28
29 #define NEED_SOCKETS
30 #include "k5-int.h"
31
32 #if !defined(HAVE_MACSOCK_H) && !defined(_MSDOS) && !defined(_WIN32)
33
34 /* needed for solaris, harmless elsewhere... */
35 #define BSD_COMP
36 #include <sys/ioctl.h>
37 #include <sys/time.h>
38 #include <errno.h>
39
40 /*
41  * The SIOCGIF* ioctls require a socket.
42  * It doesn't matter *what* kind of socket they use, but it has to be
43  * a socket.
44  *
45  * Of course, you can't just ask the kernel for a socket of arbitrary
46  * type; you have to ask for one with a valid type.
47  *
48  */
49 #ifdef HAVE_NETINET_IN_H
50
51 #include <netinet/in.h>
52
53 #ifndef USE_AF
54 #define USE_AF AF_INET
55 #define USE_TYPE SOCK_DGRAM
56 #define USE_PROTO 0
57 #endif
58
59 #endif
60
61 #ifdef KRB5_USE_NS
62
63 #include <netns/ns.h>
64
65 #ifndef USE_AF
66 #define USE_AF AF_NS
67 #define USE_TYPE SOCK_DGRAM
68 #define USE_PROTO 0             /* guess */
69 #endif
70
71 #endif
72 /*
73  * Add more address families here.
74  */
75
76 /*
77  * BSD 4.4 defines the size of an ifreq to be
78  * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
79  * However, under earlier systems, sa_len isn't present, so the size is 
80  * just sizeof(struct ifreq)
81  */
82 #ifdef HAVE_SA_LEN
83 #ifndef max
84 #define max(a,b) ((a) > (b) ? (a) : (b))
85 #endif
86 #define ifreq_size(i) max(sizeof(struct ifreq),\
87      sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
88 #else
89 #define ifreq_size(i) sizeof(struct ifreq)
90 #endif /* HAVE_SA_LEN*/
91
92
93
94 extern int errno;
95
96 /*
97  * Return all the protocol addresses of this host.
98  *
99  * We could kludge up something to return all addresses, assuming that
100  * they're valid kerberos protocol addresses, but we wouldn't know the
101  * real size of the sockaddr or know which part of it was actually the
102  * host part.
103  *
104  * This uses the SIOCGIFCONF, SIOCGIFFLAGS, and SIOCGIFADDR ioctl's.
105  */
106
107 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
108 krb5_os_localaddr(context, addr)
109     krb5_context context;
110     krb5_address FAR * FAR * FAR *addr;
111 {
112     struct ifreq *ifr, ifreq;
113     struct ifconf ifc;
114     int s, code, n, i;
115     char buf[1024];
116     krb5_address *addr_temp [ 1024/sizeof(struct ifreq) ];
117     int n_found;
118     int mem_err = 0;
119     
120     memset(buf, 0, sizeof(buf));
121     ifc.ifc_len = sizeof(buf);
122     ifc.ifc_buf = buf;
123     
124     s = socket (USE_AF, USE_TYPE, USE_PROTO);
125     if (s < 0)
126         return SOCKET_ERRNO;
127
128     code = ioctl (s, SIOCGIFCONF, (char *)&ifc);
129     if (code < 0) {
130         int retval = errno;
131         closesocket (s);
132         return retval;
133     }
134     n = ifc.ifc_len;
135     
136 n_found = 0;
137     for (i = 0; i < n; i+= ifreq_size(*ifr) ) {
138         krb5_address *address;
139         ifr = (struct ifreq *)((caddr_t) ifc.ifc_buf+i);
140
141         strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
142         if (ioctl (s, SIOCGIFFLAGS, (char *)&ifreq) < 0)
143             continue;
144
145 #ifdef IFF_LOOPBACK
146         if (ifreq.ifr_flags & IFF_LOOPBACK) 
147             continue;
148 #endif
149
150         if (!(ifreq.ifr_flags & IFF_UP)) 
151             /* interface is down; skip */
152             continue;
153
154         /* ifr->ifr_addr has what we want! */
155         switch (ifr->ifr_addr.sa_family) {
156 #ifdef HAVE_NETINET_IN_H
157         case AF_INET:
158             {
159                 struct sockaddr_in *in =
160                     (struct sockaddr_in *)&ifr->ifr_addr;
161                 
162                 address = (krb5_address *)
163                     malloc (sizeof(krb5_address));
164                 if (address) {
165                     address->magic = KV5M_ADDRESS;
166                     address->addrtype = ADDRTYPE_INET;
167                     address->length = sizeof(struct in_addr);
168                     address->contents = (unsigned char *)malloc(address->length);
169                     if (!address->contents) {
170                         krb5_xfree(address);
171                         address = 0;
172                         mem_err++;
173                     } else {
174                         memcpy ((char *)address->contents,
175                                 (char *)&in->sin_addr, 
176                                 address->length);
177                         break;
178                     }
179                 } else mem_err++;
180             }
181 #endif
182 #ifdef KRB5_USE_NS
183             case AF_XNS:
184             {  
185                 struct sockaddr_ns *ns =
186                     (struct sockaddr_ns *)&ifr->ifr_addr;
187                 address = (krb5_address *)
188                     malloc (sizeof (krb5_address) + sizeof (struct ns_addr));
189                 if (address) {
190                     address->magic = KV5M_ADDRESS;
191                     address->addrtype = ADDRTYPE_XNS; 
192
193                     /* XXX should we perhaps use ns_host instead? */
194
195                     address->length = sizeof(struct ns_addr);
196                     address->contents = (unsigned char *)malloc(address->length);
197                     if (!address->contents) {
198                         krb5_xfree(address);
199                         address = 0;
200                         mem_err++;
201                     } else {
202                         memcpy ((char *)address->contents,
203                                 (char *)&ns->sns_addr,
204                                 address->length);
205                         break;
206                     }
207                 } else mem_err++;
208                 break;
209             }
210 #endif
211         /*
212          * Add more address families here..
213          */
214         default:
215             continue;
216         }
217         if (address)
218             addr_temp[n_found++] = address;
219         address = 0;
220     }
221     closesocket(s);
222
223     *addr = (krb5_address **)malloc (sizeof (krb5_address *) * (n_found+1));
224     if (*addr == 0)
225         mem_err++;
226     
227     if (mem_err) {
228         for (i=0; i<n_found; i++) {
229             krb5_xfree(addr_temp[i]);
230             addr_temp[i] = 0;
231         }
232         return ENOMEM;
233     }
234     
235     for (i=0; i<n_found; i++) {
236         (*addr)[i] = addr_temp[i];
237     }
238     (*addr)[n_found] = 0;
239     return 0;
240 }
241
242 #else /* Windows/Mac version */
243
244 /*
245  * Hold on to your lunch!  Backup kludge method of obtaining your
246  * local IP address, courtesy of Windows Socket Network Programming,
247  * by Robert Quinn
248  */
249 #if defined(_MSDOS) || defined(_WIN32)
250 static struct hostent *local_addr_fallback_kludge()
251 {
252         static struct hostent   host;
253         static SOCKADDR_IN      addr;
254         static char *           ip_ptrs[2];
255         SOCKET                  sock;
256         int                     size = sizeof(SOCKADDR);
257         int                     err;
258
259         sock = socket(AF_INET, SOCK_DGRAM, 0);
260         if (sock == INVALID_SOCKET)
261                 return NULL;
262
263         /* connect to arbitrary port and address (NOT loopback) */
264         addr.sin_family = AF_INET;
265         addr.sin_port = htons(IPPORT_ECHO);
266         addr.sin_addr.s_addr = inet_addr("204.137.220.51");
267
268         err = connect(sock, (LPSOCKADDR) &addr, sizeof(SOCKADDR));
269         if (err == SOCKET_ERROR)
270                 return NULL;
271
272         err = getsockname(sock, (LPSOCKADDR) &addr, (int FAR *) size);
273         if (err == SOCKET_ERROR)
274                 return NULL;
275
276         closesocket(sock);
277
278         host.h_name = 0;
279         host.h_aliases = 0;
280         host.h_addrtype = AF_INET;
281         host.h_length = 4;
282         host.h_addr_list = ip_ptrs;
283         ip_ptrs[0] = (char *) &addr.sin_addr.s_addr;
284         ip_ptrs[1] = NULL;
285
286         return &host;
287 }
288 #endif
289
290 /* No ioctls in winsock so we just assume there is only one networking 
291  * card per machine, so gethostent is good enough. 
292  */
293 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
294 krb5_os_localaddr (krb5_context context, krb5_address ***addr) {
295     char host[64];                              /* Name of local machine */
296     struct hostent *hostrec;
297     int err;
298
299     *addr = calloc (2, sizeof (krb5_address *));
300     if (*addr == NULL)
301         return ENOMEM;
302
303     err = 0;
304     
305     if (gethostname (host, sizeof(host))) {
306         err = SOCKET_ERRNO;
307     }
308
309     if (!err) {
310             hostrec = gethostbyname (host);
311             if (hostrec == NULL) {
312                     err = SOCKET_ERRNO;
313             }
314     }
315
316     if (err) {
317             hostrec = local_addr_fallback_kludge();
318             if (!hostrec)
319                     return err;
320     }
321
322     (*addr)[0] = calloc (1, sizeof(krb5_address));
323     if ((*addr)[0] == NULL) {
324         free (*addr);
325         return ENOMEM;
326     }
327     (*addr)[0]->magic = KV5M_ADDRESS;
328     (*addr)[0]->addrtype = hostrec->h_addrtype;
329     (*addr)[0]->length = hostrec->h_length;
330     (*addr)[0]->contents = (unsigned char *)malloc((*addr)[0]->length);
331     if (!(*addr)[0]->contents) {
332         free((*addr)[0]);
333         free(*addr);
334         return ENOMEM;
335     } else {
336         memcpy ((*addr)[0]->contents,
337                 hostrec->h_addr,
338                 (*addr)[0]->length);
339     }
340         /* FIXME, deal with the case where gethostent returns multiple addrs */
341
342     return(0);
343 }
344 #endif
345