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    *sa_size = sizeof(tmp);
348}
349
350static int
351ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
352{
353    char buf[128], buf2[3];
354    if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
355	{
356	    /* XXX this is pretty ugly, but better than abort() */
357	    size_t i;
358	    unsigned char *p = addr->address.data;
359	    buf[0] = '\0';
360	    for(i = 0; i < addr->address.length; i++) {
361		snprintf(buf2, sizeof(buf2), "%02x", p[i]);
362		if(i > 0 && (i & 1) == 0)
363		    strlcat(buf, ":", sizeof(buf));
364		strlcat(buf, buf2, sizeof(buf));
365	    }
366	}
367    return snprintf(str, len, "IPv6:%s", buf);
368}
369
370static int
371ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
372{
373    int ret;
374    struct in6_addr in6;
375    const char *p;
376
377    p = strchr(address, ':');
378    if(p) {
379	p++;
380	if(strncasecmp(address, "ip6:", p - address) == 0 ||
381	   strncasecmp(address, "ipv6:", p - address) == 0 ||
382	   strncasecmp(address, "inet6:", p - address) == 0)
383	    address = p;
384    }
385
386    ret = inet_pton(AF_INET6, address, &in6.s6_addr);
387    if(ret == 1) {
388	addr->addr_type = KRB5_ADDRESS_INET6;
389	ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
390	if (ret)
391	    return -1;
392	memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
393	return 0;
394    }
395    return -1;
396}
397
398static int
399ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
400		   unsigned long len, krb5_address *low, krb5_address *high)
401{
402    struct in6_addr addr, laddr, haddr;
403    uint32_t m;
404    int i, sub_len;
405
406    if (len > 128) {
407	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
408			       N_("IPv6 prefix too large (%ld)", "length"), len);
409	return KRB5_PROG_ATYPE_NOSUPP;
410    }
411
412    if (inaddr->address.length != sizeof(addr)) {
413	krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
414			       N_("IPv6 addr bad length", ""));
415	return KRB5_PROG_ATYPE_NOSUPP;
416    }
417
418    memcpy(&addr, inaddr->address.data, inaddr->address.length);
419
420    for (i = 0; i < 16; i++) {
421	sub_len = min(8, len);
422
423	m = 0xff << (8 - sub_len);
424
425	laddr.s6_addr[i] = addr.s6_addr[i] & m;
426	haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;
427
428	if (len > 8)
429	    len -= 8;
430	else
431	    len = 0;
432    }
433
434    low->addr_type = KRB5_ADDRESS_INET6;
435    if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)
436	return -1;
437    memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));
438
439    high->addr_type = KRB5_ADDRESS_INET6;
440    if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {
441	krb5_free_address(context, low);
442	return -1;
443    }
444    memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));
445
446    return 0;
447}
448
449#endif /* IPv6 */
450
451#ifndef HEIMDAL_SMALLER
452
453/*
454 * table
455 */
456
457#define KRB5_ADDRESS_ARANGE	(-100)
458
459struct arange {
460    krb5_address low;
461    krb5_address high;
462};
463
464static int
465arange_parse_addr (krb5_context context,
466		   const char *address, krb5_address *addr)
467{
468    char buf[1024], *p;
469    krb5_address low0, high0;
470    struct arange *a;
471    krb5_error_code ret;
472
473    if(strncasecmp(address, "RANGE:", 6) != 0)
474	return -1;
475
476    address += 6;
477
478    p = strrchr(address, '/');
479    if (p) {
480	krb5_addresses addrmask;
481	char *q;
482	long num;
483
484	if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))
485	    return -1;
486	buf[p - address] = '\0';
487	ret = krb5_parse_address(context, buf, &addrmask);
488	if (ret)
489	    return ret;
490	if(addrmask.len != 1) {
491	    krb5_free_addresses(context, &addrmask);
492	    return -1;
493	}
494
495	address += p - address + 1;
496
497	num = strtol(address, &q, 10);
498	if (q == address || *q != '\0' || num < 0) {
499	    krb5_free_addresses(context, &addrmask);
500	    return -1;
501	}
502
503	ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,
504					      &low0, &high0);
505	krb5_free_addresses(context, &addrmask);
506	if (ret)
507	    return ret;
508
509    } else {
510	krb5_addresses low, high;
511
512	strsep_copy(&address, "-", buf, sizeof(buf));
513	ret = krb5_parse_address(context, buf, &low);
514	if(ret)
515	    return ret;
516	if(low.len != 1) {
517	    krb5_free_addresses(context, &low);
518	    return -1;
519	}
520
521	strsep_copy(&address, "-", buf, sizeof(buf));
522	ret = krb5_parse_address(context, buf, &high);
523	if(ret) {
524	    krb5_free_addresses(context, &low);
525	    return ret;
526	}
527
528	if(high.len != 1 && high.val[0].addr_type != low.val[0].addr_type) {
529	    krb5_free_addresses(context, &low);
530	    krb5_free_addresses(context, &high);
531	    return -1;
532	}
533
534	ret = krb5_copy_address(context, &high.val[0], &high0);
535	if (ret == 0) {
536	    ret = krb5_copy_address(context, &low.val[0], &low0);
537	    if (ret)
538		krb5_free_address(context, &high0);
539	}
540	krb5_free_addresses(context, &low);
541	krb5_free_addresses(context, &high);
542	if (ret)
543	    return ret;
544    }
545
546    krb5_data_alloc(&addr->address, sizeof(*a));
547    addr->addr_type = KRB5_ADDRESS_ARANGE;
548    a = addr->address.data;
549
550    if(krb5_address_order(context, &low0, &high0) < 0) {
551	a->low = low0;
552	a->high = high0;
553    } else {
554	a->low = high0;
555	a->high = low0;
556    }
557    return 0;
558}
559
560static int
561arange_free (krb5_context context, krb5_address *addr)
562{
563    struct arange *a;
564    a = addr->address.data;
565    krb5_free_address(context, &a->low);
566    krb5_free_address(context, &a->high);
567    krb5_data_free(&addr->address);
568    return 0;
569}
570
571
572static int
573arange_copy (krb5_context context, const krb5_address *inaddr,
574	     krb5_address *outaddr)
575{
576    krb5_error_code ret;
577    struct arange *i, *o;
578
579    outaddr->addr_type = KRB5_ADDRESS_ARANGE;
580    ret = krb5_data_alloc(&outaddr->address, sizeof(*o));
581    if(ret)
582	return ret;
583    i = inaddr->address.data;
584    o = outaddr->address.data;
585    ret = krb5_copy_address(context, &i->low, &o->low);
586    if(ret) {
587	krb5_data_free(&outaddr->address);
588	return ret;
589    }
590    ret = krb5_copy_address(context, &i->high, &o->high);
591    if(ret) {
592	krb5_free_address(context, &o->low);
593	krb5_data_free(&outaddr->address);
594	return ret;
595    }
596    return 0;
597}
598
599static int
600arange_print_addr (const krb5_address *addr, char *str, size_t len)
601{
602    struct arange *a;
603    krb5_error_code ret;
604    size_t l, size, ret_len;
605
606    a = addr->address.data;
607
608    l = strlcpy(str, "RANGE:", len);
609    ret_len = l;
610    if (l > len)
611	l = len;
612    size = l;
613
614    ret = krb5_print_address (&a->low, str + size, len - size, &l);
615    if (ret)
616	return ret;
617    ret_len += l;
618    if (len - size > l)
619	size += l;
620    else
621	size = len;
622
623    l = strlcat(str + size, "-", len - size);
624    ret_len += l;
625    if (len - size > l)
626	size += l;
627    else
628	size = len;
629
630    ret = krb5_print_address (&a->high, str + size, len - size, &l);
631    if (ret)
632	return ret;
633    ret_len += l;
634
635    return ret_len;
636}
637
638static int
639arange_order_addr(krb5_context context,
640		  const krb5_address *addr1,
641		  const krb5_address *addr2)
642{
643    int tmp1, tmp2, sign;
644    struct arange *a;
645    const krb5_address *a2;
646
647    if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {
648	a = addr1->address.data;
649	a2 = addr2;
650	sign = 1;
651    } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {
652	a = addr2->address.data;
653	a2 = addr1;
654	sign = -1;
655    } else {
656	abort();
657        UNREACHABLE(return 0);
658    }
659
660    if(a2->addr_type == KRB5_ADDRESS_ARANGE) {
661	struct arange *b = a2->address.data;
662	tmp1 = krb5_address_order(context, &a->low, &b->low);
663	if(tmp1 != 0)
664	    return sign * tmp1;
665	return sign * krb5_address_order(context, &a->high, &b->high);
666    } else if(a2->addr_type == a->low.addr_type) {
667	tmp1 = krb5_address_order(context, &a->low, a2);
668	if(tmp1 > 0)
669	    return sign;
670	tmp2 = krb5_address_order(context, &a->high, a2);
671	if(tmp2 < 0)
672	    return -sign;
673	return 0;
674    } else {
675	return sign * (addr1->addr_type - addr2->addr_type);
676    }
677}
678
679#endif /* HEIMDAL_SMALLER */
680
681static int
682addrport_print_addr (const krb5_address *addr, char *str, size_t len)
683{
684    krb5_error_code ret;
685    krb5_address addr1, addr2;
686    uint16_t port = 0;
687    size_t ret_len = 0, l, size = 0;
688    krb5_storage *sp;
689
690    sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
691    if (sp == NULL)
692        return ENOMEM;
693
694    /* for totally obscure reasons, these are not in network byteorder */
695    krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
696
697    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
698    krb5_ret_address(sp, &addr1);
699
700    krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
701    krb5_ret_address(sp, &addr2);
702    krb5_storage_free(sp);
703    if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
704	unsigned long value;
705	_krb5_get_int(addr2.address.data, &value, 2);
706	port = value;
707    }
708    l = strlcpy(str, "ADDRPORT:", len);
709    ret_len += l;
710    if (len > l)
711	size += l;
712    else
713	size = len;
714
715    ret = krb5_print_address(&addr1, str + size, len - size, &l);
716    if (ret)
717	return ret;
718    ret_len += l;
719    if (len - size > l)
720	size += l;
721    else
722	size = len;
723
724    ret = snprintf(str + size, len - size, ",PORT=%u", port);
725    if (ret < 0)
726	return EINVAL;
727    ret_len += ret;
728    return ret_len;
729}
730
731static struct addr_operations at[] = {
732    {
733	AF_INET,	KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),
734	ipv4_sockaddr2addr,
735	ipv4_sockaddr2port,
736	ipv4_addr2sockaddr,
737	ipv4_h_addr2sockaddr,
738	ipv4_h_addr2addr,
739	ipv4_uninteresting,
740	ipv4_is_loopback,
741	ipv4_anyaddr,
742	ipv4_print_addr,
743	ipv4_parse_addr,
744	NULL,
745	NULL,
746	NULL,
747     ipv4_mask_boundary
748    },
749#ifdef HAVE_IPV6
750    {
751	AF_INET6,	KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),
752	ipv6_sockaddr2addr,
753	ipv6_sockaddr2port,
754	ipv6_addr2sockaddr,
755	ipv6_h_addr2sockaddr,
756	ipv6_h_addr2addr,
757	ipv6_uninteresting,
758	ipv6_is_loopback,
759	ipv6_anyaddr,
760	ipv6_print_addr,
761	ipv6_parse_addr,
762	NULL,
763	NULL,
764	NULL,
765	ipv6_mask_boundary
766    } ,
767#endif
768#ifndef HEIMDAL_SMALLER
769    /* fake address type */
770    {
771	KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),
772	NULL,
773	NULL,
774	NULL,
775	NULL,
776	NULL,
777	NULL,
778	NULL,
779	NULL,
780	arange_print_addr,
781	arange_parse_addr,
782	arange_order_addr,
783	arange_free,
784	arange_copy,
785	NULL
786    },
787#endif
788    {
789	KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,
790	NULL,
791	NULL,
792	NULL,
793	NULL,
794	NULL,
795	NULL,
796	NULL,
797	NULL,
798	addrport_print_addr,
799	NULL,
800	NULL,
801	NULL,
802	NULL
803    }
804};
805
806static int num_addrs = sizeof(at) / sizeof(at[0]);
807
808static size_t max_sockaddr_size = 0;
809
810/*
811 * generic functions
812 */
813
814static struct addr_operations *
815find_af(int af)
816{
817    struct addr_operations *a;
818
819    for (a = at; a < at + num_addrs; ++a)
820	if (af == a->af)
821	    return a;
822    return NULL;
823}
824
825static struct addr_operations *
826find_atype(krb5_address_type atype)
827{
828    struct addr_operations *a;
829
830    for (a = at; a < at + num_addrs; ++a)
831	if (atype == a->atype)
832	    return a;
833    return NULL;
834}
835
836/**
837 * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
838 * the krb5_address addr.
839 *
840 * @param context a Keberos context
841 * @param sa a struct sockaddr to extract the address from
842 * @param addr an Kerberos 5 address to store the address in.
843 *
844 * @return Return an error code or 0.
845 *
846 * @ingroup krb5_address
847 */
848
849KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
850krb5_sockaddr2address (krb5_context context,
851		       const struct sockaddr *sa, krb5_address *addr)
852{
853    struct addr_operations *a = find_af(sa->sa_family);
854    if (a == NULL) {
855	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
856				N_("Address family %d not supported", ""),
857				sa->sa_family);
858	return KRB5_PROG_ATYPE_NOSUPP;
859    }
860    return (*a->sockaddr2addr)(sa, addr);
861}
862
863/**
864 * krb5_sockaddr2port extracts a port (if possible) from a "struct
865 * sockaddr.
866 *
867 * @param context a Keberos context
868 * @param sa a struct sockaddr to extract the port from
869 * @param port a pointer to an int16_t store the port in.
870 *
871 * @return Return an error code or 0. Will return
872 * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
873 *
874 * @ingroup krb5_address
875 */
876
877KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
878krb5_sockaddr2port (krb5_context context,
879		    const struct sockaddr *sa, int16_t *port)
880{
881    struct addr_operations *a = find_af(sa->sa_family);
882    if (a == NULL) {
883	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
884				N_("Address family %d not supported", ""),
885				sa->sa_family);
886	return KRB5_PROG_ATYPE_NOSUPP;
887    }
888    return (*a->sockaddr2port)(sa, port);
889}
890
891/**
892 * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
893 * and port. The argument sa_size should initially contain the size of
894 * the sa and after the call, it will contain the actual length of the
895 * address. In case of the sa is too small to fit the whole address,
896 * the up to *sa_size will be stored, and then *sa_size will be set to
897 * the required length.
898 *
899 * @param context a Keberos context
900 * @param addr the address to copy the from
901 * @param sa the struct sockaddr that will be filled in
902 * @param sa_size pointer to length of sa, and after the call, it will
903 * contain the actual length of the address.
904 * @param port set port in sa.
905 *
906 * @return Return an error code or 0. Will return
907 * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
908 *
909 * @ingroup krb5_address
910 */
911
912KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
913krb5_addr2sockaddr (krb5_context context,
914		    const krb5_address *addr,
915		    struct sockaddr *sa,
916		    krb5_socklen_t *sa_size,
917		    int port)
918{
919    struct addr_operations *a = find_atype(addr->addr_type);
920
921    if (a == NULL) {
922	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
923				N_("Address type %d not supported",
924				   "krb5_address type"),
925				addr->addr_type);
926	return KRB5_PROG_ATYPE_NOSUPP;
927    }
928    if (a->addr2sockaddr == NULL) {
929	krb5_set_error_message (context,
930				KRB5_PROG_ATYPE_NOSUPP,
931				N_("Can't convert address type %d to sockaddr", ""),
932				addr->addr_type);
933	return KRB5_PROG_ATYPE_NOSUPP;
934    }
935    (*a->addr2sockaddr)(addr, sa, sa_size, port);
936    return 0;
937}
938
939/**
940 * krb5_max_sockaddr_size returns the max size of the .Li struct
941 * sockaddr that the Kerberos library will return.
942 *
943 * @return Return an size_t of the maximum struct sockaddr.
944 *
945 * @ingroup krb5_address
946 */
947
948KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
949krb5_max_sockaddr_size (void)
950{
951    if (max_sockaddr_size == 0) {
952	struct addr_operations *a;
953
954	for(a = at; a < at + num_addrs; ++a)
955	    max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
956    }
957    return max_sockaddr_size;
958}
959
960/**
961 * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
962 * kerberos library thinks are uninteresting.  One example are link
963 * local addresses.
964 *
965 * @param sa pointer to struct sockaddr that might be interesting.
966 *
967 * @return Return a non zero for uninteresting addresses.
968 *
969 * @ingroup krb5_address
970 */
971
972KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
973krb5_sockaddr_uninteresting(const struct sockaddr *sa)
974{
975    struct addr_operations *a = find_af(sa->sa_family);
976    if (a == NULL || a->uninteresting == NULL)
977	return TRUE;
978    return (*a->uninteresting)(sa);
979}
980
981KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
982krb5_sockaddr_is_loopback(const struct sockaddr *sa)
983{
984    struct addr_operations *a = find_af(sa->sa_family);
985    if (a == NULL || a->is_loopback == NULL)
986	return TRUE;
987    return (*a->is_loopback)(sa);
988}
989
990/**
991 * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
992 * the "struct hostent" (see gethostbyname(3) ) h_addr_list
993 * component. The argument sa_size should initially contain the size
994 * of the sa, and after the call, it will contain the actual length of
995 * the address.
996 *
997 * @param context a Keberos context
998 * @param af addresses
999 * @param addr address
1000 * @param sa returned struct sockaddr
1001 * @param sa_size size of sa
1002 * @param port port to set in sa.
1003 *
1004 * @return Return an error code or 0.
1005 *
1006 * @ingroup krb5_address
1007 */
1008
1009KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1010krb5_h_addr2sockaddr (krb5_context context,
1011		      int af,
1012		      const char *addr, struct sockaddr *sa,
1013		      krb5_socklen_t *sa_size,
1014		      int port)
1015{
1016    struct addr_operations *a = find_af(af);
1017    if (a == NULL) {
1018	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1019				"Address family %d not supported", af);
1020	return KRB5_PROG_ATYPE_NOSUPP;
1021    }
1022    (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
1023    return 0;
1024}
1025
1026/**
1027 * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
1028 * that it operates on a krb5_address instead of a struct sockaddr.
1029 *
1030 * @param context a Keberos context
1031 * @param af address family
1032 * @param haddr host address from struct hostent.
1033 * @param addr returned krb5_address.
1034 *
1035 * @return Return an error code or 0.
1036 *
1037 * @ingroup krb5_address
1038 */
1039
1040KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1041krb5_h_addr2addr (krb5_context context,
1042		  int af,
1043		  const char *haddr, krb5_address *addr)
1044{
1045    struct addr_operations *a = find_af(af);
1046    if (a == NULL) {
1047	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1048				N_("Address family %d not supported", ""), af);
1049	return KRB5_PROG_ATYPE_NOSUPP;
1050    }
1051    return (*a->h_addr2addr)(haddr, addr);
1052}
1053
1054/**
1055 * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
1056 * bind(2) to.  The argument sa_size should initially contain the size
1057 * of the sa, and after the call, it will contain the actual length
1058 * of the address.
1059 *
1060 * @param context a Keberos context
1061 * @param af address family
1062 * @param sa sockaddr
1063 * @param sa_size lenght of sa.
1064 * @param port for to fill into sa.
1065 *
1066 * @return Return an error code or 0.
1067 *
1068 * @ingroup krb5_address
1069 */
1070
1071KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1072krb5_anyaddr (krb5_context context,
1073	      int af,
1074	      struct sockaddr *sa,
1075	      krb5_socklen_t *sa_size,
1076	      int port)
1077{
1078    struct addr_operations *a = find_af (af);
1079
1080    if (a == NULL) {
1081	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1082				N_("Address family %d not supported", ""), af);
1083	return KRB5_PROG_ATYPE_NOSUPP;
1084    }
1085
1086    (*a->anyaddr)(sa, sa_size, port);
1087    return 0;
1088}
1089
1090/**
1091 * krb5_print_address prints the address in addr to the string string
1092 * that have the length len. If ret_len is not NULL, it will be filled
1093 * with the length of the string if size were unlimited (not including
1094 * the final NUL) .
1095 *
1096 * @param addr address to be printed
1097 * @param str pointer string to print the address into
1098 * @param len length that will fit into area pointed to by "str".
1099 * @param ret_len return length the str.
1100 *
1101 * @return Return an error code or 0.
1102 *
1103 * @ingroup krb5_address
1104 */
1105
1106KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1107krb5_print_address (const krb5_address *addr,
1108		    char *str, size_t len, size_t *ret_len)
1109{
1110    struct addr_operations *a = find_atype(addr->addr_type);
1111    int ret;
1112
1113    if (a == NULL || a->print_addr == NULL) {
1114	char *s;
1115	int l;
1116	size_t i;
1117
1118	s = str;
1119	l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
1120	if (l < 0 || (size_t)l >= len)
1121	    return EINVAL;
1122	s += l;
1123	len -= l;
1124	for(i = 0; i < addr->address.length; i++) {
1125	    l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
1126	    if (l < 0 || (size_t)l >= len)
1127		return EINVAL;
1128	    len -= l;
1129	    s += l;
1130	}
1131	if(ret_len != NULL)
1132	    *ret_len = s - str;
1133	return 0;
1134    }
1135    ret = (*a->print_addr)(addr, str, len);
1136    if (ret < 0)
1137	return EINVAL;
1138    if(ret_len != NULL)
1139	*ret_len = ret;
1140    return 0;
1141}
1142
1143/**
1144 * krb5_parse_address returns the resolved hostname in string to the
1145 * krb5_addresses addresses .
1146 *
1147 * @param context a Keberos context
1148 * @param string
1149 * @param addresses
1150 *
1151 * @return Return an error code or 0.
1152 *
1153 * @ingroup krb5_address
1154 */
1155
1156KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1157krb5_parse_address(krb5_context context,
1158		   const char *string,
1159		   krb5_addresses *addresses)
1160{
1161    int i, n;
1162    struct addrinfo *ai, *a;
1163    int error;
1164    int save_errno;
1165
1166    addresses->len = 0;
1167    addresses->val = NULL;
1168
1169    for(i = 0; i < num_addrs; i++) {
1170	if(at[i].parse_addr) {
1171	    krb5_address addr;
1172	    if((*at[i].parse_addr)(context, string, &addr) == 0) {
1173		ALLOC_SEQ(addresses, 1);
1174		if (addresses->val == NULL) {
1175		    krb5_set_error_message(context, ENOMEM,
1176					   N_("malloc: out of memory", ""));
1177		    return ENOMEM;
1178		}
1179		addresses->val[0] = addr;
1180		return 0;
1181	    }
1182	}
1183    }
1184
1185    error = getaddrinfo (string, NULL, NULL, &ai);
1186    if (error) {
1187	krb5_error_code ret2;
1188	save_errno = errno;
1189	ret2 = krb5_eai_to_heim_errno(error, save_errno);
1190	krb5_set_error_message (context, ret2, "%s: %s",
1191				string, gai_strerror(error));
1192	return ret2;
1193    }
1194
1195    n = 0;
1196    for (a = ai; a != NULL; a = a->ai_next)
1197	++n;
1198
1199    ALLOC_SEQ(addresses, n);
1200    if (addresses->val == NULL) {
1201	krb5_set_error_message(context, ENOMEM,
1202			       N_("malloc: out of memory", ""));
1203	freeaddrinfo(ai);
1204	return ENOMEM;
1205    }
1206
1207    addresses->len = 0;
1208    for (a = ai, i = 0; a != NULL; a = a->ai_next) {
1209	if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))
1210	    continue;
1211	if(krb5_address_search(context, &addresses->val[i], addresses)) {
1212	    krb5_free_address(context, &addresses->val[i]);
1213	    continue;
1214	}
1215	i++;
1216	addresses->len = i;
1217    }
1218    freeaddrinfo (ai);
1219    return 0;
1220}
1221
1222/**
1223 * krb5_address_order compares the addresses addr1 and addr2 so that
1224 * it can be used for sorting addresses. If the addresses are the same
1225 * address krb5_address_order will return 0. Behavies like memcmp(2).
1226 *
1227 * @param context a Keberos context
1228 * @param addr1 krb5_address to compare
1229 * @param addr2 krb5_address to compare
1230 *
1231 * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and
1232 * addr2 is the same address, > 0 if addr2 is "less" then addr1.
1233 *
1234 * @ingroup krb5_address
1235 */
1236
1237KRB5_LIB_FUNCTION int KRB5_LIB_CALL
1238krb5_address_order(krb5_context context,
1239		   const krb5_address *addr1,
1240		   const krb5_address *addr2)
1241{
1242    /* this sucks; what if both addresses have order functions, which
1243       should we call? this works for now, though */
1244    struct addr_operations *a;
1245    a = find_atype(addr1->addr_type);
1246    if(a == NULL) {
1247	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1248				N_("Address family %d not supported", ""),
1249				addr1->addr_type);
1250	return KRB5_PROG_ATYPE_NOSUPP;
1251    }
1252    if(a->order_addr != NULL)
1253	return (*a->order_addr)(context, addr1, addr2);
1254    a = find_atype(addr2->addr_type);
1255    if(a == NULL) {
1256	krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
1257				N_("Address family %d not supported", ""),
1258				addr2->addr_type);
1259	return KRB5_PROG_ATYPE_NOSUPP;
1260    }
1261    if(a->order_addr != NULL)
1262	return (*a->order_addr)(context, addr1, addr2);
1263
1264    if(addr1->addr_type != addr2->addr_type)
1265	return addr1->addr_type - addr2->addr_type;
1266    if(addr1->address.length != addr2->address.length)
1267	return addr1->address.length - addr2->address.length;
1268    return memcmp (addr1->address.data,
1269		   addr2->address.data,
1270		   addr1->address.length);
1271}
1272
1273/**
1274 * krb5_address_compare compares the addresses  addr1 and addr2.
1275 * Returns TRUE if the two addresses are the same.
1276 *
1277 * @param context a Keberos context
1278 * @param addr1 address to compare
1279 * @param addr2 address to compare
1280 *
1281 * @return Return an TRUE is the address are the same FALSE if not
1282 *
1283 * @ingroup krb5_address
1284 */
1285
1286KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1287krb5_address_compare(krb5_context context,
1288		     const krb5_address *addr1,
1289		     const krb5_address *addr2)
1290{
1291    return krb5_address_order (context, addr1, addr2) == 0;
1292}
1293
1294/**
1295 * krb5_address_search checks if the address addr is a member of the
1296 * address set list addrlist .
1297 *
1298 * @param context a Keberos context.
1299 * @param addr address to search for.
1300 * @param addrlist list of addresses to look in for addr.
1301 *
1302 * @return Return an error code or 0.
1303 *
1304 * @ingroup krb5_address
1305 */
1306
1307KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1308krb5_address_search(krb5_context context,
1309		    const krb5_address *addr,
1310		    const krb5_addresses *addrlist)
1311{
1312    size_t i;
1313
1314    for (i = 0; i < addrlist->len; ++i)
1315	if (krb5_address_compare (context, addr, &addrlist->val[i]))
1316	    return TRUE;
1317    return FALSE;
1318}
1319
1320/**
1321 * krb5_free_address frees the data stored in the address that is
1322 * alloced with any of the krb5_address functions.
1323 *
1324 * @param context a Keberos context
1325 * @param address addresss to be freed.
1326 *
1327 * @return Return an error code or 0.
1328 *
1329 * @ingroup krb5_address
1330 */
1331
1332KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1333krb5_free_address(krb5_context context,
1334		  krb5_address *address)
1335{
1336    struct addr_operations *a = find_atype (address->addr_type);
1337    if(a != NULL && a->free_addr != NULL)
1338	return (*a->free_addr)(context, address);
1339    krb5_data_free (&address->address);
1340    memset(address, 0, sizeof(*address));
1341    return 0;
1342}
1343
1344/**
1345 * krb5_free_addresses frees the data stored in the address that is
1346 * alloced with any of the krb5_address functions.
1347 *
1348 * @param context a Keberos context
1349 * @param addresses addressses to be freed.
1350 *
1351 * @return Return an error code or 0.
1352 *
1353 * @ingroup krb5_address
1354 */
1355
1356KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1357krb5_free_addresses(krb5_context context,
1358		    krb5_addresses *addresses)
1359{
1360    size_t i;
1361    for(i = 0; i < addresses->len; i++)
1362	krb5_free_address(context, &addresses->val[i]);
1363    free(addresses->val);
1364    addresses->len = 0;
1365    addresses->val = NULL;
1366    return 0;
1367}
1368
1369/**
1370 * krb5_copy_address copies the content of address
1371 * inaddr to outaddr.
1372 *
1373 * @param context a Keberos context
1374 * @param inaddr pointer to source address
1375 * @param outaddr pointer to destination address
1376 *
1377 * @return Return an error code or 0.
1378 *
1379 * @ingroup krb5_address
1380 */
1381
1382KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1383krb5_copy_address(krb5_context context,
1384		  const krb5_address *inaddr,
1385		  krb5_address *outaddr)
1386{
1387    struct addr_operations *a = find_af (inaddr->addr_type);
1388    if(a != NULL && a->copy_addr != NULL)
1389	return (*a->copy_addr)(context, inaddr, outaddr);
1390    return copy_HostAddress(inaddr, outaddr);
1391}
1392
1393/**
1394 * krb5_copy_addresses copies the content of addresses
1395 * inaddr to outaddr.
1396 *
1397 * @param context a Keberos context
1398 * @param inaddr pointer to source addresses
1399 * @param outaddr pointer to destination addresses
1400 *
1401 * @return Return an error code or 0.
1402 *
1403 * @ingroup krb5_address
1404 */
1405
1406KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1407krb5_copy_addresses(krb5_context context,
1408		    const krb5_addresses *inaddr,
1409		    krb5_addresses *outaddr)
1410{
1411    size_t i;
1412    ALLOC_SEQ(outaddr, inaddr->len);
1413    if(inaddr->len > 0 && outaddr->val == NULL)
1414	return ENOMEM;
1415    for(i = 0; i < inaddr->len; i++)
1416	krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
1417    return 0;
1418}
1419
1420/**
1421 * krb5_append_addresses adds the set of addresses in source to
1422 * dest. While copying the addresses, duplicates are also sorted out.
1423 *
1424 * @param context a Keberos context
1425 * @param dest destination of copy operation
1426 * @param source adresses that are going to be added to dest
1427 *
1428 * @return Return an error code or 0.
1429 *
1430 * @ingroup krb5_address
1431 */
1432
1433KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1434krb5_append_addresses(krb5_context context,
1435		      krb5_addresses *dest,
1436		      const krb5_addresses *source)
1437{
1438    krb5_address *tmp;
1439    krb5_error_code ret;
1440    size_t i;
1441    if(source->len > 0) {
1442	tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
1443	if(tmp == NULL) {
1444	    krb5_set_error_message (context, ENOMEM,
1445				    N_("malloc: out of memory", ""));
1446	    return ENOMEM;
1447	}
1448	dest->val = tmp;
1449	for(i = 0; i < source->len; i++) {
1450	    /* skip duplicates */
1451	    if(krb5_address_search(context, &source->val[i], dest))
1452		continue;
1453	    ret = krb5_copy_address(context,
1454				    &source->val[i],
1455				    &dest->val[dest->len]);
1456	    if(ret)
1457		return ret;
1458	    dest->len++;
1459	}
1460    }
1461    return 0;
1462}
1463
1464/**
1465 * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
1466 *
1467 * @param context a Keberos context
1468 * @param res built address from addr/port
1469 * @param addr address to use
1470 * @param port port to use
1471 *
1472 * @return Return an error code or 0.
1473 *
1474 * @ingroup krb5_address
1475 */
1476
1477KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1478krb5_make_addrport (krb5_context context,
1479		    krb5_address **res, const krb5_address *addr, int16_t port)
1480{
1481    krb5_error_code ret;
1482    size_t len = addr->address.length + 2 + 4 * 4;
1483    u_char *p;
1484
1485    *res = malloc (sizeof(**res));
1486    if (*res == NULL) {
1487	krb5_set_error_message (context, ENOMEM,
1488				N_("malloc: out of memory", ""));
1489	return ENOMEM;
1490    }
1491    (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
1492    ret = krb5_data_alloc (&(*res)->address, len);
1493    if (ret) {
1494	krb5_set_error_message (context, ret,
1495				N_("malloc: out of memory", ""));
1496	free (*res);
1497	*res = NULL;
1498	return ret;
1499    }
1500    p = (*res)->address.data;
1501    *p++ = 0;
1502    *p++ = 0;
1503    *p++ = (addr->addr_type     ) & 0xFF;
1504    *p++ = (addr->addr_type >> 8) & 0xFF;
1505
1506    *p++ = (addr->address.length      ) & 0xFF;
1507    *p++ = (addr->address.length >>  8) & 0xFF;
1508    *p++ = (addr->address.length >> 16) & 0xFF;
1509    *p++ = (addr->address.length >> 24) & 0xFF;
1510
1511    memcpy (p, addr->address.data, addr->address.length);
1512    p += addr->address.length;
1513
1514    *p++ = 0;
1515    *p++ = 0;
1516    *p++ = (KRB5_ADDRESS_IPPORT     ) & 0xFF;
1517    *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
1518
1519    *p++ = (2      ) & 0xFF;
1520    *p++ = (2 >>  8) & 0xFF;
1521    *p++ = (2 >> 16) & 0xFF;
1522    *p++ = (2 >> 24) & 0xFF;
1523
1524    memcpy (p, &port, 2);
1525
1526    return 0;
1527}
1528
1529/**
1530 * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
1531 * them in `low' and `high'.
1532 *
1533 * @param context a Keberos context
1534 * @param inaddr address in prefixlen that the bondery searched
1535 * @param prefixlen width of boundery
1536 * @param low lowest address
1537 * @param high highest address
1538 *
1539 * @return Return an error code or 0.
1540 *
1541 * @ingroup krb5_address
1542 */
1543
1544KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1545krb5_address_prefixlen_boundary(krb5_context context,
1546				const krb5_address *inaddr,
1547				unsigned long prefixlen,
1548				krb5_address *low,
1549				krb5_address *high)
1550{
1551    struct addr_operations *a = find_atype (inaddr->addr_type);
1552    if(a != NULL && a->mask_boundary != NULL)
1553	return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
1554    krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
1555			   N_("Address family %d doesn't support "
1556			      "address mask operation", ""),
1557			   inaddr->addr_type);
1558    return KRB5_PROG_ATYPE_NOSUPP;
1559}
1560