1 /* 2 * Copyright (c) 1997 - 1999 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: get_addrs.c,v 1.35 1999/12/02 17:05:09 joda Exp $"); 37 38 #ifdef __osf__ 39 /* hate */ 40 struct rtentry; 41 struct mbuf; 42 #endif 43 #ifdef HAVE_NET_IF_H 44 #include <net/if.h> 45 #endif 46 47 #ifdef HAVE_SYS_SOCKIO_H 48 #include <sys/sockio.h> 49 #endif /* HAVE_SYS_SOCKIO_H */ 50 51 #ifdef HAVE_NETINET_IN6_VAR_H 52 #include <netinet/in6_var.h> 53 #endif /* HAVE_NETINET_IN6_VAR_H */ 54 55 static krb5_error_code 56 gethostname_fallback (krb5_addresses *res) 57 { 58 krb5_error_code err; 59 char hostname[MAXHOSTNAMELEN]; 60 struct hostent *hostent; 61 62 if (gethostname (hostname, sizeof(hostname))) 63 return errno; 64 hostent = roken_gethostbyname (hostname); 65 if (hostent == NULL) 66 return errno; 67 res->len = 1; 68 res->val = malloc (sizeof(*res->val)); 69 if (res->val == NULL) 70 return ENOMEM; 71 res->val[0].addr_type = hostent->h_addrtype; 72 res->val[0].address.data = NULL; 73 res->val[0].address.length = 0; 74 err = krb5_data_copy (&res->val[0].address, 75 hostent->h_addr, 76 hostent->h_length); 77 if (err) { 78 free (res->val); 79 return err; 80 } 81 return 0; 82 } 83 84 enum { 85 LOOP = 1, /* do include loopback interfaces */ 86 LOOP_IF_NONE = 2, /* include loopback if no other if's */ 87 EXTRA_ADDRESSES = 4, /* include extra addresses */ 88 SCAN_INTERFACES = 8 /* scan interfaces for addresses */ 89 }; 90 91 /* 92 * Try to figure out the addresses of all configured interfaces with a 93 * lot of magic ioctls. 94 */ 95 96 static krb5_error_code 97 find_all_addresses (krb5_context context, 98 krb5_addresses *res, int flags, 99 int af, int siocgifconf, int siocgifflags, 100 size_t ifreq_sz) 101 { 102 krb5_error_code ret; 103 int fd; 104 size_t buf_size; 105 char *buf; 106 struct ifconf ifconf; 107 int num, j = 0; 108 char *p; 109 size_t sz; 110 struct sockaddr sa_zero; 111 struct ifreq *ifr; 112 krb5_address lo_addr; 113 int got_lo = FALSE; 114 115 buf = NULL; 116 res->val = NULL; 117 118 memset (&sa_zero, 0, sizeof(sa_zero)); 119 fd = socket(af, SOCK_DGRAM, 0); 120 if (fd < 0) 121 return -1; 122 123 buf_size = 8192; 124 for (;;) { 125 buf = malloc(buf_size); 126 if (buf == NULL) { 127 ret = ENOMEM; 128 goto error_out; 129 } 130 ifconf.ifc_len = buf_size; 131 ifconf.ifc_buf = buf; 132 if (ioctl (fd, siocgifconf, &ifconf) < 0) { 133 ret = errno; 134 goto error_out; 135 } 136 /* 137 * Can the difference between a full and a overfull buf 138 * be determined? 139 */ 140 141 if (ifconf.ifc_len < buf_size) 142 break; 143 free (buf); 144 buf_size *= 2; 145 } 146 147 num = ifconf.ifc_len / ifreq_sz; 148 res->len = num; 149 res->val = calloc(num, sizeof(*res->val)); 150 if (res->val == NULL) { 151 ret = ENOMEM; 152 goto error_out; 153 } 154 155 j = 0; 156 for (p = ifconf.ifc_buf; 157 p < ifconf.ifc_buf + ifconf.ifc_len; 158 p += sz) { 159 struct ifreq ifreq; 160 struct sockaddr *sa; 161 162 ifr = (struct ifreq *)p; 163 sa = &ifr->ifr_addr; 164 165 sz = ifreq_sz; 166 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 167 sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); 168 #endif 169 #ifdef SA_LEN 170 sz = max(sz, SA_LEN(sa)); 171 #endif 172 memcpy (ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); 173 174 if (ioctl(fd, siocgifflags, &ifreq) < 0) { 175 ret = errno; 176 goto error_out; 177 } 178 179 if (!(ifreq.ifr_flags & IFF_UP)) 180 continue; 181 if (memcmp (sa, &sa_zero, sizeof(sa_zero)) == 0) 182 continue; 183 if (krb5_sockaddr_uninteresting (sa)) 184 continue; 185 186 if (ifreq.ifr_flags & IFF_LOOPBACK) { 187 if (flags & LOOP_IF_NONE) { 188 ret = krb5_sockaddr2address (sa, &lo_addr); 189 if (ret) 190 goto error_out; 191 got_lo = TRUE; 192 continue; 193 } else if((flags & LOOP) == 0) 194 continue; 195 } 196 197 ret = krb5_sockaddr2address (sa, &res->val[j]); 198 if (ret) 199 goto error_out; 200 ++j; 201 } 202 if ((flags & LOOP_IF_NONE) && got_lo) { 203 if (j == 0) 204 res->val[j++] = lo_addr; 205 else 206 krb5_free_address (context, &lo_addr); 207 } 208 209 if (j != num) { 210 void *tmp; 211 212 res->len = j; 213 tmp = realloc (res->val, j * sizeof(*res->val)); 214 if (j != 0 && tmp == NULL) { 215 ret = ENOMEM; 216 goto error_out; 217 } 218 res->val = tmp; 219 } 220 ret = 0; 221 goto cleanup; 222 223 error_out: 224 if (got_lo) 225 krb5_free_address (context, &lo_addr); 226 while(j--) { 227 krb5_free_address (context, &res->val[j]); 228 } 229 free (res->val); 230 cleanup: 231 close (fd); 232 free (buf); 233 return ret; 234 } 235 236 static krb5_error_code 237 get_addrs_int (krb5_context context, krb5_addresses *res, int flags) 238 { 239 krb5_error_code ret = -1; 240 241 if (flags & SCAN_INTERFACES) { 242 #if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) 243 if (ret) 244 ret = find_all_addresses (context, res, flags, 245 AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, 246 sizeof(struct in6_ifreq)); 247 #endif 248 #if defined(HAVE_IPV6) && defined(SIOCGIFCONF) 249 if (ret) 250 ret = find_all_addresses (context, res, flags, 251 AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, 252 sizeof(struct ifreq)); 253 #endif 254 #if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) 255 if (ret) 256 ret = find_all_addresses (context, res, flags, 257 AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, 258 sizeof(struct ifreq)); 259 if(ret || res->len == 0) 260 ret = gethostname_fallback (res); 261 #endif 262 } else 263 ret = 0; 264 265 if(ret == 0 && (flags & EXTRA_ADDRESSES)) { 266 /* append user specified addresses */ 267 krb5_addresses a; 268 ret = krb5_get_extra_addresses(context, &a); 269 if(ret) { 270 krb5_free_addresses(context, res); 271 return ret; 272 } 273 ret = krb5_append_addresses(context, res, &a); 274 if(ret) { 275 krb5_free_addresses(context, res); 276 return ret; 277 } 278 krb5_free_addresses(context, &a); 279 } 280 return ret; 281 } 282 283 /* 284 * Try to get all addresses, but return the one corresponding to 285 * `hostname' if we fail. 286 * 287 * Only include loopback address if there are no other. 288 */ 289 290 krb5_error_code 291 krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res) 292 { 293 int flags = LOOP_IF_NONE | EXTRA_ADDRESSES; 294 295 if (context->scan_interfaces) 296 flags |= SCAN_INTERFACES; 297 298 return get_addrs_int (context, res, flags); 299 } 300 301 /* 302 * Try to get all local addresses that a server should listen to. 303 * If that fails, we return the address corresponding to `hostname'. 304 */ 305 306 krb5_error_code 307 krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res) 308 { 309 return get_addrs_int (context, res, LOOP | SCAN_INTERFACES); 310 } 311