Merge from vendor branch HEIMDAL:
[dragonfly.git] / usr.sbin / ppp / ncpaddr.c
1 /*-
2  * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.sbin/ppp/ncpaddr.c,v 1.10.2.3 2003/04/29 16:05:55 ume Exp $
27  * $DragonFly: src/usr.sbin/ppp/ncpaddr.c,v 1.2 2003/06/17 04:30:00 dillon Exp $
28  */
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #ifdef __OpenBSD__
33 #include <net/if_types.h>
34 #include <net/route.h>
35 #endif
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h>
38 #include <netinet/ip.h>
39 #include <arpa/inet.h>
40 #include <sys/un.h>
41
42 #include <netdb.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <termios.h>
47
48 #include "log.h"
49 #include "ncpaddr.h"
50 #include "timer.h"
51 #include "fsm.h"
52 #include "defs.h"
53 #include "slcompress.h"
54 #include "iplist.h"
55 #include "throughput.h"
56 #include "mbuf.h"
57 #include "ipcp.h"
58 #include "descriptor.h"
59 #include "layer.h"
60 #include "lqr.h"
61 #include "hdlc.h"
62 #include "lcp.h"
63 #include "ccp.h"
64 #include "link.h"
65 #include "mp.h"
66 #include "ipv6cp.h"
67 #include "ncp.h"
68
69
70 #define ncprange_ip4addr        u.ip4.ipaddr
71 #define ncprange_ip4mask        u.ip4.mask
72 #define ncprange_ip4width       u.ip4.width
73 #define ncpaddr_ip4addr         u.ip4addr
74 #ifndef NOINET6
75 #define ncprange_ip6addr        u.ip6.ipaddr
76 #define ncprange_ip6width       u.ip6.width
77 #define ncpaddr_ip6addr         u.ip6addr
78 #endif
79
80 #define NCP_ASCIIBUFFERSIZE     52
81
82 static struct in_addr
83 bits2mask4(int bits)
84 {
85   struct in_addr result;
86   u_int32_t bit = 0x80000000;
87
88   result.s_addr = 0;
89
90   while (bits) {
91     result.s_addr |= bit;
92     bit >>= 1;
93     bits--;
94   }
95
96   result.s_addr = htonl(result.s_addr);
97   return result;
98 }
99
100 static int
101 mask42bits(struct in_addr mask)
102 {
103   u_int32_t msk = ntohl(mask.s_addr);
104   u_int32_t tst;
105   int ret;
106
107   for (ret = 32, tst = 1; tst; ret--, tst <<= 1)
108     if (msk & tst)
109       break;
110
111   for (tst <<= 1; tst; tst <<= 1)
112     if (!(msk & tst))
113       break;
114
115   return tst ? -1 : ret;
116 }
117
118 #ifndef NOINET6
119 static struct in6_addr
120 bits2mask6(int bits)
121 {
122   struct in6_addr result;
123   u_int32_t bit = 0x80;
124   u_char *c = result.s6_addr;
125
126   memset(&result, '\0', sizeof result);
127
128   while (bits) {
129     if (bit == 0) {
130       bit = 0x80;
131       c++;
132     }
133     *c |= bit;
134     bit >>= 1;
135     bits--;
136   }
137
138   return result;
139 }
140
141 static int
142 mask62bits(const struct in6_addr *mask)
143 {
144   const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
145   const u_char *c, *p, *end;
146   int masklen;
147
148   p = (const u_char *)mask;
149   for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
150     masklen += 8;
151
152   if (p < end) {
153     for (c = masks; c < masks + sizeof masks; c++)
154       if (*c == *p) {
155         masklen += c - masks;
156         break;
157       }
158   }
159
160   return masklen;
161 }
162
163 static void
164 adjust_linklocal(struct sockaddr_in6 *sin6)
165 {
166     /* XXX: ?????!?!?!!!!!  This is horrible ! */
167 #if 0
168     /*
169      * The kernel does not understand sin6_scope_id for routing at this moment.
170      * We should rather keep the embedded ID.
171      * jinmei@kame.net, 20011026
172      */
173     if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
174         IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) {
175       sin6->sin6_scope_id =
176         ntohs(*(u_short *)&sin6->sin6_addr.s6_addr[2]);
177       *(u_short *)&sin6->sin6_addr.s6_addr[2] = 0;
178     }
179 #endif
180 }
181 #endif
182
183 void
184 ncpaddr_init(struct ncpaddr *addr)
185 {
186   addr->ncpaddr_family = AF_UNSPEC;
187 }
188
189 int
190 ncpaddr_isset(const struct ncpaddr *addr)
191 {
192   return addr->ncpaddr_family != AF_UNSPEC;
193 }
194
195 int
196 ncpaddr_isdefault(const struct ncpaddr *addr)
197 {
198   switch (addr->ncpaddr_family) {
199   case AF_INET:
200     if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
201       return 1;
202     break;
203
204 #ifndef NOINET6
205   case AF_INET6:
206     if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
207       return 1;
208     break;
209 #endif
210   }
211
212   return 0;
213 }
214
215 int
216 ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
217 {
218   if (addr->ncpaddr_family != cmp->ncpaddr_family)
219     return 0;
220
221   switch (addr->ncpaddr_family) {
222   case AF_INET:
223     return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
224
225 #ifndef NOINET6
226   case AF_INET6:
227     return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
228                    sizeof addr->ncpaddr_ip6addr);
229 #endif
230
231   case AF_UNSPEC:
232     return 1;
233   }
234
235   return 0;
236 }
237
238 void
239 ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
240 {
241   switch (from->ncpaddr_family) {
242   case AF_INET:
243     addr->ncpaddr_family = AF_INET;
244     addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
245     break;
246 #ifndef NOINET6
247   case AF_INET6:
248     addr->ncpaddr_family = AF_INET6;
249     addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
250     break;
251 #endif
252   default:
253     addr->ncpaddr_family = AF_UNSPEC;
254   }
255 }
256
257 void
258 ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
259 {
260   addr->ncpaddr_family = AF_INET;
261   addr->ncpaddr_ip4addr.s_addr = ip;
262 }
263
264 int
265 ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
266 {
267   if (addr->ncpaddr_family != AF_INET)
268     return 0;
269   *ip = addr->ncpaddr_ip4addr.s_addr;
270   return 1;
271 }
272
273 void
274 ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
275 {
276   addr->ncpaddr_family = AF_INET;
277   addr->ncpaddr_ip4addr = ip;
278 }
279
280 int
281 ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
282 {
283   if (addr->ncpaddr_family != AF_INET)
284     return 0;
285   *ip = addr->ncpaddr_ip4addr;
286   return 1;
287 }
288
289 #ifndef NOINET6
290 void
291 ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
292 {
293   addr->ncpaddr_family = AF_INET6;
294   addr->ncpaddr_ip6addr = *ip6;
295 }
296
297 int
298 ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
299 {
300   if (addr->ncpaddr_family != AF_INET6)
301     return 0;
302   *ip6 = addr->ncpaddr_ip6addr;
303   return 1;
304 }
305 #endif
306
307 void
308 ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
309 {
310   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
311 #ifndef NOINET6
312   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
313 #endif
314
315   memset(host, '\0', sizeof(*host));
316
317   switch (addr->ncpaddr_family) {
318   case AF_INET:
319     host4->sin_family = AF_INET;
320     host4->sin_len = sizeof(*host4);
321     host4->sin_addr = addr->ncpaddr_ip4addr;
322     break;
323
324 #ifndef NOINET6
325   case AF_INET6:
326     host6->sin6_family = AF_INET6;
327     host6->sin6_len = sizeof(*host6);
328     host6->sin6_addr = addr->ncpaddr_ip6addr;
329     break;
330 #endif
331
332   default:
333     host->ss_family = AF_UNSPEC;
334     break;
335   }
336 }
337
338 void
339 ncpaddr_setsa(struct ncpaddr *addr, const struct sockaddr *host)
340 {
341   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
342 #ifndef NOINET6
343   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
344 #endif
345
346   switch (host->sa_family) {
347   case AF_INET:
348     addr->ncpaddr_family = AF_INET;
349     addr->ncpaddr_ip4addr = host4->sin_addr;
350     break;
351
352 #ifndef NOINET6
353   case AF_INET6:
354     if (IN6_IS_ADDR_V4MAPPED(&host6->sin6_addr)) {
355       addr->ncpaddr_family = AF_INET;
356       addr->ncpaddr_ip4addr.s_addr =
357         *(const u_int32_t *)(host6->sin6_addr.s6_addr + 12);
358     } else {
359       addr->ncpaddr_family = AF_INET6;
360       addr->ncpaddr_ip6addr = host6->sin6_addr;
361     }
362     break;
363 #endif
364
365   default:
366     addr->ncpaddr_family = AF_UNSPEC;
367   }
368 }
369
370 static char *
371 ncpaddr_ntowa(const struct ncpaddr *addr)
372 {
373   static char res[NCP_ASCIIBUFFERSIZE];
374 #ifndef NOINET6
375   struct sockaddr_in6 sin6;
376 #endif
377
378   switch (addr->ncpaddr_family) {
379   case AF_INET:
380     snprintf(res, sizeof res, "%s", inet_ntoa(addr->ncpaddr_ip4addr));
381     return res;
382
383 #ifndef NOINET6
384   case AF_INET6:
385     memset(&sin6, '\0', sizeof(sin6));
386     sin6.sin6_len = sizeof(sin6);
387     sin6.sin6_family = AF_INET6;
388     sin6.sin6_addr = addr->ncpaddr_ip6addr;
389     adjust_linklocal(&sin6);
390 #ifdef NI_WITHSCOPEID
391     if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
392                     NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
393 #else
394     if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
395                     NULL, 0, NI_NUMERICHOST) != 0)
396 #endif
397       break;
398
399     return res;
400 #endif
401   }
402
403   snprintf(res, sizeof res, "<AF_UNSPEC>");
404   return res;
405 }
406
407 const char *
408 ncpaddr_ntoa(const struct ncpaddr *addr)
409 {
410   return ncpaddr_ntowa(addr);
411 }
412
413
414 int
415 ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
416 {
417   struct ncprange range;
418
419   if (!ncprange_aton(&range, ncp, data))
420     return 0;
421
422   if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32) {
423     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
424     return 0;
425   }
426
427 #ifndef NOINET6
428   if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128) {
429     log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
430     return 0;
431   }
432 #endif
433
434   switch (range.ncprange_family) {
435   case AF_INET:
436     addr->ncpaddr_family = range.ncprange_family;
437     addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
438     return 1;
439
440 #ifndef NOINET6
441   case AF_INET6:
442     addr->ncpaddr_family = range.ncprange_family;
443     addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
444     return 1;
445 #endif
446   }
447
448   return 0;
449 }
450
451 void
452 ncprange_init(struct ncprange *range)
453 {
454   range->ncprange_family = AF_UNSPEC;
455 }
456
457 int
458 ncprange_isset(const struct ncprange *range)
459 {
460   return range->ncprange_family != AF_UNSPEC;
461 }
462
463 int
464 ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
465 {
466   if (range->ncprange_family != cmp->ncprange_family)
467     return 0;
468
469   switch (range->ncprange_family) {
470   case AF_INET:
471     if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
472       return 0;
473     return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
474
475 #ifndef NOINET6
476   case AF_INET6:
477     if (range->ncprange_ip6width != cmp->ncprange_ip6width)
478       return 0;
479     return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
480                    sizeof range->ncprange_ip6addr);
481 #endif
482
483   case AF_UNSPEC:
484     return 1;
485   }
486
487   return 0;
488 }
489
490 int
491 ncprange_isdefault(const struct ncprange *range)
492 {
493   switch (range->ncprange_family) {
494   case AF_INET:
495     if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
496       return 1;
497     break;
498
499 #ifndef NOINET6
500   case AF_INET6:
501     if (range->ncprange_ip6width == 0 &&
502         IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
503       return 1;
504     break;
505 #endif
506   }
507
508   return 0;
509 }
510
511 void
512 ncprange_setdefault(struct ncprange *range, int af)
513 {
514   memset(range, '\0', sizeof *range);
515   range->ncprange_family = af;
516 }
517
518 int
519 ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
520 {
521 #ifndef NOINET6
522   const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
523   const u_char *addrp, *rangep;
524   int bits;
525 #endif
526
527   if (range->ncprange_family != addr->ncpaddr_family)
528     return 0;
529
530   switch (range->ncprange_family) {
531   case AF_INET:
532     return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
533              range->ncprange_ip4mask.s_addr);
534
535 #ifndef NOINET6
536   case AF_INET6:
537     rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
538     addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
539
540     for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
541       if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
542         return 0;
543
544     return 1;
545 #endif
546   }
547
548   return 0;
549 }
550
551 int
552 ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
553 {
554   switch (range->ncprange_family) {
555   case AF_INET:
556     return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
557              range->ncprange_ip4mask.s_addr);
558   }
559
560   return 0;
561 }
562
563 void
564 ncprange_copy(struct ncprange *range, const struct ncprange *from)
565 {
566   switch (from->ncprange_family) {
567   case AF_INET:
568     range->ncprange_family = AF_INET;
569     range->ncprange_ip4addr = from->ncprange_ip4addr;
570     range->ncprange_ip4mask = from->ncprange_ip4mask;
571     range->ncprange_ip4width = from->ncprange_ip4width;
572     break;
573
574 #ifndef NOINET6
575   case AF_INET6:
576     range->ncprange_family = AF_INET6;
577     range->ncprange_ip6addr = from->ncprange_ip6addr;
578     range->ncprange_ip6width = from->ncprange_ip6width;
579     break;
580 #endif
581
582   default:
583     range->ncprange_family = AF_UNSPEC;
584   }
585 }
586
587 void
588 ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
589 {
590   ncprange_sethost(range, addr);
591   ncprange_setwidth(range, width);
592 }
593
594 void
595 ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
596 {
597   switch (from->ncpaddr_family) {
598   case AF_INET:
599     range->ncprange_family = AF_INET;
600     range->ncprange_ip4addr = from->ncpaddr_ip4addr;
601     if (from->ncpaddr_ip4addr.s_addr == INADDR_ANY) {
602       range->ncprange_ip4mask.s_addr = INADDR_ANY;
603       range->ncprange_ip4width = 0;
604     } else {
605       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
606       range->ncprange_ip4width = 32;
607     }
608     break;
609
610 #ifndef NOINET6
611   case AF_INET6:
612     range->ncprange_family = AF_INET6;
613     range->ncprange_ip6addr = from->ncpaddr_ip6addr;
614     range->ncprange_ip6width = 128;
615     break;
616 #endif
617
618   default:
619     range->ncprange_family = AF_UNSPEC;
620   }
621 }
622
623 int
624 ncprange_ishost(const struct ncprange *range)
625 {
626   switch (range->ncprange_family) {
627   case AF_INET:
628     return range->ncprange_ip4width == 32;
629 #ifndef NOINET6
630   case AF_INET6:
631     return range->ncprange_ip6width == 128;
632 #endif
633   }
634
635   return (0);
636 }
637
638 int
639 ncprange_setwidth(struct ncprange *range, int width)
640 {
641   switch (range->ncprange_family) {
642   case AF_INET:
643     if (width < 0 || width > 32)
644       break;
645     range->ncprange_ip4width = width;
646     range->ncprange_ip4mask = bits2mask4(width);
647     break;
648
649 #ifndef NOINET6
650   case AF_INET6:
651     if (width < 0 || width > 128)
652       break;
653     range->ncprange_ip6width = width;
654     break;
655 #endif
656
657   case AF_UNSPEC:
658     return 1;
659   }
660
661   return 0;
662 }
663
664 void
665 ncprange_setip4host(struct ncprange *range, struct in_addr from)
666 {
667   range->ncprange_family = AF_INET;
668   range->ncprange_ip4addr = from;
669   if (from.s_addr == INADDR_ANY) {
670     range->ncprange_ip4mask.s_addr = INADDR_ANY;
671     range->ncprange_ip4width = 0;
672   } else {
673     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
674     range->ncprange_ip4width = 32;
675   }
676 }
677
678 void
679 ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
680 {
681   range->ncprange_family = AF_INET;
682   range->ncprange_ip4addr = from;
683   range->ncprange_ip4mask = msk;
684   range->ncprange_ip4width = mask42bits(msk);
685 }
686
687
688 int
689 ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
690 {
691   if (range->ncprange_family != AF_INET)
692     return 0;
693   range->ncprange_ip4mask = mask;
694   range->ncprange_ip4width = mask42bits(mask);
695   return 1;
696 }
697
698 void
699 ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
700                const struct sockaddr *mask)
701 {
702   const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
703   const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
704 #ifndef NOINET6
705   const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
706   const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
707 #endif
708
709   switch (host->sa_family) {
710   case AF_INET:
711     range->ncprange_family = AF_INET;
712     range->ncprange_ip4addr = host4->sin_addr;
713     if (host4->sin_addr.s_addr == INADDR_ANY) {
714       range->ncprange_ip4mask.s_addr = INADDR_ANY;
715       range->ncprange_ip4width = 0;
716     } else if (mask4 && mask4->sin_family == AF_INET) {
717       range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
718       range->ncprange_ip4width = mask42bits(mask4->sin_addr);
719     } else {
720       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
721       range->ncprange_ip4width = 32;
722     }
723     break;
724
725 #ifndef NOINET6
726   case AF_INET6:
727     range->ncprange_family = AF_INET6;
728     range->ncprange_ip6addr = host6->sin6_addr;
729     if (IN6_IS_ADDR_UNSPECIFIED(&host6->sin6_addr))
730       range->ncprange_ip6width = 0;
731     else
732       range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
733     break;
734 #endif
735
736   default:
737     range->ncprange_family = AF_UNSPEC;
738   }
739 }
740
741 void
742 ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
743                struct sockaddr_storage *mask)
744 {
745   struct sockaddr_in *host4 = (struct sockaddr_in *)host;
746   struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
747 #ifndef NOINET6
748   struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
749   struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
750 #endif
751
752   memset(host, '\0', sizeof(*host));
753   if (mask)
754     memset(mask, '\0', sizeof(*mask));
755
756   switch (range->ncprange_family) {
757   case AF_INET:
758     host4->sin_family = AF_INET;
759     host4->sin_len = sizeof(*host4);
760     host4->sin_addr = range->ncprange_ip4addr;
761     if (mask4) {
762       mask4->sin_family = AF_INET;
763       mask4->sin_len = sizeof(*host4);
764       mask4->sin_addr = range->ncprange_ip4mask;
765     }
766     break;
767
768 #ifndef NOINET6
769   case AF_INET6:
770     host6->sin6_family = AF_INET6;
771     host6->sin6_len = sizeof(*host6);
772     host6->sin6_addr = range->ncprange_ip6addr;
773     if (mask6) {
774       mask6->sin6_family = AF_INET6;
775       mask6->sin6_len = sizeof(*host6);
776       mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
777     }
778     break;
779 #endif
780
781   default:
782     host->ss_family = AF_UNSPEC;
783     if (mask)
784       mask->ss_family = AF_UNSPEC;
785     break;
786   }
787 }
788
789 int
790 ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
791 {
792   switch (range->ncprange_family) {
793   case AF_INET:
794     addr->ncpaddr_family = AF_INET;
795     addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
796     return 1;
797 #ifndef NOINET6
798   case AF_INET6:
799     addr->ncpaddr_family = AF_INET6;
800     addr->ncpaddr_ip6addr =  range->ncprange_ip6addr;
801     return 1;
802 #endif
803   }
804
805   return 0;
806 }
807
808 int
809 ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
810 {
811   if (range->ncprange_family != AF_INET)
812     return 0;
813
814   *addr = range->ncprange_ip4addr;
815   return 1;
816 }
817
818 int
819 ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
820 {
821   switch (range->ncprange_family) {
822   case AF_INET:
823     *mask = range->ncprange_ip4mask;
824     return 1;
825   }
826
827   return 0;
828 }
829
830 int
831 ncprange_getwidth(const struct ncprange *range, int *width)
832 {
833   switch (range->ncprange_family) {
834   case AF_INET:
835     *width = range->ncprange_ip4width;
836     return 1;
837 #ifndef NOINET6
838   case AF_INET6:
839     *width = range->ncprange_ip6width;
840     return 1;
841 #endif
842   }
843
844   return 0;
845 }
846
847 const char *
848 ncprange_ntoa(const struct ncprange *range)
849 {
850   char *res;
851   struct ncpaddr addr;
852   int len;
853
854   if (!ncprange_getaddr(range, &addr))
855     return "<AF_UNSPEC>";
856
857   res = ncpaddr_ntowa(&addr);
858   len = strlen(res);
859   if (len >= NCP_ASCIIBUFFERSIZE - 1)
860     return res;
861
862   switch (range->ncprange_family) {
863   case AF_INET:
864     if (range->ncprange_ip4width == -1) {
865       /* A non-contiguous mask */
866       for (; len >= 3; res[len -= 2] = '\0')
867         if (strcmp(res + len - 2, ".0"))
868           break;
869       snprintf(res + len, sizeof res - len, "&0x%08lx",
870                (unsigned long)ntohl(range->ncprange_ip4mask.s_addr));
871     } else if (range->ncprange_ip4width < 32)
872       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
873
874     return res;
875
876 #ifndef NOINET6
877   case AF_INET6:
878     if (range->ncprange_ip6width != 128)
879       snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
880
881     return res;
882 #endif
883   }
884
885   return "<AF_UNSPEC>";
886 }
887
888 #ifndef NOINET6
889 int
890 ncprange_scopeid(const struct ncprange *range)
891 {
892   const struct in6_addr *sin6;
893   int scopeid = -1;
894
895   if (range->ncprange_family == AF_INET6) {
896     sin6 = &range->ncprange_ip6addr;
897     if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
898       if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
899         scopeid = -1;
900   }
901
902   return scopeid;
903 }
904 #endif
905
906 int
907 ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
908 {
909   int bits, len;
910   char *wp;
911   const char *cp;
912   char *s;
913
914   len = strcspn(data, "/");
915
916   if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
917     range->ncprange_family = AF_INET;
918     range->ncprange_ip4addr = ncp->ipcp.peer_ip;
919     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
920     range->ncprange_ip4width = 32;
921     return 1;
922 #ifndef NOINET6
923   } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
924     range->ncprange_family = AF_INET6;
925     range->ncprange_ip6addr = ncp->ipv6cp.hisaddr.ncpaddr_ip6addr;
926     range->ncprange_ip6width = 128;
927     return 1;
928 #endif
929   } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
930     range->ncprange_family = AF_INET;
931     range->ncprange_ip4addr = ncp->ipcp.my_ip;
932     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
933     range->ncprange_ip4width = 32;
934     return 1;
935 #ifndef NOINET6
936   } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
937     range->ncprange_family = AF_INET6;
938     range->ncprange_ip6addr = ncp->ipv6cp.myaddr.ncpaddr_ip6addr;
939     range->ncprange_ip6width = 128;
940     return 1;
941 #endif
942   } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
943     range->ncprange_family = AF_INET;
944     range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
945     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
946     range->ncprange_ip4width = 32;
947     return 1;
948   } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
949     range->ncprange_family = AF_INET;
950     range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
951     range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
952     range->ncprange_ip4width = 32;
953     return 1;
954   }
955
956   s = (char *)alloca(len + 1);
957   strncpy(s, data, len);
958   s[len] = '\0';
959   bits = -1;
960
961   if (data[len] != '\0') {
962     bits = strtol(data + len + 1, &wp, 0);
963     if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
964       log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
965       return 0;
966     }
967   }
968
969   if ((cp = strchr(data, ':')) == NULL) {
970     range->ncprange_family = AF_INET;
971
972     range->ncprange_ip4addr = GetIpAddr(s);
973
974     if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
975       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
976       return 0;
977     }
978
979     if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
980       range->ncprange_ip4mask.s_addr = INADDR_ANY;
981       range->ncprange_ip4width = 0;
982     } else if (bits == -1) {
983       range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
984       range->ncprange_ip4width = 32;
985     } else if (bits > 32) {
986       log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
987       return 0;
988     } else {
989       range->ncprange_ip4mask = bits2mask4(bits);
990       range->ncprange_ip4width = bits;
991     }
992
993     return 1;
994 #ifndef NOINET6
995   } else if (strchr(cp + 1, ':') != NULL) {
996     range->ncprange_family = AF_INET6;
997
998     if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
999       log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
1000       return 0;
1001     }
1002
1003     if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
1004       range->ncprange_ip6width = 0;
1005     else
1006       range->ncprange_ip6width = (bits == -1) ? 128 : bits;
1007     return 1;
1008 #endif
1009   }
1010
1011   return 0;
1012 }