From 37d6637784f2776e03cf8a1875980f492c0b3b46 Mon Sep 17 00:00:00 2001
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Date: Tue, 5 Mar 2019 23:59:38 +0300
Subject: [PATCH] sio: implement getsockname wrapper

SWIM wants to allow to bind to zero ports so as the kernel could
choose any free port automatically. It is needed mainly for
tests.

Zero port means that a real port is known only after bind() has
called, and getsockname() should be used to get it. SWIM uses sio
library for such lowlevel API. This is why that function is added
to sio.

Needed for #3234
---
 src/lib/core/sio.c   | 10 ++++++++++
 src/lib/core/sio.h   | 15 +++++++++++++++
 test/unit/sio.c      | 23 ++++++++++++++++++++++-
 test/unit/sio.result |  9 ++++++++-
 4 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
index bb8e508d94..8f25b8159a 100644
--- a/src/lib/core/sio.c
+++ b/src/lib/core/sio.c
@@ -293,6 +293,16 @@ sio_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen)
 	return 0;
 }
 
+int
+sio_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen)
+{
+	if (getsockname(fd, addr, addrlen) < 0) {
+		diag_set(SocketError, sio_socketname(fd), "getsockname");
+		return -1;
+	}
+	return 0;
+}
+
 const char *
 sio_strfaddr(const struct sockaddr *addr, socklen_t addrlen)
 {
diff --git a/src/lib/core/sio.h b/src/lib/core/sio.h
index f0998e2f35..093b5f5baa 100644
--- a/src/lib/core/sio.h
+++ b/src/lib/core/sio.h
@@ -86,6 +86,21 @@ sio_strfaddr(const struct sockaddr *addr, socklen_t addrlen);
 int
 sio_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen);
 
+/**
+ * Fill @a addr with a real address currently bound to @a fd
+ * socket.
+ *
+ * @param fd Socket.
+ * @param[out] addr An address structure to fill.
+ * @param[in][out] addlen On input it is a size of @a addr as a
+ *                 buffer. On output it becomes a size of a new
+ *                 content of @a addr.
+ * @retval 0 Success.
+ * @retval -1 Error.
+ */
+int
+sio_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen);
+
 /**
  * Advance write position in the iovec array
  * based on its current value and the number of
diff --git a/test/unit/sio.c b/test/unit/sio.c
index 84a86aac3f..dffdd5592d 100644
--- a/test/unit/sio.c
+++ b/test/unit/sio.c
@@ -78,6 +78,26 @@ check_uri_to_addr(void)
 	footer();
 }
 
+static void
+check_auto_bind(void)
+{
+	header();
+	plan(3);
+
+	struct sockaddr_in addr;
+	socklen_t addrlen = sizeof(addr);
+	sio_uri_to_addr("127.0.0.1:0", (struct sockaddr *) &addr);
+	int fd = sio_socket(AF_INET, SOCK_STREAM, 0);
+	is(sio_bind(fd, (struct sockaddr *) &addr, sizeof(addr)), 0,
+	   "bind to 0 works");
+	is(sio_getsockname(fd, (struct sockaddr *) &addr, &addrlen), 0,
+	   "getsockname works on 0 bind");
+	isnt(addr.sin_port, 0, "a real port is returned");
+
+	check_plan();
+	footer();
+}
+
 int
 main(void)
 {
@@ -85,8 +105,9 @@ main(void)
 	fiber_init(fiber_c_invoke);
 
 	header();
-	plan(1);
+	plan(2);
 	check_uri_to_addr();
+	check_auto_bind();
 	int rc = check_plan();
 	footer();
 
diff --git a/test/unit/sio.result b/test/unit/sio.result
index 32a5babae6..e5712ad3cc 100644
--- a/test/unit/sio.result
+++ b/test/unit/sio.result
@@ -1,5 +1,5 @@
 	*** main ***
-1..1
+1..2
 	*** check_uri_to_addr ***
     1..18
     ok 1 - invalid uri is detected
@@ -22,4 +22,11 @@
     ok 18 - invalid IP
 ok 1 - subtests
 	*** check_uri_to_addr: done ***
+	*** check_auto_bind ***
+    1..3
+    ok 1 - bind to 0 works
+    ok 2 - getsockname works on 0 bind
+    ok 3 - a real port is returned
+ok 2 - subtests
+	*** check_auto_bind: done ***
 	*** main: done ***
-- 
GitLab