Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: addr_families.c,v 1.3 2023/06/19 21:41:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2007 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "krb5_locl.h"
     37 
     38 struct addr_operations {
     39     int af;
     40     krb5_address_type atype;
     41     size_t max_sockaddr_size;
     42     krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);
     43     krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);
     44     void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,
     45 			  krb5_socklen_t *sa_size, int port);
     46     void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);
     47     krb5_error_code (*h_addr2addr)(const char *, krb5_address *);
     48     krb5_boolean (*uninteresting)(const struct sockaddr *);
     49     krb5_boolean (*is_loopback)(const struct sockaddr *);
     50     void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);
     51     int (*print_addr)(const krb5_address *, char *, size_t);
     52     int (*parse_addr)(krb5_context, const char*, krb5_address *);
     53     int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);
     54     int (*free_addr)(krb5_context, krb5_address*);
     55     int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);
     56     int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long,
     57 			 krb5_address*, krb5_address*);
     58 };
     59 
     60 /*
     61  * AF_INET - aka IPv4 implementation
     62  */
     63 
     64 static krb5_error_code
     65 ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
     66 {
     67     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
     68     unsigned char buf[4];
     69 
     70     a->addr_type = KRB5_ADDRESS_INET;
     71     memcpy (buf, &sin4->sin_addr, 4);
     72     return krb5_data_copy(&a->address, buf, 4);
     73 }
     74 
     75 static krb5_error_code
     76 ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)
     77 {
     78     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
     79 
     80     *port = sin4->sin_port;
     81     return 0;
     82 }
     83 
     84 static void
     85 ipv4_addr2sockaddr (const krb5_address *a,
     86 		    struct sockaddr *sa,
     87 		    krb5_socklen_t *sa_size,
     88 		    int port)
     89 {
     90     struct sockaddr_in tmp;
     91 
     92     memset (&tmp, 0, sizeof(tmp));
     93     tmp.sin_family = AF_INET;
     94     memcpy (&tmp.sin_addr, a->address.data, 4);
     95     tmp.sin_port = port;
     96     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
     97     *sa_size = sizeof(tmp);
     98 }
     99 
    100 static void
    101 ipv4_h_addr2sockaddr(const char *addr,
    102 		     struct sockaddr *sa,
    103 		     krb5_socklen_t *sa_size,
    104 		     int port)
    105 {
    106     struct sockaddr_in tmp;
    107 
    108     memset (&tmp, 0, sizeof(tmp));
    109     tmp.sin_family = AF_INET;
    110     tmp.sin_port   = port;
    111     tmp.sin_addr   = *((const struct in_addr *)addr);
    112     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
    113     *sa_size = sizeof(tmp);
    114 }
    115 
    116 static krb5_error_code
    117 ipv4_h_addr2addr (const char *addr,
    118 		  krb5_address *a)
    119 {
    120     unsigned char buf[4];
    121 
    122     a->addr_type = KRB5_ADDRESS_INET;
    123     memcpy(buf, addr, 4);
    124     return krb5_data_copy(&a->address, buf, 4);
    125 }
    126 
    127 /*
    128  * Are there any addresses that should be considered `uninteresting'?
    129  */
    130 
    131 static krb5_boolean
    132 ipv4_uninteresting (const struct sockaddr *sa)
    133 {
    134     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
    135 
    136     if (sin4->sin_addr.s_addr == INADDR_ANY)
    137 	return TRUE;
    138 
    139     return FALSE;
    140 }
    141 
    142 static krb5_boolean
    143 ipv4_is_loopback (const struct sockaddr *sa)
    144 {
    145     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
    146 
    147     if ((ntohl(sin4->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET)
    148 	return TRUE;
    149 
    150     return FALSE;
    151 }
    152 
    153 static void
    154 ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
    155 {
    156     struct sockaddr_in tmp;
    157 
    158     memset (&tmp, 0, sizeof(tmp));
    159     tmp.sin_family = AF_INET;
    160     tmp.sin_port   = port;
    161     tmp.sin_addr.s_addr = INADDR_ANY;
    162     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
    163     *sa_size = sizeof(tmp);
    164 }
    165 
    166 static int
    167 ipv4_print_addr (const krb5_address *addr, char *str, size_t len)
    168 {
    169     struct in_addr ia;
    170 
    171     memcpy (&ia, addr->address.data, 4);
    172 
    173     return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));
    174 }
    175 
    176 static int
    177 ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)
    178 {
    179     const char *p;
    180     struct in_addr a;
    181 
    182     p = strchr(address, ':');
    183     if(p) {
    184 	p++;
    185 	if(strncasecmp(address, "ip:", p - address) != 0 &&
    186 	   strncasecmp(address, "ip4:", p - address) != 0 &&
    187 	   strncasecmp(address, "ipv4:", p - address) != 0 &&
    188 	   strncasecmp(address, "inet:", p - address) != 0)
    189 	    return -1;
    190     } else
    191 	p = address;
    192     if(inet_aton(p, &a) == 0)
    193 	return -1;
    194     addr->addr_type = KRB5_ADDRESS_INET;
    195     if(krb5_data_alloc(&addr->address, 4) != 0)
    196 	return -1;
    197     _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);
    198     return 0;
    199 }
    200 
    201 static int
    202 ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,
    203 		   unsigned long len, krb5_address *low, krb5_address *high)
    204 {
    205     unsigned long ia;
    206     uint32_t l, h, m = 0xffffffff;
    207 
    208     if (len > 32) {
    209 	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
    210 			       N_("IPv4 prefix too large (%ld)", "len"), len);
    211 	return KRB5_PROG_ATYPE_NOSUPP;
    212     }
    213     m = m << (32 - len);
    214 
    215     _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);
    216 
    217     l = ia & m;
    218     h = l | ~m;
    219 
    220     low->addr_type = KRB5_ADDRESS_INET;
    221     if(krb5_data_alloc(&low->address, 4) != 0)
    222 	return -1;
    223     _krb5_put_int(low->address.data, l, low->address.length);
    224 
    225     high->addr_type = KRB5_ADDRESS_INET;
    226     if(krb5_data_alloc(&high->address, 4) != 0) {
    227 	krb5_free_address(context, low);
    228 	return -1;
    229     }
    230     _krb5_put_int(high->address.data, h, high->address.length);
    231 
    232     return 0;
    233 }
    234 
    235 
    236 /*
    237  * AF_INET6 - aka IPv6 implementation
    238  */
    239 
    240 #ifdef HAVE_IPV6
    241 
    242 static krb5_error_code
    243 ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
    244 {
    245     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
    246 
    247     if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    248 	unsigned char buf[4];
    249 
    250 	a->addr_type      = KRB5_ADDRESS_INET;
    251 #ifndef IN6_ADDR_V6_TO_V4
    252 #ifdef IN6_EXTRACT_V4ADDR
    253 #define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))
    254 #else
    255 #define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])
    256 #endif
    257 #endif
    258 	memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);
    259 	return krb5_data_copy(&a->address, buf, 4);
    260     } else {
    261 	a->addr_type = KRB5_ADDRESS_INET6;
    262 	return krb5_data_copy(&a->address,
    263 			      &sin6->sin6_addr,
    264 			      sizeof(sin6->sin6_addr));
    265     }
    266 }
    267 
    268 static krb5_error_code
    269 ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)
    270 {
    271     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
    272 
    273     *port = sin6->sin6_port;
    274     return 0;
    275 }
    276 
    277 static void
    278 ipv6_addr2sockaddr (const krb5_address *a,
    279 		    struct sockaddr *sa,
    280 		    krb5_socklen_t *sa_size,
    281 		    int port)
    282 {
    283     struct sockaddr_in6 tmp;
    284 
    285     memset (&tmp, 0, sizeof(tmp));
    286     tmp.sin6_family = AF_INET6;
    287     memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));
    288     tmp.sin6_port = port;
    289     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
    290     *sa_size = sizeof(tmp);
    291 }
    292 
    293 static void
    294 ipv6_h_addr2sockaddr(const char *addr,
    295 		     struct sockaddr *sa,
    296 		     krb5_socklen_t *sa_size,
    297 		     int port)
    298 {
    299     struct sockaddr_in6 tmp;
    300 
    301     memset (&tmp, 0, sizeof(tmp));
    302     tmp.sin6_family = AF_INET6;
    303     tmp.sin6_port   = port;
    304     tmp.sin6_addr   = *((const struct in6_addr *)addr);
    305     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
    306     *sa_size = sizeof(tmp);
    307 }
    308 
    309 static krb5_error_code
    310 ipv6_h_addr2addr (const char *addr,
    311 		  krb5_address *a)
    312 {
    313     a->addr_type = KRB5_ADDRESS_INET6;
    314     return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));
    315 }
    316 
    317 /*
    318  *
    319  */
    320 
    321 static krb5_boolean
    322 ipv6_uninteresting (const struct sockaddr *sa)
    323 {
    324     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
    325     const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
    326 
    327     return IN6_IS_ADDR_LINKLOCAL(in6)
    328 	|| IN6_IS_ADDR_V4COMPAT(in6);
    329 }
    330 
    331 static krb5_boolean
    332 ipv6_is_loopback (const struct sockaddr *sa)
    333 {
    334     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
    335     const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
    336 
    337     return (IN6_IS_ADDR_LOOPBACK(in6));
    338 }
    339 
    340 static void
    341 ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
    342 {
    343     struct sockaddr_in6 tmp;
    344 
    345     memset (&tmp, 0, sizeof(tmp));
    346     tmp.sin6_family = AF_INET6;
    347     tmp.sin6_port   = port;
    348     tmp.sin6_addr   = in6addr_any;
    349     *sa_size = sizeof(tmp);
    350 }
    351 
    352 static int
    353 ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
    354 {
    355     char buf[128], buf2[3];
    356     if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
    357 	{
    358 	    /* XXX this is pretty ugly, but better than abort() */
    359 	    size_t i;
    360 	    unsigned char *p = addr->address.data;
    361 	    buf[0] = '\0';
    362 	    for(i = 0; i < addr->address.length; i++) {
    363 		snprintf(buf2, sizeof(buf2), "%02x", p[i]);
    364 		if(i > 0 && (i & 1) == 0)
    365 		    strlcat(buf, ":", sizeof(buf));
    366 		strlcat(buf, buf2, sizeof(buf));
    367 	    }
    368 	}
    369     return snprintf(str, len, "IPv6:%s", buf);
    370 }
    371 
    372 static int
    373 ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
    374 {
    375     int ret;
    376     struct in6_addr in6;
    377     const char *p;
    378 
    379     p = strchr(address, ':');
    380     if(p) {
    381 	p++;
    382 	if(strncasecmp(address, "ip6:", p - address) == 0 ||
    383 	   strncasecmp(address, "ipv6:", p - address) == 0 ||
    384 	   strncasecmp(address, "inet6:", p - address) == 0)
    385 	    address = p;
    386     }
    387 
    388     ret = inet_pton(AF_INET6, address, &in6.s6_addr);
    389     if(ret == 1) {
    390 	addr->addr_type = KRB5_ADDRESS_INET6;
    391 	ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
    392 	if (ret)
    393 	    return -1;
    394 	memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
    395 	return 0;
    396     }
    397     return -1;
    398 }
    399 
    400 static int
    401 ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
    402 		   unsigned long len, krb5_address *low, krb5_address *high)
    403 {
    404     struct in6_addr addr, laddr, haddr;
    405     uint32_t m;
    406     int i, sub_len;
    407 
    408     if (len > 128) {
    409 	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
    410 			       N_("IPv6 prefix too large (%ld)", "length"), len);
    411 	return KRB5_PROG_ATYPE_NOSUPP;
    412     }
    413 
    414     if (inaddr->address.length != sizeof(addr)) {
    415 	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
    416 			       N_("IPv6 addr bad length", ""));
    417 	return KRB5_PROG_ATYPE_NOSUPP;
    418     }
    419 
    420     memcpy(&addr, inaddr->address.data, inaddr->address.length);
    421 
    422     for (i = 0; i < 16; i++) {
    423 	sub_len = min(8, len);
    424 
    425 	m = 0xff << (8 - sub_len);
    426 
    427 	laddr.s6_addr[i] = addr.s6_addr[i] & m;
    428 	haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;
    429 
    430 	if (len > 8)
    431 	    len -= 8;
    432 	else
    433 	    len = 0;
    434     }
    435 
    436     low->addr_type = KRB5_ADDRESS_INET6;
    437     if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)
    438 	return -1;
    439     memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));
    440 
    441     high->addr_type = KRB5_ADDRESS_INET6;
    442     if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {
    443 	krb5_free_address(context, low);
    444 	return -1;
    445     }
    446     memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));
    447 
    448     return 0;
    449 }
    450 
    451 #endif /* IPv6 */
    452 
    453 #ifndef HEIMDAL_SMALLER
    454 
    455 /*
    456  * table
    457  */
    458 
    459 #define KRB5_ADDRESS_ARANGE	(-100)
    460 
    461 struct arange {
    462     krb5_address low;
    463     krb5_address high;
    464 };
    465 
    466 static int
    467 arange_parse_addr (krb5_context context,
    468 		   const char *address, krb5_address *addr)
    469 {
    470     char buf[1024], *p;
    471     krb5_address low0, high0;
    472     struct arange *a;
    473     krb5_error_code ret;
    474 
    475     if(strncasecmp(address, "RANGE:", 6) != 0)
    476 	return -1;
    477 
    478     address += 6;
    479 
    480     p = strrchr(address, '/');
    481     if (p) {
    482 	krb5_addresses addrmask;
    483 	char *q;
    484 	long num;
    485 
    486 	if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))
    487 	    return -1;
    488 	buf[p - address] = '\0';
    489 	ret = krb5_parse_address(context, buf, &addrmask);
    490 	if (ret)
    491 	    return ret;
    492 	if(addrmask.len != 1) {
    493 	    krb5_free_addresses(context, &addrmask);
    494 	    return -1;
    495 	}
    496 
    497 	address += p - address + 1;
    498 
    499 	num = strtol(address, &q, 10);
    500 	if (q == address || *q != '\0' || num < 0) {
    501 	    krb5_free_addresses(context, &addrmask);
    502 	    return -1;
    503 	}
    504 
    505 	ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,
    506 					      &low0, &high0);
    507 	krb5_free_addresses(context, &addrmask);
    508 	if (ret)
    509 	    return ret;
    510 
    511     } else {
    512 	krb5_addresses low, high;
    513 
    514 	strsep_copy(&address, "-", buf, sizeof(buf));
    515 	ret = krb5_parse_address(context, buf, &low);
    516 	if(ret)
    517 	    return ret;
    518 	if(low.len != 1) {
    519 	    krb5_free_addresses(context, &low);
    520 	    return -1;
    521 	}
    522 
    523 	strsep_copy(&address, "-", buf, sizeof(buf));
    524 	ret = krb5_parse_address(context, buf, &high);
    525 	if(ret) {
    526 	    krb5_free_addresses(context, &low);
    527 	    return ret;
    528 	}
    529 
    530 	if(high.len != 1 || high.val[0].addr_type != low.val[0].addr_type) {
    531 	    krb5_free_addresses(context, &low);
    532 	    krb5_free_addresses(context, &high);
    533 	    return -1;
    534 	}
    535 
    536 	ret = krb5_copy_address(context, &high.val[0], &high0);
    537 	if (ret == 0) {
    538 	    ret = krb5_copy_address(context, &low.val[0], &low0);
    539 	    if (ret)
    540 		krb5_free_address(context, &high0);
    541 	}
    542 	krb5_free_addresses(context, &low);
    543 	krb5_free_addresses(context, &high);
    544 	if (ret)
    545 	    return ret;
    546     }
    547 
    548     krb5_data_alloc(&addr->address, sizeof(*a));
    549     addr->addr_type = KRB5_ADDRESS_ARANGE;
    550     a = addr->address.data;
    551 
    552     if(krb5_address_order(context, &low0, &high0) < 0) {
    553 	a->low = low0;
    554 	a->high = high0;
    555     } else {
    556 	a->low = high0;
    557 	a->high = low0;
    558     }
    559     return 0;
    560 }
    561 
    562 static int
    563 arange_free (krb5_context context, krb5_address *addr)
    564 {
    565     struct arange *a;
    566     a = addr->address.data;
    567     krb5_free_address(context, &a->low);
    568     krb5_free_address(context, &a->high);
    569     krb5_data_free(&addr->address);
    570     return 0;
    571 }
    572 
    573 
    574 static int
    575 arange_copy (krb5_context context, const krb5_address *inaddr,
    576 	     krb5_address *outaddr)
    577 {
    578     krb5_error_code ret;
    579     struct arange *i, *o;
    580 
    581     outaddr->addr_type = KRB5_ADDRESS_ARANGE;
    582     ret = krb5_data_alloc(&outaddr->address, sizeof(*o));
    583     if(ret)
    584 	return ret;
    585     i = inaddr->address.data;
    586     o = outaddr->address.data;
    587     ret = krb5_copy_address(context, &i->low, &o->low);
    588     if(ret) {
    589 	krb5_data_free(&outaddr->address);
    590 	return ret;
    591     }
    592     ret = krb5_copy_address(context, &i->high, &o->high);
    593     if(ret) {
    594 	krb5_free_address(context, &o->low);
    595 	krb5_data_free(&outaddr->address);
    596 	return ret;
    597     }
    598     return 0;
    599 }
    600 
    601 static int
    602 arange_print_addr (const krb5_address *addr, char *str, size_t len)
    603 {
    604     struct arange *a;
    605     krb5_error_code ret;
    606     size_t l, size, ret_len;
    607 
    608     a = addr->address.data;
    609 
    610     l = strlcpy(str, "RANGE:", len);
    611     ret_len = l;
    612     if (l > len)
    613 	l = len;
    614     size = l;
    615 
    616     ret = krb5_print_address (&a->low, str + size, len - size, &l);
    617     if (ret)
    618 	return ret;
    619     ret_len += l;
    620     if (len - size > l)
    621 	size += l;
    622     else
    623 	size = len;
    624 
    625     l = strlcat(str + size, "-", len - size);
    626     ret_len += l;
    627     if (len - size > l)
    628 	size += l;
    629     else
    630 	size = len;
    631 
    632     ret = krb5_print_address (&a->high, str + size, len - size, &l);
    633     if (ret)
    634 	return ret;
    635     ret_len += l;
    636 
    637     return ret_len;
    638 }
    639 
    640 static int
    641 arange_order_addr(krb5_context context,
    642 		  const krb5_address *addr1,
    643 		  const krb5_address *addr2)
    644 {
    645     int tmp1, tmp2, sign;
    646     struct arange *a;
    647     const krb5_address *a2;
    648 
    649     if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {
    650 	a = addr1->address.data;
    651 	a2 = addr2;
    652 	sign = 1;
    653     } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {
    654 	a = addr2->address.data;
    655 	a2 = addr1;
    656 	sign = -1;
    657     } else {
    658 	abort();
    659         UNREACHABLE(return 0);
    660     }
    661 
    662     if(a2->addr_type == KRB5_ADDRESS_ARANGE) {
    663 	struct arange *b = a2->address.data;
    664 	tmp1 = krb5_address_order(context, &a->low, &b->low);
    665 	if(tmp1 != 0)
    666 	    return sign * tmp1;
    667 	return sign * krb5_address_order(context, &a->high, &b->high);
    668     } else if(a2->addr_type == a->low.addr_type) {
    669 	tmp1 = krb5_address_order(context, &a->low, a2);
    670 	if(tmp1 > 0)
    671 	    return sign;
    672 	tmp2 = krb5_address_order(context, &a->high, a2);
    673 	if(tmp2 < 0)
    674 	    return -sign;
    675 	return 0;
    676     } else {
    677 	return sign * (addr1->addr_type - addr2->addr_type);
    678     }
    679 }
    680 
    681 #endif /* HEIMDAL_SMALLER */
    682 
    683 static int
    684 addrport_print_addr (const krb5_address *addr, char *str, size_t len)
    685 {
    686     krb5_error_code ret;
    687     krb5_address addr1, addr2;
    688     uint16_t port = 0;
    689     size_t ret_len = 0, l, size = 0;
    690     krb5_storage *sp;
    691 
    692     sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
    693     if (sp == NULL)
    694         return ENOMEM;
    695 
    696     /* for totally obscure reasons, these are not in network byteorder */
    697     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
    698 
    699     krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
    700     krb5_ret_address(sp, &addr1);
    701 
    702     krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
    703     krb5_ret_address(sp, &addr2);
    704     krb5_storage_free(sp);
    705     if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
    706 	unsigned long value;
    707 	_krb5_get_int(addr2.address.data, &value, 2);
    708 	port = value;
    709     }
    710     l = strlcpy(str, "ADDRPORT:", len);
    711     ret_len += l;
    712     if (len > l)
    713 	size += l;
    714     else
    715 	size = len;
    716 
    717     ret = krb5_print_address(&addr1, str + size, len - size, &l);
    718     if (ret)
    719 	return ret;
    720     ret_len += l;
    721     if (len - size > l)
    722 	size += l;
    723     else
    724 	size = len;
    725 
    726     ret = snprintf(str + size, len - size, ",PORT=%u", port);
    727     if (ret < 0)
    728 	return EINVAL;
    729     ret_len += ret;
    730     return ret_len;
    731 }
    732 
    733 static struct addr_operations at[] = {
    734     {
    735 	AF_INET,	KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),
    736 	ipv4_sockaddr2addr,
    737 	ipv4_sockaddr2port,
    738 	ipv4_addr2sockaddr,
    739 	ipv4_h_addr2sockaddr,
    740 	ipv4_h_addr2addr,
    741 	ipv4_uninteresting,
    742 	ipv4_is_loopback,
    743 	ipv4_anyaddr,
    744 	ipv4_print_addr,
    745 	ipv4_parse_addr,
    746 	NULL,
    747 	NULL,
    748 	NULL,
    749      ipv4_mask_boundary
    750     },
    751 #ifdef HAVE_IPV6
    752     {
    753 	AF_INET6,	KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),
    754 	ipv6_sockaddr2addr,
    755 	ipv6_sockaddr2port,
    756 	ipv6_addr2sockaddr,
    757 	ipv6_h_addr2sockaddr,
    758 	ipv6_h_addr2addr,
    759 	ipv6_uninteresting,
    760 	ipv6_is_loopback,
    761 	ipv6_anyaddr,
    762 	ipv6_print_addr,
    763 	ipv6_parse_addr,
    764 	NULL,
    765 	NULL,
    766 	NULL,
    767 	ipv6_mask_boundary
    768     } ,
    769 #endif
    770 #ifndef HEIMDAL_SMALLER
    771     /* fake address type */
    772     {
    773 	KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),
    774 	NULL,
    775 	NULL,
    776 	NULL,
    777 	NULL,
    778 	NULL,
    779 	NULL,
    780 	NULL,
    781 	NULL,
    782 	arange_print_addr,
    783 	arange_parse_addr,
    784 	arange_order_addr,
    785 	arange_free,
    786 	arange_copy,
    787 	NULL
    788     },
    789 #endif
    790     {
    791 	KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,
    792 	NULL,
    793 	NULL,
    794 	NULL,
    795 	NULL,
    796 	NULL,
    797 	NULL,
    798 	NULL,
    799 	NULL,
    800 	addrport_print_addr,
    801 	NULL,
    802 	NULL,
    803 	NULL,
    804 	NULL,
    805 	NULL
    806     }
    807 };
    808 
    809 static int num_addrs = sizeof(at) / sizeof(at[0]);
    810 
    811 static size_t max_sockaddr_size = 0;
    812 
    813 /*
    814  * generic functions
    815  */
    816 
    817 static struct addr_operations *
    818 find_af(int af)
    819 {
    820     struct addr_operations *a;
    821 
    822     for (a = at; a < at + num_addrs; ++a)
    823 	if (af == a->af)
    824 	    return a;
    825     return NULL;
    826 }
    827 
    828 static struct addr_operations *
    829 find_atype(krb5_address_type atype)
    830 {
    831     struct addr_operations *a;
    832 
    833     for (a = at; a < at + num_addrs; ++a)
    834 	if (atype == a->atype)
    835 	    return a;
    836     return NULL;
    837 }
    838 
    839 /**
    840  * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
    841  * the krb5_address addr.
    842  *
    843  * @param context a Keberos context
    844  * @param sa a struct sockaddr to extract the address from
    845  * @param addr an Kerberos 5 address to store the address in.
    846  *
    847  * @return Return an error code or 0.
    848  *
    849  * @ingroup krb5_address
    850  */
    851 
    852 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    853 krb5_sockaddr2address (krb5_context context,
    854 		       const struct sockaddr *sa, krb5_address *addr)
    855 {
    856     struct addr_operations *a = find_af(sa->sa_family);
    857     if (a == NULL) {
    858 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
    859 				N_("Address family %d not supported", ""),
    860 				sa->sa_family);
    861 	return KRB5_PROG_ATYPE_NOSUPP;
    862     }
    863     return (*a->sockaddr2addr)(sa, addr);
    864 }
    865 
    866 /**
    867  * krb5_sockaddr2port extracts a port (if possible) from a "struct
    868  * sockaddr.
    869  *
    870  * @param context a Keberos context
    871  * @param sa a struct sockaddr to extract the port from
    872  * @param port a pointer to an int16_t store the port in.
    873  *
    874  * @return Return an error code or 0. Will return
    875  * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
    876  *
    877  * @ingroup krb5_address
    878  */
    879 
    880 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    881 krb5_sockaddr2port (krb5_context context,
    882 		    const struct sockaddr *sa, int16_t *port)
    883 {
    884     struct addr_operations *a = find_af(sa->sa_family);
    885     if (a == NULL) {
    886 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
    887 				N_("Address family %d not supported", ""),
    888 				sa->sa_family);
    889 	return KRB5_PROG_ATYPE_NOSUPP;
    890     }
    891     return (*a->sockaddr2port)(sa, port);
    892 }
    893 
    894 /**
    895  * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
    896  * and port. The argument sa_size should initially contain the size of
    897  * the sa and after the call, it will contain the actual length of the
    898  * address. In case of the sa is too small to fit the whole address,
    899  * the up to *sa_size will be stored, and then *sa_size will be set to
    900  * the required length.
    901  *
    902  * @param context a Keberos context
    903  * @param addr the address to copy the from
    904  * @param sa the struct sockaddr that will be filled in
    905  * @param sa_size pointer to length of sa, and after the call, it will
    906  * contain the actual length of the address.
    907  * @param port set port in sa.
    908  *
    909  * @return Return an error code or 0. Will return
    910  * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
    911  *
    912  * @ingroup krb5_address
    913  */
    914 
    915 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    916 krb5_addr2sockaddr (krb5_context context,
    917 		    const krb5_address *addr,
    918 		    struct sockaddr *sa,
    919 		    krb5_socklen_t *sa_size,
    920 		    int port)
    921 {
    922     struct addr_operations *a = find_atype(addr->addr_type);
    923 
    924     if (a == NULL) {
    925 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
    926 				N_("Address type %d not supported",
    927 				   "krb5_address type"),
    928 				addr->addr_type);
    929 	return KRB5_PROG_ATYPE_NOSUPP;
    930     }
    931     if (a->addr2sockaddr == NULL) {
    932 	krb5_set_error_message (context,
    933 				KRB5_PROG_ATYPE_NOSUPP,
    934 				N_("Can't convert address type %d to sockaddr", ""),
    935 				addr->addr_type);
    936 	return KRB5_PROG_ATYPE_NOSUPP;
    937     }
    938     (*a->addr2sockaddr)(addr, sa, sa_size, port);
    939     return 0;
    940 }
    941 
    942 /**
    943  * krb5_max_sockaddr_size returns the max size of the .Li struct
    944  * sockaddr that the Kerberos library will return.
    945  *
    946  * @return Return an size_t of the maximum struct sockaddr.
    947  *
    948  * @ingroup krb5_address
    949  */
    950 
    951 KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
    952 krb5_max_sockaddr_size (void)
    953 {
    954     if (max_sockaddr_size == 0) {
    955 	struct addr_operations *a;
    956 
    957 	for(a = at; a < at + num_addrs; ++a)
    958 	    max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
    959     }
    960     return max_sockaddr_size;
    961 }
    962 
    963 /**
    964  * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
    965  * kerberos library thinks are uninteresting.  One example are link
    966  * local addresses.
    967  *
    968  * @param sa pointer to struct sockaddr that might be interesting.
    969  *
    970  * @return Return a non zero for uninteresting addresses.
    971  *
    972  * @ingroup krb5_address
    973  */
    974 
    975 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
    976 krb5_sockaddr_uninteresting(const struct sockaddr *sa)
    977 {
    978     struct addr_operations *a = find_af(sa->sa_family);
    979     if (a == NULL || a->uninteresting == NULL)
    980 	return TRUE;
    981     return (*a->uninteresting)(sa);
    982 }
    983 
    984 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
    985 krb5_sockaddr_is_loopback(const struct sockaddr *sa)
    986 {
    987     struct addr_operations *a = find_af(sa->sa_family);
    988     if (a == NULL || a->is_loopback == NULL)
    989 	return TRUE;
    990     return (*a->is_loopback)(sa);
    991 }
    992 
    993 /**
    994  * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
    995  * the "struct hostent" (see gethostbyname(3) ) h_addr_list
    996  * component. The argument sa_size should initially contain the size
    997  * of the sa, and after the call, it will contain the actual length of
    998  * the address.
    999  *
   1000  * @param context a Keberos context
   1001  * @param af addresses
   1002  * @param addr address
   1003  * @param sa returned struct sockaddr
   1004  * @param sa_size size of sa
   1005  * @param port port to set in sa.
   1006  *
   1007  * @return Return an error code or 0.
   1008  *
   1009  * @ingroup krb5_address
   1010  */
   1011 
   1012 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1013 krb5_h_addr2sockaddr (krb5_context context,
   1014 		      int af,
   1015 		      const char *addr, struct sockaddr *sa,
   1016 		      krb5_socklen_t *sa_size,
   1017 		      int port)
   1018 {
   1019     struct addr_operations *a = find_af(af);
   1020     if (a == NULL) {
   1021 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
   1022 				"Address family %d not supported", af);
   1023 	return KRB5_PROG_ATYPE_NOSUPP;
   1024     }
   1025     (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
   1026     return 0;
   1027 }
   1028 
   1029 /**
   1030  * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
   1031  * that it operates on a krb5_address instead of a struct sockaddr.
   1032  *
   1033  * @param context a Keberos context
   1034  * @param af address family
   1035  * @param haddr host address from struct hostent.
   1036  * @param addr returned krb5_address.
   1037  *
   1038  * @return Return an error code or 0.
   1039  *
   1040  * @ingroup krb5_address
   1041  */
   1042 
   1043 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1044 krb5_h_addr2addr (krb5_context context,
   1045 		  int af,
   1046 		  const char *haddr, krb5_address *addr)
   1047 {
   1048     struct addr_operations *a = find_af(af);
   1049     if (a == NULL) {
   1050 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
   1051 				N_("Address family %d not supported", ""), af);
   1052 	return KRB5_PROG_ATYPE_NOSUPP;
   1053     }
   1054     return (*a->h_addr2addr)(haddr, addr);
   1055 }
   1056 
   1057 /**
   1058  * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
   1059  * bind(2) to.  The argument sa_size should initially contain the size
   1060  * of the sa, and after the call, it will contain the actual length
   1061  * of the address.
   1062  *
   1063  * @param context a Keberos context
   1064  * @param af address family
   1065  * @param sa sockaddr
   1066  * @param sa_size lenght of sa.
   1067  * @param port for to fill into sa.
   1068  *
   1069  * @return Return an error code or 0.
   1070  *
   1071  * @ingroup krb5_address
   1072  */
   1073 
   1074 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1075 krb5_anyaddr (krb5_context context,
   1076 	      int af,
   1077 	      struct sockaddr *sa,
   1078 	      krb5_socklen_t *sa_size,
   1079 	      int port)
   1080 {
   1081     struct addr_operations *a = find_af (af);
   1082 
   1083     if (a == NULL) {
   1084 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
   1085 				N_("Address family %d not supported", ""), af);
   1086 	return KRB5_PROG_ATYPE_NOSUPP;
   1087     }
   1088 
   1089     (*a->anyaddr)(sa, sa_size, port);
   1090     return 0;
   1091 }
   1092 
   1093 /**
   1094  * krb5_print_address prints the address in addr to the string string
   1095  * that have the length len. If ret_len is not NULL, it will be filled
   1096  * with the length of the string if size were unlimited (not including
   1097  * the final NUL) .
   1098  *
   1099  * @param addr address to be printed
   1100  * @param str pointer string to print the address into
   1101  * @param len length that will fit into area pointed to by "str".
   1102  * @param ret_len return length the str.
   1103  *
   1104  * @return Return an error code or 0.
   1105  *
   1106  * @ingroup krb5_address
   1107  */
   1108 
   1109 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1110 krb5_print_address (const krb5_address *addr,
   1111 		    char *str, size_t len, size_t *ret_len)
   1112 {
   1113     struct addr_operations *a = find_atype(addr->addr_type);
   1114     int ret;
   1115 
   1116     if (a == NULL || a->print_addr == NULL) {
   1117 	char *s;
   1118 	int l;
   1119 	size_t i;
   1120 
   1121 	s = str;
   1122 	l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
   1123 	if (l < 0 || (size_t)l >= len)
   1124 	    return EINVAL;
   1125 	s += l;
   1126 	len -= l;
   1127 	for(i = 0; i < addr->address.length; i++) {
   1128 	    l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
   1129 	    if (l < 0 || (size_t)l >= len)
   1130 		return EINVAL;
   1131 	    len -= l;
   1132 	    s += l;
   1133 	}
   1134 	if(ret_len != NULL)
   1135 	    *ret_len = s - str;
   1136 	return 0;
   1137     }
   1138     ret = (*a->print_addr)(addr, str, len);
   1139     if (ret < 0)
   1140 	return EINVAL;
   1141     if(ret_len != NULL)
   1142 	*ret_len = ret;
   1143     return 0;
   1144 }
   1145 
   1146 /**
   1147  * krb5_parse_address returns the resolved hostname in string to the
   1148  * krb5_addresses addresses .
   1149  *
   1150  * @param context a Keberos context
   1151  * @param string
   1152  * @param addresses
   1153  *
   1154  * @return Return an error code or 0.
   1155  *
   1156  * @ingroup krb5_address
   1157  */
   1158 
   1159 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1160 krb5_parse_address(krb5_context context,
   1161 		   const char *string,
   1162 		   krb5_addresses *addresses)
   1163 {
   1164     int i, n;
   1165     struct addrinfo *ai, *a;
   1166     struct addrinfo hint;
   1167     int error;
   1168     int save_errno;
   1169 
   1170     addresses->len = 0;
   1171     addresses->val = NULL;
   1172 
   1173     for(i = 0; i < num_addrs; i++) {
   1174 	if(at[i].parse_addr) {
   1175 	    krb5_address addr;
   1176 	    if((*at[i].parse_addr)(context, string, &addr) == 0) {
   1177 		ALLOC_SEQ(addresses, 1);
   1178 		if (addresses->val == NULL)
   1179 		    return krb5_enomem(context);
   1180 		addresses->val[0] = addr;
   1181 		return 0;
   1182 	    }
   1183 	}
   1184     }
   1185 
   1186     /* if not parsed as numeric address, do a name lookup */
   1187     memset(&hint, 0, sizeof(hint));
   1188     hint.ai_family = AF_UNSPEC;
   1189     error = getaddrinfo (string, NULL, &hint, &ai);
   1190     if (error) {
   1191 	krb5_error_code ret2;
   1192 	save_errno = errno;
   1193 	ret2 = krb5_eai_to_heim_errno(error, save_errno);
   1194 	krb5_set_error_message (context, ret2, "%s: %s",
   1195 				string, gai_strerror(error));
   1196 	return ret2;
   1197     }
   1198 
   1199     n = 0;
   1200     for (a = ai; a != NULL; a = a->ai_next)
   1201 	++n;
   1202 
   1203     ALLOC_SEQ(addresses, n);
   1204     if (addresses->val == NULL) {
   1205 	freeaddrinfo(ai);
   1206 	return krb5_enomem(context);
   1207     }
   1208 
   1209     addresses->len = 0;
   1210     for (a = ai, i = 0; a != NULL; a = a->ai_next) {
   1211 	if (krb5_sockaddr2address (context, a->ai_addr, &addresses->val[i]))
   1212 	    continue;
   1213 	if(krb5_address_search(context, &addresses->val[i], addresses)) {
   1214 	    krb5_free_address(context, &addresses->val[i]);
   1215 	    continue;
   1216 	}
   1217 	i++;
   1218 	addresses->len = i;
   1219     }
   1220     freeaddrinfo (ai);
   1221     return 0;
   1222 }
   1223 
   1224 /**
   1225  * krb5_address_order compares the addresses addr1 and addr2 so that
   1226  * it can be used for sorting addresses. If the addresses are the same
   1227  * address krb5_address_order will return 0. Behavies like memcmp(2).
   1228  *
   1229  * @param context a Keberos context
   1230  * @param addr1 krb5_address to compare
   1231  * @param addr2 krb5_address to compare
   1232  *
   1233  * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and
   1234  * addr2 is the same address, > 0 if addr2 is "less" then addr1.
   1235  *
   1236  * @ingroup krb5_address
   1237  */
   1238 
   1239 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
   1240 krb5_address_order(krb5_context context,
   1241 		   const krb5_address *addr1,
   1242 		   const krb5_address *addr2)
   1243 {
   1244     /* this sucks; what if both addresses have order functions, which
   1245        should we call? this works for now, though */
   1246     struct addr_operations *a;
   1247     a = find_atype(addr1->addr_type);
   1248     if(a == NULL) {
   1249 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
   1250 				N_("Address family %d not supported", ""),
   1251 				addr1->addr_type);
   1252 	return KRB5_PROG_ATYPE_NOSUPP;
   1253     }
   1254     if(a->order_addr != NULL)
   1255 	return (*a->order_addr)(context, addr1, addr2);
   1256     a = find_atype(addr2->addr_type);
   1257     if(a == NULL) {
   1258 	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
   1259 				N_("Address family %d not supported", ""),
   1260 				addr2->addr_type);
   1261 	return KRB5_PROG_ATYPE_NOSUPP;
   1262     }
   1263     if(a->order_addr != NULL)
   1264 	return (*a->order_addr)(context, addr1, addr2);
   1265 
   1266     if(addr1->addr_type != addr2->addr_type)
   1267 	return addr1->addr_type - addr2->addr_type;
   1268     if(addr1->address.length != addr2->address.length)
   1269 	return addr1->address.length - addr2->address.length;
   1270     return memcmp (addr1->address.data,
   1271 		   addr2->address.data,
   1272 		   addr1->address.length);
   1273 }
   1274 
   1275 /**
   1276  * krb5_address_compare compares the addresses  addr1 and addr2.
   1277  * Returns TRUE if the two addresses are the same.
   1278  *
   1279  * @param context a Keberos context
   1280  * @param addr1 address to compare
   1281  * @param addr2 address to compare
   1282  *
   1283  * @return Return an TRUE is the address are the same FALSE if not
   1284  *
   1285  * @ingroup krb5_address
   1286  */
   1287 
   1288 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
   1289 krb5_address_compare(krb5_context context,
   1290 		     const krb5_address *addr1,
   1291 		     const krb5_address *addr2)
   1292 {
   1293     return krb5_address_order (context, addr1, addr2) == 0;
   1294 }
   1295 
   1296 /**
   1297  * krb5_address_search checks if the address addr is a member of the
   1298  * address set list addrlist .
   1299  *
   1300  * @param context a Keberos context.
   1301  * @param addr address to search for.
   1302  * @param addrlist list of addresses to look in for addr.
   1303  *
   1304  * @return Return an error code or 0.
   1305  *
   1306  * @ingroup krb5_address
   1307  */
   1308 
   1309 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
   1310 krb5_address_search(krb5_context context,
   1311 		    const krb5_address *addr,
   1312 		    const krb5_addresses *addrlist)
   1313 {
   1314     size_t i;
   1315 
   1316     for (i = 0; i < addrlist->len; ++i)
   1317 	if (krb5_address_compare (context, addr, &addrlist->val[i]))
   1318 	    return TRUE;
   1319     return FALSE;
   1320 }
   1321 
   1322 /**
   1323  * krb5_free_address frees the data stored in the address that is
   1324  * alloced with any of the krb5_address functions.
   1325  *
   1326  * @param context a Keberos context
   1327  * @param address addresss to be freed.
   1328  *
   1329  * @return Return an error code or 0.
   1330  *
   1331  * @ingroup krb5_address
   1332  */
   1333 
   1334 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1335 krb5_free_address(krb5_context context,
   1336 		  krb5_address *address)
   1337 {
   1338     struct addr_operations *a = find_atype (address->addr_type);
   1339     if(a != NULL && a->free_addr != NULL)
   1340 	return (*a->free_addr)(context, address);
   1341     krb5_data_free (&address->address);
   1342     memset(address, 0, sizeof(*address));
   1343     return 0;
   1344 }
   1345 
   1346 /**
   1347  * krb5_free_addresses frees the data stored in the address that is
   1348  * alloced with any of the krb5_address functions.
   1349  *
   1350  * @param context a Keberos context
   1351  * @param addresses addressses to be freed.
   1352  *
   1353  * @return Return an error code or 0.
   1354  *
   1355  * @ingroup krb5_address
   1356  */
   1357 
   1358 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1359 krb5_free_addresses(krb5_context context,
   1360 		    krb5_addresses *addresses)
   1361 {
   1362     size_t i;
   1363     for(i = 0; i < addresses->len; i++)
   1364 	krb5_free_address(context, &addresses->val[i]);
   1365     free(addresses->val);
   1366     addresses->len = 0;
   1367     addresses->val = NULL;
   1368     return 0;
   1369 }
   1370 
   1371 /**
   1372  * krb5_copy_address copies the content of address
   1373  * inaddr to outaddr.
   1374  *
   1375  * @param context a Keberos context
   1376  * @param inaddr pointer to source address
   1377  * @param outaddr pointer to destination address
   1378  *
   1379  * @return Return an error code or 0.
   1380  *
   1381  * @ingroup krb5_address
   1382  */
   1383 
   1384 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1385 krb5_copy_address(krb5_context context,
   1386 		  const krb5_address *inaddr,
   1387 		  krb5_address *outaddr)
   1388 {
   1389     struct addr_operations *a = find_af (inaddr->addr_type);
   1390     if(a != NULL && a->copy_addr != NULL)
   1391 	return (*a->copy_addr)(context, inaddr, outaddr);
   1392     return copy_HostAddress(inaddr, outaddr);
   1393 }
   1394 
   1395 /**
   1396  * krb5_copy_addresses copies the content of addresses
   1397  * inaddr to outaddr.
   1398  *
   1399  * @param context a Keberos context
   1400  * @param inaddr pointer to source addresses
   1401  * @param outaddr pointer to destination addresses
   1402  *
   1403  * @return Return an error code or 0.
   1404  *
   1405  * @ingroup krb5_address
   1406  */
   1407 
   1408 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1409 krb5_copy_addresses(krb5_context context,
   1410 		    const krb5_addresses *inaddr,
   1411 		    krb5_addresses *outaddr)
   1412 {
   1413     size_t i;
   1414     ALLOC_SEQ(outaddr, inaddr->len);
   1415     if(inaddr->len > 0 && outaddr->val == NULL)
   1416 	return krb5_enomem(context);
   1417     for(i = 0; i < inaddr->len; i++)
   1418 	krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
   1419     return 0;
   1420 }
   1421 
   1422 /**
   1423  * krb5_append_addresses adds the set of addresses in source to
   1424  * dest. While copying the addresses, duplicates are also sorted out.
   1425  *
   1426  * @param context a Keberos context
   1427  * @param dest destination of copy operation
   1428  * @param source adresses that are going to be added to dest
   1429  *
   1430  * @return Return an error code or 0.
   1431  *
   1432  * @ingroup krb5_address
   1433  */
   1434 
   1435 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1436 krb5_append_addresses(krb5_context context,
   1437 		      krb5_addresses *dest,
   1438 		      const krb5_addresses *source)
   1439 {
   1440     krb5_address *tmp;
   1441     krb5_error_code ret;
   1442     size_t i;
   1443     if(source->len > 0) {
   1444 	tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
   1445 	if (tmp == NULL)
   1446 	    return krb5_enomem(context);
   1447 	dest->val = tmp;
   1448 	for(i = 0; i < source->len; i++) {
   1449 	    /* skip duplicates */
   1450 	    if(krb5_address_search(context, &source->val[i], dest))
   1451 		continue;
   1452 	    ret = krb5_copy_address(context,
   1453 				    &source->val[i],
   1454 				    &dest->val[dest->len]);
   1455 	    if(ret)
   1456 		return ret;
   1457 	    dest->len++;
   1458 	}
   1459     }
   1460     return 0;
   1461 }
   1462 
   1463 /**
   1464  * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
   1465  *
   1466  * @param context a Keberos context
   1467  * @param res built address from addr/port
   1468  * @param addr address to use
   1469  * @param port port to use
   1470  *
   1471  * @return Return an error code or 0.
   1472  *
   1473  * @ingroup krb5_address
   1474  */
   1475 
   1476 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1477 krb5_make_addrport (krb5_context context,
   1478 		    krb5_address **res, const krb5_address *addr, int16_t port)
   1479 {
   1480     krb5_error_code ret;
   1481     size_t len = addr->address.length + 2 + 4 * 4;
   1482     u_char *p;
   1483 
   1484     *res = malloc (sizeof(**res));
   1485     if (*res == NULL)
   1486 	return krb5_enomem(context);
   1487     (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
   1488     ret = krb5_data_alloc (&(*res)->address, len);
   1489     if (ret) {
   1490 	free (*res);
   1491 	*res = NULL;
   1492 	return krb5_enomem(context);
   1493     }
   1494     p = (*res)->address.data;
   1495     *p++ = 0;
   1496     *p++ = 0;
   1497     *p++ = (addr->addr_type     ) & 0xFF;
   1498     *p++ = (addr->addr_type >> 8) & 0xFF;
   1499 
   1500     *p++ = (addr->address.length      ) & 0xFF;
   1501     *p++ = (addr->address.length >>  8) & 0xFF;
   1502     *p++ = (addr->address.length >> 16) & 0xFF;
   1503     *p++ = (addr->address.length >> 24) & 0xFF;
   1504 
   1505     memcpy (p, addr->address.data, addr->address.length);
   1506     p += addr->address.length;
   1507 
   1508     *p++ = 0;
   1509     *p++ = 0;
   1510     *p++ = (KRB5_ADDRESS_IPPORT     ) & 0xFF;
   1511     *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
   1512 
   1513     *p++ = (2      ) & 0xFF;
   1514     *p++ = (2 >>  8) & 0xFF;
   1515     *p++ = (2 >> 16) & 0xFF;
   1516     *p++ = (2 >> 24) & 0xFF;
   1517 
   1518     memcpy (p, &port, 2);
   1519 
   1520     return 0;
   1521 }
   1522 
   1523 /**
   1524  * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
   1525  * them in `low' and `high'.
   1526  *
   1527  * @param context a Keberos context
   1528  * @param inaddr address in prefixlen that the bondery searched
   1529  * @param prefixlen width of boundery
   1530  * @param low lowest address
   1531  * @param high highest address
   1532  *
   1533  * @return Return an error code or 0.
   1534  *
   1535  * @ingroup krb5_address
   1536  */
   1537 
   1538 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1539 krb5_address_prefixlen_boundary(krb5_context context,
   1540 				const krb5_address *inaddr,
   1541 				unsigned long prefixlen,
   1542 				krb5_address *low,
   1543 				krb5_address *high)
   1544 {
   1545     struct addr_operations *a = find_atype (inaddr->addr_type);
   1546     if(a != NULL && a->mask_boundary != NULL)
   1547 	return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
   1548     krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
   1549 			   N_("Address family %d doesn't support "
   1550 			      "address mask operation", ""),
   1551 			   inaddr->addr_type);
   1552     return KRB5_PROG_ATYPE_NOSUPP;
   1553 }
   1554