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