summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2018-01-09 14:02:02 +0000
committerMichael Meeks <michael.meeks@collabora.com>2018-01-09 22:03:17 +0000
commita1ee97c222d60bbb81c597327e2b5ff89e903970 (patch)
tree1c7bce7af2a9b81ca5c7a5b405505d3c7a82233e
parentloleaflet: updated UI pot (diff)
downloadonline-a1ee97c222d60bbb81c597327e2b5ff89e903970.tar.gz
online-a1ee97c222d60bbb81c597327e2b5ff89e903970.zip
Add IPv6 support, and configuration option.
Default to listening on both IPv44 and IPv6 for public interfaces. Change-Id: Ib04e3bf65e7dcf2a798d381297b15ee9c56e9259
-rw-r--r--loolwsd.xml.in4
-rw-r--r--net/ServerSocket.hpp25
-rw-r--r--net/Socket.cpp56
-rw-r--r--net/Socket.hpp9
-rw-r--r--wsd/LOOLWSD.cpp48
5 files changed, 111 insertions, 31 deletions
diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 754aa6c30f..1609c7bc57 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 4d4bb3532d..7ae7e7141f 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 eb2f19fb80..d0a791ec9c 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 736be21812..0175995346 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 b3336f012f..adbe06c017 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);