/* * $Id$ * Copyright 2006-2014 Chung, Hyung-Hwan. This file is part of QSE. QSE is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QSE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QSE. If not, see . */ /* 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 #include #include #include #include "mem.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; cur.base = -1; 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_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_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; cur.base = -1; 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; }