788 lines
18 KiB
C
788 lines
18 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* Copyright (c) 1996-1999 by Internet Software Consortium
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <qse/cmn/ipad.h>
|
|
#include <qse/cmn/hton.h>
|
|
#include <qse/cmn/str.h>
|
|
#include <qse/cmn/fmt.h>
|
|
#include "mem-prv.h"
|
|
|
|
#if 0
|
|
const qse_ip4ad_t qse_ip4ad_any =
|
|
{
|
|
0 /* 0.0.0.0 */
|
|
};
|
|
|
|
const qse_ip4ad_t qse_ip4ad_loopback =
|
|
{
|
|
#if defined(QSE_ENDIAN_BIG)
|
|
0x7F000001u /* 127.0.0.1 */
|
|
#elif defined(QSE_ENDIAN_LITTLE)
|
|
0x0100007Fu
|
|
#else
|
|
# error Unknown endian
|
|
#endif
|
|
};
|
|
|
|
const qse_ip6ad_t qse_ip6ad_any =
|
|
{
|
|
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* :: */
|
|
};
|
|
|
|
const qse_ip6ad_t qse_ip6ad_loopback =
|
|
{
|
|
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } /* ::1 */
|
|
};
|
|
#endif
|
|
|
|
static int str_to_ip4ad (int mbs, const void* str, qse_size_t len, qse_ip4ad_t* ipad)
|
|
{
|
|
const void* end;
|
|
int dots = 0, digits = 0;
|
|
qse_uint32_t acc = 0, addr = 0;
|
|
qse_wchar_t c;
|
|
|
|
end = (mbs? (const void*)((const qse_mchar_t*)str + len):
|
|
(const void*)((const qse_wchar_t*)str + len));
|
|
|
|
do
|
|
{
|
|
if (str >= end)
|
|
{
|
|
if (dots < 3 || digits == 0) return -1;
|
|
addr = (addr << 8) | acc;
|
|
break;
|
|
}
|
|
|
|
if (mbs)
|
|
{
|
|
c = *(const qse_mchar_t*)str;
|
|
str = (const qse_mchar_t*)str + 1;
|
|
}
|
|
else
|
|
{
|
|
c = *(const qse_wchar_t*)str;
|
|
str = (const qse_wchar_t*)str + 1;
|
|
}
|
|
|
|
if (c >= QSE_WT('0') && c <= QSE_WT('9'))
|
|
{
|
|
if (digits > 0 && acc == 0) return -1;
|
|
acc = acc * 10 + (c - QSE_T('0'));
|
|
if (acc > 255) return -1;
|
|
digits++;
|
|
}
|
|
else if (c == QSE_WT('.'))
|
|
{
|
|
if (dots >= 3 || digits == 0) return -1;
|
|
addr = (addr << 8) | acc;
|
|
dots++; acc = 0; digits = 0;
|
|
}
|
|
else return -1;
|
|
}
|
|
while (1);
|
|
|
|
if (ipad != QSE_NULL) ipad->value = qse_hton32(addr);
|
|
return 0;
|
|
}
|
|
|
|
int qse_mbstoip4ad (const qse_mchar_t* str, qse_ip4ad_t* ipad)
|
|
{
|
|
return str_to_ip4ad (1, str, qse_mbslen(str), ipad);
|
|
}
|
|
|
|
int qse_wcstoip4ad (const qse_wchar_t* str, qse_ip4ad_t* ipad)
|
|
{
|
|
return str_to_ip4ad (0, str, qse_wcslen(str), ipad);
|
|
}
|
|
|
|
int qse_mbsntoip4ad (const qse_mchar_t* str, qse_size_t len, qse_ip4ad_t* ipad)
|
|
{
|
|
return str_to_ip4ad (1, str, len, ipad);
|
|
}
|
|
|
|
int qse_wcsntoip4ad (const qse_wchar_t* str, qse_size_t len, qse_ip4ad_t* ipad)
|
|
{
|
|
return str_to_ip4ad (0, str, len, ipad);
|
|
}
|
|
|
|
#define __BTOA(type_t,b,p,end) \
|
|
do { \
|
|
type_t* sp = p; \
|
|
do { \
|
|
if (p >= end) { \
|
|
if (p == sp) break; \
|
|
if (p - sp > 1) p[-2] = p[-1]; \
|
|
p[-1] = (b % 10) + '0'; \
|
|
} \
|
|
else *p++ = (b % 10) + '0'; \
|
|
b /= 10; \
|
|
} while (b > 0); \
|
|
if (p - sp > 1) { \
|
|
type_t t = sp[0]; \
|
|
sp[0] = p[-1]; \
|
|
p[-1] = t; \
|
|
} \
|
|
} while (0);
|
|
|
|
#define __ADDDOT(p, end) \
|
|
do { \
|
|
if (p >= end) break; \
|
|
*p++ = '.'; \
|
|
} while (0)
|
|
|
|
qse_size_t qse_ip4adtombs (
|
|
const qse_ip4ad_t* ipad, qse_mchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_byte_t b;
|
|
qse_mchar_t* p, * end;
|
|
qse_uint32_t ip;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
ip = ipad->value;
|
|
|
|
p = buf;
|
|
end = buf + size - 1;
|
|
|
|
#if defined(QSE_ENDIAN_BIG)
|
|
b = (ip >> 24) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 16) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 8) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 0) & 0xFF; __BTOA (qse_mchar_t, b, p, end);
|
|
#elif defined(QSE_ENDIAN_LITTLE)
|
|
b = (ip >> 0) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 8) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 16) & 0xFF; __BTOA (qse_mchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 24) & 0xFF; __BTOA (qse_mchar_t, b, p, end);
|
|
#else
|
|
# error Unknown Endian
|
|
#endif
|
|
|
|
*p = QSE_MT('\0');
|
|
return p - buf;
|
|
}
|
|
|
|
qse_size_t qse_ip4adtowcs (
|
|
const qse_ip4ad_t* ipad, qse_wchar_t* buf, qse_size_t size)
|
|
{
|
|
qse_byte_t b;
|
|
qse_wchar_t* p, * end;
|
|
qse_uint32_t ip;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
ip = ipad->value;
|
|
|
|
p = buf;
|
|
end = buf + size - 1;
|
|
|
|
#if defined(QSE_ENDIAN_BIG)
|
|
b = (ip >> 24) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 16) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 8) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 0) & 0xFF; __BTOA (qse_wchar_t, b, p, end);
|
|
#elif defined(QSE_ENDIAN_LITTLE)
|
|
b = (ip >> 0) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 8) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 16) & 0xFF; __BTOA (qse_wchar_t, b, p, end); __ADDDOT (p, end);
|
|
b = (ip >> 24) & 0xFF; __BTOA (qse_wchar_t, b, p, end);
|
|
#else
|
|
# error Unknown Endian
|
|
#endif
|
|
|
|
*p = QSE_WT('\0');
|
|
return p - buf;
|
|
}
|
|
|
|
int qse_mbstoip6ad (const qse_mchar_t* src, qse_ip6ad_t* ipad)
|
|
{
|
|
return qse_mbsntoip6ad (src, qse_mbslen(src), ipad);
|
|
}
|
|
|
|
int qse_mbsntoip6ad (const qse_mchar_t* src, qse_size_t len, qse_ip6ad_t* ipad)
|
|
{
|
|
qse_ip6ad_t tmp;
|
|
qse_uint8_t* tp, * endp, * colonp;
|
|
const qse_mchar_t* curtok;
|
|
qse_mchar_t ch;
|
|
int saw_xdigit;
|
|
unsigned int val;
|
|
const qse_mchar_t* src_end;
|
|
|
|
src_end = src + len;
|
|
|
|
QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp));
|
|
tp = &tmp.value[0];
|
|
endp = &tmp.value[QSE_COUNTOF(tmp.value)];
|
|
colonp = QSE_NULL;
|
|
|
|
/* Leading :: requires some special handling. */
|
|
if (src < src_end && *src == QSE_MT(':'))
|
|
{
|
|
src++;
|
|
if (src >= src_end || *src != QSE_MT(':')) return -1;
|
|
}
|
|
|
|
curtok = src;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
|
|
while (src < src_end)
|
|
{
|
|
int v1;
|
|
|
|
ch = *src++;
|
|
|
|
if (ch >= QSE_MT('0') && ch <= QSE_MT('9'))
|
|
v1 = ch - QSE_MT('0');
|
|
else if (ch >= QSE_MT('A') && ch <= QSE_MT('F'))
|
|
v1 = ch - QSE_MT('A') + 10;
|
|
else if (ch >= QSE_MT('a') && ch <= QSE_MT('f'))
|
|
v1 = ch - QSE_MT('a') + 10;
|
|
else v1 = -1;
|
|
if (v1 >= 0)
|
|
{
|
|
val <<= 4;
|
|
val |= v1;
|
|
if (val > 0xffff) return -1;
|
|
saw_xdigit = 1;
|
|
continue;
|
|
}
|
|
|
|
if (ch == QSE_MT(':'))
|
|
{
|
|
curtok = src;
|
|
if (!saw_xdigit)
|
|
{
|
|
if (colonp) return -1;
|
|
colonp = tp;
|
|
continue;
|
|
}
|
|
else if (src >= src_end)
|
|
{
|
|
/* a colon can't be the last character */
|
|
return -1;
|
|
}
|
|
|
|
*tp++ = (qse_uint8_t)(val >> 8) & 0xff;
|
|
*tp++ = (qse_uint8_t)val & 0xff;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
continue;
|
|
}
|
|
|
|
if (ch == QSE_MT('.') && ((tp + QSE_SIZEOF(qse_ip4ad_t)) <= endp) &&
|
|
qse_mbsntoip4ad(curtok, src_end - curtok, (qse_ip4ad_t*)tp) == 0)
|
|
{
|
|
tp += QSE_SIZEOF(qse_ip4ad_t);
|
|
saw_xdigit = 0;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (saw_xdigit)
|
|
{
|
|
if (tp + QSE_SIZEOF(qse_uint16_t) > endp) return -1;
|
|
*tp++ = (qse_uint8_t)(val >> 8) & 0xff;
|
|
*tp++ = (qse_uint8_t)val & 0xff;
|
|
}
|
|
if (colonp != QSE_NULL)
|
|
{
|
|
/*
|
|
* Since some memmove()'s erroneously fail to handle
|
|
* overlapping regions, we'll do the shift by hand.
|
|
*/
|
|
qse_size_t n = tp - colonp;
|
|
qse_size_t i;
|
|
|
|
for (i = 1; i <= n; i++)
|
|
{
|
|
endp[-i] = colonp[n - i];
|
|
colonp[n - i] = 0;
|
|
}
|
|
tp = endp;
|
|
}
|
|
|
|
if (tp != endp) return -1;
|
|
|
|
*ipad = tmp;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int qse_wcstoip6ad (const qse_wchar_t* src, qse_ip6ad_t* ipad)
|
|
{
|
|
return qse_wcsntoip6ad (src, qse_wcslen(src), ipad);
|
|
}
|
|
|
|
int qse_wcsntoip6ad (const qse_wchar_t* src, qse_size_t len, qse_ip6ad_t* ipad)
|
|
{
|
|
qse_ip6ad_t tmp;
|
|
qse_uint8_t* tp, * endp, * colonp;
|
|
const qse_wchar_t* curtok;
|
|
qse_wchar_t ch;
|
|
int saw_xdigit;
|
|
unsigned int val;
|
|
const qse_wchar_t* src_end;
|
|
|
|
src_end = src + len;
|
|
|
|
QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp));
|
|
tp = &tmp.value[0];
|
|
endp = &tmp.value[QSE_COUNTOF(tmp.value)];
|
|
colonp = QSE_NULL;
|
|
|
|
/* Leading :: requires some special handling. */
|
|
if (src < src_end && *src == QSE_WT(':'))
|
|
{
|
|
src++;
|
|
if (src >= src_end || *src != QSE_WT(':')) return -1;
|
|
}
|
|
|
|
curtok = src;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
|
|
while (src < src_end)
|
|
{
|
|
int v1;
|
|
|
|
ch = *src++;
|
|
|
|
if (ch >= QSE_WT('0') && ch <= QSE_WT('9'))
|
|
v1 = ch - QSE_WT('0');
|
|
else if (ch >= QSE_WT('A') && ch <= QSE_WT('F'))
|
|
v1 = ch - QSE_WT('A') + 10;
|
|
else if (ch >= QSE_WT('a') && ch <= QSE_WT('f'))
|
|
v1 = ch - QSE_WT('a') + 10;
|
|
else v1 = -1;
|
|
if (v1 >= 0)
|
|
{
|
|
val <<= 4;
|
|
val |= v1;
|
|
if (val > 0xffff) return -1;
|
|
saw_xdigit = 1;
|
|
continue;
|
|
}
|
|
|
|
if (ch == QSE_WT(':'))
|
|
{
|
|
curtok = src;
|
|
if (!saw_xdigit)
|
|
{
|
|
if (colonp) return -1;
|
|
colonp = tp;
|
|
continue;
|
|
}
|
|
else if (src >= src_end)
|
|
{
|
|
/* a colon can't be the last character */
|
|
return -1;
|
|
}
|
|
|
|
*tp++ = (qse_uint8_t)(val >> 8) & 0xff;
|
|
*tp++ = (qse_uint8_t)val & 0xff;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
continue;
|
|
}
|
|
|
|
if (ch == QSE_WT('.') && ((tp + QSE_SIZEOF(qse_ip4ad_t)) <= endp) &&
|
|
qse_wcsntoip4ad(curtok, src_end - curtok, (qse_ip4ad_t*)tp) == 0)
|
|
{
|
|
tp += QSE_SIZEOF(qse_ip4ad_t);
|
|
saw_xdigit = 0;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (saw_xdigit)
|
|
{
|
|
if (tp + QSE_SIZEOF(qse_uint16_t) > endp) return -1;
|
|
*tp++ = (qse_uint8_t)(val >> 8) & 0xff;
|
|
*tp++ = (qse_uint8_t)val & 0xff;
|
|
}
|
|
if (colonp != QSE_NULL)
|
|
{
|
|
/*
|
|
* Since some memmove()'s erroneously fail to handle
|
|
* overlapping regions, we'll do the shift by hand.
|
|
*/
|
|
qse_size_t n = tp - colonp;
|
|
qse_size_t i;
|
|
|
|
for (i = 1; i <= n; i++)
|
|
{
|
|
endp[-i] = colonp[n - i];
|
|
colonp[n - i] = 0;
|
|
}
|
|
tp = endp;
|
|
}
|
|
|
|
if (tp != endp) return -1;
|
|
|
|
*ipad = tmp;
|
|
return 0;
|
|
}
|
|
|
|
qse_size_t qse_ip6adtombs (const qse_ip6ad_t* ipad, qse_mchar_t* buf, qse_size_t size)
|
|
{
|
|
/*
|
|
* Note that int32_t and int16_t need only be "at least" large enough
|
|
* to contain a value of the specified size. On some systems, like
|
|
* Crays, there is no such thing as an integer variable with 16 bits.
|
|
* Keep this in mind if you think this function should have been coded
|
|
* to use pointer overlays. All the world's not a VAX.
|
|
*/
|
|
|
|
#define IP6ADDR_NWORDS (QSE_SIZEOF(ipad->value) / QSE_SIZEOF(qse_uint16_t))
|
|
|
|
qse_mchar_t tmp[QSE_COUNTOF(QSE_MT("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))], *tp;
|
|
struct { int base, len; } best, cur;
|
|
qse_uint16_t words[IP6ADDR_NWORDS];
|
|
int i;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
/*
|
|
* Preprocess:
|
|
* Copy the input (bytewise) array into a wordwise array.
|
|
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
|
*/
|
|
QSE_MEMSET (words, 0, QSE_SIZEOF(words));
|
|
for (i = 0; i < QSE_SIZEOF(ipad->value); i++)
|
|
words[i / 2] |= (ipad->value[i] << ((1 - (i % 2)) << 3));
|
|
best.base = -1;
|
|
best.len = 0;
|
|
cur.base = -1;
|
|
cur.len = 0;
|
|
|
|
for (i = 0; i < IP6ADDR_NWORDS; i++)
|
|
{
|
|
if (words[i] == 0)
|
|
{
|
|
if (cur.base == -1)
|
|
{
|
|
cur.base = i;
|
|
cur.len = 1;
|
|
}
|
|
else
|
|
{
|
|
cur.len++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cur.base != -1)
|
|
{
|
|
if (best.base == -1 || cur.len > best.len) best = cur;
|
|
cur.base = -1;
|
|
}
|
|
}
|
|
}
|
|
if (cur.base != -1)
|
|
{
|
|
if (best.base == -1 || cur.len > best.len) best = cur;
|
|
}
|
|
if (best.base != -1 && best.len < 2) best.base = -1;
|
|
|
|
/*
|
|
* Format the result.
|
|
*/
|
|
tp = tmp;
|
|
for (i = 0; i < IP6ADDR_NWORDS; i++)
|
|
{
|
|
/* Are we inside the best run of 0x00's? */
|
|
if (best.base != -1 && i >= best.base &&
|
|
i < (best.base + best.len))
|
|
{
|
|
if (i == best.base) *tp++ = QSE_MT(':');
|
|
continue;
|
|
}
|
|
|
|
/* Are we following an initial run of 0x00s or any real hex? */
|
|
if (i != 0) *tp++ = QSE_MT(':');
|
|
|
|
/* Is this address an encapsulated IPv4? ipv4-compatible or ipv4-mapped */
|
|
if (i == 6 && best.base == 0 &&
|
|
(best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
|
|
{
|
|
qse_ip4ad_t ip4ad;
|
|
QSE_MEMCPY (&ip4ad.value, ipad->value+12, QSE_SIZEOF(ip4ad.value));
|
|
tp += qse_ip4adtombs (&ip4ad, tp, QSE_COUNTOF(tmp) - (tp - tmp));
|
|
break;
|
|
}
|
|
|
|
tp += qse_fmtuintmaxtombs (
|
|
tp, QSE_COUNTOF(tmp) - (tp - tmp),
|
|
words[i], 16, 0, QSE_MT('\0'), QSE_NULL);
|
|
}
|
|
|
|
/* Was it a trailing run of 0x00's? */
|
|
if (best.base != -1 &&
|
|
(best.base + best.len) == IP6ADDR_NWORDS) *tp++ = QSE_MT(':');
|
|
*tp++ = QSE_MT('\0');
|
|
|
|
return qse_mbsxcpy (buf, size, tmp);
|
|
|
|
#undef IP6ADDR_NWORDS
|
|
}
|
|
|
|
qse_size_t qse_ip6adtowcs (const qse_ip6ad_t* ipad, qse_wchar_t* buf, qse_size_t size)
|
|
{
|
|
/*
|
|
* Note that int32_t and int16_t need only be "at least" large enough
|
|
* to contain a value of the specified size. On some systems, like
|
|
* Crays, there is no such thing as an integer variable with 16 bits.
|
|
* Keep this in mind if you think this function should have been coded
|
|
* to use pointer overlays. All the world's not a VAX.
|
|
*/
|
|
|
|
#define IP6ADDR_NWORDS (QSE_SIZEOF(ipad->value) / QSE_SIZEOF(qse_uint16_t))
|
|
|
|
qse_wchar_t tmp[QSE_COUNTOF(QSE_MT("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))], *tp;
|
|
struct { int base, len; } best, cur;
|
|
qse_uint16_t words[IP6ADDR_NWORDS];
|
|
int i;
|
|
|
|
if (size <= 0) return 0;
|
|
|
|
/*
|
|
* Preprocess:
|
|
* Copy the input (bytewise) array into a wordwise array.
|
|
* Find the longest run of 0x00's in src[] for :: shorthanding.
|
|
*/
|
|
QSE_MEMSET (words, 0, QSE_SIZEOF(words));
|
|
for (i = 0; i < QSE_SIZEOF(ipad->value); i++)
|
|
words[i / 2] |= (ipad->value[i] << ((1 - (i % 2)) << 3));
|
|
best.base = -1;
|
|
best.len = 0;
|
|
cur.base = -1;
|
|
cur.len = 0;
|
|
|
|
for (i = 0; i < IP6ADDR_NWORDS; i++)
|
|
{
|
|
if (words[i] == 0)
|
|
{
|
|
if (cur.base == -1)
|
|
{
|
|
cur.base = i;
|
|
cur.len = 1;
|
|
}
|
|
else
|
|
{
|
|
cur.len++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cur.base != -1)
|
|
{
|
|
if (best.base == -1 || cur.len > best.len) best = cur;
|
|
cur.base = -1;
|
|
}
|
|
}
|
|
}
|
|
if (cur.base != -1)
|
|
{
|
|
if (best.base == -1 || cur.len > best.len) best = cur;
|
|
}
|
|
if (best.base != -1 && best.len < 2) best.base = -1;
|
|
|
|
/*
|
|
* Format the result.
|
|
*/
|
|
tp = tmp;
|
|
for (i = 0; i < IP6ADDR_NWORDS; i++)
|
|
{
|
|
/* Are we inside the best run of 0x00's? */
|
|
if (best.base != -1 && i >= best.base &&
|
|
i < (best.base + best.len))
|
|
{
|
|
if (i == best.base) *tp++ = QSE_MT(':');
|
|
continue;
|
|
}
|
|
|
|
/* Are we following an initial run of 0x00s or any real hex? */
|
|
if (i != 0) *tp++ = QSE_MT(':');
|
|
|
|
/* Is this address an encapsulated IPv4? ipv4-compatible or ipv4-mapped */
|
|
if (i == 6 && best.base == 0 &&
|
|
(best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
|
|
{
|
|
qse_ip4ad_t ip4ad;
|
|
QSE_MEMCPY (&ip4ad.value, ipad->value+12, QSE_SIZEOF(ip4ad.value));
|
|
tp += qse_ip4adtowcs (&ip4ad, tp, QSE_COUNTOF(tmp) - (tp - tmp));
|
|
break;
|
|
}
|
|
|
|
tp += qse_fmtuintmaxtowcs (
|
|
tp, QSE_COUNTOF(tmp) - (tp - tmp),
|
|
words[i], 16, 0, QSE_WT('\0'), QSE_NULL);
|
|
}
|
|
|
|
/* Was it a trailing run of 0x00's? */
|
|
if (best.base != -1 &&
|
|
(best.base + best.len) == IP6ADDR_NWORDS) *tp++ = QSE_MT(':');
|
|
*tp++ = QSE_MT('\0');
|
|
|
|
return qse_wcsxcpy (buf, size, tmp);
|
|
|
|
#undef IP6ADDR_NWORDS
|
|
}
|
|
|
|
int qse_prefixtoip4ad (int prefix, qse_ip4ad_t* ipad)
|
|
{
|
|
static qse_uint32_t tab[] =
|
|
{
|
|
0x00000000,
|
|
0x80000000, 0xC0000000, 0xE0000000, 0xF0000000,
|
|
0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000,
|
|
0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000,
|
|
0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
|
|
0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
|
|
0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00,
|
|
0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0,
|
|
0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF
|
|
};
|
|
|
|
if (prefix < 0 || prefix > QSE_SIZEOF(*ipad) * 8) return -1;
|
|
ipad->value = qse_hton32(tab[prefix]);
|
|
return 0;
|
|
|
|
/*
|
|
int p, k;
|
|
qse_uint32_t mask = 0;
|
|
|
|
for (k = 24; prefix > 0; k -= 8)
|
|
{
|
|
p = (prefix >= 8)? 0: (8 - prefix);
|
|
mask |= ((0xFF << p) & 0xFF) << k;
|
|
prefix -= 8;
|
|
}
|
|
|
|
ipad->value = qse_hton32(mask);
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
int qse_prefixtoip6ad (int prefix, qse_ip6ad_t* ipad)
|
|
{
|
|
int i;
|
|
|
|
if (prefix < 0 || prefix > QSE_SIZEOF(*ipad) * 8) return -1;
|
|
|
|
QSE_MEMSET (ipad, 0, QSE_SIZEOF(*ipad));
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (prefix > 8)
|
|
{
|
|
ipad->value[i] = 0xFF;
|
|
prefix -= 8;
|
|
}
|
|
else
|
|
{
|
|
ipad->value[i] = 0xFF << (8 - prefix);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define __COUNTUP(x,cnt,rest) \
|
|
switch (x) \
|
|
{ \
|
|
case 0xFF000000: \
|
|
cnt += 8; break; \
|
|
case 0x00000000: \
|
|
if (rest) return -1; break; \
|
|
case 0xFE000000: cnt++; \
|
|
case 0xFC000000: cnt++; \
|
|
case 0xF8000000: cnt++; \
|
|
case 0XF0000000: cnt++; \
|
|
case 0xE0000000: cnt++; \
|
|
case 0xC0000000: cnt++; \
|
|
case 0x80000000: cnt++; \
|
|
if (rest) return -1; break; \
|
|
default: return -1; \
|
|
}
|
|
|
|
int qse_ip4adtoprefix (const qse_ip4ad_t* ipad)
|
|
{
|
|
int pfx = 0;
|
|
qse_uint32_t x, nm;
|
|
|
|
nm = qse_ntoh32(ipad->value);
|
|
|
|
if (nm)
|
|
{
|
|
x = nm & 0xFF000000; nm <<= 8;
|
|
__COUNTUP (x, pfx, nm);
|
|
}
|
|
if (nm)
|
|
{
|
|
x = nm & 0xFF000000; nm <<= 8;
|
|
__COUNTUP (x, pfx, nm);
|
|
}
|
|
if (nm)
|
|
{
|
|
x = nm & 0xFF000000; nm <<= 8;
|
|
__COUNTUP (x, pfx, nm);
|
|
}
|
|
if (nm)
|
|
{
|
|
x = nm & 0xFF000000;
|
|
__COUNTUP (x, pfx, 0);
|
|
}
|
|
|
|
return pfx;
|
|
}
|
|
|
|
/* TODO: qse_ip6adtoprefix() */
|