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

Reply via email to