#include "krb5_locl.h"
#ifdef __osf__
struct rtentry;
struct mbuf;
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <ifaddrs.h>
static krb5_error_code
gethostname_fallback (krb5_context context, krb5_addresses *res)
{
krb5_error_code ret;
char hostname[MAXHOSTNAMELEN];
struct hostent *hostent;
if (gethostname (hostname, sizeof(hostname))) {
ret = errno;
krb5_set_error_message(context, ret, "gethostname: %s", strerror(ret));
return ret;
}
hostent = roken_gethostbyname (hostname);
if (hostent == NULL) {
ret = errno;
krb5_set_error_message (context, ret, "gethostbyname %s: %s",
hostname, strerror(ret));
return ret;
}
res->len = 1;
res->val = malloc (sizeof(*res->val));
if (res->val == NULL) {
krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
return ENOMEM;
}
res->val[0].addr_type = hostent->h_addrtype;
res->val[0].address.data = NULL;
res->val[0].address.length = 0;
ret = krb5_data_copy (&res->val[0].address,
hostent->h_addr,
hostent->h_length);
if (ret) {
free (res->val);
return ret;
}
return 0;
}
enum {
LOOP = 1,
LOOP_IF_NONE = 2,
EXTRA_ADDRESSES = 4,
SCAN_INTERFACES = 8
};
static krb5_error_code
find_all_addresses (krb5_context context, krb5_addresses *res, int flags)
{
struct sockaddr sa_zero;
struct ifaddrs *ifa0, *ifa;
krb5_error_code ret = ENXIO;
unsigned int num, idx;
krb5_addresses ignore_addresses;
if (getifaddrs(&ifa0) == -1) {
ret = errno;
krb5_set_error_message(context, ret, "getifaddrs: %s", strerror(ret));
return (ret);
}
memset(&sa_zero, 0, sizeof(sa_zero));
for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++)
;
if (num == 0) {
freeifaddrs(ifa0);
krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
return (ENXIO);
}
if (flags & EXTRA_ADDRESSES) {
ret = krb5_get_ignore_addresses(context, &ignore_addresses);
if(ret)
return ret;
}
res->val = calloc(num, sizeof(*res->val));
if (res->val == NULL) {
krb5_free_addresses(context, &ignore_addresses);
freeifaddrs(ifa0);
krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
return ENOMEM;
}
for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) {
if ((ifa->ifa_flags & IFF_UP) == 0)
continue;
if (ifa->ifa_addr == NULL)
continue;
if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
continue;
if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
continue;
if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
if ((flags & LOOP) == 0)
continue;
}
ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]);
if (ret) {
continue;
}
if((flags & EXTRA_ADDRESSES) &&
krb5_address_search(context, &res->val[idx], &ignore_addresses)) {
krb5_free_address(context, &res->val[idx]);
flags &= ~LOOP_IF_NONE;
continue;
}
idx++;
}
if ((flags & LOOP_IF_NONE) != 0 && idx == 0) {
for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
if ((ifa->ifa_flags & IFF_UP) == 0)
continue;
if (ifa->ifa_addr == NULL)
continue;
if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
continue;
if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
continue;
if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
ret = krb5_sockaddr2address(context,
ifa->ifa_addr, &res->val[idx]);
if (ret) {
continue;
}
if((flags & EXTRA_ADDRESSES) &&
krb5_address_search(context, &res->val[idx],
&ignore_addresses)) {
krb5_free_address(context, &res->val[idx]);
continue;
}
idx++;
}
}
}
if (flags & EXTRA_ADDRESSES)
krb5_free_addresses(context, &ignore_addresses);
freeifaddrs(ifa0);
if (ret) {
free(res->val);
res->val = NULL;
} else
res->len = idx;
return (ret);
}
static krb5_error_code
get_addrs_int (krb5_context context, krb5_addresses *res, int flags)
{
krb5_error_code ret = -1;
res->len = 0;
res->val = NULL;
if (flags & SCAN_INTERFACES) {
ret = find_all_addresses (context, res, flags);
if(ret || res->len == 0)
ret = gethostname_fallback (context, res);
} else {
ret = 0;
}
if(ret == 0 && (flags & EXTRA_ADDRESSES)) {
krb5_addresses a;
ret = krb5_get_extra_addresses(context, &a);
if(ret) {
krb5_free_addresses(context, res);
return ret;
}
ret = krb5_append_addresses(context, res, &a);
if(ret) {
krb5_free_addresses(context, res);
return ret;
}
krb5_free_addresses(context, &a);
}
if(res->len == 0) {
free(res->val);
res->val = NULL;
}
return ret;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res)
{
int flags = LOOP_IF_NONE | EXTRA_ADDRESSES;
if (context->scan_interfaces)
flags |= SCAN_INTERFACES;
return get_addrs_int (context, res, flags);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res)
{
return get_addrs_int (context, res, LOOP | SCAN_INTERFACES);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_all_any_addrs(krb5_context context, krb5_addresses *res)
{
krb5_error_code ret;
krb5_addresses as;
krb5_address a;
struct sockaddr_storage ss;
krb5_socklen_t sa_size;
unsigned int n;
int types[] = {
AF_INET6,
AF_INET
};
memset(&a, 0, sizeof(a));
res->len = 0;
res->val = NULL;
for (n = 0; n < sizeof(types)/sizeof(types[0]); n++) {
sa_size = sizeof(ss);
ret = krb5_anyaddr(context, types[n], (struct sockaddr *)&ss, &sa_size, 0);
if (ret)
continue;
ret = krb5_sockaddr2address(context, (struct sockaddr *)&ss, &a);
if (ret)
continue;
as.val = &a;
as.len = 1;
ret = krb5_append_addresses(context, res, &as);
krb5_free_address(context, &a);
if(ret) {
krb5_free_addresses(context, res);
return ret;
}
}
if (res->len == 0) {
krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
return (ENXIO);
}
return 0;
}