From f48846833addc18d7f144fb6cf7437dc3075715a Mon Sep 17 00:00:00 2001
From: Florian Fischer <florian.fl.fischer@fau.de>
Date: Tue, 2 Feb 2021 17:35:56 +0100
Subject: [PATCH] improve networking code

Use getaddrinfo instead to get the socket information instead of
using always ipv4 sockets.
Pass host and port as strings for use with getaddrinfo.
---
 apps/EchoServer.cpp                 | 14 ++++++-------
 emper/io.hpp                        |  2 +-
 emper/io/io.cpp                     | 28 +++++++++++++++-----------
 tests/ConcurrentNetworkEchoTest.cpp |  5 ++---
 tests/SimpleDiskAndNetworkTest.cpp  |  4 +++-
 tests/SimpleNetworkTest.cpp         | 11 +++++-----
 tests/fixtures/network.cpp          | 31 +++++++++++++++++------------
 tests/fixtures/network.hpp          |  2 +-
 8 files changed, 53 insertions(+), 44 deletions(-)

diff --git a/apps/EchoServer.cpp b/apps/EchoServer.cpp
index 8c1e8e72..e0c96ef1 100644
--- a/apps/EchoServer.cpp
+++ b/apps/EchoServer.cpp
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: LGPL-3.0-or-later
 // Copyright © 2020-2021 Florian Fischer
+#include <sys/socket.h>
 #include <sys/types.h>
 
 #include <cerrno>
@@ -9,17 +10,16 @@
 #include <string>
 
 #include "Common.hpp"
+#include "Debug.hpp"
 #include "Runtime.hpp"
-#include "emper-common.h"
 #include "io.hpp"
 
-const int DECIMAL = 10;
-const std::string HOST = "0.0.0.0";
-const int PORT = 12345;
+const std::string HOST = "::";
+const std::string PORT = "12345";
 
 auto main(int argc, char* argv[]) -> int {
-	int port = PORT;
 	std::string host = HOST;
+	std::string port = PORT;
 
 	if (argc > 2) {
 		std::cerr << "Usage: " << argv[0] << " [port]" << std::endl;
@@ -27,9 +27,7 @@ auto main(int argc, char* argv[]) -> int {
 	}
 
 	if (argc > 1) {
-		long aport = strtol(argv[1], nullptr, DECIMAL);
-		assert(aport <= INT_MAX && aport >= INT_MIN);
-		port = (int)aport;
+		port = std::string(argv[1]);
 	}
 
 	std::cout << "Echoserver listening on " << host << ":" << port << std::endl;
diff --git a/emper/io.hpp b/emper/io.hpp
index 4addc610..10b22878 100644
--- a/emper/io.hpp
+++ b/emper/io.hpp
@@ -466,6 +466,6 @@ inline void closeAndForget(int fd) {
  *
  * @return nullptr on error, otherwise the TCP listener Fiber
  */
-auto tcp_listener(std::string &host, int &port, const std::function<void(int)> &handler,
+auto tcp_listener(std::string &host, std::string &port, const std::function<void(int)> &handler,
 									int backlog = 1024) -> Fiber *;
 }	 // namespace emper::io
diff --git a/emper/io/io.cpp b/emper/io/io.cpp
index 140f91c9..83c1abeb 100644
--- a/emper/io/io.cpp
+++ b/emper/io/io.cpp
@@ -2,8 +2,7 @@
 // Copyright © 2020-2021 Florian Fischer
 #include "io.hpp"
 
-#include <arpa/inet.h>
-#include <netinet/in.h>
+#include <netdb.h>
 #include <sys/socket.h>
 
 #include <cerrno>
@@ -19,23 +18,28 @@
 #endif
 
 namespace emper::io {
-auto tcp_listener(std::string& host, int& port, const std::function<void(int)>& handler,
+auto tcp_listener(std::string& host, std::string& port, const std::function<void(int)>& handler,
 									int backlog) -> Fiber* {
 	int listen_socket;
-	struct sockaddr_in servaddr;
+	struct addrinfo* servaddr;
+
+	int err = getaddrinfo(host.c_str(), port.c_str(), nullptr, &servaddr);
+	if (err) {
+		if (err == EAI_SYSTEM) {
+			LOGE("getaddrinfo failed: " << strerror(errno));
+			return nullptr;
+		}
+
+		LOGE("error in getaddrinfo: " << gai_strerror(err));
+		return nullptr;
+	}
 
 	// socket creation and verification
-	listen_socket = socket(AF_INET, SOCK_STREAM, 0);
+	listen_socket = socket(servaddr->ai_family, servaddr->ai_socktype, servaddr->ai_protocol);
 	if (listen_socket == -1) {
 		LOGE("listen socket creation failed: " << strerror(errno));
 		return nullptr;
 	}
-	memset(&servaddr, 0, sizeof(servaddr));
-
-	// assign IP, PORT
-	servaddr.sin_family = AF_INET;
-	servaddr.sin_addr.s_addr = inet_addr(host.c_str());
-	servaddr.sin_port = htons(port);
 
 	int reuseaddr = 1;
 	if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) {
@@ -43,7 +47,7 @@ auto tcp_listener(std::string& host, int& port, const std::function<void(int)>&
 		return nullptr;
 	}
 
-	if (bind(listen_socket, (sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
+	if (bind(listen_socket, servaddr->ai_addr, servaddr->ai_addrlen) == -1) {
 		LOGE("bind failed: " << strerror(errno));
 		return nullptr;
 	}
diff --git a/tests/ConcurrentNetworkEchoTest.cpp b/tests/ConcurrentNetworkEchoTest.cpp
index 0ccfddab..3a2583c3 100644
--- a/tests/ConcurrentNetworkEchoTest.cpp
+++ b/tests/ConcurrentNetworkEchoTest.cpp
@@ -13,7 +13,6 @@
 #include "fixtures/network.hpp"
 #include "io.hpp"
 
-#define PORT 4241
 #define BUF_SIZE 1024
 
 auto main(int argc, char* argv[]) -> int {
@@ -23,7 +22,7 @@ auto main(int argc, char* argv[]) -> int {
 
 	unsigned int client_count = 10;
 	unsigned int echos = 1000;
-	int port = PORT;
+	std::string port = "4241";
 	std::string host = "127.0.0.1";
 
 	if (argc > 3) {
@@ -50,7 +49,7 @@ auto main(int argc, char* argv[]) -> int {
 			spawn(
 					[&] {
 						const std::vector<std::string> msgs = {"foo"};
-						echo_client("127.0.0.1", PORT, msgs, echos);
+						echo_client(host, port, msgs, echos);
 					},
 					cps);
 		}
diff --git a/tests/SimpleDiskAndNetworkTest.cpp b/tests/SimpleDiskAndNetworkTest.cpp
index 60a2e569..182bcdbf 100644
--- a/tests/SimpleDiskAndNetworkTest.cpp
+++ b/tests/SimpleDiskAndNetworkTest.cpp
@@ -170,7 +170,9 @@ void emperTest() {
 	spawn(
 			[] {
 				const std::vector<std::string> msgs{"foo", "bar"};
-				echo_client("127.0.0.1", PORT, msgs);
+				std::string port = std::to_string(PORT);
+				std::string host("127.0.0.1");
+				echo_client(host, port, msgs);
 			},
 			cps);
 
diff --git a/tests/SimpleNetworkTest.cpp b/tests/SimpleNetworkTest.cpp
index 35158625..d3161bc5 100644
--- a/tests/SimpleNetworkTest.cpp
+++ b/tests/SimpleNetworkTest.cpp
@@ -9,17 +9,18 @@
 #include "io.hpp"												 // for tcp_listener
 
 #define MAX 1024
-int PORT = 4242;
-std::string ADDR = "127.0.0.1";
 
 void emperTest() {
+	std::string port = "4242";
+	std::string addr = "127.0.0.1";
+
 	CPS cps;
-	async(emper::io::tcp_listener(ADDR, PORT, echo_serve));
+	async(emper::io::tcp_listener(addr, port, echo_serve));
 
 	spawn(
-			[] {
+			[&] {
 				const std::vector<std::string> strings = {"foo", "bar", std::string(MAX, 'a'), "quit\n"};
-				echo_client(ADDR.c_str(), PORT, strings);
+				echo_client(addr, port, strings);
 			},
 			cps);
 
diff --git a/tests/fixtures/network.cpp b/tests/fixtures/network.cpp
index 97858086..7ca1c6ce 100644
--- a/tests/fixtures/network.cpp
+++ b/tests/fixtures/network.cpp
@@ -2,17 +2,18 @@
 // Copyright © 2020-2021 Florian Fischer
 #include "network.hpp"
 
-#include <arpa/inet.h>
-#include <netinet/in.h>
+#include <netdb.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 
 #include <cassert>
 #include <cstdlib>
 #include <cstring>
+#include <ostream>
 #include <vector>
 
 #include "Common.hpp"
+#include "Debug.hpp"
 #include "io.hpp"
 
 #define MAX 1024
@@ -47,22 +48,26 @@ void echo_serve(int client_socket) {
 	}
 }
 
-void echo_client(const char* addr, int port, const std::vector<std::string>& msgs,
+void echo_client(std::string& host, std::string& port, const std::vector<std::string>& msgs,
 								 size_t iterations) {
-	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+	struct addrinfo* server;
+
+	int err = getaddrinfo(host.c_str(), port.c_str(), nullptr, &server);
+	if (err) {
+		if (err == EAI_SYSTEM) {
+			DIE_MSG_ERRNO("getaddrinfo failed");
+		} else {
+			LOGE("error in getaddrinfo: " << gai_strerror(err));
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	int sockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
 	if (sockfd == -1) {
 		DIE_MSG_ERRNO("client socket creation failed");
 	}
 
-	struct sockaddr_in server_addr;
-	// assign IP, PORT
-	memset(&server_addr, 0, sizeof(server_addr));
-	server_addr.sin_family = AF_INET;
-	server_addr.sin_addr.s_addr = inet_addr(addr);
-	server_addr.sin_port = htons(port);
-
-	if (emper::io::connectAndWait(sockfd, reinterpret_cast<struct sockaddr*>(&server_addr),
-																sizeof(server_addr)) == -1) {
+	if (emper::io::connectAndWait(sockfd, server->ai_addr, server->ai_addrlen) == -1) {
 		DIE_MSG_ERRNO("connect failed");
 	}
 
diff --git a/tests/fixtures/network.hpp b/tests/fixtures/network.hpp
index 271cb2d3..8c50c153 100644
--- a/tests/fixtures/network.hpp
+++ b/tests/fixtures/network.hpp
@@ -8,5 +8,5 @@
 
 void echo_serve(int client_socket);
 
-void echo_client(const char* addr, int port, const std::vector<std::string>& msgs,
+void echo_client(std::string& host, std::string& port, const std::vector<std::string>& msgs,
 								 size_t iterations = 1);
-- 
GitLab