Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions include/net/net_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,30 @@ int net_addr_pton(sa_family_t family, const char *src, void *dst);
char *net_addr_ntop(sa_family_t family, const void *src,
char *dst, size_t size);

/**
* @brief Parse a string that contains either IPv4 or IPv6 address
* and optional port, and store the information in user supplied
* sockaddr struct.
*
* @details Syntax of the IP address string:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say this should specify explicitly what will be on sockaddr if port is not specified (e.g. port field set to 0, or yet better not touched, to let the caller to initialize it to default value prior to call).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that makes sense.

* 192.0.2.1:80
* 192.0.2.42
* [2001:db8::1]:8080
* [2001:db8::2]
* 2001:db::42
* Note that the str_len parameter is used to restrict the amount of
* characters that are checked. If the string does not contain port
* number, then the port number in sockaddr is not modified.
*
* @param str String that contains the IP address.
* @param str_len Length of the string to be parsed.
* @param addr Pointer to user supplied struct sockaddr.
*
* @return True if parsing could be done, false otherwise.
*/
bool net_ipaddr_parse(const char *str, size_t str_len,
struct sockaddr *addr);

/**
* @brief Compare TCP sequence numbers.
*
Expand Down
203 changes: 203 additions & 0 deletions subsys/net/ip/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,206 @@ bool net_header_fits(struct net_pkt *pkt, u8_t *hdr, size_t hdr_size)

return false;
}

#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
static bool convert_port(const char *buf, u16_t *port)
{
unsigned long tmp;
char *endptr;

tmp = strtoul(buf, &endptr, 10);
if ((endptr == buf && tmp == 0) ||
!(*buf != '\0' && *endptr == '\0') ||
((unsigned long)(unsigned short)tmp != tmp)) {
return false;
}

*port = tmp;

return true;
}
#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */

#if defined(CONFIG_NET_IPV6)
static bool parse_ipv6(const char *str, size_t str_len,
struct sockaddr *addr, bool has_port)
{
char *ptr = NULL;
struct in6_addr *addr6;
char ipaddr[INET6_ADDRSTRLEN + 1];
int end, len, ret, i;
u16_t port;

len = min(INET6_ADDRSTRLEN, str_len);

for (i = 0; i < len; i++) {
if (!str[i]) {
len = i;
break;
}
}

if (has_port) {
/* IPv6 address with port number */
ptr = memchr(str, ']', len);
if (!ptr) {
return false;
}

end = min(len, ptr - (str + 1));
memcpy(ipaddr, str + 1, end);
} else {
end = len;
memcpy(ipaddr, str, end);
}

ipaddr[end] = '\0';

addr6 = &net_sin6(addr)->sin6_addr;

ret = net_addr_pton(AF_INET6, ipaddr, addr6);
if (ret < 0) {
return false;
}

net_sin6(addr)->sin6_family = AF_INET6;

if (!has_port) {
return true;
}

if ((ptr + 1) < (str + str_len) && *(ptr + 1) == ':') {
len = str_len - end;

/* Re-use the ipaddr buf for port conversion */
memcpy(ipaddr, ptr + 2, len);
ipaddr[len] = '\0';

ret = convert_port(ipaddr, &port);
if (!ret) {
return false;
}

net_sin6(addr)->sin6_port = htons(port);

NET_DBG("IPv6 host %s port %d",
net_addr_ntop(AF_INET6, addr6,
ipaddr, sizeof(ipaddr) - 1),
port);
} else {
NET_DBG("IPv6 host %s",
net_addr_ntop(AF_INET6, addr6,
ipaddr, sizeof(ipaddr) - 1));
}

return true;
}
#endif /* CONFIG_NET_IPV6 */

#if defined(CONFIG_NET_IPV4)
static bool parse_ipv4(const char *str, size_t str_len,
struct sockaddr *addr, bool has_port)
{
char *ptr = NULL;
char ipaddr[NET_IPV4_ADDR_LEN + 1];
struct in_addr *addr4;
int end, len, ret, i;
u16_t port;

len = min(NET_IPV4_ADDR_LEN, str_len);

for (i = 0; i < len; i++) {
if (!str[i]) {
len = i;
break;
}
}

if (has_port) {
/* IPv4 address with port number */
ptr = memchr(str, ':', len);
if (!ptr) {
return false;
}

end = min(len, ptr - str);
} else {
end = len;
}

memcpy(ipaddr, str, end);
ipaddr[end] = '\0';

addr4 = &net_sin(addr)->sin_addr;

ret = net_addr_pton(AF_INET, ipaddr, addr4);
if (ret < 0) {
return false;
}

net_sin(addr)->sin_family = AF_INET;

if (!has_port) {
return true;
}

memcpy(ipaddr, ptr + 1, str_len - end);
ipaddr[str_len - end] = '\0';

ret = convert_port(ipaddr, &port);
if (!ret) {
return false;
}

net_sin(addr)->sin_port = htons(port);

NET_DBG("IPv4 host %s port %d",
net_addr_ntop(AF_INET, addr4,
ipaddr, sizeof(ipaddr) - 1),
port);
return true;
}
#endif /* CONFIG_NET_IPV4 */

bool net_ipaddr_parse(const char *str, size_t str_len, struct sockaddr *addr)
{
int i, count;

if (*str == '[') {
#if defined(CONFIG_NET_IPV6)
return parse_ipv6(str, str_len, addr, true);
#else
return false;
#endif /* CONFIG_NET_IPV6 */
}

for (count = i = 0; str[i] && i < str_len; i++) {
if (str[i] == ':') {
count++;
}
}

if (count == 1) {
#if defined(CONFIG_NET_IPV4)
return parse_ipv4(str, str_len, addr, true);
#else
return false;
#endif /* CONFIG_NET_IPV4 */
}

#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6)
if (!parse_ipv4(str, str_len, addr, false)) {
return parse_ipv6(str, str_len, addr, false);
}

return true;
#endif

#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
return parse_ipv4(str, str_len, addr, false);
#endif

#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
return parse_ipv6(str, str_len, addr, false);
#endif
}
Loading