]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/krb5/addr_families.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / krb5 / addr_families.c
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 }