net: Add function to parse IP address string
The net_ipaddr_parse() will take a string with optional port
number and convert its information into struct sockaddr.
The format of the IP string can be:
192.0.2.1:80
192.0.2.42
[2001:db8::1]:8080
[2001:db8::2]
2001:db::42
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
diff --git a/include/net/net_ip.h b/include/net/net_ip.h
index 0db5140..e68b9e9 100644
--- a/include/net/net_ip.h
+++ b/include/net/net_ip.h
@@ -870,6 +870,30 @@
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:
+ * 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.
*
* @details This function compares TCP sequence numbers,
diff --git a/subsys/net/ip/utils.c b/subsys/net/ip/utils.c
index 6741d7b..e4ad0ec 100644
--- a/subsys/net/ip/utils.c
+++ b/subsys/net/ip/utils.c
@@ -518,3 +518,206 @@
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
+}