[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-3-0' - loolwsd.xml.in net/ServerSocket.hpp net/Socket.cpp net/Socket.hpp wsd/LOOLWSD.cpp

Michael Meeks michael.meeks at collabora.com
Thu Jan 18 12:48:17 UTC 2018


 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 4f425b1d11611fa54c899df1f5b907f034efc291
Author: Michael Meeks <michael.meeks at 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
    Reviewed-on: https://gerrit.libreoffice.org/47961
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>
    Tested-by: Jan Holesovsky <kendy at collabora.com>

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 13424cd3..0ad24d5b 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()
@@ -221,6 +227,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 0b6af504..a8ac63a7 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 622396f7..79646ce2 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" },
@@ -758,6 +764,18 @@ void LOOLWSD::initialize(Application& self)
 
     Log::initialize("wsd", logLevel, withColor, logToFile, logProperties);
 
+    {
+        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
@@ -2518,40 +2536,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)
@@ -2565,6 +2582,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 << ".");
@@ -2576,14 +2594,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);


More information about the Libreoffice-commits mailing list