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