0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-03-16 22:41:46 +01:00

ircd: Move and improve socket listener device.

This commit is contained in:
Jason Volk 2017-03-24 16:45:26 -07:00
parent b6b78f9674
commit 8ef53f2640
6 changed files with 292 additions and 193 deletions

40
include/ircd/listen.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "socket.h"
namespace ircd {
struct listener
{
struct acceptor;
IRCD_EXCEPTION(ircd::error, error)
private:
std::unique_ptr<struct acceptor> acceptor;
public:
listener(const json::doc &options);
~listener() noexcept;
};
} // namespace ircd

View file

@ -43,9 +43,10 @@ libircd_la_SOURCES = \
locale.cc \
http.cc \
json.cc \
matrix.cc \
parse.cc \
conf.cc
conf.cc \
listen.cc \
matrix.cc
if JS
libircd_la_SOURCES += \

View file

@ -24,6 +24,7 @@
*/
#include <ircd/socket.h>
#include <ircd/listen.h>
#include <ircd/ctx/continuation.h>
namespace ircd
@ -103,11 +104,47 @@ try
//json::test();
//exit(0);
module listener("listen.so");
module client_versions("client_versions.so");
module client_register("client_register.so");
module client_login("client_login.so");
listener matrics
{
std::string { json::obj
{
{ "name", "Chat Matrix" },
{ "host", "127.0.0.1" },
{ "port", 6667 },
{
"ssl",
{
{
"certificate",
{
{
"file",
{
{ "pem", "/home/jason/cdc.z.cert" }
}
}
}
},
{
"private_key",
{
{
"file",
{
{ "pem", "/home/jason/cdc.z.key" }
}
}
}
}
}
}
}}
};
// This is the main program loop. Right now all it does is sleep until notified
// to shutdown, but it can do other things eventually. Other subsystems may have
// spawned their own main loops.

211
ircd/listen.cc Normal file
View file

@ -0,0 +1,211 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <ircd/listen.h>
namespace ircd {
const size_t DEFAULT_STACK_SIZE
{
256_KiB // can be optimized
};
struct listener::acceptor
{
static log::log log;
std::string name;
size_t backlog;
asio::ssl::context ssl;
ip::tcp::endpoint ep;
ip::tcp::acceptor a;
ctx::context context;
explicit operator std::string() const;
bool accept();
void main();
acceptor(const json::doc &opts);
~acceptor() noexcept;
};
} // namespace ircd
///////////////////////////////////////////////////////////////////////////////
//
// ircd::listener
//
ircd::listener::listener(const json::doc &opts)
:acceptor{std::make_unique<struct acceptor>(opts)}
{
}
ircd::listener::~listener()
noexcept
{
}
///////////////////////////////////////////////////////////////////////////////
//
// ircd::listener::acceptor
//
ircd::log::log
ircd::listener::acceptor::log
{
"listener"
};
ircd::listener::acceptor::acceptor(const json::doc &opts)
try
:name
{
unquote(opts.get("name", "IRCd (ssl)"s))
}
,backlog
{
opts.get<size_t>("backlog", asio::socket_base::max_connections)
}
,ssl
{
asio::ssl::context::method::sslv23_server
}
,ep
{
ip::address::from_string(unquote(opts.get("host", "127.0.0.1"s))),
opts.get<uint16_t>("port", 6667)
}
,a
{
*ircd::ios
}
,context
{
"listener",
opts.get<size_t>("stack_size", DEFAULT_STACK_SIZE),
std::bind(&acceptor::main, this),
context.POST // defer until ctor body binds the socket
}
{
log.debug("%s attempting to open listening socket", std::string(*this));
a.open(ep.protocol());
a.set_option(ip::tcp::acceptor::reuse_address(true));
a.bind(ep);
if(opts.has("ssl.certificate.file.pem"))
{
const std::string filename{opts["ssl.certificate.file.pem"]};
ssl.use_certificate_file(filename, asio::ssl::context::pem);
log.info("%s using certificate file '%s'",
std::string(*this),
filename);
}
if(opts.has("ssl.private_key.file.pem"))
{
const std::string filename{opts["ssl.private_key.file.pem"]};
ssl.use_private_key_file(filename, asio::ssl::context::pem);
log.info("%s using private key file '%s'",
std::string(*this),
filename);
}
a.listen(backlog);
// Allows main() to run and print its log message
ctx::yield();
}
catch(const boost::system::system_error &e)
{
throw error("listener: %s", e.what());
}
ircd::listener::acceptor::~acceptor()
noexcept
{
a.cancel();
}
void
ircd::listener::acceptor::main()
try
{
log.info("%s ready", std::string(*this));
while(accept());
log.info("%s closing", std::string(*this));
}
catch(const ircd::ctx::interrupted &e)
{
log.warning("%s interrupted", std::string(*this));
}
catch(const std::exception &e)
{
log.error("%s %s", std::string(*this), e);
}
bool
ircd::listener::acceptor::accept()
try
{
auto sock(std::make_shared<ircd::socket>(ssl));
a.async_accept(sock->ssl.lowest_layer(), yield(continuation()));
sock->ssl.async_handshake(socket::handshake_type::server, yield(continuation()));
add_client(std::move(sock));
return true;
}
catch(const boost::system::system_error &e)
{
switch(e.code().value())
{
using namespace boost::system::errc;
case success: return true;
case operation_canceled: return false;
default: throw;
}
}
catch(const std::exception &e)
{
log.error("%s: in accept(): %s", std::string(*this), e);
return true;
}
ircd::listener::acceptor::operator std::string()
const
{
std::string ret(256, char());
const auto length
{
fmt::snprintf(&ret.front(), ret.size(), "'%s' @ [%s]:%u",
name,
string(ep.address()),
ep.port())
};
ret.resize(length);
return ret;
}

View file

@ -34,6 +34,4 @@ client_module_LTLIBRARIES = \
client/client_login.la
moduledir=@moduledir@
listen_la_SOURCES = listen.cc
module_LTLIBRARIES = \
listen.la

View file

@ -1,188 +0,0 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/asio.hpp>
#include <ircd/ctx/continuation.h>
#include <ircd/socket.h>
using namespace ircd;
const size_t STACK_SIZE
{
256_KiB // can be optimized
};
struct listener
{
static struct log::log log;
std::string name;
size_t backlog;
ip::address host;
ip::tcp::endpoint ep;
ctx::dock cond;
ircd::context context;
asio::ssl::context ssl;
ip::tcp::acceptor acceptor;
bool configured() const;
private:
bool accept();
void main();
public:
listener(const std::string &name = {});
listener(listener &&) = default;
};
struct log::log listener::log
{
"listener", 'P'
};
listener::listener(const std::string &name)
try
:name
{
name
}
,backlog
{
boost::asio::socket_base::max_connections
}
,context
{
"listener", STACK_SIZE, std::bind(&listener::main, this)
}
,ssl
{
asio::ssl::context::method::sslv23_server
}
,acceptor
{
*ios
}
{
ssl.use_certificate_file("/home/jason/cdc.z.cert", asio::ssl::context::pem);
ssl.use_private_key_file("/home/jason/cdc.z.key", asio::ssl::context::pem);
}
catch(const boost::system::system_error &e)
{
throw error("listener: %s", e.what());
}
bool
listener::configured()
const
{
return ep != ip::tcp::endpoint{};
}
void
listener::main()
try
{
// The listener context starts after there is a valid configuration
cond.wait([this]
{
return configured();
});
log.debug("Attempting bind() to [%s]:%u",
ep.address().to_string().c_str(),
ep.port());
acceptor.open(ep.protocol());
acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
acceptor.bind(ep);
acceptor.listen(backlog);
log.info("Listener bound to [%s]:%u",
ep.address().to_string().c_str(),
ep.port());
while(accept());
log.info("Listener closing @ [%s]:%u",
ep.address().to_string().c_str(),
ep.port());
}
catch(const ircd::ctx::interrupted &e)
{
log.warning("Listener closing @ [%s]:%u: %s",
ep.address().to_string().c_str(),
ep.port(),
e.what());
}
catch(const std::exception &e)
{
log.error("Listener closing @ [%s]:%u: %s",
ep.address().to_string().c_str(),
ep.port(),
e.what());
}
bool
listener::accept()
try
{
auto sock(std::make_shared<ircd::socket>(ssl));
acceptor.async_accept(sock->ssl.lowest_layer(), yield(continuation()));
sock->ssl.async_handshake(socket::handshake_type::server, yield(continuation()));
add_client(std::move(sock));
return true;
}
catch(const boost::system::system_error &e)
{
switch(e.code().value())
{
using namespace boost::system::errc;
case success: return true;
case operation_canceled: return false;
default: throw;
}
}
catch(const std::exception &e)
{
log.error("Listener @ [%s]:%u: accept(): %s",
ep.address().to_string().c_str(),
ep.port(),
e.what());
return true;
}
std::map<std::string, listener> listeners;
extern "C" void gogo()
{
const auto iit(listeners.emplace("foo"s, "foo"s));
auto &foo(iit.first->second);
foo.host = ip::address::from_string("127.0.0.1");
foo.ep = ip::tcp::endpoint(foo.host, 6667);
foo.cond.notify_one();
}
mapi::header IRCD_MODULE
{
"P-Line - instructions for listening sockets", &gogo
};