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:
parent
b6b78f9674
commit
8ef53f2640
6 changed files with 292 additions and 193 deletions
40
include/ircd/listen.h
Normal file
40
include/ircd/listen.h
Normal 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
|
|
@ -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 += \
|
||||
|
|
39
ircd/ircd.cc
39
ircd/ircd.cc
|
@ -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
211
ircd/listen.cc
Normal 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;
|
||||
}
|
|
@ -34,6 +34,4 @@ client_module_LTLIBRARIES = \
|
|||
client/client_login.la
|
||||
|
||||
moduledir=@moduledir@
|
||||
listen_la_SOURCES = listen.cc
|
||||
module_LTLIBRARIES = \
|
||||
listen.la
|
||||
|
|
|
@ -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
|
||||
};
|
Loading…
Add table
Reference in a new issue