← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/net-boost-asio into lp:widelands

 

Notabilis has proposed merging lp:~widelands-dev/widelands/net-boost-asio into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1689087 in widelands: "Implementing a relay server"
  https://bugs.launchpad.net/widelands/+bug/1689087

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/net-boost-asio/+merge/324364

Replaced the SDL-Net network code with Boost.Asio. This removes SDL-Net as a required library, unfortunately we now require boost-system.

Also added support for IPv6. As of now, this only works for games in the local network, since the metaserver host does not support IPv6 at the moment.

Warning: This branch is not yet ready for merge. I removed the sdl-net library but I don't know / can't test how to add the boost requirements to the build process for Windows and MacOS. For Linux I only had to add the option to link with boost-system in CMakeList.txt. It would be great if someone could test the other operating systems and, if needed, add the required linking for them. The commit where I removed the sdl-net references is:
http://bazaar.launchpad.net/~widelands-dev/widelands/net-boost-asio/revision/8365
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/net-boost-asio into lp:widelands.
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2017-02-28 08:31:53 +0000
+++ CMakeLists.txt	2017-05-20 19:06:48 +0000
@@ -50,7 +50,8 @@
   COMPONENTS
     unit_test_framework
     regex
-  REQUIRED)
+  REQUIRED
+    system)
 
 find_package (PythonInterp REQUIRED)
 
@@ -60,7 +61,6 @@
 find_package(SDL2 REQUIRED)
 find_package(SDL2_image REQUIRED)
 find_package(SDL2_mixer REQUIRED)
-find_package(SDL2_net REQUIRED)
 find_package(SDL2_ttf REQUIRED)
 find_package(ZLIB REQUIRED)
 find_package(ICU REQUIRED)

=== modified file 'CREDITS'
--- CREDITS	2014-10-13 15:04:50 +0000
+++ CREDITS	2017-05-20 19:06:48 +0000
@@ -10,7 +10,6 @@
 
     libSDL2
     libSDL2_mixer
-    libSDL2_net
     libSDL2_image
     libSDL2_ttf
     * All files from SDL2-Project

=== modified file 'appveyor.yml'
--- appveyor.yml	2016-10-06 20:34:46 +0000
+++ appveyor.yml	2017-05-20 19:06:48 +0000
@@ -19,7 +19,7 @@
   - cmd: "bash --login -c \"pacman -Su --noconfirm\""
   - cmd: "bash --login -c \"pacman -Su --noconfirm\""
   # Installed required libs
-  - cmd: "bash --login -c \"pacman --noconfirm -S mingw-w64-%MINGWSUFFIX%-ninja mingw-w64-%MINGWSUFFIX%-boost mingw-w64-%MINGWSUFFIX%-SDL2_net mingw-w64-%MINGWSUFFIX%-SDL2_ttf mingw-w64-%MINGWSUFFIX%-SDL2_mixer mingw-w64-%MINGWSUFFIX%-SDL2_image mingw-w64-%MINGWSUFFIX%-glbinding\""
+  - cmd: "bash --login -c \"pacman --noconfirm -S mingw-w64-%MINGWSUFFIX%-ninja mingw-w64-%MINGWSUFFIX%-boost mingw-w64-%MINGWSUFFIX%-SDL2_ttf mingw-w64-%MINGWSUFFIX%-SDL2_mixer mingw-w64-%MINGWSUFFIX%-SDL2_image mingw-w64-%MINGWSUFFIX%-glbinding\""
 
 shallow_clone: true
 

=== removed file 'cmake/Modules/FindSDL2_net.cmake'
--- cmake/Modules/FindSDL2_net.cmake	2014-10-13 15:04:50 +0000
+++ cmake/Modules/FindSDL2_net.cmake	1970-01-01 00:00:00 +0000
@@ -1,88 +0,0 @@
-# - Locate SDL2_net library
-# This module defines:
-#  SDL2_NET_LIBRARIES, the name of the library to link against
-#  SDL2_NET_INCLUDE_DIRS, where to find the headers
-#  SDL2_NET_FOUND, if false, do not try to link against
-#  SDL2_NET_VERSION_STRING - human-readable string containing the version of SDL2_net
-#
-# For backward compatiblity the following variables are also set:
-#  SDL2NET_LIBRARY (same value as SDL2_NET_LIBRARIES)
-#  SDL2NET_INCLUDE_DIR (same value as SDL2_NET_INCLUDE_DIRS)
-#  SDL2NET_FOUND (same value as SDL2_NET_FOUND)
-#
-# $SDL2DIR is an environment variable that would
-# correspond to the ./configure --prefix=$SDL2DIR
-# used in building SDL2.
-#
-# Created by Eric Wing. This was influenced by the FindSDL2.cmake
-# module, but with modifications to recognize OS X frameworks and
-# additional Unix paths (FreeBSD, etc).
-
-#=============================================================================
-# Copyright 2005-2009 Kitware, Inc.
-# Copyright 2012 Benjamin Eikel
-#
-# Distributed under the OSI-approved BSD License (the "License");
-# see accompanying file Copyright.txt for details.
-#
-# This software is distributed WITHOUT ANY WARRANTY; without even the
-# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the License for more information.
-#=============================================================================
-# (To distribute this file outside of CMake, substitute the full
-#  License text for the above reference.)
-
-if(NOT SDL2_NET_INCLUDE_DIR AND SDL2NET_INCLUDE_DIR)
-  set(SDL2_NET_INCLUDE_DIR ${SDL2NET_INCLUDE_DIR} CACHE PATH "directory cache
-entry initialized from old variable name")
-endif()
-find_path(SDL2_NET_INCLUDE_DIR SDL_net.h
-  HINTS
-    ENV SDL2NETDIR
-    ENV SDL2DIR
-  PATH_SUFFIXES include/SDL2 include
-)
-
-if(NOT SDL2_NET_LIBRARY AND SDL2NET_LIBRARY)
-  set(SDL2_NET_LIBRARY ${SDL2NET_LIBRARY} CACHE FILEPATH "file cache entry
-initialized from old variable name")
-endif()
-find_library(SDL2_NET_LIBRARY
-  NAMES SDL2_net
-  HINTS
-    ENV SDL2NETDIR
-    ENV SDL2DIR
-  PATH_SUFFIXES lib
-)
-
-if(SDL2_NET_INCLUDE_DIR AND EXISTS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h")
-  file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+[0-9]+$")
-  file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+[0-9]+$")
-  file(STRINGS "${SDL2_NET_INCLUDE_DIR}/SDL_net.h" SDL_NET_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+[0-9]+$")
-  string(REGEX REPLACE "^#define[ \t]+SDL_NET_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_MAJOR "${SDL_NET_VERSION_MAJOR_LINE}")
-  string(REGEX REPLACE "^#define[ \t]+SDL_NET_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_MINOR "${SDL_NET_VERSION_MINOR_LINE}")
-  string(REGEX REPLACE "^#define[ \t]+SDL_NET_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL_NET_VERSION_PATCH "${SDL_NET_VERSION_PATCH_LINE}")
-  set(SDL2_NET_VERSION_STRING ${SDL_NET_VERSION_MAJOR}.${SDL_NET_VERSION_MINOR}.${SDL_NET_VERSION_PATCH})
-  unset(SDL_NET_VERSION_MAJOR_LINE)
-  unset(SDL_NET_VERSION_MINOR_LINE)
-  unset(SDL_NET_VERSION_PATCH_LINE)
-  unset(SDL_NET_VERSION_MAJOR)
-  unset(SDL_NET_VERSION_MINOR)
-  unset(SDL_NET_VERSION_PATCH)
-endif()
-
-set(SDL2_NET_LIBRARIES ${SDL2_NET_LIBRARY})
-set(SDL2_NET_INCLUDE_DIRS ${SDL2_NET_INCLUDE_DIR})
-
-# include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
-
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_net
-                                  REQUIRED_VARS SDL2_NET_LIBRARIES SDL2_NET_INCLUDE_DIRS
-                                  VERSION_VAR SDL2_NET_VERSION_STRING)
-
-# for backward compatiblity
-set(SDL2NET_LIBRARY ${SDL2_NET_LIBRARIES})
-set(SDL2NET_INCLUDE_DIR ${SDL2_NET_INCLUDE_DIRS})
-set(SDL2NET_FOUND ${SDL2_NET_FOUND})
-
-mark_as_advanced(SDL2_NET_LIBRARY SDL2_NET_INCLUDE_DIR)

=== modified file 'cmake/WlFunctions.cmake'
--- cmake/WlFunctions.cmake	2016-02-06 16:17:23 +0000
+++ cmake/WlFunctions.cmake	2017-05-20 19:06:48 +0000
@@ -12,7 +12,6 @@
     USES_SDL2
     USES_SDL2_IMAGE
     USES_SDL2_MIXER
-    USES_SDL2_NET
     USES_SDL2_TTF
     USES_ZLIB
     USES_ICU
@@ -127,11 +126,6 @@
     target_link_libraries(${NAME} ${SDL2MIXER_LIBRARY})
   endif()
 
-  if(ARG_USES_SDL2_NET)
-    wl_include_system_directories(${NAME} ${SDL2NET_INCLUDE_DIR})
-    target_link_libraries(${NAME} ${SDL2NET_LIBRARY})
-  endif()
-
   if(ARG_USES_SDL2_IMAGE)
     wl_include_system_directories(${NAME} ${SDL2IMAGE_INCLUDE_DIR})
     target_link_libraries(${NAME} ${SDL2IMAGE_LIBRARY})

=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt	2017-05-07 20:27:21 +0000
+++ src/network/CMakeLists.txt	2017-05-20 19:06:48 +0000
@@ -23,8 +23,6 @@
     network_player_settings_backend.cc
     network_player_settings_backend.h
     network_protocol.h
-    network_system.h
-  USES_SDL2_NET
   DEPENDS
     ai
     base_exceptions

=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc	2017-05-11 10:45:44 +0000
+++ src/network/gameclient.cc	2017-05-20 19:06:48 +0000
@@ -42,7 +42,6 @@
 #include "network/internet_gaming.h"
 #include "network/network_gaming_messages.h"
 #include "network/network_protocol.h"
-#include "network/network_system.h"
 #include "scripting/lua_interface.h"
 #include "scripting/lua_table.h"
 #include "ui_basic/messagebox.h"
@@ -89,17 +88,13 @@
 	std::vector<ChatMessage> chatmessages;
 };
 
-GameClient::GameClient(const std::string& host,
-                       const uint16_t port,
-                       const std::string& playername,
-                       bool internet)
+GameClient::GameClient(const NetAddress& host, const std::string& playername, bool internet)
    : d(new GameClientImpl), internet_(internet) {
-	d->net = NetClient::connect(host, port);
+	d->net = NetClient::connect(host);
 	if (!d->net || !d->net->is_connected()) {
 		throw WLWarning(_("Could not establish connection to host"),
-		                _("Widelands could not establish a connection to the given "
-		                  "address.\n"
-		                  "Either no Widelands server was running at the supposed port or\n"
+		                _("Widelands could not establish a connection to the given address. "
+		                  "Either no Widelands server was running at the supposed port or "
 		                  "the server shut down as you tried to connect."));
 	}
 

=== modified file 'src/network/gameclient.h'
--- src/network/gameclient.h	2017-05-11 10:45:44 +0000
+++ src/network/gameclient.h	2017-05-20 19:06:48 +0000
@@ -36,13 +36,11 @@
  * launch, as well as dealing with the actual network protocol.
  */
 struct GameClient : public GameController,
-                    public GameSettingsProvider,
-                    private SyncCallback,
-                    public ChatProvider {
-	GameClient(const std::string& host,
-	           const uint16_t port,
-	           const std::string& playername,
-	           bool internet = false);
+					public GameSettingsProvider,
+					private SyncCallback,
+					public ChatProvider {
+	GameClient(const NetAddress& host, const std::string& playername, bool internet = false);
+
 	virtual ~GameClient();
 
 	void run();

=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc	2017-05-11 10:45:44 +0000
+++ src/network/gamehost.cc	2017-05-20 19:06:48 +0000
@@ -54,7 +54,6 @@
 #include "network/network_lan_promotion.h"
 #include "network/network_player_settings_backend.h"
 #include "network/network_protocol.h"
-#include "network/network_system.h"
 #include "profile/profile.h"
 #include "scripting/lua_interface.h"
 #include "ui_basic/progresswindow.h"

=== modified file 'src/network/internet_gaming.cc'
--- src/network/internet_gaming.cc	2017-05-16 18:29:06 +0000
+++ src/network/internet_gaming.cc	2017-05-20 19:06:48 +0000
@@ -95,7 +95,10 @@
 void InternetGaming::initialize_connection() {
 	// First of all try to connect to the metaserver
 	log("InternetGaming: Connecting to the metaserver.\n");
-	net = NetClient::connect(meta_, port_);
+	NetAddress addr;
+	net.reset();
+	if (NetAddress::resolve_to_v4(&addr, meta_, port_))
+		net = NetClient::connect(addr);
 	if (!net || !net->is_connected())
 		throw WLWarning(_("Could not establish connection to host"),
 		                _("Widelands could not establish a connection to the given address.\n"

=== modified file 'src/network/internet_gaming.h'
--- src/network/internet_gaming.h	2017-05-09 19:17:12 +0000
+++ src/network/internet_gaming.h	2017-05-20 19:06:48 +0000
@@ -170,7 +170,7 @@
 	std::string pwd_;
 	bool reg_;
 	std::string meta_;
-	uint32_t port_;
+	uint16_t port_;
 
 	/// local clients name and rights
 	std::string clientname_;

=== modified file 'src/network/netclient.cc'
--- src/network/netclient.cc	2017-05-11 10:45:44 +0000
+++ src/network/netclient.cc	2017-05-20 19:06:48 +0000
@@ -4,8 +4,9 @@
 
 #include "base/log.h"
 
-std::unique_ptr<NetClient> NetClient::connect(const std::string& ip_address, const uint16_t port) {
-	std::unique_ptr<NetClient> ptr(new NetClient(ip_address, port));
+std::unique_ptr<NetClient> NetClient::connect(const NetAddress& host) {
+
+	std::unique_ptr<NetClient> ptr(new NetClient(host));
 	if (ptr->is_connected()) {
 		return ptr;
 	} else {
@@ -17,20 +18,18 @@
 NetClient::~NetClient() {
 	if (is_connected())
 		close();
-	if (sockset_ != nullptr)
-		SDLNet_FreeSocketSet(sockset_);
 }
 
 bool NetClient::is_connected() const {
-	return sock_ != nullptr;
+	return socket_.is_open();
 }
 
 void NetClient::close() {
 	if (!is_connected())
 		return;
-	SDLNet_TCP_DelSocket(sockset_, sock_);
-	SDLNet_TCP_Close(sock_);
-	sock_ = nullptr;
+	boost::system::error_code ec;
+	socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+	socket_.close(ec);
 }
 
 bool NetClient::try_receive(RecvPacket* packet) {
@@ -38,41 +37,53 @@
 		return false;
 
 	uint8_t buffer[512];
-	while (SDLNet_CheckSockets(sockset_, 0) > 0) {
-
-		const int32_t bytes = SDLNet_TCP_Recv(sock_, buffer, sizeof(buffer));
-		if (bytes <= 0) {
-			// Error while receiving
-			close();
-			return false;
-		}
-
-		deserializer_.read_data(buffer, bytes);
+	boost::system::error_code ec;
+	size_t length = socket_.read_some(boost::asio::buffer(buffer, 512), ec);
+	if (!ec) {
+		assert(length > 0);
+		assert(length <= 512);
+		// Has read something
+		deserializer_.read_data(buffer, length);
+	}
+
+	if (ec && ec != boost::asio::error::would_block) {
+		// Connection closed or some error, close the socket
+		close();
+		return false;
 	}
 	// Get one packet from the deserializer
 	return deserializer_.write_packet(packet);
 }
 
 void NetClient::send(const SendPacket& packet) {
-	if (is_connected()) {
-		SDLNet_TCP_Send(sock_, packet.get_data(), packet.get_size());
+	if (!is_connected())
+		return;
+
+	boost::system::error_code ec;
+	size_t written = boost::asio::write(socket_,
+						boost::asio::buffer(packet.get_data(), packet.get_size()), ec);
+	// This one is an assertion of mine, I am not sure if it will hold
+	// If it doesn't, set the socket to blocking before writing
+	assert(ec != boost::asio::error::would_block);
+	assert(written == packet.get_size() || ec);
+	if (ec) {
+		close();
 	}
 }
 
-NetClient::NetClient(const std::string& ip_address, const uint16_t port)
-   : sock_(nullptr), sockset_(nullptr), deserializer_() {
-
-	IPaddress addr;
-	if (SDLNet_ResolveHost(&addr, ip_address.c_str(), port) != 0) {
-		log("[Client]: Failed to resolve host address %s:%u.\n", ip_address.c_str(), port);
-		return;
-	}
-	log("[Client]: Trying to connect to %s:%u ... ", ip_address.c_str(), port);
-	sock_ = SDLNet_TCP_Open(&addr);
-	if (is_connected()) {
+NetClient::NetClient(const NetAddress& host)
+   : io_service_(), socket_(io_service_), deserializer_() {
+
+	boost::system::error_code ec;
+	const boost::asio::ip::address address = boost::asio::ip::address::from_string(host.ip, ec);
+	assert(!ec);
+	const boost::asio::ip::tcp::endpoint destination(address, host.port);
+
+	log("[Client]: Trying to connect to %s:%u ... ", host.ip.c_str(), host.port);
+	socket_.connect(destination, ec);
+	if (!ec && is_connected()) {
 		log("success\n");
-		sockset_ = SDLNet_AllocSocketSet(1);
-		SDLNet_TCP_AddSocket(sockset_, sock_);
+		socket_.non_blocking(true);
 	} else {
 		log("failed\n");
 	}

=== modified file 'src/network/netclient.h'
--- src/network/netclient.h	2017-05-11 10:45:44 +0000
+++ src/network/netclient.h	2017-05-20 19:06:48 +0000
@@ -22,23 +22,25 @@
 
 #include <memory>
 
-#include <SDL_net.h>
+#include <boost/asio.hpp>
 
 #include "network/network.h"
 
 /**
  * NetClient manages the network connection for a network game in which this computer
  * participates as a client.
+ * This class only tries to create a single socket, either for IPv4 and IPv6.
+ * Which is used depends on what kind of address is given on call to connect().
  */
 class NetClient {
 public:
+
 	/**
 	 * Tries to establish a connection to the given host.
-	 * @param ip_address A hostname or an IPv4 address as string.
-	 * @param port The port to connect to.
-	 * @return A pointer to a connected \c NetClient object or a nullptr if the connection failed.
+	 * \param host The host to connect to.
+	 * \return A pointer to a connected \c NetClient object or an invalid pointer if the connection failed.
 	 */
-	static std::unique_ptr<NetClient> connect(const std::string& ip_address, const uint16_t port);
+	static std::unique_ptr<NetClient> connect(const NetAddress& host);
 
 	/**
 	 * Closes the connection.
@@ -48,7 +50,7 @@
 
 	/**
 	 * Returns whether the client is connected.
-	 * @return \c true if the connection is open, \c false otherwise.
+	 * \return \c true if the connection is open, \c false otherwise.
 	 */
 	bool is_connected() const;
 
@@ -60,30 +62,35 @@
 
 	/**
 	 * Tries to receive a packet.
-	 * @param packet A packet that should be overwritten with the received data.
-	 * @return \c true if a packet is available, \c false otherwise.
+	 * \param packet A packet that should be overwritten with the received data.
+	 * \return \c true if a packet is available, \c false otherwise.
 	 *   The given packet is only modified when \c true is returned.
 	 *   Calling this on a closed connection will return false.
 	 */
-	bool try_receive(RecvPacket* packet);
+	bool try_receive(RecvPacket *packet);
 
 	/**
 	 * Sends a packet.
 	 * Calling this on a closed connection will silently fail.
-	 * @param packet The packet to send.
+	 * \param packet The packet to send.
 	 */
-	void send(const SendPacket& packet);
+	 void send(const SendPacket& packet);
 
 private:
-	NetClient(const std::string& ip_address, const uint16_t port);
-
-	/// The socket that connects us to the host
-	TCPsocket sock_;
-
-	/// Socket set used for selection
-	SDLNet_SocketSet sockset_;
-
-	/// Deserializer acts as a buffer for packets (reassembly/splitting up)
+	/**
+	 * Tries to establish a connection to the given host.
+	 * If the connection attempt failed, is_connected() will return \c false.
+	 * \param host The host to connect to.
+	 */
+	NetClient(const NetAddress& host);
+
+	/// An io_service needed by boost.asio. Primary needed for asynchronous operations.
+	boost::asio::io_service io_service_;
+
+	/// The socket that connects us to the host.
+	boost::asio::ip::tcp::socket socket_;
+
+	/// Deserializer acts as a buffer for packets (splitting stream to packets)
 	Deserializer deserializer_;
 };
 

=== modified file 'src/network/nethost.cc'
--- src/network/nethost.cc	2017-05-11 10:45:44 +0000
+++ src/network/nethost.cc	2017-05-20 19:06:48 +0000
@@ -4,7 +4,7 @@
 
 #include "base/log.h"
 
-NetHost::Client::Client(TCPsocket sock) : socket(sock), deserializer() {
+NetHost::Client::Client(boost::asio::ip::tcp::socket&& sock) : socket(std::move(sock)), deserializer() {
 }
 
 std::unique_ptr<NetHost> NetHost::listen(const uint16_t port) {
@@ -22,11 +22,10 @@
 	while (!clients_.empty()) {
 		close(clients_.begin()->first);
 	}
-	SDLNet_FreeSocketSet(sockset_);
 }
 
 bool NetHost::is_listening() const {
-	return svrsock_ != nullptr;
+	return acceptor_v4_.is_open() || acceptor_v6_.is_open();
 }
 
 bool NetHost::is_connected(const ConnectionId id) const {
@@ -34,11 +33,16 @@
 }
 
 void NetHost::stop_listening() {
-	if (!is_listening())
-		return;
-	SDLNet_TCP_DelSocket(sockset_, svrsock_);
-	SDLNet_TCP_Close(svrsock_);
-	svrsock_ = nullptr;
+	boost::system::error_code ec;
+	if (acceptor_v4_.is_open()) {
+		log("[NetHost]: Closing a listening IPv4 socket\n");
+		acceptor_v4_.close(ec);
+	}
+	if (acceptor_v6_.is_open()) {
+		log("[NetHost]: Closing a listening IPv6 socket\n");
+		acceptor_v6_.close(ec);
+	}
+	// Ignore errors
 }
 
 void NetHost::close(const ConnectionId id) {
@@ -47,68 +51,136 @@
 		// Not connected anyway
 		return;
 	}
-	SDLNet_TCP_DelSocket(sockset_, iter_client->second.socket);
-	SDLNet_TCP_Close(iter_client->second.socket);
+	boost::system::error_code ec;
+	iter_client->second.socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+	iter_client->second.socket.close(ec);
 	clients_.erase(iter_client);
 }
 
 bool NetHost::try_accept(ConnectionId* new_id) {
 	if (!is_listening())
 		return false;
+	boost::system::error_code ec;
+	boost::asio::ip::tcp::socket socket(io_service_);
+	if (acceptor_v4_.is_open()) {
+		acceptor_v4_.accept(socket, ec);
+		if (ec == boost::asio::error::would_block) {
+			// No client wants to connect
+			// New socket don't need to be closed since it isn't open yet
+		} else if (ec) {
+			// Some other error, close the acceptor
+			acceptor_v4_.close(ec);
+		} else {
+			log("[NetHost]: Accepting IPv4 connection from %s\n",
+				socket.remote_endpoint().address().to_string().c_str());
+		}
+	}
+	if (acceptor_v6_.is_open() && !socket.is_open()) {
+		// IPv4 did not get a connection
+		acceptor_v6_.accept(socket, ec);
+		if (ec == boost::asio::error::would_block) {
+			;
+		} else if (ec) {
+			acceptor_v6_.close(ec);
+		} else {
+			log("[NetHost]: Accepting IPv6 connection from %s\n",
+				socket.remote_endpoint().address().to_string().c_str());
+		}
+	}
 
-	TCPsocket sock = SDLNet_TCP_Accept(svrsock_);
-	// No client wants to connect
-	if (sock == nullptr)
+	if (!socket.is_open()) {
+		// No new connection
 		return false;
-	SDLNet_TCP_AddSocket(sockset_, sock);
+	}
+
+	socket.non_blocking(true);
+
 	ConnectionId id = next_id_++;
 	assert(id > 0);
 	assert(clients_.count(id) == 0);
-	clients_.insert(std::make_pair(id, Client(sock)));
+	clients_.insert(std::make_pair(id, Client(std::move(socket))));
 	assert(clients_.count(id) == 1);
 	*new_id = id;
 	return true;
 }
 
 bool NetHost::try_receive(const ConnectionId id, RecvPacket* packet) {
-
 	// Always read all available data into buffers
 	uint8_t buffer[512];
-	while (SDLNet_CheckSockets(sockset_, 0) > 0) {
-		for (auto& e : clients_) {
-			if (SDLNet_SocketReady(e.second.socket)) {
-				const int32_t bytes = SDLNet_TCP_Recv(e.second.socket, buffer, sizeof(buffer));
-				if (bytes <= 0) {
-					// Error while receiving
-					close(e.first);
-					// We have to run the for-loop again since we modified the map
-					break;
-				}
 
-				e.second.deserializer.read_data(buffer, bytes);
-			}
+	boost::system::error_code ec;
+	for (auto it = clients_.begin(); it != clients_.end(); ) {
+		size_t length = it->second.socket.read_some(boost::asio::buffer(buffer, 512), ec);
+		if (ec == boost::asio::error::would_block) {
+			// Nothing to read
+			assert(length == 0);
+			++it;
+			continue;
+		} else if (ec) {
+			assert(length == 0);
+			// Connection closed or some error, close the socket
+			// close() will remove the client from the map so we have to increment the iterator first
+			ConnectionId id_to_remove = it->first;
+			++it;
+			close(id_to_remove);
+			continue;
 		}
+		assert(length > 0);
+		assert(length <= 512);
+		// Read something
+		it->second.deserializer.read_data(buffer, length);
+		++it;
 	}
 
 	// Now check whether there is data for the requested client
 	if (!is_connected(id))
 		return false;
 
-	// Get one packet from the deserializer
+	// Try to get one packet from the deserializer
 	return clients_.at(id).deserializer.write_packet(packet);
 }
 
 void NetHost::send(const ConnectionId id, const SendPacket& packet) {
+	boost::system::error_code ec;
 	if (is_connected(id)) {
-		SDLNet_TCP_Send(clients_.at(id).socket, packet.get_data(), packet.get_size());
-	}
-}
-
-NetHost::NetHost(const uint16_t port) : svrsock_(nullptr), sockset_(nullptr), next_id_(1) {
-
-	IPaddress myaddr;
-	SDLNet_ResolveHost(&myaddr, nullptr, port);
-	svrsock_ = SDLNet_TCP_Open(&myaddr);
-	// Maximal 16 sockets! This mean we can have at most 15 clients_ in our game (+ metaserver)
-	sockset_ = SDLNet_AllocSocketSet(16);
+		size_t written = boost::asio::write(clients_.at(id).socket,
+											boost::asio::buffer(packet.get_data(), packet.get_size()), ec);
+		// This one is an assertion of mine, I am not sure if it will hold
+		// If it doesn't, set the socket to blocking before writing
+		assert(ec != boost::asio::error::would_block);
+		assert(written == packet.get_size() || ec);
+		if (ec) {
+			close(id);
+		}
+	}
+}
+
+NetHost::NetHost(const uint16_t port)
+	: clients_(), next_id_(1), io_service_(), acceptor_v4_(io_service_), acceptor_v6_(io_service_) {
+
+	if (open_acceptor(&acceptor_v4_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))) {
+		log("[NetHost]: Opening a listening IPv4 socket on port %u\n", port);
+	}
+	if (open_acceptor(&acceptor_v6_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), port))) {
+		log("[NetHost]: Opening a listening IPv6 socket on port %u\n", port);
+	}
+}
+
+bool NetHost::open_acceptor(boost::asio::ip::tcp::acceptor *acceptor,
+						const boost::asio::ip::tcp::endpoint& endpoint) {
+	try {
+		acceptor->open(endpoint.protocol());
+		acceptor->non_blocking(true);
+		const boost::asio::socket_base::reuse_address option_reuse(true);
+		acceptor->set_option(option_reuse);
+		if (endpoint.protocol() == boost::asio::ip::tcp::v6()) {
+			const boost::asio::ip::v6_only option_v6only(true);
+			acceptor->set_option(option_v6only);
+		}
+		acceptor->bind(endpoint);
+		acceptor->listen(boost::asio::socket_base::max_connections);
+		return true;
+	} catch (const boost::system::system_error&) {
+		return false;
+	}
 }

=== modified file 'src/network/nethost.h'
--- src/network/nethost.h	2017-05-11 10:45:44 +0000
+++ src/network/nethost.h	2017-05-20 19:06:48 +0000
@@ -23,22 +23,24 @@
 #include <map>
 #include <memory>
 
-#include <SDL_net.h>
+#include <boost/asio.hpp>
 
 #include "network/network.h"
 
 /**
  * NetHost manages the client connections of a network game in which this computer
  * participates as a server.
+ * This class tries to create sockets for IPv4 and IPv6.
  */
 class NetHost {
 public:
+	/// IDs used to enumerate the clients.
 	using ConnectionId = uint32_t;
 
 	/**
 	 * Tries to listen on the given port.
-	 * @param port The port to listen on.
-	 * @return A pointer to a listening \c NetHost object or a nullptr if the connection failed.
+	 * \param port The port to listen on.
+	 * \return A pointer to a listening \c NetHost object or a nullptr if the connection failed.
 	 */
 	static std::unique_ptr<NetHost> listen(const uint16_t port);
 
@@ -49,14 +51,14 @@
 
 	/**
 	 * Returns whether the server is started and is listening.
-	 * @return \c true if the server is listening, \c false otherwise.
+	 * \return \c true if the server is listening, \c false otherwise.
 	 */
 	bool is_listening() const;
 
 	/**
 	 * Returns whether the given client is connected.
-	 * @param The id of the client to check.
-	 * @return \c true if the connection is open, \c false otherwise.
+	 * \param The id of the client to check.
+	 * \return \c true if the connection is open, \c false otherwise.
 	 */
 	bool is_connected(ConnectionId id) const;
 
@@ -67,14 +69,14 @@
 
 	/**
 	 * Closes the connection to the given client.
-	 * @param id The id of the client to close the connection to.
+	 * \param id The id of the client to close the connection to.
 	 */
 	void close(ConnectionId id);
 
 	/**
 	 * Tries to accept a new client.
-	 * @param new_id The connection id of the new client will be stored here.
-	 * @return \c true if a client has connected, \c false otherwise.
+	 * \param new_id The connection id of the new client will be stored here.
+	 * \return \c true if a client has connected, \c false otherwise.
 	 *   The given id is only modified when \c true is returned.
 	 *   Calling this on a closed server will return false.
 	 *   The returned id is always greater than 0.
@@ -83,9 +85,9 @@
 
 	/**
 	 * Tries to receive a packet.
-	 * @param id The connection id of the client that should be received.
-	 * @param packet A packet that should be overwritten with the received data.
-	 * @return \c true if a packet is available, \c false otherwise.
+	 * \param id The connection id of the client that should be received.
+	 * \param packet A packet that should be overwritten with the received data.
+	 * \return \c true if a packet is available, \c false otherwise.
 	 *   The given packet is only modified when \c true is returned.
 	 *   Calling this on a closed connection will return false.
 	 */
@@ -94,26 +96,49 @@
 	/**
 	 * Sends a packet.
 	 * Calling this on a closed connection will silently fail.
-	 * @param id The connection id of the client that should be sent to.
-	 * @param packet The packet to send.
+	 * \param id The connection id of the client that should be sent to.
+	 * \param packet The packet to send.
 	 */
 	void send(ConnectionId id, const SendPacket& packet);
 
 private:
+	/**
+	 * Tries to listen on the given port.
+	 * If it fails, is_listening() will return \c false.
+	 * \param port The port to listen on.
+	 */
 	NetHost(const uint16_t port);
 
-	class Client {
-	public:
-		Client(TCPsocket sock);
-
-		TCPsocket socket;
+	bool open_acceptor(boost::asio::ip::tcp::acceptor *acceptor,
+						const boost::asio::ip::tcp::endpoint& endpoint);
+
+	/**
+	 * Helper structure to store variables about a connected client.
+	 */
+	struct Client {
+		/**
+		 * Initializes the structure with the given socket.
+		 * \param sock The socket to listen on. The socket is moved by this
+		 *             constructor so the given socket is no longer valid.
+		 */
+		Client(boost::asio::ip::tcp::socket&& sock);
+
+		/// The socket to send/receive with.
+		boost::asio::ip::tcp::socket socket;
+		/// The deserializer to feed the received data to. It will transform it into data packets.
 		Deserializer deserializer;
 	};
 
-	TCPsocket svrsock_;
-	SDLNet_SocketSet sockset_;
+	/// A map of connected clients.
 	std::map<NetHost::ConnectionId, Client> clients_;
+	/// The next client id that will be used
 	NetHost::ConnectionId next_id_;
+	/// An io_service needed by boost.asio. Primary needed for async operations.
+	boost::asio::io_service io_service_;
+	/// The acceptor we get IPv4 connection requests to.
+	boost::asio::ip::tcp::acceptor acceptor_v4_;
+	/// The acceptor we get IPv6 connection requests to.
+	boost::asio::ip::tcp::acceptor acceptor_v6_;
 };
 
 #endif  // end of include guard: WL_NETWORK_NETHOST_H

=== modified file 'src/network/network.cc'
--- src/network/network.cc	2017-05-14 14:40:24 +0000
+++ src/network/network.cc	2017-05-20 19:06:48 +0000
@@ -19,8 +19,44 @@
 
 #include "network/network.h"
 
+#include <SDL.h>
+#include <boost/asio.hpp>
+
 #include "base/log.h"
 
+
+namespace {
+
+bool do_resolve(const boost::asio::ip::tcp& protocol, NetAddress *addr, const std::string& hostname, uint16_t port) {
+	assert(addr != nullptr);
+	try {
+		boost::asio::io_service io_service;
+		boost::asio::ip::tcp::resolver resolver(io_service);
+		boost::asio::ip::tcp::resolver::query query(protocol, hostname, boost::lexical_cast<std::string>(port));
+		boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
+		if (iter == boost::asio::ip::tcp::resolver::iterator()) {
+			// Resolution failed
+			return false;
+		}
+		addr->ip = iter->endpoint().address().to_string();
+		addr->port = port;
+		return true;
+	} catch (const boost::system::system_error&) {
+		// Resolution failed
+		return false;
+	}
+}
+
+}
+
+bool NetAddress::resolve_to_v4(NetAddress *addr, const std::string& hostname, uint16_t port) {
+	return do_resolve(boost::asio::ip::tcp::v4(), addr, hostname, port);
+}
+
+bool NetAddress::resolve_to_v6(NetAddress *addr, const std::string& hostname, uint16_t port) {
+	return do_resolve(boost::asio::ip::tcp::v6(), addr, hostname, port);
+}
+
 CmdNetCheckSync::CmdNetCheckSync(uint32_t const dt, SyncCallback* const cb)
    : Command(dt), callback_(cb) {
 }

=== modified file 'src/network/network.h'
--- src/network/network.h	2017-05-11 16:13:34 +0000
+++ src/network/network.h	2017-05-20 19:06:48 +0000
@@ -24,7 +24,6 @@
 #include <string>
 #include <vector>
 
-#include <SDL_net.h>
 #include <boost/lexical_cast.hpp>
 
 #include "base/wexception.h"
@@ -36,6 +35,35 @@
 class Deserializer;
 class FileRead;
 
+/**
+ * Simple structure to hold the IP address and port of a server.
+ * This structure should not contain a hostname.
+ */
+struct NetAddress {
+	/**
+	 * Tries to resolve the given hostname to an IPv4 address.
+	 * \param[out] addr An NetAddress structure to write the result to,
+	 *                  if resolution succeeds.
+	 * \param hostname The name of the host.
+	 * \param port The port on the host.
+	 * \return \c True if the resolution succeeded, \c false otherwise.
+	 */
+	static bool resolve_to_v4(NetAddress *addr, const std::string& hostname, uint16_t port);
+
+	/**
+	 * Tries to resolve the given hostname to an IPv6 address.
+	 * \param[out] addr An NetAddress structure to write the result to,
+	 *                  if resolution succeeds.
+	 * \param hostname The name of the host.
+	 * \param port The port on the host.
+	 * \return \c True if the resolution succeeded, \c false otherwise.
+	 */
+	static bool resolve_to_v6(NetAddress *addr, const std::string& hostname, uint16_t port);
+
+	std::string ip;
+	uint16_t port;
+};
+
 struct SyncCallback {
 	virtual ~SyncCallback() {
 	}

=== modified file 'src/network/network_lan_promotion.cc'
--- src/network/network_lan_promotion.cc	2017-01-25 18:55:59 +0000
+++ src/network/network_lan_promotion.cc	2017-05-20 19:06:48 +0000
@@ -19,116 +19,293 @@
 
 #include "network/network_lan_promotion.h"
 
-#include <cstdio>
-#include <cstring>
+#ifndef _WIN32
+#include <ifaddrs.h>
+#endif
 
+#include "base/i18n.h"
 #include "base/log.h"
-#include "base/macros.h"
+#include "base/warning.h"
 #include "build_info.h"
 #include "network/constants.h"
 
+namespace {
+
+	static const char *ip_versions[] = {"IPv4", "IPv6"};
+
+	/**
+	 * Returns the matching string for the given IP address.
+	 * \param addr The address object to get the IP version for.
+	 * \return A pointer to a constant string naming the IP version.
+	 */
+	const char* get_ip_version_string(const boost::asio::ip::address& addr) {
+		assert(!addr.is_unspecified());
+		if (addr.is_v4()) {
+			return ip_versions[0];
+		} else {
+			assert(addr.is_v6());
+			return ip_versions[1];
+		}
+	}
+
+	/**
+	 * Returns the matching string for the given IP address.
+	 * \param version A whatever object to get the IP version for.
+	 * \return A pointer to a constant string naming the IP version.
+	 */
+	const char* get_ip_version_string(const boost::asio::ip::udp& version) {
+		if (version == boost::asio::ip::udp::v4()) {
+			return ip_versions[0];
+		} else {
+			assert(version == boost::asio::ip::udp::v6());
+			return ip_versions[1];
+		}
+	}
+}
+
 /*** class LanBase ***/
-
-LanBase::LanBase() {
-
-	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);  //  open the socket
-
-	int32_t opt = 1;
-	//  the cast to char* is because microsoft wants it that way
-	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&opt), sizeof(opt));
+LanBase::LanBase(uint16_t port)
+	: io_service(), socket_v4(io_service), socket_v6(io_service) {
 
 #ifndef _WIN32
-
-	//  get a list of all local broadcast addresses
-	struct if_nameindex* ifnames = if_nameindex();
-	struct ifreq ifr;
-
-	for (int32_t i = 0; ifnames[i].if_index; ++i) {
-		strncpy(ifr.ifr_name, ifnames[i].if_name, IFNAMSIZ);
-
-		DIAG_OFF("-Wold-style-cast")
-		if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
-			continue;
-
-		if (!(ifr.ifr_flags & IFF_BROADCAST))
-			continue;
-
-		if (ioctl(sock, SIOCGIFBRDADDR, &ifr) < 0)
-			continue;
-		DIAG_ON("-Wold-style-cast")
-
-		broadcast_addresses.push_back(
-		   reinterpret_cast<sockaddr_in*>(&ifr.ifr_broadaddr)->sin_addr.s_addr);
-	}
-
-	if_freenameindex(ifnames);
+	// Iterate over all interfaces. If they support IPv4, store the broadcast-address
+	// of the interface and try to start the socket. If they support IPv6, just start
+	// the socket. There is one fixed broadcast-address for IPv6 (well, actually multicast)
+
+	// Adapted example out of "man getifaddrs"
+	// Admittedly: I don't like this part. But boost is not able to iterate over
+	// the local IPs at this time. If they ever add it, replace this code
+	struct ifaddrs *ifaddr, *ifa;
+	int family, s, n;
+	char host[NI_MAXHOST];
+	if (getifaddrs(&ifaddr) == -1) {
+		perror("getifaddrs");
+		exit(EXIT_FAILURE);
+	}
+	for (ifa = ifaddr, n = 0; ifa != nullptr; ifa = ifa->ifa_next, n++) {
+		if (ifa->ifa_addr == nullptr)
+			continue;
+		family = ifa->ifa_addr->sa_family;
+		if (family == AF_INET && (ifa->ifa_flags & IFF_BROADCAST)) {
+			s = getnameinfo(ifa->ifa_ifu.ifu_broadaddr, sizeof(struct sockaddr_in),
+						host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST);
+			if (s == 0) {
+				start_socket(&socket_v4, boost::asio::ip::udp::v4(), port);
+				broadcast_addresses_v4.insert(host);
+			}
+		} else if (family == AF_INET6 && (ifa->ifa_flags & IFF_BROADCAST)) {
+			start_socket(&socket_v6, boost::asio::ip::udp::v6(), port);
+			// Nothing to insert here. There is only one "broadcast" address for IPv6 (I think)
+		}
+	}
+	freeifaddrs(ifaddr);
 #else
 	//  As Microsoft does not seem to support if_nameindex, we just broadcast to
 	//  INADDR_BROADCAST.
-	broadcast_addresses.push_back(INADDR_BROADCAST);
+	broadcast_addresses_v4.insert("255.255.255.255");
 #endif
+
+	// Okay, needed this for development. But might be useful to debug network problems
+	for (const std::string& ip : broadcast_addresses_v4)
+		log("[LAN] Will broadcast to %s\n", ip.c_str());
+
+	if (!is_open()) {
+		// Hm, not good. Just try to open them and hope for the best
+		start_socket(&socket_v4, boost::asio::ip::udp::v4(), port);
+		start_socket(&socket_v6, boost::asio::ip::udp::v6(), port);
+	}
+
+	if (!is_open()) {
+		// Still not open? Go back to main menu.
+		report_network_error();
+	}
 }
 
 LanBase::~LanBase() {
-	closesocket(sock);
-}
-
-void LanBase::bind(uint16_t port) {
-	sockaddr_in addr;
-
-	DIAG_OFF("-Wold-style-cast")
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = INADDR_ANY;
-	addr.sin_port = htons(port);
-	DIAG_ON("-Wold-style-cast")
-
-	::bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
+	close_socket(&socket_v4);
+	close_socket(&socket_v6);
 }
 
 bool LanBase::avail() {
-	fd_set fds;
-	timeval tv;
-
-	DIAG_OFF("-Wold-style-cast")
-	FD_ZERO(&fds);
-	FD_SET(sock, &fds);
-	DIAG_ON("-Wold-style-cast")
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	return select(sock + 1, &fds, nullptr, nullptr, &tv) == 1;
-}
-
-ssize_t LanBase::receive(void* const buf, size_t const len, sockaddr_in* const addr) {
-	socklen_t addrlen = sizeof(sockaddr_in);
-	return recvfrom(
-	   sock, static_cast<DATATYPE*>(buf), len, 0, reinterpret_cast<sockaddr*>(addr), &addrlen);
-}
-
-void LanBase::send(void const* const buf, size_t const len, sockaddr_in const* const addr) {
-	sendto(sock, static_cast<const DATATYPE*>(buf), len, 0, reinterpret_cast<const sockaddr*>(addr),
-	       sizeof(sockaddr_in));
-}
-
-void LanBase::broadcast(void const* const buf, size_t const len, uint16_t const port) {
-	for (const in_addr_t& temp_address : broadcast_addresses) {
-		sockaddr_in addr;
-		addr.sin_family = AF_INET;
-		addr.sin_addr.s_addr = temp_address;
-		DIAG_OFF("-Wold-style-cast")
-		addr.sin_port = htons(port);
-		DIAG_ON("-Wold-style-cast")
-
-		sendto(sock, static_cast<const DATATYPE*>(buf), len, 0,
-		       reinterpret_cast<const sockaddr*>(&addr), sizeof(addr));
+	boost::system::error_code ec;
+	bool available_v4 = (socket_v4.is_open() && socket_v4.available(ec) > 0);
+	if (ec) {
+		close_socket(&socket_v4);
+		available_v4 = false;
+	}
+	bool available_v6 = (socket_v6.is_open() && socket_v6.available(ec) > 0);
+	if (ec) {
+		close_socket(&socket_v6);
+		available_v4 = false;
+	}
+	return available_v4 || available_v6;
+}
+
+bool LanBase::is_open() {
+	return socket_v4.is_open() || socket_v6.is_open();
+}
+
+ssize_t LanBase::receive(void* const buf, size_t const len, NetAddress *addr) {
+	assert(buf != nullptr);
+	assert(addr != nullptr);
+	boost::asio::ip::udp::endpoint sender_endpoint;
+	size_t recv_len;
+	if (socket_v4.is_open()) {
+		try {
+			if (socket_v4.available() > 0) {
+				recv_len = socket_v4.receive_from(boost::asio::buffer(buf, len), sender_endpoint);
+				*addr = NetAddress{sender_endpoint.address().to_string(), sender_endpoint.port()};
+				assert(recv_len <= len);
+				return recv_len;
+			}
+		} catch (const boost::system::system_error&) {
+			// Some network error. Close the socket
+			close_socket(&socket_v4);
+		}
+	}
+	// We only reach this point if there was nothing to receive for IPv4
+	if (socket_v6.is_open()) {
+		try {
+			if (socket_v6.available() > 0) {
+				recv_len = socket_v6.receive_from(boost::asio::buffer(buf, len), sender_endpoint);
+				*addr = NetAddress{sender_endpoint.address().to_string(), sender_endpoint.port()};
+				assert(recv_len <= len);
+				return recv_len;
+			}
+		} catch (const boost::system::system_error&) {
+			close_socket(&socket_v6);
+		}
+	}
+	// Nothing to receive at all. So lonely here...
+	return 0;
+}
+
+bool LanBase::send(void const* const buf, size_t const len, const NetAddress& addr) {
+	boost::system::error_code ec;
+	const boost::asio::ip::address address = boost::asio::ip::address::from_string(addr.ip, ec);
+	// If this assert failed, then there is some bug in the code. NetAddress should only be filled
+	// with valid IP addresses (e.g. no hostnames)
+	assert(!ec);
+	boost::asio::ip::udp::endpoint destination(address, addr.port);
+	boost::asio::ip::udp::socket *socket = nullptr;
+	if (destination.address().is_v4()) {
+		socket = &socket_v4;
+	} else if (destination.address().is_v6()) {
+		socket = &socket_v6;
+	} else {
+		NEVER_HERE();
+	}
+	assert(socket != nullptr);
+	if (!socket->is_open()) {
+		// I think this shouldn't happen normally. It might happen, though, if we receive
+		// a broadcast and learn the IP, then our sockets goes down, then we try to send
+		log("[LAN] Error: trying to send to an %s address but socket is not open",
+			get_ip_version_string(address));
+		return false;
+	}
+	socket->send_to(boost::asio::buffer(buf, len), destination, 0, ec);
+	if (ec) {
+		close_socket(socket);
+		return false;
+	}
+	return true;
+}
+
+bool LanBase::broadcast(void const* const buf, size_t const len, uint16_t const port) {
+	boost::system::error_code ec;
+	bool error = false;
+	if (socket_v4.is_open()) {
+		for (const std::string& address : broadcast_addresses_v4) {
+			boost::asio::ip::udp::endpoint destination(boost::asio::ip::address::from_string(address), port);
+			socket_v4.send_to(boost::asio::buffer(buf, len), destination, 0, ec);
+			if (ec) {
+				close_socket(&socket_v4);
+				error = true;
+				break;
+			}
+		}
+	}
+	if (socket_v6.is_open()) {
+		boost::asio::ip::udp::endpoint destination(boost::asio::ip::address::from_string("ff02::1"), port);
+		socket_v6.send_to(boost::asio::buffer(buf, len), destination, 0, ec);
+		if (ec) {
+			close_socket(&socket_v6);
+				error = true;
+		}
+	}
+	return !error;
+}
+
+void LanBase::start_socket(boost::asio::ip::udp::socket *socket, boost::asio::ip::udp version, uint16_t port) {
+
+    if (socket->is_open())
+		return;
+
+	boost::system::error_code ec;
+	// Try to open the socket
+	socket->open(version, ec);
+	if (ec) {
+		log("[LAN] Failed to start an %s socket: %s\n",
+			get_ip_version_string(version), ec.message().c_str());
+		return;
+	}
+
+	const boost::asio::socket_base::broadcast option_broadcast(true);
+	socket->set_option(option_broadcast, ec);
+	if (ec) {
+		log("[LAN] Error setting options for %s socket, closing socket: %s\n",
+			get_ip_version_string(version), ec.message().c_str());
+		// Retrieve the error code to avoid throwing but ignore it
+		close_socket(socket);
+		return;
+	}
+
+	const boost::asio::socket_base::reuse_address option_reuse(true);
+	socket->set_option(option_reuse, ec);
+	// This one isn't really needed so ignore the error
+
+
+	if (version == boost::asio::ip::udp::v6()) {
+		const boost::asio::ip::v6_only option_v6only(true);
+		socket->set_option(option_v6only, ec);
+		// This one might not be needed, ignore the error and see whether we fail on bind()
+	}
+
+	socket->bind(boost::asio::ip::udp::endpoint(version, port), ec);
+	if (ec) {
+		log("[LAN] Error binding %s socket to UDP port %d, closing socket: %s\n",
+			get_ip_version_string(version), port, ec.message().c_str());
+		close_socket(socket);
+		return;
+	}
+
+	log("[LAN] Started an %s socket on UDP port %d\n", get_ip_version_string(version), port);
+}
+
+void LanBase::report_network_error() {
+	// No socket open? Sorry, but we can't continue this way
+	throw WLWarning(_("Failed to use the local network!"),
+		_("Widelands was unable to use the local network. "
+		  "Maybe some other process is already running a server on port %d, %d or %d "
+		  "or your network setup is broken."),
+		WIDELANDS_LAN_DISCOVERY_PORT, WIDELANDS_LAN_PROMOTION_PORT, WIDELANDS_PORT);
+}
+
+void LanBase::close_socket(boost::asio::ip::udp::socket *socket) {
+	boost::system::error_code ec;
+	if (socket->is_open()) {
+		const boost::asio::ip::udp::endpoint& endpoint = socket->local_endpoint(ec);
+		if (!ec)
+			log("[LAN] Closing an %s socket.\n", get_ip_version_string(endpoint.protocol()));
+		socket->shutdown(boost::asio::ip::udp::socket::shutdown_both, ec);
+		socket->close(ec);
 	}
 }
 
 /*** class LanGamePromoter ***/
 
-LanGamePromoter::LanGamePromoter() {
-	bind(WIDELANDS_LAN_PROMOTION_PORT);
+LanGamePromoter::LanGamePromoter()
+	: LanBase(WIDELANDS_LAN_PROMOTION_PORT) {
 
 	needupdate = true;
 
@@ -140,12 +317,13 @@
 
 	strncpy(gameinfo.gameversion, build_id().c_str(), sizeof(gameinfo.gameversion));
 
-	gethostname(gameinfo.hostname, sizeof(gameinfo.hostname));
+	strncpy(gameinfo.hostname, boost::asio::ip::host_name().c_str(), sizeof(gameinfo.hostname));
 }
 
 LanGamePromoter::~LanGamePromoter() {
 	gameinfo.state = LAN_GAME_CLOSED;
 
+	// Don't care about errors at this point
 	broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT);
 }
 
@@ -153,20 +331,23 @@
 	if (needupdate) {
 		needupdate = false;
 
-		broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT);
+		if (!broadcast(&gameinfo, sizeof(gameinfo), WIDELANDS_LAN_DISCOVERY_PORT))
+			report_network_error();
 	}
 
 	while (avail()) {
 		char magic[8];
-		sockaddr_in addr;
+		NetAddress addr;
 
 		if (receive(magic, 8, &addr) < 8)
 			continue;
 
-		log("Received %s packet\n", magic);
+		log("Received %s packet from %s\n", magic, addr.ip.c_str());
 
-		if (!strncmp(magic, "QUERY", 6) && magic[6] == LAN_PROMOTION_PROTOCOL_VERSION)
-			send(&gameinfo, sizeof(gameinfo), &addr);
+		if (!strncmp(magic, "QUERY", 6) && magic[6] == LAN_PROMOTION_PROTOCOL_VERSION) {
+			if (!send(&gameinfo, sizeof(gameinfo), addr))
+				report_network_error();
+		}
 	}
 }
 
@@ -178,8 +359,8 @@
 
 /*** class LanGameFinder ***/
 
-LanGameFinder::LanGameFinder() : callback(nullptr) {
-	bind(WIDELANDS_LAN_DISCOVERY_PORT);
+LanGameFinder::LanGameFinder()
+	: LanBase(WIDELANDS_LAN_DISCOVERY_PORT), callback(nullptr) {
 
 	reset();
 }
@@ -192,18 +373,19 @@
 	strncpy(magic, "QUERY", 8);
 	magic[6] = LAN_PROMOTION_PROTOCOL_VERSION;
 
-	broadcast(magic, 8, WIDELANDS_LAN_PROMOTION_PORT);
+	if (!broadcast(magic, 8, WIDELANDS_LAN_PROMOTION_PORT))
+		report_network_error();
 }
 
 void LanGameFinder::run() {
 	while (avail()) {
 		NetGameInfo info;
-		sockaddr_in addr;
+		NetAddress addr;
 
 		if (receive(&info, sizeof(info), &addr) < static_cast<int32_t>(sizeof(info)))
 			continue;
 
-		log("Received %s packet\n", info.magic);
+		log("Received %s packet from %s\n", info.magic, addr.ip.c_str());
 
 		if (strncmp(info.magic, "GAME", 6))
 			continue;
@@ -226,8 +408,8 @@
 		if (!was_in_list) {
 			opengames.push_back(new NetOpenGame);
 			DIAG_OFF("-Wold-style-cast")
-			opengames.back()->address = addr.sin_addr.s_addr;
-			opengames.back()->port = htons(WIDELANDS_PORT);
+			addr.port = WIDELANDS_PORT;
+			opengames.back()->address = addr;
 			DIAG_ON("-Wold-style-cast")
 			opengames.back()->info = info;
 			callback(GameOpened, opengames.back(), userdata);

=== modified file 'src/network/network_lan_promotion.h'
--- src/network/network_lan_promotion.h	2017-05-07 20:27:21 +0000
+++ src/network/network_lan_promotion.h	2017-05-20 19:06:48 +0000
@@ -20,22 +20,17 @@
 #ifndef WL_NETWORK_NETWORK_LAN_PROMOTION_H
 #define WL_NETWORK_NETWORK_LAN_PROMOTION_H
 
+#include <boost/asio.hpp>
 #include <list>
-
-#ifndef _WIN32
-#include <sys/socket.h>
-#endif
-#include <sys/types.h>
-
-#include "network/network_system.h"
+#include <set>
+
+#include "network/network.h"
 
 #define LAN_PROMOTION_PROTOCOL_VERSION 1
 
 #define LAN_GAME_CLOSED 0
 #define LAN_GAME_OPEN 1
 
-// TODO(Notabilis): Update file for IPv6
-
 struct NetGameInfo {
 	char magic[6];
 	uint8_t version;
@@ -47,31 +42,111 @@
 };
 
 struct NetOpenGame {
-	in_addr_t address;
-	in_port_t port;
+	NetAddress address;
 	NetGameInfo info;
 };
 
+/**
+ * Base class for UDP networking.
+ * This class is used by derived classes to find open games on the
+ * local network and to announce a just opened game on the local network.
+ * This class tries to create sockets for IPv4 and IPv6.
+ */
 struct LanBase {
 protected:
-	LanBase();
+	/**
+	 * Tries to start a socket on the given port.
+	 * Sockets for IPv4 and IPv6 are started.
+	 * When both fail, report_network_error() is called.
+	 * \param port The port to listen on.
+	 */
+	LanBase(uint16_t port);
+
+	/**
+	 * Destructor.
+	 */
 	~LanBase();
 
-	void bind(uint16_t);
-
+	/**
+	 * Returns whether data is available to be read.
+	 * \return \c True when receive() will return data, \c false otherwise.
+	 */
 	bool avail();
 
-	ssize_t receive(void*, size_t, sockaddr_in*);
-
-	void send(void const*, size_t, sockaddr_in const*);
-	void broadcast(void const*, size_t, uint16_t);
+	/**
+	 * Returns whether at least one of the sockets is open.
+	 * If this returns \c false, you probably have a problem.
+	 * \return \c True when a socket is ready, \c false otherwise.
+	 */
+	bool is_open();
+
+	/**
+	 * Tries to receive some data.
+	 * \param[out] buf The buffer to read data into.
+	 * \param len The length of the buffer.
+	 * \param[out] addr The address we received data from. Since UDP is a connection-less
+	 *                  protocol, each receive() might receive data from another address.
+	 * \return How many bytes have been written to \c buf. If 0 is returned there either was no data
+	 *         available (check before with avail()) or there was some error (check with is_open())
+	 */
+	ssize_t receive(void *buf, size_t len, NetAddress *addr);
+
+	/**
+	 * Sends data to a specified address.
+	 * \param buf The data to send.
+	 * \param len The length of the buffer.
+	 * \param addr The address to send to.
+	 */
+	bool send(void const *buf, size_t len, const NetAddress& addr);
+
+	/**
+	 * Broadcast some data in the local network.
+	 * \param buf The data to send.
+	 * \param len The length of the buffer.
+	 * \param port The port to send to. No address is required.
+	 */
+	bool broadcast(void const* buf, size_t len, uint16_t port);
+
+	/**
+	 * Throws a WLWarning exception to jump back to the main menu.
+	 * Calling this on network errors is in the responsibility of derived classes.
+	 * (Most of the time, aborting makes sense when an error occurred. But e.g. in
+	 * the destructor simply ignoring the error is okay.)
+	 */
+	void report_network_error();
 
 private:
-	int32_t sock;
-
-	std::list<in_addr_t> broadcast_addresses;
+
+	/**
+	 * Opens a listening UDP socket.
+	 * \param[out] The socket to open. The object has to be created but the socket not opened before.
+	 *             If it already has been opened before, nothing will be done.
+	 * \param version Whether a IPv4 or IPv6 socket should be opened.
+	 * \param port The port to listen on.
+	 */
+	void start_socket(boost::asio::ip::udp::socket *socket, boost::asio::ip::udp version, uint16_t port);
+
+	/**
+	 * Closes the given socket.
+	 * Does nothing if the socket already has been closed.
+	 * \param socket The socket to close.
+	 */
+	void close_socket(boost::asio::ip::udp::socket *socket);
+
+	/// No idea what this does. I think it is only really used when asynchronous operations are done.
+    boost::asio::io_service io_service;
+    /// The socket for IPv4.
+    boost::asio::ip::udp::socket socket_v4;
+    /// The socket for IPv6.
+    boost::asio::ip::udp::socket socket_v6;
+    /// The found broadcast addresses for IPv4.
+    /// No addresses for v6, there is only one fixed address
+	std::set<std::string> broadcast_addresses_v4;
 };
 
+/**
+ * Used to promote opened games locally.
+ */
 struct LanGamePromoter : public LanBase {
 	LanGamePromoter();
 	~LanGamePromoter();
@@ -85,6 +160,9 @@
 	bool needupdate;
 };
 
+/**
+ * Used to listen for open games while in the LAN-screen.
+ */
 struct LanGameFinder : LanBase {
 	enum { GameOpened, GameClosed, GameUpdated };
 

=== removed file 'src/network/network_system.h'
--- src/network/network_system.h	2017-01-25 18:55:59 +0000
+++ src/network/network_system.h	1970-01-01 00:00:00 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2004-2017 by the Widelands Development Team
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *
- */
-
-#ifndef WL_NETWORK_NETWORK_SYSTEM_H
-#define WL_NETWORK_NETWORK_SYSTEM_H
-
-#include <stdint.h>
-#ifndef _WIN32
-// These includes work on Linux and should be fine on any other Unix-alike.
-// If not, this is the right place to conditionally include what is needed.
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-// be compatible to microsoft
-#define closesocket close
-#define DATATYPE void
-
-#else
-
-// This is the header to include according to the documentation
-// at msdn.microsoft.com
-#include <winsock2.h>
-
-#define DATATYPE char
-// microsoft doesn't have these
-using in_port_t = uint16_t;
-using in_addr_t = uint32_t;
-
-#ifndef s_addr
-#define s_addr S_addr
-#endif
-
-// This is no typedef on purpose
-#define socklen_t int32_t
-
-#endif
-
-#endif  // end of include guard: WL_NETWORK_NETWORK_SYSTEM_H

=== modified file 'src/ui_fsmenu/internet_lobby.cc'
--- src/ui_fsmenu/internet_lobby.cc	2017-05-11 06:31:35 +0000
+++ src/ui_fsmenu/internet_lobby.cc	2017-05-20 19:06:48 +0000
@@ -373,15 +373,8 @@
 		}
 		std::string ip = InternetGaming::ref().ip();
 
-		// TODO(Notabilis): Change this for IPv6
-		//  convert IPv6 addresses returned by the metaserver to IPv4 addresses.
-		//  At the moment SDL_net does not support IPv6 anyways.
-		if (!ip.compare(0, 7, "::ffff:")) {
-			ip = ip.substr(7);
-			log("InternetGaming: cut IPv6 address: %s\n", ip.c_str());
-		}
-
-		GameClient netgame(ip, WIDELANDS_PORT, InternetGaming::ref().get_local_clientname(), true);
+		GameClient netgame(NetAddress{ip, WIDELANDS_PORT},
+							InternetGaming::ref().get_local_clientname(), true);
 		netgame.run();
 	} else
 		throw wexception("No server selected! That should not happen!");

=== modified file 'src/ui_fsmenu/netsetup_lan.cc'
--- src/ui_fsmenu/netsetup_lan.cc	2017-02-27 13:45:46 +0000
+++ src/ui_fsmenu/netsetup_lan.cc	2017-05-20 19:06:48 +0000
@@ -135,7 +135,7 @@
 	discovery.run();
 }
 
-bool FullscreenMenuNetSetupLAN::get_host_address(uint32_t& addr, uint16_t& port) {
+bool FullscreenMenuNetSetupLAN::get_host_address(NetAddress *addr) {
 	const std::string& host = hostname.text();
 
 	const uint32_t opengames_size = opengames.size();
@@ -143,20 +143,17 @@
 		const NetOpenGame& game = *opengames[i];
 
 		if (!strcmp(game.info.hostname, host.c_str())) {
-			addr = game.address;
-			port = game.port;
+			*addr = game.address;
 			return true;
 		}
 	}
 
-	if (hostent* const he = gethostbyname(host.c_str())) {
-		addr = (reinterpret_cast<in_addr*>(he->h_addr_list[0]))->s_addr;
-		DIAG_OFF("-Wold-style-cast")
-		port = htons(WIDELANDS_PORT);
-		DIAG_ON("-Wold-style-cast")
-		return true;
-	} else
-		return false;
+	// The user probably entered a hostname on his own. Try to resolve it
+	if (NetAddress::resolve_to_v6(addr, host, WIDELANDS_PORT))
+		return true;
+	if (NetAddress::resolve_to_v4(addr, host, WIDELANDS_PORT))
+		return true;
+    return false;
 }
 
 const std::string& FullscreenMenuNetSetupLAN::get_playername() {

=== modified file 'src/ui_fsmenu/netsetup_lan.h'
--- src/ui_fsmenu/netsetup_lan.h	2017-05-07 20:27:21 +0000
+++ src/ui_fsmenu/netsetup_lan.h	2017-05-20 19:06:48 +0000
@@ -34,8 +34,6 @@
 struct NetOpenGame;
 struct NetGameInfo;
 
-// TODO(Notabilis): Update for IPv6
-
 class FullscreenMenuNetSetupLAN : public FullscreenMenuBase {
 public:
 	FullscreenMenuNetSetupLAN();
@@ -43,12 +41,10 @@
 	void think() override;
 
 	/**
-	 * \param[out] addr filled in with the IP address of the chosen server
-	 * \param[out] port filled in with the port of the chosen server
-	 * \return \c true if a valid server has been chosen. If \c false is
-	 * returned, the values of \p addr and \p port are undefined.
+	 * \param[out] addr filled in with the host name or IP address and port of the chosen server
+	 * \return \c True if the address is valid, \c false otherwise. In that case \c addr is not modified
 	 */
-	bool get_host_address(uint32_t& addr, uint16_t& port);
+	bool get_host_address(NetAddress *addr);
 
 	/**
 	 * \return the name chosen by the player

=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc	2017-05-13 13:14:29 +0000
+++ src/wlapplication.cc	2017-05-20 19:06:48 +0000
@@ -350,9 +350,6 @@
 	// This might grab the input.
 	refresh_graphics();
 
-	if (SDLNet_Init() == -1)
-		throw wexception("SDLNet_Init failed: %s\n", SDLNet_GetError());
-
 	// seed random number generator used for random tribe selection
 	std::srand(time(nullptr));
 
@@ -378,8 +375,6 @@
 	delete UI::g_fh1;
 	UI::g_fh1 = nullptr;
 
-	SDLNet_Quit();
-
 	TTF_Quit();  // TODO(unknown): not here
 
 	assert(g_fs);
@@ -1164,10 +1159,6 @@
 			FullscreenMenuNetSetupLAN ns;
 			menu_result = ns.run<FullscreenMenuBase::MenuTarget>();
 			std::string playername = ns.get_playername();
-			// TODO(Notabilis): This has to be updated for IPv6
-			uint32_t addr;
-			uint16_t port;
-			bool const host_address = ns.get_host_address(addr, port);
 
 			switch (menu_result) {
 			case FullscreenMenuBase::MenuTarget::kHostgame: {
@@ -1176,18 +1167,16 @@
 				break;
 			}
 			case FullscreenMenuBase::MenuTarget::kJoingame: {
-				if (!host_address)
-					throw WLWarning(
-					   "Invalid Address", "%s", "The address of the game server is invalid");
+				NetAddress addr;
+				if (!ns.get_host_address(&addr)) {
+					UI::WLMessageBox mmb(&ns, _("Invalid address"),
+								_("The entered hostname or address is invalid and can't be connected to."),
+								UI::WLMessageBox::MBoxType::kOk);
+					mmb.run<UI::Panel::Returncodes>();
+					break;
+				}
 
-				// TODO(Notabilis): Make this prettier. I am aware that this is quite ugly but it should
-				// work
-				// for now and will be removed shortly when we switch to boost.asio
-				char ip_str[] = {"255.255.255.255"};
-				sprintf(ip_str, "%d.%d.%d.%d", (addr & 0x000000ff), (addr & 0x0000ff00) >> 8,
-				        (addr & 0x00ff0000) >> 16, (addr & 0xff000000) >> 24);
-				port = (port >> 8) | ((port & 0xFF) << 8);
-				GameClient netgame(ip_str, port, playername);
+				GameClient netgame(addr, playername);
 				netgame.run();
 				break;
 			}

=== modified file 'utils/macos/build_app.sh'
--- utils/macos/build_app.sh	2016-11-30 00:18:17 +0000
+++ utils/macos/build_app.sh	2017-05-20 19:06:48 +0000
@@ -109,7 +109,6 @@
    export SDL2IMAGEDIR="$(brew --prefix sdl2_image)"
    export SDL2MIXERDIR="$(brew --prefix sdl2_mixer)"
    export SDL2TTFDIR="$(brew --prefix sdl2_ttf)"
-   export SDL2NETDIR="$(brew --prefix sdl2_net)"
    export BOOST_ROOT="$(brew --prefix boost)"
    export ICU_ROOT="$(brew --prefix icu4c)"
 

=== modified file 'utils/win32/innosetup/Widelands.iss'
--- utils/win32/innosetup/Widelands.iss	2017-02-27 08:52:41 +0000
+++ utils/win32/innosetup/Widelands.iss	2017-05-20 19:06:48 +0000
@@ -131,7 +131,6 @@
 Source: {#DLLFolder}\SDL2.dll;                     DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
 Source: {#DLLFolder}\SDL2_image.dll;               DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
 Source: {#DLLFolder}\libSDL2_mixer-2-0-0.dll;      DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
-Source: {#DLLFolder}\SDL2_net.dll;                 DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
 Source: {#DLLFolder}\SDL2_ttf.dll;                 DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
 Source: {#DLLFolder}\zlib1.dll;                    DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"
 Source: {#DLLFolder}\libFLAC-8.dll;                DestDir: {app};            Flags: ignoreversion;                                                       Components: "Widelands"


Follow ups