1 /* 2 * Copyright (c) 1997 - 2001 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.41 2001/05/14 06:14:46 assar 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 #include <ifaddrs.h> 47 48 static krb5_error_code 49 gethostname_fallback (krb5_context context, krb5_addresses *res) 50 { 51 krb5_error_code ret; 52 char hostname[MAXHOSTNAMELEN]; 53 struct hostent *hostent; 54 55 if (gethostname (hostname, sizeof(hostname))) { 56 ret = errno; 57 krb5_set_error_string (context, "gethostname: %s", strerror(ret)); 58 return ret; 59 } 60 hostent = roken_gethostbyname (hostname); 61 if (hostent == NULL) { 62 ret = errno; 63 krb5_set_error_string (context, "gethostbyname %s: %s", 64 hostname, strerror(ret)); 65 return ret; 66 } 67 res->len = 1; 68 res->val = malloc (sizeof(*res->val)); 69 if (res->val == NULL) { 70 krb5_set_error_string(context, "malloc: out of memory"); 71 return ENOMEM; 72 } 73 res->val[0].addr_type = hostent->h_addrtype; 74 res->val[0].address.data = NULL; 75 res->val[0].address.length = 0; 76 ret = krb5_data_copy (&res->val[0].address, 77 hostent->h_addr, 78 hostent->h_length); 79 if (ret) { 80 free (res->val); 81 return ret; 82 } 83 return 0; 84 } 85 86 enum { 87 LOOP = 1, /* do include loopback interfaces */ 88 LOOP_IF_NONE = 2, /* include loopback if no other if's */ 89 EXTRA_ADDRESSES = 4, /* include extra addresses */ 90 SCAN_INTERFACES = 8 /* scan interfaces for addresses */ 91 }; 92 93 /* 94 * Try to figure out the addresses of all configured interfaces with a 95 * lot of magic ioctls. 96 */ 97 98 static krb5_error_code 99 find_all_addresses (krb5_context context, krb5_addresses *res, int flags) 100 { 101 struct sockaddr sa_zero; 102 struct ifaddrs *ifa0, *ifa; 103 krb5_error_code ret = ENXIO; 104 int num, idx; 105 106 res->val = NULL; 107 108 if (getifaddrs(&ifa0) == -1) { 109 ret = errno; 110 krb5_set_error_string(context, "getifaddrs: %s", strerror(ret)); 111 return (ret); 112 } 113 114 memset(&sa_zero, 0, sizeof(sa_zero)); 115 116 /* First, count all the ifaddrs. */ 117 for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++) 118 /* nothing */; 119 120 if (num == 0) { 121 freeifaddrs(ifa0); 122 krb5_set_error_string(context, "no addresses found"); 123 return (ENXIO); 124 } 125 126 /* Allocate storage for them. */ 127 res->val = calloc(num, sizeof(*res->val)); 128 if (res->val == NULL) { 129 freeifaddrs(ifa0); 130 krb5_set_error_string (context, "malloc: out of memory"); 131 return (ENOMEM); 132 } 133 134 /* Now traverse the list. */ 135 for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) { 136 if ((ifa->ifa_flags & IFF_UP) == 0) 137 continue; 138 if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0) 139 continue; 140 if (krb5_sockaddr_uninteresting(ifa->ifa_addr)) 141 continue; 142 143 if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) { 144 /* We'll deal with the LOOP_IF_NONE case later. */ 145 if ((flags & LOOP) == 0) 146 continue; 147 } 148 149 ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]); 150 if (ret) { 151 /* 152 * The most likely error here is going to be "Program 153 * lacks support for address type". This is no big 154 * deal -- just continue, and we'll listen on the 155 * addresses who's type we *do* support. 156 */ 157 continue; 158 } 159 idx++; 160 } 161 162 /* 163 * If no addresses were found, and LOOP_IF_NONE is set, then find 164 * the loopback addresses and add them to our list. 165 */ 166 if ((flags & LOOP_IF_NONE) != 0 && idx == 0) { 167 for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { 168 if ((ifa->ifa_flags & IFF_UP) == 0) 169 continue; 170 if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0) 171 continue; 172 if (krb5_sockaddr_uninteresting(ifa->ifa_addr)) 173 continue; 174 175 if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) { 176 ret = krb5_sockaddr2address(context, 177 ifa->ifa_addr, &res->val[idx]); 178 if (ret) { 179 /* 180 * See comment above. 181 */ 182 continue; 183 } 184 idx++; 185 } 186 } 187 } 188 189 freeifaddrs(ifa0); 190 if (ret) 191 free(res->val); 192 else 193 res->len = idx; /* Now a count. */ 194 return (ret); 195 } 196 197 static krb5_error_code 198 get_addrs_int (krb5_context context, krb5_addresses *res, int flags) 199 { 200 krb5_error_code ret = -1; 201 202 if (flags & SCAN_INTERFACES) { 203 ret = find_all_addresses (context, res, flags); 204 if(ret || res->len == 0) 205 ret = gethostname_fallback (context, res); 206 } else 207 ret = 0; 208 209 if(ret == 0 && (flags & EXTRA_ADDRESSES)) { 210 /* append user specified addresses */ 211 krb5_addresses a; 212 ret = krb5_get_extra_addresses(context, &a); 213 if(ret) { 214 krb5_free_addresses(context, res); 215 return ret; 216 } 217 ret = krb5_append_addresses(context, res, &a); 218 if(ret) { 219 krb5_free_addresses(context, res); 220 return ret; 221 } 222 krb5_free_addresses(context, &a); 223 } 224 return ret; 225 } 226 227 /* 228 * Try to get all addresses, but return the one corresponding to 229 * `hostname' if we fail. 230 * 231 * Only include loopback address if there are no other. 232 */ 233 234 krb5_error_code 235 krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res) 236 { 237 int flags = LOOP_IF_NONE | EXTRA_ADDRESSES; 238 239 if (context->scan_interfaces) 240 flags |= SCAN_INTERFACES; 241 242 return get_addrs_int (context, res, flags); 243 } 244 245 /* 246 * Try to get all local addresses that a server should listen to. 247 * If that fails, we return the address corresponding to `hostname'. 248 */ 249 250 krb5_error_code 251 krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res) 252 { 253 return get_addrs_int (context, res, LOOP | SCAN_INTERFACES); 254 } 255