hcl/lib/sa-utl.h

362 lines
7.2 KiB
C
Raw Normal View History

static int str_to_ipv4 (const ooch_t* str, hcl_oow_t len, struct in_addr* inaddr)
{
const ooch_t* end;
int dots = 0, digits = 0;
hcl_uint32_t acc = 0, addr = 0;
ooch_t c;
end = str + len;
do
{
if (str >= end)
{
if (dots < 3 || digits == 0) return -1;
addr = (addr << 8) | acc;
break;
}
c = *str++;
if (c >= '0' && c <= '9')
{
if (digits > 0 && acc == 0) return -1;
acc = acc * 10 + (c - '0');
if (acc > 255) return -1;
digits++;
}
else if (c == '.')
{
if (dots >= 3 || digits == 0) return -1;
addr = (addr << 8) | acc;
dots++; acc = 0; digits = 0;
}
else return -1;
}
while (1);
inaddr->s_addr = hcl_hton32(addr);
return 0;
}
#if (HCL_SIZEOF_STRUCT_SOCKADDR_IN6 > 0)
static int str_to_ipv6 (const ooch_t* src, hcl_oow_t len, struct in6_addr* inaddr)
{
hcl_uint8_t* tp, * endp, * colonp;
const ooch_t* curtok;
ooch_t ch;
int saw_xdigit;
unsigned int val;
const ooch_t* src_end;
src_end = src + len;
HCL_MEMSET (inaddr, 0, HCL_SIZEOF(*inaddr));
tp = &inaddr->s6_addr[0];
endp = &inaddr->s6_addr[HCL_COUNTOF(inaddr->s6_addr)];
colonp = HCL_NULL;
/* Leading :: requires some special handling. */
if (src < src_end && *src == ':')
{
src++;
if (src >= src_end || *src != ':') return -1;
}
curtok = src;
saw_xdigit = 0;
val = 0;
while (src < src_end)
{
int v1;
ch = *src++;
if (ch >= '0' && ch <= '9')
v1 = ch - '0';
else if (ch >= 'A' && ch <= 'F')
v1 = ch - 'A' + 10;
else if (ch >= 'a' && ch <= 'f')
v1 = ch - 'a' + 10;
else v1 = -1;
if (v1 >= 0)
{
val <<= 4;
val |= v1;
if (val > 0xffff) return -1;
saw_xdigit = 1;
continue;
}
if (ch == ':')
{
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++ = (hcl_uint8_t)(val >> 8) & 0xff;
*tp++ = (hcl_uint8_t)val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + HCL_SIZEOF(struct in_addr)) <= endp) &&
str_to_ipv4(curtok, src_end - curtok, (struct in_addr*)tp) == 0)
{
tp += HCL_SIZEOF(struct in_addr*);
saw_xdigit = 0;
break;
}
return -1;
}
if (saw_xdigit)
{
if (tp + HCL_SIZEOF(hcl_uint16_t) > endp) return -1;
*tp++ = (hcl_uint8_t)(val >> 8) & 0xff;
*tp++ = (hcl_uint8_t)val & 0xff;
}
if (colonp != HCL_NULL)
{
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
hcl_oow_t n = tp - colonp;
hcl_oow_t i;
for (i = 1; i <= n; i++)
{
endp[-i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp) return -1;
return 0;
}
#endif
int str_to_sockaddr (hcl_t* hcl, const ooch_t* str, hcl_oow_t len, hcl_sckaddr_t* sckaddr, hcl_scklen_t* scklen)
{
const ooch_t* p;
const ooch_t* end;
oocs_t tmp;
sockaddr_t* nwad = (hcl_sckaddr_t*)sckaddr;
p = str;
end = str + len;
if (p >= end)
{
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "blank address");
return -1;
}
HCL_MEMSET (nwad, 0, HCL_SIZEOF(*nwad));
#if (HCL_SIZEOF_STRUCT_SOCKADDR_IN6 > 0)
if (*p == '[')
{
/* IPv6 address */
tmp.ptr = (ooch_t*)++p; /* skip [ and remember the position */
while (p < end && *p != '%' && *p != ']') p++;
if (p >= end) goto no_rbrack;
tmp.len = p - tmp.ptr;
if (*p == '%')
{
/* handle scope id */
hcl_uint32_t x;
p++; /* skip % */
if (p >= end)
{
/* premature end */
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id blank");
return -1;
}
if (*p >= '0' && *p <= '9')
{
/* numeric scope id */
nwad->in6.sin6_scope_id = 0;
do
{
x = nwad->in6.sin6_scope_id * 10 + (*p - '0');
if (x < nwad->in6.sin6_scope_id)
{
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id too large");
return -1; /* overflow */
}
nwad->in6.sin6_scope_id = x;
p++;
}
while (p < end && *p >= '0' && *p <= '9');
}
else
{
#if 0
TODO:
/* interface name as a scope id? */
const ooch_t* stmp = p;
unsigned int index;
do p++; while (p < end && *p != ']');
if (hcl_nwifwcsntoindex (stmp, p - stmp, &index) <= -1) return -1;
tmpad.u.in6.scope = index;
#endif
}
if (p >= end || *p != ']') goto no_rbrack;
}
p++; /* skip ] */
if (str_to_ipv6(tmp.ptr, tmp.len, &nwad->in6.sin6_addr) <= -1) goto unrecog;
nwad->in6.sin6_family = AF_INET6;
}
else
{
#endif
/* IPv4 address */
tmp.ptr = (ooch_t*)p;
while (p < end && *p != ':') p++;
tmp.len = p - tmp.ptr;
if (str_to_ipv4(tmp.ptr, tmp.len, &nwad->in4.sin_addr) <= -1)
{
#if (HCL_SIZEOF_STRUCT_SOCKADDR_IN6 > 0)
/* check if it is an IPv6 address not enclosed in [].
* the port number can't be specified in this format. */
if (p >= end || *p != ':')
{
/* without :, it can't be an ipv6 address */
goto unrecog;
}
while (p < end && *p != '%') p++;
tmp.len = p - tmp.ptr;
if (str_to_ipv6(tmp.ptr, tmp.len, &nwad->in6.sin6_addr) <= -1) goto unrecog;
if (p < end && *p == '%')
{
/* handle scope id */
hcl_uint32_t x;
p++; /* skip % */
if (p >= end)
{
/* premature end */
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id blank");
return -1;
}
if (*p >= '0' && *p <= '9')
{
/* numeric scope id */
nwad->in6.sin6_scope_id = 0;
do
{
x = nwad->in6.sin6_scope_id * 10 + (*p - '0');
if (x < nwad->in6.sin6_scope_id)
{
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id too large");
return -1; /* overflow */
}
nwad->in6.sin6_scope_id = x;
p++;
}
while (p < end && *p >= '0' && *p <= '9');
}
else
{
#if 0
TODO
/* interface name as a scope id? */
const ooch_t* stmp = p;
unsigned int index;
do p++; while (p < end);
if (hcl_nwifwcsntoindex(stmp, p - stmp, &index) <= -1) return -1;
nwad->in6.sin6_scope_id = index;
#endif
}
}
if (p < end) goto unrecog; /* some gargage after the end? */
nwad->in6.sin6_family = AF_INET6;
*scklen = HCL_SIZEOF(nwad->in6);
return nwad->in6.sin6_family;
#else
goto unrecog;
#endif
}
nwad->in4.sin_family = AF_INET;
#if (HCL_SIZEOF_STRUCT_SOCKADDR_IN6 > 0)
}
#endif
if (p < end && *p == ':')
{
/* port number */
hcl_uint32_t port = 0;
p++; /* skip : */
tmp.ptr = (ooch_t*)p;
while (p < end && *p >= '0' && *p <= '9')
{
port = port * 10 + (*p - '0');
p++;
}
tmp.len = p - tmp.ptr;
if (tmp.len <= 0 || tmp.len >= 6 ||
port > HCL_TYPE_MAX(hcl_uint16_t))
{
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "port number blank or too large");
return -1;
}
#if (HCL_SIZEOF_STRUCT_SOCKADDR_IN6 > 0)
if (nwad->in4.sin_family == AF_INET)
nwad->in4.sin_port = hcl_hton16(port);
else
nwad->in6.sin6_port = hcl_hton16(port);
#else
nwad->in4.sin_port = hcl_hton16(port);
#endif
}
*scklen = (nwad->in4.sin_family == AF_INET)? HCL_SIZEOF(nwad->in4): HCL_SIZEOF(nwad->in6);
return nwad->in4.sin_family;
unrecog:
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "unrecognized address");
return -1;
no_rbrack:
if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "missing right bracket");
return -1;
}