add new osmo_sockaddr_from_str_and_uint() function

The function is basically a shortcut for getaddrinfo with storing the
output data into our 'struct osmo_sockaddr'.

Change-Id: I6b5c0bf8ca97e6358d992fb2ff45ffd53ba15197
Related: SYS#6657
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
index db55863..03f1d39 100644
--- a/include/osmocom/core/socket.h
+++ b/include/osmocom/core/socket.h
@@ -74,6 +74,7 @@
 
 int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os);
 int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len);
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port);
 
 int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *addr);
 
diff --git a/src/core/libosmocore.map b/src/core/libosmocore.map
index ae8a068..3d6aa42 100644
--- a/src/core/libosmocore.map
+++ b/src/core/libosmocore.map
@@ -374,6 +374,7 @@
 osmo_signal_unregister_handler;
 osmo_sockaddr_cmp;
 osmo_sockaddr_from_octets;
+osmo_sockaddr_from_str_and_uint;
 osmo_sockaddr_in_to_str_and_uint;
 osmo_sockaddr_is_any;
 osmo_sockaddr_is_local;
diff --git a/src/core/socket.c b/src/core/socket.c
index fa5fb88..1dc8e46 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1662,6 +1662,27 @@
 	}
 }
 
+/*! Convert an IP address string (and port number) into a 'struct osmo_sockaddr'.
+ *  \param[out] osa_out caller-allocated osmo_sockaddr storage
+ *  \param[in] ipstr IP[v4,v6] address in string format
+ *  \param[in] port port number (host byte order)
+ *  \returns 0 on success; negative on error. */
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port)
+{
+	struct addrinfo *ai = addrinfo_helper(AF_UNSPEC, 0, 0, ipstr, port, true);
+
+	if (!ai)
+		return -EIO;
+
+	if (ai->ai_addrlen > sizeof(*osa_out))
+		return -ENOSPC;
+
+	memcpy(&osa_out->u.sa, ai->ai_addr, ai->ai_addrlen);
+	freeaddrinfo(ai);
+
+	return 0;
+}
+
 /*! Initialize a unix domain socket (including bind/connect)
  *  \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
  *  \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c
index ddb6926..34130b2 100644
--- a/tests/socket/socket_test.c
+++ b/tests/socket/socket_test.c
@@ -315,6 +315,7 @@
 	const char *result;
 	struct osmo_sockaddr localhost4 = {};
 	struct osmo_sockaddr localhost6 = {};
+	struct osmo_sockaddr osa = {};
 
 	localhost4.u.sin = (struct sockaddr_in){
 		.sin_family = AF_INET,
@@ -383,6 +384,18 @@
 	result = osmo_sockaddr_to_str(&localhost6);
 	printf("Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer\n");
 	OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
+
+	printf("Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0\n");
+	OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "0.0.0.0", 1234) == 0);
+	OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+	printf("Checking osmo_sockaddr_from_str_and_uint for ::\n");
+	OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "::", 1234) == 0);
+	OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+	printf("Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4\n");
+	OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "1.2.3.4", 1234) == 0);
+	OSMO_ASSERT(!osmo_sockaddr_is_any(&osa));
 }
 
 static void test_osa_netmask_prefixlen(void)
diff --git a/tests/socket/socket_test.ok b/tests/socket/socket_test.ok
index 236c011..2b1c100 100644
--- a/tests/socket/socket_test.ok
+++ b/tests/socket/socket_test.ok
@@ -31,3 +31,6 @@
 Checking osmo_sockaddr_to_str_buf long IPv6
 Checking osmo_sockaddr_to_str_buf long IPv6 port
 Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer
+Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0
+Checking osmo_sockaddr_from_str_and_uint for ::
+Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4