1 /* $Id: ncbi_ipv6.c 598940 2019-12-17 16:10:55Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Anton Lavrentiev
27  *
28  * File Description:
29  *   IPv6 addressing support
30  *
31  */
32 
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_priv.h"
35 #include <connect/ncbi_socket.h>
36 #include <connect/ncbi_ipv6.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 
44 struct SIPDNSsfx {
45     const char*  sfx;
46     const size_t len;
47 };
48 static const struct SIPDNSsfx kIPv6DNS = { ".ip6.arpa",      9 };
49 static const struct SIPDNSsfx kIPv4DNS = { ".in-addr.arpa", 13 };
50 
51 
x_NcbiIsIPv4(const TNCBI_IPv6Addr * addr,int compat)52 static int/*bool*/ x_NcbiIsIPv4(const TNCBI_IPv6Addr* addr, int/*bool*/ compat)
53 {
54     /* RFC 4291 2.1, 3
55        NB: 2.5.5.1 and 2.5.5.2 - both obsoleted by RFC 6052 2.1 */
56     unsigned short word;
57     size_t i;
58     for (i = 0;  i < 5;  ++i) {
59         memcpy(&word, addr->octet + i * sizeof(word), sizeof(word));
60         if (word)
61             return 0/*false*/;
62     }
63     memcpy(&word, addr->octet + i * sizeof(word), sizeof(word));
64     if    (word == 0x0000) {
65         /* IPv4-compatible IPv6 */
66         if (compat) {
67             unsigned int temp;
68             memcpy(&temp, addr->octet + sizeof(addr->octet) - sizeof(temp),
69                    sizeof(temp));
70             if (SOCK_NetToHostLong(temp) & 0xFF000000)
71                 return 1/*true*/;
72         }
73         return 0/*false*/;
74     }
75     /* mapped IPv4 */
76     return word == 0xFFFF ? 1/*true*/ : 0/*false*/;
77 }
78 
79 
NcbiIsEmptyIPv6(const TNCBI_IPv6Addr * addr)80 extern int/*bool*/ NcbiIsEmptyIPv6(const TNCBI_IPv6Addr* addr)
81 {
82     return !addr
83         ||  !memcchr(addr->octet, 0, sizeof(addr->octet))
84         ||  (x_NcbiIsIPv4(addr, 0/*mapped*/)  &&  !NcbiIPv6ToIPv4(addr, 0))
85         ? 1/*true*/ : 0/*false*/;
86 }
87 
88 
NcbiIsIPv4(const TNCBI_IPv6Addr * addr)89 extern int/*bool*/ NcbiIsIPv4(const TNCBI_IPv6Addr* addr)
90 {
91     return addr  &&  x_NcbiIsIPv4(addr, 0/*mapped*/) ? 1/*true*/ : 0/*false*/;
92 }
93 
94 
NcbiIsIPv4Ex(const TNCBI_IPv6Addr * addr,int compat)95 extern int/*bool*/ NcbiIsIPv4Ex(const TNCBI_IPv6Addr* addr, int/*bool*/ compat)
96 {
97     return addr  &&  x_NcbiIsIPv4(addr, compat) ? 1/*true*/ : 0/*false*/;
98 }
99 
100 
NcbiIPv6ToIPv4(const TNCBI_IPv6Addr * addr,size_t pfxlen)101 extern unsigned int NcbiIPv6ToIPv4(const TNCBI_IPv6Addr* addr, size_t pfxlen)
102 {
103     unsigned int ipv4;
104     static const size_t size = sizeof(ipv4);
105     if (!addr)
106         return 0;
107     if (pfxlen == 0) {
108         if (!x_NcbiIsIPv4(addr, 1/*compat*/))
109             return 0;
110         pfxlen  = 96;
111     }
112     switch (pfxlen) {  /*RFC 6052 2.2*/
113     case 32:
114         memcpy(        &ipv4,            &addr->octet[4],  size);
115         break;
116     case 40:
117         memcpy(        &ipv4,            &addr->octet[5],  size - 1);
118         memcpy((char*) &ipv4 + size - 1, &addr->octet[9],         1);
119         break;
120     case 48:
121         memcpy(        &ipv4,            &addr->octet[6],  size - 2);
122         memcpy((char*) &ipv4 + size - 2, &addr->octet[9],         2);
123         break;
124     case 56:
125         memcpy(        &ipv4,            &addr->octet[7],  size - 3);
126         memcpy((char*) &ipv4 + size - 3, &addr->octet[9],         3);
127         break;
128     case 64:
129         memcpy(        &ipv4,            &addr->octet[9],  size);
130         break;
131     case 96:
132         memcpy(        &ipv4,            &addr->octet[12], size);
133         break;
134     default:
135         assert(0);
136         return (unsigned int)(-1)/*failure*/;
137     }
138     return ipv4;
139 }
140 
141 
NcbiIPv4ToIPv6(TNCBI_IPv6Addr * addr,unsigned int ipv4,size_t pfxlen)142 extern int/*bool*/ NcbiIPv4ToIPv6(TNCBI_IPv6Addr* addr,
143                                   unsigned int ipv4, size_t pfxlen)
144 {
145     static const size_t size = sizeof(ipv4);
146     if (!addr)
147         return 0/*failure*/;
148     if (pfxlen == 0) {
149         /* creates IPv6 mapped */
150         memset(addr, 0, sizeof(*addr));
151         memset(addr->octet + (5 << 1), '\xFF', sizeof(unsigned short));
152         pfxlen  = 96;
153     }
154     switch (pfxlen) {  /*RFC 6052 2.2*/
155     case 32:
156         memcpy(&addr->octet[4],         &ipv4,            size);
157         break;
158     case 40:
159         memcpy(&addr->octet[5],         &ipv4,            size - 1);
160         memcpy(&addr->octet[9], (char*) &ipv4 + size - 1,        1);
161         break;
162     case 48:
163         memcpy(&addr->octet[6],         &ipv4,            size - 2);
164         memcpy(&addr->octet[9], (char*) &ipv4 + size - 2,        2);
165         break;
166     case 56:
167         memcpy(&addr->octet[7],         &ipv4,            size - 3);
168         memcpy(&addr->octet[9], (char*) &ipv4 + size - 3,        3);
169         break;
170     case 64:
171         memcpy(&addr->octet[9],         &ipv4,            size);
172         break;
173     case 96:
174         memcpy(&addr->octet[12],        &ipv4,            size);
175         break;
176     default:
177         assert(0);
178         return 0/*failure*/;
179     }
180     return 1/*success*/;
181 }
182 
183 
184 /* Parse "str" as an IP address, and return 0 if failed, otherwise a pointer to
185  * the first non-parsed char (which is neither a digit nor a dot) and "dst"
186  * updated with the just read IPv4 address in network byte order.
187  */
x_StringToIPv4(unsigned int * dst,const char * str,size_t len)188 static const char* x_StringToIPv4(unsigned int* dst,
189                                   const char* str, size_t len)
190 {
191     size_t n;
192     int octets = 0;
193     unsigned int tmp;
194     int/*bool*/ was_digit = 0/*false*/;
195     unsigned char *ptr = (unsigned char*) &tmp;
196 
197     *ptr = 0;
198     for (n = 0;  n < len;  ++n) {
199         char c = str[n];
200         if ('0' <= c  &&  c <= '9') {
201             unsigned int val;
202             if (was_digit  &&  !*ptr)
203                 return 0/*leading "0" in octet*/;
204             val = (unsigned int)(*ptr * 10 + (c - '0'));
205             if (val > 255)
206                 return 0;
207             *ptr = (unsigned char) val;
208             if (!was_digit) {
209                 ++octets;
210                 assert(octets <= 4);
211                 was_digit = 1/*true*/;
212             }
213         } else if (c == '.') {
214             if (!was_digit  ||  octets >= 4)
215                 return 0;
216             was_digit = 0/*false*/;
217             *++ptr = 0;
218         } else
219             break;
220     }
221     if (octets != 4)
222         return 0/*failure*/;
223 
224     *dst = tmp;
225     return str + n;
226 }
227 
228 
x_IPv4ToString(char * buf,size_t bufsize,const void * src)229 static char* x_IPv4ToString(char* buf, size_t bufsize, const void* src)
230 {
231     char tmp[sizeof("255.255.255.255")];
232     unsigned char* ptr = (unsigned char*) src;
233     size_t len
234         = (size_t) sprintf(tmp, "%u.%u.%u.%u", ptr[0], ptr[1], ptr[2], ptr[3]);
235     return len < bufsize ? (char*) memcpy(buf, tmp, len + 1/*EOS*/) + len : 0;
236 }
237 
238 
239 /* Returns ptr past read (0 on error) */
x_StringToIPv6(TNCBI_IPv6Addr * addr,const char * str,size_t len)240 static const char* x_StringToIPv6(TNCBI_IPv6Addr* addr,
241                                   const char* str, size_t len)
242 {
243     unsigned short word;
244     struct {
245         const char* ptr;
246         size_t      len;
247     } token[sizeof(addr->octet) / sizeof(word) + 1];
248     size_t maxt = sizeof(token) / sizeof(token[0]) - 1, t, n;
249     TNCBI_IPv6Addr temp;
250     unsigned char* dst;
251     int/*bool*/ ipv4;
252     unsigned int ip;
253     size_t gap;
254 
255     if (len < 2  ||  (str[n = 0] == ':'  &&  str[++n] != ':'))
256         return 0/*failure*/;
257     gap = 0;
258     ipv4 = 0/*false*/;
259     token[t = 0].ptr = str + n;
260     while (n <= len) {
261         assert(t <= maxt);
262         if (n == len  ||  str[n] == ':') {
263             token[t].len = (size_t)(&str[n] - token[t].ptr);
264             if (token[t].len) {
265                 if (n++ == len) {
266                     if (++t > maxt)
267                         return 0/*failure*/;
268                     break;
269                 }
270             } else {
271                 if (n++ == len)
272                     break;
273                 if (gap++)  /*RFC 4291 2.2, 2*/
274                     return 0/*failure*/;
275             }
276             /*str[n - 1] == ':'*/
277             token[t].len++;
278             if (++t > maxt)
279                 return 0/*failure*/;
280             token[t].ptr = str + n;
281             continue;
282         }
283         if (!isxdigit((unsigned char) str[n])) {
284             token[t].len = (size_t)(&str[n] - token[t].ptr);
285             if (token[t].len) {
286                 if (str[n] == '.') {
287                     if (t <= maxt - sizeof(ip) / sizeof(word)) {
288                         const char* end
289                             = x_StringToIPv4(&ip,
290                                              token[t].ptr,
291                                              token[t].len + (len - n));
292                         if (end  &&  *end != ':'
293                             &&  t <= (maxt -= sizeof(ip) / sizeof(word))) {
294                             token[t].len = (size_t)(end - token[t].ptr);
295                             ipv4 = 1/*true*/;
296                             break;
297                         }
298                     }
299                     return 0/*failure*/;
300                 }
301                 if (++t > maxt)
302                     return 0/*failure*/;
303             }
304             break;
305         }
306         n++;
307     }
308 
309     assert(t <= maxt);
310     if (t < maxt  &&  !gap)
311         return 0/*failure*/;
312 
313     dst = temp.octet;
314     for (n = 0;  n < t;  ++n) {
315         assert(token[n].len);
316         if (*token[n].ptr != ':') {
317             char* end;
318             long  val;
319             assert(isxdigit((unsigned char) token[n].ptr[0]));
320             errno = 0;
321             val = strtol(token[n].ptr, &end, 16);
322             if (errno  ||  val ^ (val & 0xFFFF))
323                 return 0/*failure*/;
324             assert(end == token[n].ptr + token[n].len - (*end == ':'));
325             if (*end == ':'  &&  n == t - !ipv4)
326                 return 0/*failure*/;
327             word = SOCK_HostToNetShort((unsigned short) val);
328             memcpy(dst, &word, sizeof(word));
329             dst += sizeof(word);
330         } else {
331             gap = (maxt - t) * sizeof(word) + sizeof(word);
332             memset(dst, 0, gap);
333             dst += gap;
334         }
335     }
336     if (ipv4) {
337         memcpy(dst, &ip, sizeof(ip));
338         ++t;
339     }
340 
341     *addr = temp;
342     return token[t - 1].ptr + token[t - 1].len;
343 }
344 
345 
NcbiStringToIPv4(unsigned int * addr,const char * str,size_t len)346 extern const char* NcbiStringToIPv4(unsigned int* addr,
347                                     const char* str, size_t len)
348 {
349     size_t n;
350     if (!addr)
351         return 0/*failure*/;
352     *addr = 0;
353     if (!str)
354         return 0/*failure*/;
355     if (!len)
356         len = strlen(str);
357     for (n = 0;  n < len;  ++n) {
358         if (!isspace((unsigned char) str[n]))
359             break;
360     }
361     return x_StringToIPv4(addr, str + n, len - n);
362 }
363 
364 
NcbiStringToIPv6(TNCBI_IPv6Addr * addr,const char * str,size_t len)365 extern const char* NcbiStringToIPv6(TNCBI_IPv6Addr* addr,
366                                     const char* str, size_t len)
367 {
368     size_t n;
369     if (!addr)
370         return 0/*failure*/;
371     memset(addr, 0, sizeof(*addr));
372     if (!str  ||  !*str)
373         return 0/*failure*/;
374     if (!len)
375         len = strlen(str);
376     for (n = 0;  n < len;  ++n) {
377         if (!isspace((unsigned char) str[n]))
378             break;
379     }
380     return x_StringToIPv6(addr, str + n, len - n);
381 }
382 
383 
x_IPv6ToString(char * buf,size_t bufsize,const TNCBI_IPv6Addr * addr)384 static char* x_IPv6ToString(char* buf, size_t bufsize,
385                             const TNCBI_IPv6Addr* addr)
386 {
387     char ipv6[64/*enough for sizeof(8 * "xxxx:")*/];
388     char ipv4[sizeof("255.255.255.255")];
389     size_t i, n, pos, len, zpos, zlen;
390     unsigned short word;
391     char* ptr = ipv6;
392 
393     if (x_NcbiIsIPv4(addr, 1/*compat*/)) {
394         unsigned int ip;
395         n = sizeof(addr->octet) - sizeof(ip);
396         memcpy(&ip, addr->octet + n, sizeof(ip));
397         SOCK_ntoa(ip, ipv4, sizeof(ipv4));
398         n /= sizeof(word);
399     } else {
400         n = sizeof(addr->octet) / sizeof(word);
401         *ipv4 = '\0';
402     }
403 
404     pos = i = zpos = zlen = 0;
405     for (;;) {
406         if (i < n) {
407             memcpy(&word, &addr->octet[i * sizeof(word)], sizeof(word));
408             if (!word) {
409                 ++i;
410                 continue;
411             }
412         }
413         len = i - pos;
414         if (len > 1) {  /*RFC 5952 4.2.2*/
415             if (zlen < len) {
416                 zlen = len;  /*RFC 5952 4.2.1*/
417                 zpos = pos;
418             }
419         }
420         if (i == n)
421             break;
422         pos = ++i;
423     }
424 
425     i = 0;
426     while (i < n) {
427         if (zlen  &&  zpos == i) {
428             assert(zlen > 1);
429             *ptr++ = ':';
430             if (zlen == n - i)
431                 *ptr++ = ':';
432             i += zlen;
433             zlen = 0;  /*RFC 5952 4.2.3*/
434             continue;
435         }
436         memcpy(&word, &addr->octet[i * sizeof(word)], sizeof(word));
437         ptr += sprintf(ptr, &":%x"[!i],  /*RFC 5952 4.1, 4.3*/
438                        SOCK_NetToHostShort(word));
439         ++i;
440     }
441     assert(ptr > ipv6);
442 
443     i = strlen(ipv4);
444     if (i) {
445         if (ptr[-1] != ':')
446             *ptr++ = ':';
447     }
448     n = (size_t)(ptr - ipv6);
449     len = n + i;
450     if (len < bufsize) {
451         memcpy(buf, ipv6, n);
452         buf += n;
453         memcpy(buf, ipv4, i);
454         buf += i;
455         *buf = '\0';
456     } else
457         buf = 0;
458     return buf;
459 }
460 
461 
462 /* Returns ptr past written (points to '\0'); 0 on error */
NcbiIPv4ToString(char * buf,size_t bufsize,unsigned int addr)463 extern char* NcbiIPv4ToString(char* buf, size_t bufsize,
464                               unsigned int addr)
465 {
466     /* sanity */
467     if (!buf  ||  !bufsize)
468         return 0;
469     *buf = '\0';
470 
471     return x_IPv4ToString(buf, bufsize, &addr);
472 }
473 
474 
475 /* Returns ptr past written (points to '\0'); 0 on error */
NcbiIPv6ToString(char * buf,size_t bufsize,const TNCBI_IPv6Addr * addr)476 extern char* NcbiIPv6ToString(char* buf, size_t bufsize,
477                               const TNCBI_IPv6Addr* addr)
478 {
479     /* sanity */
480     if (!buf  ||  !bufsize)
481         return 0;
482     *buf = '\0';
483     if (!addr)
484         return 0;
485 
486     return x_IPv6ToString(buf, bufsize, addr);
487 }
488 
489 
NcbiAddrToString(char * buf,size_t bufsize,const TNCBI_IPv6Addr * addr)490 extern char* NcbiAddrToString(char* buf, size_t bufsize,
491                               const TNCBI_IPv6Addr* addr)
492 {
493     if (!buf  ||  !bufsize)
494         return 0;
495     *buf = '\0';
496     if (!addr)
497         return 0;
498 
499     if (x_NcbiIsIPv4(addr, 0/*mapped*/)) {
500         unsigned int ipv4 = NcbiIPv6ToIPv4(addr, 0);
501         return x_IPv4ToString(buf, bufsize, &ipv4);
502     }
503     return x_IPv6ToString(buf, bufsize, addr);
504 }
505 
506 
NcbiAddrToDNS(char * buf,size_t bufsize,const TNCBI_IPv6Addr * addr)507 extern const char* NcbiAddrToDNS(char* buf, size_t bufsize,
508                                  const TNCBI_IPv6Addr* addr)
509 {
510     char tmp[sizeof(addr->octet)*4 + 16/*slack*/], *dst = tmp;
511     const struct SIPDNSsfx* sfx;
512     const unsigned char* src;
513     size_t n, len;
514 
515     if (!buf  ||  !bufsize)
516         return 0;
517     *buf = '\0';
518     if (!addr)
519         return 0;
520 
521     len = 0;
522     src = addr->octet + sizeof(addr->octet) - 1;
523     if (x_NcbiIsIPv4(addr, 0/*mapped*/)) {
524         sfx = &kIPv4DNS;
525         for (n = 0;  n < sizeof(unsigned int);  ++n) {
526             size_t off = (size_t)sprintf(dst, "%d.",    *src--);
527             dst += off;
528             len += off;
529         }
530     } else {
531         sfx = &kIPv6DNS;
532         for (n = 0;  n < sizeof(addr->octet);  ++n) {
533             size_t off = (size_t)sprintf(dst, "%x.%x.", *src & 0xF, *src >> 4);
534             dst += off;
535             len += off;
536             --src;
537         }
538     }
539     if (len + sfx->len <= bufsize) {
540         memcpy(buf, tmp,               len);
541         buf += len;
542         memcpy(buf, sfx->sfx + 1, sfx->len);
543         buf += sfx->len;
544     } else
545         buf = 0;
546     return buf;
547 }
548 
549 
550 /* NB: "str" is actually bounded by the ".in-addr.apra" suffix */
x_DNSToIPv4(unsigned int * addr,const char * str,size_t len)551 static const char* x_DNSToIPv4(unsigned int* addr,
552                                const char* str, size_t len)
553 {
554     size_t n;
555     unsigned int temp;
556     CORE_DEBUG_ARG(const char* end = str + len;)
557     unsigned char* ptr = (unsigned char*) &temp + sizeof(temp);
558     assert(*end == '.');
559     if (len < 7/*"x.x.x.x"*/  ||  15/*xxx.xxx.xxx.xxx*/ < len)
560         return 0/*failure*/;
561     for (n = 0;  n < sizeof(temp);  ++n) {
562         char  s[4];
563         char* e;
564         long  d;
565         errno = 0;
566         d = strtol(str, &e, 10);  /*NB: "str" may be at "in-addr" safely here*/
567         if (errno  ||  str == e  ||  e - str > 3  ||  *e != '.'
568             ||  d < 0  ||  255 < d
569             ||  sprintf(s, "%u", (unsigned int) d) != (int)(e - str)) {
570             return 0/*failure*/;
571         }
572         assert(e <= end);
573         *--ptr = (unsigned char) d;
574         str = ++e;
575     }
576     *addr = temp;
577     return --str;
578 }
579 
580 
581 /* NB: "str" is actually bounded by the ".ip6.arpa" suffix */
x_DNSToIPv6(TNCBI_IPv6Addr * addr,const char * str,size_t len)582 static const char* x_DNSToIPv6(TNCBI_IPv6Addr* addr,
583                                const char* str, size_t len)
584 {
585     static const char xdigits[] = "0123456789abcdef";
586     CORE_DEBUG_ARG(const char* end = str + len;)
587     TNCBI_IPv6Addr temp;
588     unsigned char* dst;
589     size_t n;
590     assert(*end == '.');
591     if (len != 4 * sizeof(addr->octet) - 1)
592         return 0/*failure*/;
593     dst = temp.octet + sizeof(temp.octet) - 1;
594     for (n = 0;  n < 2 * sizeof(addr->octet);  ++n) {
595         const char* ptr = strchr(xdigits, tolower((unsigned char)(*str++)));
596         unsigned char val;
597         assert(str <= end);
598         if (!ptr  ||  *str++ != '.')
599             return 0/*failure*/;
600         val = (unsigned char)(ptr - xdigits);
601         if (n & 1) {
602             val   <<= 4;
603             *dst-- |= val;
604         } else
605             *dst    = val;
606     }
607     *addr = temp;
608     return --str;
609 }
610 
611 
612 enum ENcbiIP_Form {
613     eNcbiIP_Dot = 1,  /* Accept dotted notation */
614     eNcbiIP_Dns = 2   /* Accept DNS notation    */
615 };
616 typedef unsigned int TNcbiIP_Form;  /* Bitwise OR of ENcbiIP_Form */
617 
s_StringToAddr(TNCBI_IPv6Addr * addr,const char * str,size_t len,TNcbiIP_Form how)618 static const char* s_StringToAddr(TNCBI_IPv6Addr* addr,
619                                   const char* str, size_t len,
620                                   TNcbiIP_Form how)
621 {
622     unsigned int ipv4;
623     const char* tmp;
624     size_t n;
625 
626     if (!addr)
627         return 0/*failure*/;
628     memset(addr, 0, sizeof(*addr));
629     if (!str  ||  !*str)
630         return 0/*failure*/;
631 
632     if (!len)
633         len = strlen(str);
634     for (n = 0;  n < len;  ++n) {
635         if (!isspace((unsigned char) str[n]))
636             break;
637     }
638     str += n;
639     len -= n;
640     for (n = 0;  n < len;  ++n) {
641         if (!str[n]  ||  isspace((unsigned char) str[n]))
642             break;
643     }
644     if (!(len = n))
645         return 0/*failure*/;
646 
647     if (how & eNcbiIP_Dns) {
648         size_t/*bool*/ dns = str[--n] == '.' ? 1/*true*/ : 0/*false*/;
649         if (len > kIPv4DNS.len
650             &&  strncasecmp(tmp = str + len - (kIPv4DNS.len + dns),
651                             kIPv4DNS.sfx, kIPv4DNS.len) == 0) {
652             if (x_DNSToIPv4(&ipv4, str, len - (kIPv4DNS.len + dns)) == tmp) {
653                 NcbiIPv4ToIPv6(addr, ipv4, 0);
654                 return tmp + (kIPv4DNS.len + dns);
655             } else if (dns)
656                 return 0/*failure*/;
657         }
658         if (len > kIPv6DNS.len
659             &&  strncasecmp(tmp = str + len - (kIPv6DNS.len + dns),
660                             kIPv6DNS.sfx, kIPv6DNS.len) == 0) {
661             if (x_DNSToIPv6(addr,  str, len - (kIPv6DNS.len + dns)) == tmp)
662                 return tmp + (kIPv6DNS.len + dns);
663             else if (dns)
664                 return 0/*failure*/;
665         }
666     }
667     if (!(how & eNcbiIP_Dot))
668         return 0/*failure*/;
669 
670     if ((tmp = x_StringToIPv4(&ipv4, str, len)) != 0) {
671         NcbiIPv4ToIPv6(addr, ipv4, 0);
672         return tmp;
673     }
674     return x_StringToIPv6(addr, str, len);
675 }
676 
677 
NcbiIPToAddr(TNCBI_IPv6Addr * addr,const char * str,size_t len)678 extern const char* NcbiIPToAddr(TNCBI_IPv6Addr* addr,
679                                 const char* str, size_t len)
680 {
681     const char* rv = s_StringToAddr(addr, str, len, eNcbiIP_Dot);
682     assert(!rv  ||  rv > str);
683     return rv;
684 }
685 
686 
NcbiStringToAddr(TNCBI_IPv6Addr * addr,const char * str,size_t len)687 extern const char* NcbiStringToAddr(TNCBI_IPv6Addr* addr,
688                                     const char* str, size_t len)
689 {
690     const char* rv = s_StringToAddr(addr, str, len, eNcbiIP_Dns | eNcbiIP_Dot);
691     assert(!rv  ||  rv > str);
692     return rv;
693 }
694 
695 
NcbiDNSIPToAddr(TNCBI_IPv6Addr * addr,const char * str,size_t len)696 extern const char* NcbiDNSIPToAddr(TNCBI_IPv6Addr* addr,
697                                    const char* str, size_t len)
698 {
699     const char* rv = s_StringToAddr(addr, str, len, eNcbiIP_Dns);
700     assert(!rv  ||  rv > str);
701     return rv;
702 }
703 
704 
NcbiIsInIPv6Network(const TNCBI_IPv6Addr * base,unsigned int bits,const TNCBI_IPv6Addr * addr)705 extern int/*bool*/ NcbiIsInIPv6Network(const TNCBI_IPv6Addr* base,
706                                        unsigned int          bits,
707                                        const TNCBI_IPv6Addr* addr)
708 {
709     size_t n;
710 
711     if (!base  ||  !addr)
712         return 0/*false*/;
713 
714     if (bits > (sizeof(base->octet) << 3))
715         return 0/*false*/;
716 
717     for (n = 0;  n < sizeof(addr->octet);  ++n) {
718         unsigned char mask;
719         if (!bits)
720             mask  =                  0;
721         else if (8 > bits) {
722             mask  = (unsigned char)(~0 << (8 - bits));
723             bits  =                  0;
724         } else {
725             mask  = (unsigned char)(~0);
726             bits -=                  8;
727         }
728         if ((addr->octet[n] & mask) ^ base->octet[n])
729             return 0/*false*/;
730     }
731 
732     return 1/*true*/;
733 }
734 
735 
NcbiIPv6Subnet(TNCBI_IPv6Addr * addr,unsigned int bits)736 extern int/*bool*/ NcbiIPv6Subnet(TNCBI_IPv6Addr* addr, unsigned int bits)
737 {
738     int/*bool*/ zero = 1/*true*/;
739 
740     if (addr) {
741         size_t n;
742         for (n = 0;  n < sizeof(addr->octet);  ++n) {
743             if (!bits)
744                 addr->octet[n] = 0;
745             else if (8 > bits) {
746                 unsigned char mask = (unsigned char)(~0 << (8 - bits));
747                 if (addr->octet[n] &= mask)
748                     zero = 0/*false*/;
749                 bits  = 0;
750             } else {
751                 bits -= 8;
752                 if (addr->octet[n])
753                     zero = 0/*false*/;
754             }
755         }
756     }
757 
758     return !zero;
759 }
760