1/*
2 * Copyright (c) 1997-2007 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
36struct addr_operations {
37    int af;
38    krb5_address_type atype;
39    size_t max_sockaddr_size;
40    krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);
41    krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);
42    void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,
43			  krb5_socklen_t *sa_size, int port);
44    void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);
45    krb5_error_code (*h_addr2addr)(const char *, krb5_address *);
46    krb5_boolean (*uninteresting)(const struct sockaddr *);
47    krb5_boolean (*is_loopback)(const struct sockaddr *);
48    void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);
49    int (*print_addr)(const krb5_address *, char *, size_t);
50    int (*parse_addr)(krb5_context, const char*, krb5_address *);
51    int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);
52    int (*free_addr)(krb5_context, krb5_address*);
53    int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);
54    int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long,
55			 krb5_address*, krb5_address*);
56};
57
58/*
59 * AF_INET - aka IPv4 implementation
60 */
61
62static krb5_error_code
63ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
64{
65    const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
66    unsigned char buf[4];
67
68    a->addr_type = KRB5_ADDRESS_INET;
69    memcpy (buf, &sin4->sin_addr, 4);
70    return krb5_data_copy(&a->address, buf, 4);
71}
72
73static krb5_error_code
74ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)
75{
76    const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
77
78    *port = sin4->sin_port;
79    return 0;
80}
81
82static void
83ipv4_addr2sockaddr (const krb5_address *a,
84		    struct sockaddr *sa,
85		    krb5_socklen_t *sa_size,
86		    int port)
87{
88    struct sockaddr_in tmp;
89
90    memset (&tmp, 0, sizeof(tmp));
91    tmp.sin_family = AF_INET;
92    memcpy (&tmp.sin_addr, a->address.data, 4);
93    tmp.sin_port = port;
94    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
95    *sa_size = sizeof(tmp);
96}
97
98static void
99ipv4_h_addr2sockaddr(const char *addr,
100		     struct sockaddr *sa,
101		     krb5_socklen_t *sa_size,
102		     int port)
103{
104    struct sockaddr_in tmp;
105
106    memset (&tmp, 0, sizeof(tmp));
107    tmp.sin_family = AF_INET;
108    tmp.sin_port   = port;
109    tmp.sin_addr   = *((const struct in_addr *)addr);
110    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
111    *sa_size = sizeof(tmp);
112}
113
114static krb5_error_code
115ipv4_h_addr2addr (const char *addr,
116		  krb5_address *a)
117{
118    unsigned char buf[4];
119
120    a->addr_type = KRB5_ADDRESS_INET;
121    memcpy(buf, addr, 4);
122    return krb5_data_copy(&a->address, buf, 4);
123}
124
125/*
126 * Are there any addresses that should be considered `uninteresting'?
127 */
128
129static krb5_boolean
130ipv4_uninteresting (const struct sockaddr *sa)
131{
132    const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
133
134    if (sin4->sin_addr.s_addr == INADDR_ANY)
135	return TRUE;
136
137    return FALSE;
138}
139
140static krb5_boolean
141ipv4_is_loopback (const struct sockaddr *sa)
142{
143    const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
144
145    if ((ntohl(sin4->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET)
146	return TRUE;
147
148    return FALSE;
149}
150
151static void
152ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
153{
154    struct sockaddr_in tmp;
155
156    memset (&tmp, 0, sizeof(tmp));
157    tmp.sin_family = AF_INET;
158    tmp.sin_port   = port;
159    tmp.sin_addr.s_addr = INADDR_ANY;
160    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
161    *sa_size = sizeof(tmp);
162}
163
164static int
165ipv4_print_addr (const krb5_address *addr, char *str, size_t len)
166{
167    struct in_addr ia;
168
169    memcpy (&ia, addr->address.data, 4);
170
171    return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));
172}
173
174static int
175ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)
176{
177    const char *p;
178    struct in_addr a;
179
180    p = strchr(address, ':');
181    if(p) {
182	p++;
183	if(strncasecmp(address, "ip:", p - address) != 0 &&
184	   strncasecmp(address, "ip4:", p - address) != 0 &&
185	   strncasecmp(address, "ipv4:", p - address) != 0 &&
186	   strncasecmp(address, "inet:", p - address) != 0)
187	    return -1;
188    } else
189	p = address;
190    if(inet_aton(p, &a) == 0)
191	return -1;
192    addr->addr_type = KRB5_ADDRESS_INET;
193    if(krb5_data_alloc(&addr->address, 4) != 0)
194	return -1;
195    _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);
196    return 0;
197}
198
199static int
200ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,
201		   unsigned long len, krb5_address *low, krb5_address *high)
202{
203    unsigned long ia;
204    uint32_t l, h, m = 0xffffffff;
205
206    if (len > 32) {
207	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
208			       N_("IPv4 prefix too large (%ld)", "len"), len);
209	return KRB5_PROG_ATYPE_NOSUPP;
210    }
211    m = m << (32 - len);
212
213    _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);
214
215    l = ia & m;
216    h = l | ~m;
217
218    low->addr_type = KRB5_ADDRESS_INET;
219    if(krb5_data_alloc(&low->address, 4) != 0)
220	return -1;
221    _krb5_put_int(low->address.data, l, low->address.length);
222
223    high->addr_type = KRB5_ADDRESS_INET;
224    if(krb5_data_alloc(&high->address, 4) != 0) {
225	krb5_free_address(context, low);
226	return -1;
227    }
228    _krb5_put_int(high->address.data, h, high->address.length);
229
230    return 0;
231}
232
233
234/*
235 * AF_INET6 - aka IPv6 implementation
236 */
237
238#ifdef HAVE_IPV6
239
240static krb5_error_code
241ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
242{
243    const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
244
245    if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
246	unsigned char buf[4];
247
248	a->addr_type      = KRB5_ADDRESS_INET;
249#ifndef IN6_ADDR_V6_TO_V4
250#ifdef IN6_EXTRACT_V4ADDR
251#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))
252#else
253#define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])
254#endif
255#endif
256	memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);
257	return krb5_data_copy(&a->address, buf, 4);
258    } else {
259	a->addr_type = KRB5_ADDRESS_INET6;
260	return krb5_data_copy(&a->address,
261			      &sin6->sin6_addr,
262			      sizeof(sin6->sin6_addr));
263    }
264}
265
266static krb5_error_code
267ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)
268{
269    const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
270
271    *port = sin6->sin6_port;
272    return 0;
273}
274
275static void
276ipv6_addr2sockaddr (const krb5_address *a,
277		    struct sockaddr *sa,
278		    krb5_socklen_t *sa_size,
279		    int port)
280{
281    struct sockaddr_in6 tmp;
282
283    memset (&tmp, 0, sizeof(tmp));
284    tmp.sin6_family = AF_INET6;
285    memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));
286    tmp.sin6_port = port;
287    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
288    *sa_size = sizeof(tmp);
289}
290
291static void
292ipv6_h_addr2sockaddr(const char *addr,
293		     struct sockaddr *sa,
294		     krb5_socklen_t *sa_size,
295		     int port)
296{
297    struct sockaddr_in6 tmp;
298
299    memset (&tmp, 0, sizeof(tmp));
300    tmp.sin6_family = AF_INET6;
301    tmp.sin6_port   = port;
302    tmp.sin6_addr   = *((const struct in6_addr *)addr);
303    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
304    *sa_size = sizeof(tmp);
305}
306
307static krb5_error_code
308ipv6_h_addr2addr (const char *addr,
309		  krb5_address *a)
310{
311    a->addr_type = KRB5_ADDRESS_INET6;
312    return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));
313}
314
315/*
316 *
317 */
318
319static krb5_boolean
320ipv6_uninteresting (const struct sockaddr *sa)
321{
322    const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
323    const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
324
325    return IN6_IS_ADDR_LINKLOCAL(in6)
326	|| IN6_IS_ADDR_V4COMPAT(in6);
327}
328
329static krb5_boolean
330ipv6_is_loopback (const struct sockaddr *sa)
331{
332    const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
333    const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
334
335    return (IN6_IS_ADDR_LOOPBACK(in6));
336}
337
338static void
339ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
340{
341    struct sockaddr_in6 tmp;
342
343    memset (&tmp, 0, sizeof(tmp));
344    tmp.sin6_family = AF_INET6;
345    tmp.sin6_port   = port;
346    tmp.sin6_addr   = in6addr_any;
347    memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
348    *sa_size = sizeof(tmp);
349}
350
351static int
352ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
353{
354    char buf[128], buf2[3];
355    if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
356	{
357	    /* XXX this is pretty ugly, but better than abort() */
358	    size_t i;
359	    unsigned char *p = addr->address.data;
360	    buf[0] = '\0';
361	    for(i = 0; i < addr->address.length; i++) {
362		snprintf(buf2, sizeof(buf2), "%02x", p[i]);
363		if(i > 0 && (i & 1) == 0)
364		    strlcat(buf, ":", sizeof(buf));
365		strlcat(buf, buf2, sizeof(buf));
366	    }
367	}
368    return snprintf(str, len, "IPv6:%s", buf);
369}
370
371static int
372ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
373{
374    int ret;
375    struct in6_addr in6;
376    const char *p;
377
378    p = strchr(address, ':');
379    if(p) {
380	p++;
381	if(strncasecmp(address, "ip6:", p - address) == 0 ||
382	   strncasecmp(address, "ipv6:", p - address) == 0 ||
383	   strncasecmp(address, "inet6:", p - address) == 0)
384	    address = p;
385    }
386
387    ret = inet_pton(AF_INET6, address, &in6.s6_addr);
388    if(ret == 1) {
389	addr->addr_type = KRB5_ADDRESS_INET6;
390	ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
391	if (ret)
392	    return -1;
393	memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
394	return 0;
395    }
396    return -1;
397}
398
399static int
400ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
401		   unsigned long len, krb5_address *low, krb5_address *high)
402{
403    struct in6_addr addr, laddr, haddr;
404    size_t sub_len;
405    uint32_t m;
406    int i;
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
461struct arange {
462    krb5_address low;
463    krb5_address high;
464};
465
466static int
467arange_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
562static int
563arange_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
574static int
575arange_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
601static int
602arange_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
640static int
641arange_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
683static int
684addrport_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 -1;
729    ret_len += ret;
730    return (int)ret_len;
731}
732
733static 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    }
806};
807
808static int num_addrs = sizeof(at) / sizeof(at[0]);
809
810static size_t max_sockaddr_size = 0;
811
812/*
813 * generic functions
814 */
815
816static struct addr_operations *
817find_af(int af)
818{
819    struct addr_operations *a;
820
821    for (a = at; a < at + num_addrs; ++a)
822	if (af == a->af)
823	    return a;
824    return NULL;
825}
826
827static struct addr_operations *
828find_atype(krb5_address_type atype)
829{
830    struct addr_operations *a;
831
832    for (a = at; a < at + num_addrs; ++a)
833	if (atype == a->atype)
834	    return a;
835    return NULL;
836}
837
838/**
839 * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
840 * the krb5_address addr.
841 *
842 * @param context a Keberos context
843 * @param sa a struct sockaddr to extract the address from
844 * @param addr an Kerberos 5 address to store the address in.
845 *
846 * @return Return an error code or 0.
847 *
848 * @ingroup krb5_address
849 */
850
851KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
852krb5_sockaddr2address (krb5_context context,
853		       const struct sockaddr *sa, krb5_address *addr)
854{
855    struct addr_operations *a = find_af(sa->sa_family);
856    if (a == NULL) {
857	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
858				N_("Address family %d not supported", ""),
859				sa->sa_family);
860	return KRB5_PROG_ATYPE_NOSUPP;
861    }
862    return (*a->sockaddr2addr)(sa, addr);
863}
864
865/**
866 * krb5_sockaddr2port extracts a port (if possible) from a "struct
867 * sockaddr.
868 *
869 * @param context a Keberos context
870 * @param sa a struct sockaddr to extract the port from
871 * @param port a pointer to an int16_t store the port in.
872 *
873 * @return Return an error code or 0. Will return
874 * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
875 *
876 * @ingroup krb5_address
877 */
878
879KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
880krb5_sockaddr2port (krb5_context context,
881		    const struct sockaddr *sa, int16_t *port)
882{
883    struct addr_operations *a = find_af(sa->sa_family);
884    if (a == NULL) {
885	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
886				N_("Address family %d not supported", ""),
887				sa->sa_family);
888	return KRB5_PROG_ATYPE_NOSUPP;
889    }
890    return (*a->sockaddr2port)(sa, port);
891}
892
893/**
894 * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
895 * and port. The argument sa_size should initially contain the size of
896 * the sa and after the call, it will contain the actual length of the
897 * address. In case of the sa is too small to fit the whole address,
898 * the up to *sa_size will be stored, and then *sa_size will be set to
899 * the required length.
900 *
901 * @param context a Keberos context
902 * @param addr the address to copy the from
903 * @param sa the struct sockaddr that will be filled in
904 * @param sa_size pointer to length of sa, and after the call, it will
905 * contain the actual length of the address.
906 * @param port set port in sa.
907 *
908 * @return Return an error code or 0. Will return
909 * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
910 *
911 * @ingroup krb5_address
912 */
913
914KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
915krb5_addr2sockaddr (krb5_context context,
916		    const krb5_address *addr,
917		    struct sockaddr *sa,
918		    krb5_socklen_t *sa_size,
919		    int port)
920{
921    struct addr_operations *a = find_atype(addr->addr_type);
922
923    if (a == NULL) {
924	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
925				N_("Address type %d not supported",
926				   "krb5_address type"),
927				addr->addr_type);
928	return KRB5_PROG_ATYPE_NOSUPP;
929    }
930    if (a->addr2sockaddr == NULL) {
931	krb5_set_error_message (context,
932				KRB5_PROG_ATYPE_NOSUPP,
933				N_("Can't convert address type %d to sockaddr", ""),
934				addr->addr_type);
935	return KRB5_PROG_ATYPE_NOSUPP;
936    }
937    (*a->addr2sockaddr)(addr, sa, sa_size, port);
938    return 0;
939}
940
941/**
942 * krb5_max_sockaddr_size returns the max size of the .Li struct
943 * sockaddr that the Kerberos library will return.
944 *
945 * @return Return an size_t of the maximum struct sockaddr.
946 *
947 * @ingroup krb5_address
948 */
949
950KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
951krb5_max_sockaddr_size (void)
952{
953    if (max_sockaddr_size == 0) {
954	struct addr_operations *a;
955
956	for(a = at; a < at + num_addrs; ++a)
957	    max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
958    }
959    return max_sockaddr_size;
960}
961
962/**
963 * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
964 * kerberos library thinks are uninteresting.  One example are link
965 * local addresses.
966 *
967 * @param sa pointer to struct sockaddr that might be interesting.
968 *
969 * @return Return a non zero for uninteresting addresses.
970 *
971 * @ingroup krb5_address
972 */
973
974KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
975krb5_sockaddr_uninteresting(const struct sockaddr *sa)
976{
977    struct addr_operations *a = find_af(sa->sa_family);
978    if (a == NULL || a->uninteresting == NULL)
979	return TRUE;
980    return (*a->uninteresting)(sa);
981}
982
983KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
984krb5_sockaddr_is_loopback(const struct sockaddr *sa)
985{
986    struct addr_operations *a = find_af(sa->sa_family);
987    if (a == NULL || a->is_loopback == NULL)
988	return TRUE;
989    return (*a->is_loopback)(sa);
990}
991
992/**
993 * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
994 * the "struct hostent" (see gethostbyname(3) ) h_addr_list
995 * component. The argument sa_size should initially contain the size
996 * of the sa, and after the call, it will contain the actual length of
997 * the address.
998 *
999 * @param context a Keberos context
1000 * @param af addresses
1001 * @param addr address
1002 * @param sa returned struct sockaddr
1003 * @param sa_size size of sa
1004 * @param port port to set in sa.
1005 *
1006 * @return Return an error code or 0.
1007 *
1008 * @ingroup krb5_address
1009 */
1010
1011KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1012krb5_h_addr2sockaddr (krb5_context context,
1013		      int af,
1014		      const char *addr, struct sockaddr *sa,
1015		      krb5_socklen_t *sa_size,
1016		      int port)
1017{
1018    struct addr_operations *a = find_af(af);
1019    if (a == NULL) {
1020	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1021				"Address family %d not supported", af);
1022	return KRB5_PROG_ATYPE_NOSUPP;
1023    }
1024    (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
1025    return 0;
1026}
1027
1028/**
1029 * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
1030 * that it operates on a krb5_address instead of a struct sockaddr.
1031 *
1032 * @param context a Keberos context
1033 * @param af address family
1034 * @param haddr host address from struct hostent.
1035 * @param addr returned krb5_address.
1036 *
1037 * @return Return an error code or 0.
1038 *
1039 * @ingroup krb5_address
1040 */
1041
1042KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1043krb5_h_addr2addr (krb5_context context,
1044		  int af,
1045		  const char *haddr, krb5_address *addr)
1046{
1047    struct addr_operations *a = find_af(af);
1048    if (a == NULL) {
1049	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1050				N_("Address family %d not supported", ""), af);
1051	return KRB5_PROG_ATYPE_NOSUPP;
1052    }
1053    return (*a->h_addr2addr)(haddr, addr);
1054}
1055
1056/**
1057 * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
1058 * bind(2) to.  The argument sa_size should initially contain the size
1059 * of the sa, and after the call, it will contain the actual length
1060 * of the address.
1061 *
1062 * @param context a Keberos context
1063 * @param af address family
1064 * @param sa sockaddr
1065 * @param sa_size lenght of sa.
1066 * @param port for to fill into sa.
1067 *
1068 * @return Return an error code or 0.
1069 *
1070 * @ingroup krb5_address
1071 */
1072
1073KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1074krb5_anyaddr (krb5_context context,
1075	      int af,
1076	      struct sockaddr *sa,
1077	      krb5_socklen_t *sa_size,
1078	      int port)
1079{
1080    struct addr_operations *a = find_af (af);
1081
1082    if (a == NULL) {
1083	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1084				N_("Address family %d not supported", ""), af);
1085	return KRB5_PROG_ATYPE_NOSUPP;
1086    }
1087
1088    (*a->anyaddr)(sa, sa_size, port);
1089    return 0;
1090}
1091
1092/**
1093 * krb5_print_address prints the address in addr to the string string
1094 * that have the length len. If ret_len is not NULL, it will be filled
1095 * with the length of the string if size were unlimited (not including
1096 * the final NUL) .
1097 *
1098 * @param addr address to be printed
1099 * @param str pointer string to print the address into
1100 * @param len length that will fit into area pointed to by "str".
1101 * @param ret_len return length the str.
1102 *
1103 * @return Return an error code or 0.
1104 *
1105 * @ingroup krb5_address
1106 */
1107
1108KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1109krb5_print_address (const krb5_address *addr,
1110		    char *str, size_t len, size_t *ret_len)
1111{
1112    struct addr_operations *a = find_atype(addr->addr_type);
1113    int ret;
1114
1115    if (a == NULL || a->print_addr == NULL) {
1116	char *s;
1117	int l;
1118	size_t i;
1119
1120	s = str;
1121	l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
1122	if (l < 0 || (size_t)l >= len)
1123	    return EINVAL;
1124	s += l;
1125	len -= l;
1126	for(i = 0; i < addr->address.length; i++) {
1127	    l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
1128	    if (l < 0 || (size_t)l >= len)
1129		return EINVAL;
1130	    len -= l;
1131	    s += l;
1132	}
1133	if(ret_len != NULL)
1134	    *ret_len = s - str;
1135	return 0;
1136    }
1137    ret = (*a->print_addr)(addr, str, len);
1138    if (ret < 0)
1139	return EINVAL;
1140    if(ret_len != NULL)
1141	*ret_len = ret;
1142    return 0;
1143}
1144
1145/**
1146 * krb5_parse_address returns the resolved hostname in string to the
1147 * krb5_addresses addresses .
1148 *
1149 * @param context a Keberos context
1150 * @param string string to parse as an address
1151 * @param addresses return address, free with krb5_free_addresses()
1152 *
1153 * @return Return an error code or 0.
1154 *
1155 * @ingroup krb5_address
1156 */
1157
1158KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1159krb5_parse_address(krb5_context context,
1160		   const char *string,
1161		   krb5_addresses *addresses)
1162{
1163    int i, n;
1164    struct addrinfo *ai, *a;
1165    int error;
1166    int save_errno;
1167
1168    addresses->len = 0;
1169    addresses->val = NULL;
1170
1171    for(i = 0; i < num_addrs; i++) {
1172	if(at[i].parse_addr) {
1173	    krb5_address addr;
1174	    if((*at[i].parse_addr)(context, string, &addr) == 0) {
1175		ALLOC_SEQ(addresses, 1);
1176		if (addresses->val == NULL) {
1177		    krb5_set_error_message(context, ENOMEM,
1178					   N_("malloc: out of memory", ""));
1179		    return ENOMEM;
1180		}
1181		addresses->val[0] = addr;
1182		return 0;
1183	    }
1184	}
1185    }
1186
1187    error = getaddrinfo (string, NULL, NULL, &ai);
1188    if (error) {
1189	krb5_error_code ret2;
1190	save_errno = errno;
1191	ret2 = krb5_eai_to_heim_errno(error, save_errno);
1192	krb5_set_error_message (context, ret2, "%s: %s",
1193				string, gai_strerror(error));
1194	return ret2;
1195    }
1196
1197    n = 0;
1198    for (a = ai; a != NULL; a = a->ai_next)
1199	++n;
1200
1201    ALLOC_SEQ(addresses, n);
1202    if (addresses->val == NULL) {
1203	krb5_set_error_message(context, ENOMEM,
1204			       N_("malloc: out of memory", ""));
1205	freeaddrinfo(ai);
1206	return ENOMEM;
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
1239KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1240krb5_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 (int)(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
1288KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1289krb5_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
1309KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1310krb5_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
1334KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1335krb5_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
1358KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1359krb5_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
1384KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1385krb5_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
1408KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1409krb5_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 ENOMEM;
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
1435KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1436krb5_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	    krb5_set_error_message (context, ENOMEM,
1447				    N_("malloc: out of memory", ""));
1448	    return ENOMEM;
1449	}
1450	dest->val = tmp;
1451	for(i = 0; i < source->len; i++) {
1452	    /* skip duplicates */
1453	    if(krb5_address_search(context, &source->val[i], dest))
1454		continue;
1455	    ret = krb5_copy_address(context,
1456				    &source->val[i],
1457				    &dest->val[dest->len]);
1458	    if(ret)
1459		return ret;
1460	    dest->len++;
1461	}
1462    }
1463    return 0;
1464}
1465
1466/**
1467 * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
1468 *
1469 * @param context a Keberos context
1470 * @param res built address from addr/port
1471 * @param addr address to use
1472 * @param port port to use
1473 *
1474 * @return Return an error code or 0.
1475 *
1476 * @ingroup krb5_address
1477 */
1478
1479KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1480krb5_make_addrport (krb5_context context,
1481		    krb5_address **res, const krb5_address *addr, int16_t port)
1482{
1483    krb5_error_code ret;
1484    size_t len = addr->address.length + 2 + 4 * 4;
1485    u_char *p;
1486
1487    *res = malloc (sizeof(**res));
1488    if (*res == NULL) {
1489	krb5_set_error_message (context, ENOMEM,
1490				N_("malloc: out of memory", ""));
1491	return ENOMEM;
1492    }
1493    (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
1494    ret = krb5_data_alloc (&(*res)->address, len);
1495    if (ret) {
1496	krb5_set_error_message (context, ret,
1497				N_("malloc: out of memory", ""));
1498	free (*res);
1499	*res = NULL;
1500	return ret;
1501    }
1502    p = (*res)->address.data;
1503    *p++ = 0;
1504    *p++ = 0;
1505    *p++ = (addr->addr_type     ) & 0xFF;
1506    *p++ = (addr->addr_type >> 8) & 0xFF;
1507
1508    *p++ = (addr->address.length      ) & 0xFF;
1509    *p++ = (addr->address.length >>  8) & 0xFF;
1510    *p++ = (addr->address.length >> 16) & 0xFF;
1511    *p++ = (addr->address.length >> 24) & 0xFF;
1512
1513    memcpy (p, addr->address.data, addr->address.length);
1514    p += addr->address.length;
1515
1516    *p++ = 0;
1517    *p++ = 0;
1518    *p++ = (KRB5_ADDRESS_IPPORT     ) & 0xFF;
1519    *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
1520
1521    *p++ = (2      ) & 0xFF;
1522    *p++ = (2 >>  8) & 0xFF;
1523    *p++ = (2 >> 16) & 0xFF;
1524    *p++ = (2 >> 24) & 0xFF;
1525
1526    memcpy (p, &port, 2);
1527
1528    return 0;
1529}
1530
1531/**
1532 * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
1533 * them in `low' and `high'.
1534 *
1535 * @param context a Keberos context
1536 * @param inaddr address in prefixlen that the bondery searched
1537 * @param prefixlen width of boundery
1538 * @param low lowest address
1539 * @param high highest address
1540 *
1541 * @return Return an error code or 0.
1542 *
1543 * @ingroup krb5_address
1544 */
1545
1546KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1547krb5_address_prefixlen_boundary(krb5_context context,
1548				const krb5_address *inaddr,
1549				unsigned long prefixlen,
1550				krb5_address *low,
1551				krb5_address *high)
1552{
1553    struct addr_operations *a = find_atype (inaddr->addr_type);
1554    if(a != NULL && a->mask_boundary != NULL)
1555	return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
1556    krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
1557			   N_("Address family %d doesn't support "
1558			      "address mask operation", ""),
1559			   inaddr->addr_type);
1560    return KRB5_PROG_ATYPE_NOSUPP;
1561}
1562