diff --git a/lib/krb5/addr_families.c b/lib/krb5/addr_families.c new file mode 100644 index 000000000..09613f465 --- /dev/null +++ b/lib/krb5/addr_families.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Kungliga Tekniska + * Högskolan and its contributors. + * + * 4. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +RCSID("$Id$"); + +struct addr_operations { + int af; + krb5_address_type atype; + size_t max_sockaddr_size; + krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *); + krb5_boolean (*uninteresting)(const struct sockaddr *); + void (*h_addr2sockaddr)(const char *, struct sockaddr *, int *, int); + void (*anyaddr)(struct sockaddr *, int *, int); +}; + +/* + * AF_INET - aka IPv4 implementation + */ + +static krb5_error_code +ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; + unsigned char buf[4]; + + a->addr_type = KRB5_ADDRESS_INET; + memcpy (buf, &sin->sin_addr, 4); + return krb5_data_copy(&a->address, buf, 4); +} + +/* + * Are there any addresses that should be considered `uninteresting'? + */ + +static krb5_boolean +ipv4_uninteresting (const struct sockaddr *sa) +{ + return FALSE; +} + +static void +ipv4_h_addr2sockaddr(const char *addr, + struct sockaddr *sa, int *sa_size, int port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + memset (sin, 0, sizeof(*sin)); + *sa_size = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = port; + sin->sin_addr = *((struct in_addr *)addr); +} + +static void +ipv4_anyaddr (struct sockaddr *sa, int *sa_size, int port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + memset (sin, 0, sizeof(*sin)); + *sa_size = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = port; + sin->sin_addr.s_addr = INADDR_ANY; +} + +/* + * AF_INET6 - aka IPv6 implementation + */ + +#if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6) + +static krb5_error_code +ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + unsigned char buf[4]; + + a->addr_type = KRB5_ADDRESS_INET; +#ifndef IN6_ADDR_V6_TO_V4 +#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x)) +#endif + memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4); + return krb5_data_copy(&a->address, buf, 4); + } else { + a->addr_type = KRB5_ADDRESS_INET6; + return krb5_data_copy(&a->address, + &sin6->sin6_addr, + sizeof(sin6->sin6_addr)); + } +} + +/* + * + */ + +static krb5_boolean +ipv6_uninteresting (const struct sockaddr *sa) +{ +#ifndef IN6_IS_ADDR_LOOPBACK +#define IN6_IS_ADDR_LOOPBACK(x) IN6_IS_LOOPBACK(*x) +#endif + + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; + + return IN6_IS_ADDR_LOOPBACK(in6) + || IN6_IS_ADDR_LINKLOCAL(in6) + || IN6_IS_ADDR_V4COMPAT(in6); +} + +static void +ipv6_h_addr2sockaddr(const char *addr, + struct sockaddr *sa, int *sa_size, int port) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + memset (sin6, 0, sizeof(*sin6)); + *sa_size = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = port; + sin6->sin6_addr = *((struct in6_addr *)addr); +} + +static void +ipv6_anyaddr (struct sockaddr *sa, int *sa_size, int port) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + memset (sin6, 0, sizeof(*sin6)); + *sa_size = sizeof(*sin6); + sin6->sin6_family = AF_INET; + sin6->sin6_port = port; + sin6->sin6_addr = in6addr_any; +} + +#endif /* IPv6 */ + +/* + * table + */ + +static struct addr_operations at[] = { + {AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in), + ipv4_sockaddr2addr, ipv4_uninteresting, ipv4_h_addr2sockaddr, + ipv4_anyaddr}, +#if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_IN6) + {AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6), + ipv6_sockaddr2addr, ipv6_uninteresting, ipv6_h_addr2sockaddr, + ipv6_anyaddr} +#endif +}; + +static int num_addrs = sizeof(at) / sizeof(at[0]); + +static size_t max_sockaddr_size = 0; + +/* + * generic functions + */ + +static struct addr_operations * +find_addr_type(int atype) +{ + struct addr_operations *a; + + for (a = at; a < at + num_addrs; ++a) + if (atype == a->af) + return a; + return NULL; +} + +krb5_error_code +krb5_sockaddr2address (const struct sockaddr *sa, krb5_address *addr) +{ + struct addr_operations *a = find_addr_type(sa->sa_family); + if (a == NULL) + return KRB5_PROG_ATYPE_NOSUPP; + return (*a->sockaddr2addr)(sa, addr); +} + +size_t +krb5_max_sockaddr_size () +{ + if (max_sockaddr_size == 0) { + struct addr_operations *a; + + for(a = at; a < at + num_addrs; ++a) + max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size); + } + return max_sockaddr_size; +} + +krb5_boolean +krb5_sockaddr_uninteresting(const struct sockaddr *sa) +{ + struct addr_operations *a = find_addr_type(sa->sa_family); + if (a == NULL) + return TRUE; + return (*a->uninteresting)(sa); +} + +krb5_error_code +krb5_h_addr2sockaddr (int af, + const char *addr, struct sockaddr *sa, int *sa_size, + int port) +{ + struct addr_operations *a = find_addr_type(af); + if (a == NULL) + return KRB5_PROG_ATYPE_NOSUPP; + (*a->h_addr2sockaddr)(addr, sa, sa_size, port); + return 0; +} + +krb5_error_code +krb5_addr2sockaddr (krb5_address *addr, + struct sockaddr *sa, + int *sa_size, + int port) +{ + struct addr_operations *a; + + for (a = at; a < at + num_addrs; ++a) + if (addr->addr_type == a->atype) + break; + if (a == at + num_addrs) + return KRB5_PROG_ATYPE_NOSUPP; + + (*a->h_addr2sockaddr)(addr->address.data, sa, sa_size, port); + return 0; +} + +krb5_error_code +krb5_anyaddr (int af, + struct sockaddr *sa, + int *sa_size, + int port) +{ + struct addr_operations *a; + + for (a = at; a < at + num_addrs; ++a) + if (a->af == af) + break; + if (a == at + num_addrs) + return KRB5_PROG_ATYPE_NOSUPP; + + (*a->anyaddr)(sa, sa_size, port); + return 0; +}