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