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