r19604: This is a massive commit, and I appologise in advance for it's size.
[kai/samba.git] / source4 / heimdal / lib / krb5 / addr_families.c
1 /*
2  * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #include "krb5_locl.h"
35
36 RCSID("$Id: addr_families.c,v 1.53 2006/10/22 06:54:00 lha Exp $");
37
38 struct addr_operations {
39     int af;
40     krb5_address_type atype;
41     size_t max_sockaddr_size;
42     krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);
43     krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);
44     void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,
45                           krb5_socklen_t *sa_size, int port);
46     void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);
47     krb5_error_code (*h_addr2addr)(const char *, krb5_address *);
48     krb5_boolean (*uninteresting)(const struct sockaddr *);
49     void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);
50     int (*print_addr)(const krb5_address *, char *, size_t);
51     int (*parse_addr)(krb5_context, const char*, krb5_address *);
52     int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);
53     int (*free_addr)(krb5_context, krb5_address*);
54     int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);
55     int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long, 
56                                      krb5_address*, krb5_address*);
57 };
58
59 /*
60  * AF_INET - aka IPv4 implementation
61  */
62
63 static krb5_error_code
64 ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
65 {
66     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
67     unsigned char buf[4];
68
69     a->addr_type = KRB5_ADDRESS_INET;
70     memcpy (buf, &sin4->sin_addr, 4);
71     return krb5_data_copy(&a->address, buf, 4);
72 }
73
74 static krb5_error_code
75 ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)
76 {
77     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
78
79     *port = sin4->sin_port;
80     return 0;
81 }
82
83 static void
84 ipv4_addr2sockaddr (const krb5_address *a,
85                     struct sockaddr *sa,
86                     krb5_socklen_t *sa_size,
87                     int port)
88 {
89     struct sockaddr_in tmp;
90
91     memset (&tmp, 0, sizeof(tmp));
92     tmp.sin_family = AF_INET;
93     memcpy (&tmp.sin_addr, a->address.data, 4);
94     tmp.sin_port = port;
95     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
96     *sa_size = sizeof(tmp);
97 }
98
99 static void
100 ipv4_h_addr2sockaddr(const char *addr,
101                      struct sockaddr *sa,
102                      krb5_socklen_t *sa_size,
103                      int port)
104 {
105     struct sockaddr_in tmp;
106
107     memset (&tmp, 0, sizeof(tmp));
108     tmp.sin_family = AF_INET;
109     tmp.sin_port   = port;
110     tmp.sin_addr   = *((const struct in_addr *)addr);
111     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
112     *sa_size = sizeof(tmp);
113 }
114
115 static krb5_error_code
116 ipv4_h_addr2addr (const char *addr,
117                   krb5_address *a)
118 {
119     unsigned char buf[4];
120
121     a->addr_type = KRB5_ADDRESS_INET;
122     memcpy(buf, addr, 4);
123     return krb5_data_copy(&a->address, buf, 4);
124 }
125
126 /*
127  * Are there any addresses that should be considered `uninteresting'?
128  */
129
130 static krb5_boolean
131 ipv4_uninteresting (const struct sockaddr *sa)
132 {
133     const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
134
135     if (sin4->sin_addr.s_addr == INADDR_ANY)
136         return TRUE;
137
138     return FALSE;
139 }
140
141 static void
142 ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
143 {
144     struct sockaddr_in tmp;
145
146     memset (&tmp, 0, sizeof(tmp));
147     tmp.sin_family = AF_INET;
148     tmp.sin_port   = port;
149     tmp.sin_addr.s_addr = INADDR_ANY;
150     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
151     *sa_size = sizeof(tmp);
152 }
153
154 static int
155 ipv4_print_addr (const krb5_address *addr, char *str, size_t len)
156 {
157     struct in_addr ia;
158
159     memcpy (&ia, addr->address.data, 4);
160
161     return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));
162 }
163
164 static int
165 ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)
166 {
167     const char *p;
168     struct in_addr a;
169
170     p = strchr(address, ':');
171     if(p) {
172         p++;
173         if(strncasecmp(address, "ip:", p - address) != 0 &&
174            strncasecmp(address, "ip4:", p - address) != 0 &&
175            strncasecmp(address, "ipv4:", p - address) != 0 &&
176            strncasecmp(address, "inet:", p - address) != 0)
177             return -1;
178     } else
179         p = address;
180 #ifdef HAVE_INET_ATON
181     if(inet_aton(p, &a) == 0)
182         return -1;
183 #elif defined(HAVE_INET_ADDR)
184     a.s_addr = inet_addr(p);
185     if(a.s_addr == INADDR_NONE)
186         return -1;
187 #else
188     return -1;
189 #endif
190     addr->addr_type = KRB5_ADDRESS_INET;
191     if(krb5_data_alloc(&addr->address, 4) != 0)
192         return -1;
193     _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);
194     return 0;
195 }
196
197 static int
198 ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,
199                    unsigned long len, krb5_address *low, krb5_address *high)
200 {
201     unsigned long ia;
202     uint32_t l, h, m = 0xffffffff;
203
204     if (len > 32) {
205         krb5_set_error_string(context, "IPv4 prefix too large (%ld)", len);
206         return KRB5_PROG_ATYPE_NOSUPP;
207     }
208     m = m << (32 - len);
209
210     _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);
211
212     l = ia & m;
213     h = l | ~m;
214
215     low->addr_type = KRB5_ADDRESS_INET;
216     if(krb5_data_alloc(&low->address, 4) != 0)
217         return -1;
218     _krb5_put_int(low->address.data, l, low->address.length);
219
220     high->addr_type = KRB5_ADDRESS_INET;
221     if(krb5_data_alloc(&high->address, 4) != 0) {
222         krb5_free_address(context, low);
223         return -1;
224     }
225     _krb5_put_int(high->address.data, h, high->address.length);
226
227     return 0;
228 }
229
230
231 /*
232  * AF_INET6 - aka IPv6 implementation
233  */
234
235 #ifdef HAVE_IPV6
236
237 static krb5_error_code
238 ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
239 {
240     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
241
242     if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
243         unsigned char buf[4];
244
245         a->addr_type      = KRB5_ADDRESS_INET;
246 #ifndef IN6_ADDR_V6_TO_V4
247 #ifdef IN6_EXTRACT_V4ADDR
248 #define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))
249 #else
250 #define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])
251 #endif
252 #endif
253         memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);
254         return krb5_data_copy(&a->address, buf, 4);
255     } else {
256         a->addr_type = KRB5_ADDRESS_INET6;
257         return krb5_data_copy(&a->address,
258                               &sin6->sin6_addr,
259                               sizeof(sin6->sin6_addr));
260     }
261 }
262
263 static krb5_error_code
264 ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)
265 {
266     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
267
268     *port = sin6->sin6_port;
269     return 0;
270 }
271
272 static void
273 ipv6_addr2sockaddr (const krb5_address *a,
274                     struct sockaddr *sa,
275                     krb5_socklen_t *sa_size,
276                     int port)
277 {
278     struct sockaddr_in6 tmp;
279
280     memset (&tmp, 0, sizeof(tmp));
281     tmp.sin6_family = AF_INET6;
282     memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));
283     tmp.sin6_port = port;
284     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
285     *sa_size = sizeof(tmp);
286 }
287
288 static void
289 ipv6_h_addr2sockaddr(const char *addr,
290                      struct sockaddr *sa,
291                      krb5_socklen_t *sa_size,
292                      int port)
293 {
294     struct sockaddr_in6 tmp;
295
296     memset (&tmp, 0, sizeof(tmp));
297     tmp.sin6_family = AF_INET6;
298     tmp.sin6_port   = port;
299     tmp.sin6_addr   = *((const struct in6_addr *)addr);
300     memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
301     *sa_size = sizeof(tmp);
302 }
303
304 static krb5_error_code
305 ipv6_h_addr2addr (const char *addr,
306                   krb5_address *a)
307 {
308     a->addr_type = KRB5_ADDRESS_INET6;
309     return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));
310 }
311
312 /*
313  * 
314  */
315
316 static krb5_boolean
317 ipv6_uninteresting (const struct sockaddr *sa)
318 {
319     const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
320     const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
321     
322     return
323         IN6_IS_ADDR_LINKLOCAL(in6)
324         || IN6_IS_ADDR_V4COMPAT(in6);
325 }
326
327 static void
328 ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
329 {
330     struct sockaddr_in6 tmp;
331
332     memset (&tmp, 0, sizeof(tmp));
333     tmp.sin6_family = AF_INET6;
334     tmp.sin6_port   = port;
335     tmp.sin6_addr   = in6addr_any;
336     *sa_size = sizeof(tmp);
337 }
338
339 static int
340 ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
341 {
342     char buf[128], buf2[3];
343 #ifdef HAVE_INET_NTOP
344     if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
345 #endif
346         {
347             /* XXX this is pretty ugly, but better than abort() */
348             int i;
349             unsigned char *p = addr->address.data;
350             buf[0] = '\0';
351             for(i = 0; i < addr->address.length; i++) {
352                 snprintf(buf2, sizeof(buf2), "%02x", p[i]);
353                 if(i > 0 && (i & 1) == 0)
354                     strlcat(buf, ":", sizeof(buf));
355                 strlcat(buf, buf2, sizeof(buf));
356             }
357         }
358     return snprintf(str, len, "IPv6:%s", buf);
359 }
360
361 static int
362 ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
363 {
364     int ret;
365     struct in6_addr in6;
366     const char *p;
367
368     p = strchr(address, ':');
369     if(p) {
370         p++;
371         if(strncasecmp(address, "ip6:", p - address) == 0 ||
372            strncasecmp(address, "ipv6:", p - address) == 0 ||
373            strncasecmp(address, "inet6:", p - address) == 0)
374             address = p;
375     }
376
377     ret = inet_pton(AF_INET6, address, &in6.s6_addr);
378     if(ret == 1) {
379         addr->addr_type = KRB5_ADDRESS_INET6;
380         ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
381         if (ret)
382             return -1;
383         memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
384         return 0;
385     }
386     return -1;
387 }
388
389 static int
390 ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
391                    unsigned long len, krb5_address *low, krb5_address *high)
392 {
393     struct in6_addr addr, laddr, haddr;
394     uint32_t m;
395     int i, sub_len;
396
397     if (len > 128) {
398         krb5_set_error_string(context, "IPv6 prefix too large (%ld)", len);
399         return KRB5_PROG_ATYPE_NOSUPP;
400     }
401
402     if (inaddr->address.length != sizeof(addr)) {
403         krb5_set_error_string(context, "IPv6 addr bad length");
404         return KRB5_PROG_ATYPE_NOSUPP;
405     }
406
407     memcpy(&addr, inaddr->address.data, inaddr->address.length);
408
409     for (i = 0; i < 16; i++) {
410         sub_len = min(8, len);
411
412         m = 0xff << (8 - sub_len);
413         
414         laddr.s6_addr[i] = addr.s6_addr[i] & m;
415         haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;
416
417         if (len > 8)
418             len -= 8;
419         else
420             len = 0;
421     }
422
423     low->addr_type = KRB5_ADDRESS_INET6;
424     if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)
425         return -1;
426     memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));
427
428     high->addr_type = KRB5_ADDRESS_INET6;
429     if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {
430         krb5_free_address(context, low);
431         return -1;
432     }
433     memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));
434
435     return 0;
436 }
437
438 #endif /* IPv6 */
439
440 /*
441  * table
442  */
443
444 #define KRB5_ADDRESS_ARANGE     (-100)
445
446 struct arange {
447     krb5_address low;
448     krb5_address high;
449 };
450
451 static int
452 arange_parse_addr (krb5_context context, 
453                    const char *address, krb5_address *addr)
454 {
455     char buf[1024], *p;
456     krb5_address low0, high0;
457     struct arange *a;
458     krb5_error_code ret;
459     
460     if(strncasecmp(address, "RANGE:", 6) != 0)
461         return -1;
462     
463     address += 6;
464
465     p = strrchr(address, '/');
466     if (p) {
467         krb5_addresses addrmask;
468         char *q;
469         long num;
470
471         if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))
472             return -1;
473         buf[p - address] = '\0';
474         ret = krb5_parse_address(context, buf, &addrmask);
475         if (ret)
476             return ret;
477         if(addrmask.len != 1) {
478             krb5_free_addresses(context, &addrmask);
479             return -1;
480         }
481         
482         address += p - address + 1;
483
484         num = strtol(address, &q, 10);
485         if (q == address || *q != '\0' || num < 0) {
486             krb5_free_addresses(context, &addrmask);
487             return -1;
488         }
489
490         ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,
491                                               &low0, &high0);
492         krb5_free_addresses(context, &addrmask);
493         if (ret)
494             return ret;
495
496     } else {
497         krb5_addresses low, high;
498         
499         strsep_copy(&address, "-", buf, sizeof(buf));
500         ret = krb5_parse_address(context, buf, &low);
501         if(ret)
502             return ret;
503         if(low.len != 1) {
504             krb5_free_addresses(context, &low);
505             return -1;
506         }
507         
508         strsep_copy(&address, "-", buf, sizeof(buf));
509         ret = krb5_parse_address(context, buf, &high);
510         if(ret) {
511             krb5_free_addresses(context, &low);
512             return ret;
513         }
514         
515         if(high.len != 1 && high.val[0].addr_type != low.val[0].addr_type) {
516             krb5_free_addresses(context, &low);
517             krb5_free_addresses(context, &high);
518             return -1;
519         }
520
521         ret = krb5_copy_address(context, &high.val[0], &high0);
522         if (ret == 0) {
523             ret = krb5_copy_address(context, &low.val[0], &low0);
524             if (ret)
525                 krb5_free_address(context, &high0);
526         }
527         krb5_free_addresses(context, &low);
528         krb5_free_addresses(context, &high);
529         if (ret)
530             return ret;
531     }
532
533     krb5_data_alloc(&addr->address, sizeof(*a));
534     addr->addr_type = KRB5_ADDRESS_ARANGE;
535     a = addr->address.data;
536
537     if(krb5_address_order(context, &low0, &high0) < 0) {
538         a->low = low0;
539         a->high = high0;
540     } else {
541         a->low = high0;
542         a->high = low0;
543     }
544     return 0;
545 }
546
547 static int
548 arange_free (krb5_context context, krb5_address *addr)
549 {
550     struct arange *a;
551     a = addr->address.data;
552     krb5_free_address(context, &a->low);
553     krb5_free_address(context, &a->high);
554     krb5_data_free(&addr->address);
555     return 0;
556 }
557
558
559 static int
560 arange_copy (krb5_context context, const krb5_address *inaddr, 
561              krb5_address *outaddr)
562 {
563     krb5_error_code ret;
564     struct arange *i, *o;
565
566     outaddr->addr_type = KRB5_ADDRESS_ARANGE;
567     ret = krb5_data_alloc(&outaddr->address, sizeof(*o));
568     if(ret)
569         return ret;
570     i = inaddr->address.data;
571     o = outaddr->address.data;
572     ret = krb5_copy_address(context, &i->low, &o->low);
573     if(ret) {
574         krb5_data_free(&outaddr->address);
575         return ret;
576     }
577     ret = krb5_copy_address(context, &i->high, &o->high);
578     if(ret) {
579         krb5_free_address(context, &o->low);
580         krb5_data_free(&outaddr->address);
581         return ret;
582     }
583     return 0;
584 }
585
586 static int
587 arange_print_addr (const krb5_address *addr, char *str, size_t len)
588 {
589     struct arange *a;
590     krb5_error_code ret;
591     size_t l, size, ret_len;
592
593     a = addr->address.data;
594
595     l = strlcpy(str, "RANGE:", len);
596     ret_len = l;
597     if (l > len)
598         l = len;
599     size = l;
600         
601     ret = krb5_print_address (&a->low, str + size, len - size, &l);
602     if (ret)
603         return ret;
604     ret_len += l;
605     if (len - size > l)
606         size += l;
607     else
608         size = len;
609
610     l = strlcat(str + size, "-", len - size);
611     ret_len += l;
612     if (len - size > l)
613         size += l;
614     else
615         size = len;
616
617     ret = krb5_print_address (&a->high, str + size, len - size, &l);
618     if (ret)
619         return ret;
620     ret_len += l;
621
622     return ret_len;
623 }
624
625 static int
626 arange_order_addr(krb5_context context, 
627                   const krb5_address *addr1, 
628                   const krb5_address *addr2)
629 {
630     int tmp1, tmp2, sign;
631     struct arange *a;
632     const krb5_address *a2;
633
634     if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {
635         a = addr1->address.data;
636         a2 = addr2;
637         sign = 1;
638     } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {
639         a = addr2->address.data;
640         a2 = addr1;
641         sign = -1;
642     } else
643         abort();
644         
645     if(a2->addr_type == KRB5_ADDRESS_ARANGE) {
646         struct arange *b = a2->address.data;
647         tmp1 = krb5_address_order(context, &a->low, &b->low);
648         if(tmp1 != 0)
649             return sign * tmp1;
650         return sign * krb5_address_order(context, &a->high, &b->high);
651     } else if(a2->addr_type == a->low.addr_type) {
652         tmp1 = krb5_address_order(context, &a->low, a2);
653         if(tmp1 > 0)
654             return sign;
655         tmp2 = krb5_address_order(context, &a->high, a2);
656         if(tmp2 < 0)
657             return -sign;
658         return 0;
659     } else {
660         return sign * (addr1->addr_type - addr2->addr_type);
661     }
662 }
663
664 static int
665 addrport_print_addr (const krb5_address *addr, char *str, size_t len)
666 {
667     krb5_error_code ret;
668     krb5_address addr1, addr2;
669     uint16_t port = 0;
670     size_t ret_len = 0, l, size = 0;
671     krb5_storage *sp;
672
673     sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
674     /* for totally obscure reasons, these are not in network byteorder */
675     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
676
677     krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
678     krb5_ret_address(sp, &addr1);
679
680     krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
681     krb5_ret_address(sp, &addr2);
682     krb5_storage_free(sp);
683     if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
684         unsigned long value;
685         _krb5_get_int(addr2.address.data, &value, 2);
686         port = value;
687     }
688     l = strlcpy(str, "ADDRPORT:", len);
689     ret_len += l;
690     if (len > l)
691         size += l;
692     else
693         size = len;
694
695     ret = krb5_print_address(&addr1, str + size, len - size, &l);
696     if (ret)
697         return ret;
698     ret_len += l;
699     if (len - size > l)
700         size += l;
701     else
702         size = len;
703
704     ret = snprintf(str + size, len - size, ",PORT=%u", port);
705     if (ret < 0)
706         return EINVAL;
707     ret_len += ret;
708     return ret_len;
709 }
710
711 static struct addr_operations at[] = {
712     {AF_INET,   KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),
713      ipv4_sockaddr2addr, 
714      ipv4_sockaddr2port,
715      ipv4_addr2sockaddr,
716      ipv4_h_addr2sockaddr,
717      ipv4_h_addr2addr,
718      ipv4_uninteresting, ipv4_anyaddr, ipv4_print_addr, ipv4_parse_addr,
719      NULL, NULL, NULL, ipv4_mask_boundary },
720 #ifdef HAVE_IPV6
721     {AF_INET6,  KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),
722      ipv6_sockaddr2addr, 
723      ipv6_sockaddr2port,
724      ipv6_addr2sockaddr,
725      ipv6_h_addr2sockaddr,
726      ipv6_h_addr2addr,
727      ipv6_uninteresting, ipv6_anyaddr, ipv6_print_addr, ipv6_parse_addr,
728      NULL, NULL, NULL, ipv6_mask_boundary } ,
729 #endif
730     {KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,
731      NULL, NULL, NULL, NULL, NULL, 
732      NULL, NULL, addrport_print_addr, NULL, NULL, NULL, NULL },
733     /* fake address type */
734     {KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),
735      NULL, NULL, NULL, NULL, NULL, NULL, NULL,
736      arange_print_addr, arange_parse_addr, 
737      arange_order_addr, arange_free, arange_copy }
738 };
739
740 static int num_addrs = sizeof(at) / sizeof(at[0]);
741
742 static size_t max_sockaddr_size = 0;
743
744 /*
745  * generic functions
746  */
747
748 static struct addr_operations *
749 find_af(int af)
750 {
751     struct addr_operations *a;
752
753     for (a = at; a < at + num_addrs; ++a)
754         if (af == a->af)
755             return a;
756     return NULL;
757 }
758
759 static struct addr_operations *
760 find_atype(int atype)
761 {
762     struct addr_operations *a;
763
764     for (a = at; a < at + num_addrs; ++a)
765         if (atype == a->atype)
766             return a;
767     return NULL;
768 }
769
770 krb5_error_code KRB5_LIB_FUNCTION
771 krb5_sockaddr2address (krb5_context context,
772                        const struct sockaddr *sa, krb5_address *addr)
773 {
774     struct addr_operations *a = find_af(sa->sa_family);
775     if (a == NULL) {
776         krb5_set_error_string (context, "Address family %d not supported",
777                                sa->sa_family);
778         return KRB5_PROG_ATYPE_NOSUPP;
779     }
780     return (*a->sockaddr2addr)(sa, addr);
781 }
782
783 krb5_error_code KRB5_LIB_FUNCTION
784 krb5_sockaddr2port (krb5_context context,
785                     const struct sockaddr *sa, int16_t *port)
786 {
787     struct addr_operations *a = find_af(sa->sa_family);
788     if (a == NULL) {
789         krb5_set_error_string (context, "Address family %d not supported",
790                                sa->sa_family);
791         return KRB5_PROG_ATYPE_NOSUPP;
792     }
793     return (*a->sockaddr2port)(sa, port);
794 }
795
796 krb5_error_code KRB5_LIB_FUNCTION
797 krb5_addr2sockaddr (krb5_context context,
798                     const krb5_address *addr,
799                     struct sockaddr *sa,
800                     krb5_socklen_t *sa_size,
801                     int port)
802 {
803     struct addr_operations *a = find_atype(addr->addr_type);
804
805     if (a == NULL) {
806         krb5_set_error_string (context, "Address type %d not supported",
807                                addr->addr_type);
808         return KRB5_PROG_ATYPE_NOSUPP;
809     }
810     if (a->addr2sockaddr == NULL) {
811         krb5_set_error_string (context, "Can't convert address type %d to sockaddr",
812                                addr->addr_type);
813         return KRB5_PROG_ATYPE_NOSUPP;
814     }
815     (*a->addr2sockaddr)(addr, sa, sa_size, port);
816     return 0;
817 }
818
819 size_t KRB5_LIB_FUNCTION
820 krb5_max_sockaddr_size (void)
821 {
822     if (max_sockaddr_size == 0) {
823         struct addr_operations *a;
824
825         for(a = at; a < at + num_addrs; ++a)
826             max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
827     }
828     return max_sockaddr_size;
829 }
830
831 krb5_boolean KRB5_LIB_FUNCTION
832 krb5_sockaddr_uninteresting(const struct sockaddr *sa)
833 {
834     struct addr_operations *a = find_af(sa->sa_family);
835     if (a == NULL || a->uninteresting == NULL)
836         return TRUE;
837     return (*a->uninteresting)(sa);
838 }
839
840 krb5_error_code KRB5_LIB_FUNCTION
841 krb5_h_addr2sockaddr (krb5_context context,
842                       int af,
843                       const char *addr, struct sockaddr *sa,
844                       krb5_socklen_t *sa_size,
845                       int port)
846 {
847     struct addr_operations *a = find_af(af);
848     if (a == NULL) {
849         krb5_set_error_string (context, "Address family %d not supported", af);
850         return KRB5_PROG_ATYPE_NOSUPP;
851     }
852     (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
853     return 0;
854 }
855
856 krb5_error_code KRB5_LIB_FUNCTION
857 krb5_h_addr2addr (krb5_context context,
858                   int af,
859                   const char *haddr, krb5_address *addr)
860 {
861     struct addr_operations *a = find_af(af);
862     if (a == NULL) {
863         krb5_set_error_string (context, "Address family %d not supported", af);
864         return KRB5_PROG_ATYPE_NOSUPP;
865     }
866     return (*a->h_addr2addr)(haddr, addr);
867 }
868
869 krb5_error_code KRB5_LIB_FUNCTION
870 krb5_anyaddr (krb5_context context,
871               int af,
872               struct sockaddr *sa,
873               krb5_socklen_t *sa_size,
874               int port)
875 {
876     struct addr_operations *a = find_af (af);
877
878     if (a == NULL) {
879         krb5_set_error_string (context, "Address family %d not supported", af);
880         return KRB5_PROG_ATYPE_NOSUPP;
881     }
882
883     (*a->anyaddr)(sa, sa_size, port);
884     return 0;
885 }
886
887 krb5_error_code KRB5_LIB_FUNCTION
888 krb5_print_address (const krb5_address *addr, 
889                     char *str, size_t len, size_t *ret_len)
890 {
891     struct addr_operations *a = find_atype(addr->addr_type);
892     int ret;
893
894     if (a == NULL || a->print_addr == NULL) {
895         char *s;
896         int l;
897         int i;
898
899         s = str;
900         l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
901         if (l < 0 || l >= len)
902             return EINVAL;
903         s += l;
904         len -= l;
905         for(i = 0; i < addr->address.length; i++) {
906             l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
907             if (l < 0 || l >= len)
908                 return EINVAL;
909             len -= l;
910             s += l;
911         }
912         if(ret_len != NULL)
913             *ret_len = s - str;
914         return 0;
915     }
916     ret = (*a->print_addr)(addr, str, len);
917     if (ret < 0)
918         return EINVAL;
919     if(ret_len != NULL)
920         *ret_len = ret;
921     return 0;
922 }
923
924 krb5_error_code KRB5_LIB_FUNCTION
925 krb5_parse_address(krb5_context context,
926                    const char *string,
927                    krb5_addresses *addresses)
928 {
929     int i, n;
930     struct addrinfo *ai, *a;
931     int error;
932     int save_errno;
933
934     addresses->len = 0;
935     addresses->val = NULL;
936
937     for(i = 0; i < num_addrs; i++) {
938         if(at[i].parse_addr) {
939             krb5_address addr;
940             if((*at[i].parse_addr)(context, string, &addr) == 0) {
941                 ALLOC_SEQ(addresses, 1);
942                 if (addresses->val == NULL) {
943                     krb5_set_error_string(context, "malloc: out of memory");
944                     return ENOMEM;
945                 }
946                 addresses->val[0] = addr;
947                 return 0;
948             }
949         }
950     }
951
952     error = getaddrinfo (string, NULL, NULL, &ai);
953     if (error) {
954         save_errno = errno;
955         krb5_set_error_string (context, "%s: %s", string, gai_strerror(error));
956         return krb5_eai_to_heim_errno(error, save_errno);
957     }
958     
959     n = 0;
960     for (a = ai; a != NULL; a = a->ai_next)
961         ++n;
962
963     ALLOC_SEQ(addresses, n);
964     if (addresses->val == NULL) {
965         krb5_set_error_string(context, "malloc: out of memory");
966         freeaddrinfo(ai);
967         return ENOMEM;
968     }
969
970     addresses->len = 0;
971     for (a = ai, i = 0; a != NULL; a = a->ai_next) {
972         if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))
973             continue;
974         if(krb5_address_search(context, &addresses->val[i], addresses))
975             continue;
976         addresses->len = i;
977         i++;
978     }
979     freeaddrinfo (ai);
980     return 0;
981 }
982
983 int KRB5_LIB_FUNCTION
984 krb5_address_order(krb5_context context,
985                    const krb5_address *addr1,
986                    const krb5_address *addr2)
987 {
988     /* this sucks; what if both addresses have order functions, which
989        should we call? this works for now, though */
990     struct addr_operations *a;
991     a = find_atype(addr1->addr_type); 
992     if(a == NULL) {
993         krb5_set_error_string (context, "Address family %d not supported", 
994                                addr1->addr_type);
995         return KRB5_PROG_ATYPE_NOSUPP;
996     }
997     if(a->order_addr != NULL) 
998         return (*a->order_addr)(context, addr1, addr2); 
999     a = find_atype(addr2->addr_type); 
1000     if(a == NULL) {
1001         krb5_set_error_string (context, "Address family %d not supported", 
1002                                addr2->addr_type);
1003         return KRB5_PROG_ATYPE_NOSUPP;
1004     }
1005     if(a->order_addr != NULL) 
1006         return (*a->order_addr)(context, addr1, addr2);
1007
1008     if(addr1->addr_type != addr2->addr_type)
1009         return addr1->addr_type - addr2->addr_type;
1010     if(addr1->address.length != addr2->address.length)
1011         return addr1->address.length - addr2->address.length;
1012     return memcmp (addr1->address.data,
1013                    addr2->address.data,
1014                    addr1->address.length);
1015 }
1016
1017 krb5_boolean KRB5_LIB_FUNCTION
1018 krb5_address_compare(krb5_context context,
1019                      const krb5_address *addr1,
1020                      const krb5_address *addr2)
1021 {
1022     return krb5_address_order (context, addr1, addr2) == 0;
1023 }
1024
1025 krb5_boolean KRB5_LIB_FUNCTION
1026 krb5_address_search(krb5_context context,
1027                     const krb5_address *addr,
1028                     const krb5_addresses *addrlist)
1029 {
1030     int i;
1031
1032     for (i = 0; i < addrlist->len; ++i)
1033         if (krb5_address_compare (context, addr, &addrlist->val[i]))
1034             return TRUE;
1035     return FALSE;
1036 }
1037
1038 krb5_error_code KRB5_LIB_FUNCTION
1039 krb5_free_address(krb5_context context,
1040                   krb5_address *address)
1041 {
1042     struct addr_operations *a = find_atype (address->addr_type);
1043     if(a != NULL && a->free_addr != NULL)
1044         return (*a->free_addr)(context, address);
1045     krb5_data_free (&address->address);
1046     memset(address, 0, sizeof(*address));
1047     return 0;
1048 }
1049
1050 krb5_error_code KRB5_LIB_FUNCTION
1051 krb5_free_addresses(krb5_context context,
1052                     krb5_addresses *addresses)
1053 {
1054     int i;
1055     for(i = 0; i < addresses->len; i++)
1056         krb5_free_address(context, &addresses->val[i]);
1057     free(addresses->val);
1058     addresses->len = 0;
1059     addresses->val = NULL;
1060     return 0;
1061 }
1062
1063 krb5_error_code KRB5_LIB_FUNCTION
1064 krb5_copy_address(krb5_context context,
1065                   const krb5_address *inaddr,
1066                   krb5_address *outaddr)
1067 {
1068     struct addr_operations *a = find_af (inaddr->addr_type);
1069     if(a != NULL && a->copy_addr != NULL)
1070         return (*a->copy_addr)(context, inaddr, outaddr);
1071     return copy_HostAddress(inaddr, outaddr);
1072 }
1073
1074 krb5_error_code KRB5_LIB_FUNCTION
1075 krb5_copy_addresses(krb5_context context,
1076                     const krb5_addresses *inaddr,
1077                     krb5_addresses *outaddr)
1078 {
1079     int i;
1080     ALLOC_SEQ(outaddr, inaddr->len);
1081     if(inaddr->len > 0 && outaddr->val == NULL)
1082         return ENOMEM;
1083     for(i = 0; i < inaddr->len; i++)
1084         krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
1085     return 0;
1086 }
1087
1088 krb5_error_code KRB5_LIB_FUNCTION
1089 krb5_append_addresses(krb5_context context,
1090                       krb5_addresses *dest,
1091                       const krb5_addresses *source)
1092 {
1093     krb5_address *tmp;
1094     krb5_error_code ret;
1095     int i;
1096     if(source->len > 0) {
1097         tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
1098         if(tmp == NULL) {
1099             krb5_set_error_string(context, "realloc: out of memory");
1100             return ENOMEM;
1101         }
1102         dest->val = tmp;
1103         for(i = 0; i < source->len; i++) {
1104             /* skip duplicates */
1105             if(krb5_address_search(context, &source->val[i], dest))
1106                 continue;
1107             ret = krb5_copy_address(context, 
1108                                     &source->val[i], 
1109                                     &dest->val[dest->len]);
1110             if(ret)
1111                 return ret;
1112             dest->len++;
1113         }
1114     }
1115     return 0;
1116 }
1117
1118 /*
1119  * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
1120  */
1121
1122 krb5_error_code KRB5_LIB_FUNCTION
1123 krb5_make_addrport (krb5_context context,
1124                     krb5_address **res, const krb5_address *addr, int16_t port)
1125 {
1126     krb5_error_code ret;
1127     size_t len = addr->address.length + 2 + 4 * 4;
1128     u_char *p;
1129
1130     *res = malloc (sizeof(**res));
1131     if (*res == NULL) {
1132         krb5_set_error_string(context, "malloc: out of memory");
1133         return ENOMEM;
1134     }
1135     (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
1136     ret = krb5_data_alloc (&(*res)->address, len);
1137     if (ret) {
1138         krb5_set_error_string(context, "malloc: out of memory");
1139         free (*res);
1140         *res = NULL;
1141         return ret;
1142     }
1143     p = (*res)->address.data;
1144     *p++ = 0;
1145     *p++ = 0;
1146     *p++ = (addr->addr_type     ) & 0xFF;
1147     *p++ = (addr->addr_type >> 8) & 0xFF;
1148
1149     *p++ = (addr->address.length      ) & 0xFF;
1150     *p++ = (addr->address.length >>  8) & 0xFF;
1151     *p++ = (addr->address.length >> 16) & 0xFF;
1152     *p++ = (addr->address.length >> 24) & 0xFF;
1153
1154     memcpy (p, addr->address.data, addr->address.length);
1155     p += addr->address.length;
1156
1157     *p++ = 0;
1158     *p++ = 0;
1159     *p++ = (KRB5_ADDRESS_IPPORT     ) & 0xFF;
1160     *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
1161
1162     *p++ = (2      ) & 0xFF;
1163     *p++ = (2 >>  8) & 0xFF;
1164     *p++ = (2 >> 16) & 0xFF;
1165     *p++ = (2 >> 24) & 0xFF;
1166
1167     memcpy (p, &port, 2);
1168     p += 2;
1169
1170     return 0;
1171 }
1172
1173 /*
1174  * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
1175  * them in `low' and `high'.
1176  */
1177
1178 krb5_error_code KRB5_LIB_FUNCTION
1179 krb5_address_prefixlen_boundary(krb5_context context,
1180                                 const krb5_address *inaddr,
1181                                 unsigned long prefixlen,
1182                                 krb5_address *low,
1183                                 krb5_address *high)
1184 {
1185     struct addr_operations *a = find_atype (inaddr->addr_type);
1186     if(a != NULL && a->mask_boundary != NULL)
1187         return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
1188     krb5_set_error_string(context, "Address family %d doesn't support "
1189                           "address mask operation", inaddr->addr_type);
1190     return KRB5_PROG_ATYPE_NOSUPP;
1191 }