loolwsd.xml.in | 4 +++ net/ServerSocket.hpp | 25 ++++++++++------------ net/Socket.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ net/Socket.hpp | 9 ++++++-- wsd/LOOLWSD.cpp | 48 ++++++++++++++++++++++++++++++------------- 5 files changed, 111 insertions(+), 31 deletions(-)
New commits: commit a1ee97c222d60bbb81c597327e2b5ff89e903970 Author: Michael Meeks <michael.me...@collabora.com> Date: Tue Jan 9 14:02:02 2018 +0000 Add IPv6 support, and configuration option. Default to listening on both IPv44 and IPv6 for public interfaces. Change-Id: Ib04e3bf65e7dcf2a798d381297b15ee9c56e9259 diff --git a/loolwsd.xml.in b/loolwsd.xml.in index 754aa6c3..1609c7bc 100644 --- a/loolwsd.xml.in +++ b/loolwsd.xml.in @@ -60,6 +60,10 @@ </outgoing> </trace> + <net desc="Network settings"> + <proto type="string" default="all" desc="Protocol to use IPv4, IPv6 or all for both">all</proto> + </net> + <ssl desc="SSL settings"> <enable type="bool" default="true">true</enable> <termination desc="Connection via proxy where loolwsd acts as working via https, but actually uses http." type="bool" default="true">false</termination> diff --git a/net/ServerSocket.hpp b/net/ServerSocket.hpp index 4d4bb353..7ae7e714 100644 --- a/net/ServerSocket.hpp +++ b/net/ServerSocket.hpp @@ -27,27 +27,20 @@ public: class ServerSocket : public Socket { public: - ServerSocket(SocketPoll& clientPoller, std::shared_ptr<SocketFactory> sockFactory) : + ServerSocket(Socket::Type type, SocketPoll& clientPoller, std::shared_ptr<SocketFactory> sockFactory) : + Socket(type), + _type(type), _clientPoller(clientPoller), _sockFactory(std::move(sockFactory)) { } + enum Type { Local, Public }; + /// Binds to a local address (Servers only). /// Does not retry on error. - /// Returns true on success only. - bool bind(const Poco::Net::SocketAddress& address) - { - // Enable address reuse to avoid stalling after - // recycling, when previous socket is TIME_WAIT. - //TODO: Might be worth refactoring out. - const int reuseAddress = 1; - constexpr unsigned int len = sizeof(reuseAddress); - ::setsockopt(getFD(), SOL_SOCKET, SO_REUSEADDR, &reuseAddress, len); - - const int rc = ::bind(getFD(), address.addr(), address.length()); - return rc == 0; - } + /// Returns true only on success. + bool bind(Type type, int port); /// Listen to incoming connections (Servers only). /// Does not retry on error. @@ -55,6 +48,9 @@ public: bool listen(const int backlog = 64) { const int rc = ::listen(getFD(), backlog); + + if (rc) + LOG_SYS("Failed to listen"); return rc == 0; } @@ -107,6 +103,7 @@ public: } private: + Socket::Type _type; SocketPoll& _clientPoller; std::shared_ptr<SocketFactory> _sockFactory; }; diff --git a/net/Socket.cpp b/net/Socket.cpp index eb2f19fb..d0a791ec 100644 --- a/net/Socket.cpp +++ b/net/Socket.cpp @@ -28,6 +28,12 @@ int SocketPoll::DefaultPollTimeoutMs = 5000; std::atomic<bool> SocketPoll::InhibitThreadChecks(false); std::atomic<bool> Socket::InhibitThreadChecks(false); +int Socket::createSocket(Socket::Type type) +{ + int domain = type == Type::IPv4 ? AF_INET : AF_INET6; + return socket(domain, SOCK_STREAM | SOCK_NONBLOCK, 0); +} + // help with initialization order namespace { std::vector<int> &getWakeupsArray() @@ -183,6 +189,56 @@ void SocketPoll::dumpState(std::ostream& os) i->dumpState(os); } +/// Returns true on success only. +bool ServerSocket::bind(Type type, int port) +{ + // Enable address reuse to avoid stalling after + // recycling, when previous socket is TIME_WAIT. + //TODO: Might be worth refactoring out. + const int reuseAddress = 1; + constexpr unsigned int len = sizeof(reuseAddress); + ::setsockopt(getFD(), SOL_SOCKET, SO_REUSEADDR, &reuseAddress, len); + + int rc; + + if (_type == Socket::Type::IPv4) + { + struct sockaddr_in addrv4; + std::memset(&addrv4, 0, sizeof(addrv4)); + addrv4.sin_family = AF_INET; + addrv4.sin_port = htons(port); + if (type == Type::Public) + addrv4.sin_addr.s_addr = type == htonl(INADDR_ANY); + else + addrv4.sin_addr.s_addr = type == htonl(INADDR_LOOPBACK); + + rc = ::bind(getFD(), (const sockaddr *)&addrv4, sizeof(addrv4)); + } + else + { + struct sockaddr_in6 addrv6; + std::memset(&addrv6, 0, sizeof(addrv6)); + addrv6.sin6_family = AF_INET6; + addrv6.sin6_port = htons(port); + if (type == Type::Public) + addrv6.sin6_addr = in6addr_any; + else + addrv6.sin6_addr = in6addr_loopback; + + int ipv6only = _type == Socket::Type::All ? 0 : 1; + if (::setsockopt(getFD(), IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)) == -1) + LOG_SYS("Failed set ipv6 socket to %d" << ipv6only); + + rc = ::bind(getFD(), (const sockaddr *)&addrv6, sizeof(addrv6)); + } + + if (rc) + LOG_SYS("Failed to bind to: " << (_type == Socket::Type::IPv4 ? "IPv4" : "IPv6") << " port: " << port); + + return rc == 0; +} + + namespace HttpHelper { void sendUncompressedFileContent(const std::shared_ptr<StreamSocket>& socket, diff --git a/net/Socket.hpp b/net/Socket.hpp index 736be218..01759953 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -96,8 +96,10 @@ public: static const int MaximumSendBufferSize = 128 * 1024; static std::atomic<bool> InhibitThreadChecks; - Socket() : - _fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)), + enum Type { IPv4, IPv6, All }; + + Socket(Type type) : + _fd(createSocket(type)), _sendBufferSize(DefaultSendBufferSize), _owner(std::this_thread::get_id()) { @@ -112,6 +114,9 @@ public: close(_fd); } + /// Create socket of the given type. + int createSocket(Type type); + /// Returns the OS native socket fd. int getFD() const { return _fd; } diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index b3336f01..adbe06c0 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -166,7 +166,12 @@ using Poco::XML::InputSource; using Poco::XML::Node; using Poco::XML::NodeList; +/// Port for external clients to connect to int ClientPortNumber = DEFAULT_CLIENT_PORT_NUMBER; +/// Protocols to listen on +Socket::Type ClientPortProto = Socket::Type::All; + +/// Port for prisoners to connect to int MasterPortNumber = DEFAULT_MASTER_PORT_NUMBER; /// New LOK child processes ready to host documents. @@ -655,6 +660,7 @@ void LOOLWSD::initialize(Application& self) { "logging.color", "true" }, { "logging.level", "trace" }, { "loleaflet_logging", "false" }, + { "net.proto", "all" }, { "ssl.enable", "true" }, { "ssl.termination", "true" }, { "ssl.cert_file_path", LOOLWSD_CONFIGDIR "/cert.pem" }, @@ -764,6 +770,18 @@ void LOOLWSD::initialize(Application& self) LOG_INF("Setting log-level to [trace] and delaying setting to requested [" << LogLevel << "]."); } + { + std::string proto = getConfigValue<std::string>(conf, "net.proto", ""); + if (!Poco::icompare(proto, "ipv4")) + ClientPortProto = Socket::Type::IPv4; + else if (!Poco::icompare(proto, "ipv6")) + ClientPortProto = Socket::Type::IPv6; + else if (!Poco::icompare(proto, "all")) + ClientPortProto = Socket::Type::All; + else + LOG_WRN("Invalid protocol: " << proto); + } + #if ENABLE_SSL LOOLWSD::SSLEnabled.set(getConfigValue<bool>(conf, "ssl.enable", true)); #else @@ -2526,40 +2544,39 @@ private: /// Create a new server socket - accepted sockets will be added /// to the @clientSockets' poll when created with @factory. - std::shared_ptr<ServerSocket> getServerSocket(const Poco::Net::SocketAddress& addr, + std::shared_ptr<ServerSocket> getServerSocket(ServerSocket::Type type, int port, SocketPoll &clientSocket, std::shared_ptr<SocketFactory> factory) { - std::shared_ptr<ServerSocket> serverSocket = std::make_shared<ServerSocket>(clientSocket, factory); + auto serverSocket = std::make_shared<ServerSocket>( + type == ServerSocket::Type::Local ? Socket::Type::IPv4 : ClientPortProto, + clientSocket, factory); - if (!serverSocket->bind(addr)) - { - LOG_SYS("Failed to bind to: " << addr.toString()); + if (!serverSocket->bind(type, port)) return nullptr; - } if (serverSocket->listen()) return serverSocket; - LOG_SYS("Failed to listen on: " << addr.toString()); return nullptr; } + /// Create the internal only, local socket for forkit / kits prisoners to talk to. std::shared_ptr<ServerSocket> findPrisonerServerPort(int& port) { std::shared_ptr<SocketFactory> factory = std::make_shared<PrisonerSocketFactory>(); LOG_INF("Trying to listen on prisoner port " << port << "."); - std::shared_ptr<ServerSocket> socket = getServerSocket(SocketAddress("127.0.0.1", port), - PrisonerPoll, factory); + std::shared_ptr<ServerSocket> socket = getServerSocket( + ServerSocket::Type::Local, port, PrisonerPoll, factory); // If we fail, try the next 100 ports. for (int i = 0; i < 100 && !socket; ++i) { ++port; LOG_INF("Prisoner port " << (port - 1) << " is busy, trying " << port << "."); - socket = getServerSocket(SocketAddress("127.0.0.1", port), - PrisonerPoll, factory); + socket = getServerSocket( + ServerSocket::Type::Local, port, PrisonerPoll, factory); } if (!UnitWSD::isUnitTesting() && !socket) @@ -2573,6 +2590,7 @@ private: return socket; } + /// Create the externally listening public socket std::shared_ptr<ServerSocket> findServerPort(int port) { LOG_INF("Trying to listen on client port " << port << "."); @@ -2584,14 +2602,14 @@ private: #endif factory = std::make_shared<PlainSocketFactory>(); - std::shared_ptr<ServerSocket> socket = getServerSocket(SocketAddress(port), - WebServerPoll, factory); + std::shared_ptr<ServerSocket> socket = getServerSocket( + ServerSocket::Type::Public, port, WebServerPoll, factory); while (!socket) { ++port; LOG_INF("Client port " << (port - 1) << " is busy, trying " << port << "."); - socket = getServerSocket(SocketAddress(port), - WebServerPoll, factory); + socket = getServerSocket( + ServerSocket::Type::Public, port, WebServerPoll, factory); } LOG_INF("Listening to client connections on port " << port); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits