364 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 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 = (sockaddr_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, y;
 | |
| 
 | |
| 			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 */
 | |
| 				y = 0;
 | |
| 				do
 | |
| 				{
 | |
| 					x = y * 10 + (*p - '0');
 | |
| 					if (x < y)
 | |
| 					{
 | |
| 						if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id too large");
 | |
| 						return -1; /* overflow */
 | |
| 					}
 | |
| 					y = x;
 | |
| 					p++;
 | |
| 				}
 | |
| 				while (p < end && *p >= '0' && *p <= '9');
 | |
| 				//nwad->in6.sin6_scope_id = y;
 | |
| 			}
 | |
| 			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, y;
 | |
| 
 | |
| 				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 */
 | |
| 					y = 0;
 | |
| 					do
 | |
| 					{
 | |
| 						x = y * 10 + (*p - '0');
 | |
| 						if (x < y)
 | |
| 						{
 | |
| 							if (hcl) hcl_seterrbfmt (hcl, HCL_EINVAL, "scope id too large");
 | |
| 							return -1; /* overflow */
 | |
| 						}
 | |
| 						y = x;
 | |
| 						p++;
 | |
| 					}
 | |
| 					while (p < end && *p >= '0' && *p <= '9');
 | |
| 					//nwad->in6.sin6_scope_id = y;
 | |
| 				}
 | |
| 				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;
 | |
| }
 |