0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-01-13 16:33:53 +01:00

ircd:Ⓜ️ Rework/cleanup request/session related.

This commit is contained in:
Jason Volk 2018-01-20 02:35:50 -08:00
parent 93364b6072
commit 617b0d0777
7 changed files with 419 additions and 327 deletions

View file

@ -59,12 +59,8 @@ namespace ircd::m::io
{
struct sync;
struct fetch;
struct session;
struct request;
struct response;
bool verify_x_matrix_authorization(const string_view &authorization, const string_view &method, const string_view &uri, const string_view &content);
// Synchronous acquire many requests
size_t acquire(vector_view<event::fetch>);
size_t acquire(vector_view<room::fetch>);
@ -86,8 +82,6 @@ namespace ircd::m::io
namespace ircd::m
{
using io::get;
using io::session;
using io::request;
using io::response;
}
@ -227,62 +221,8 @@ struct ircd::m::room::state::fetch
fetch() = default;
};
struct ircd::m::io::request
{
struct authorization;
struct keys;
string_view origin;
string_view destination;
string_view method;
string_view path;
string_view query;
string_view fragment;
std::string _content;
json::object content;
string_view generate_authorization(const mutable_buffer &out) const;
void operator()(server &, const vector_view<const http::header> & = {}) const;
void operator()(const vector_view<const http::header> & = {}) const;
request(const string_view &method,
const string_view &path,
const string_view &query = {},
json::members body = {})
:method{method}
,path{path}
,query{query}
,_content{json::strung(body)}
,content{_content}
{}
request(const string_view &method,
const string_view &path,
const string_view &query,
const json::object &content)
:method{method}
,path{path}
,query{query}
,content{content}
{}
request() = default;
};
struct ircd::m::io::response
:json::object
{
response(server &, parse::buffer &);
};
struct ircd::m::io::session
{
ircd::server server;
std::string destination;
std::string access_token;
json::object operator()(parse::buffer &pb, request &);
session(const net::remote &);
session() = default;
response(server::request &, parse::buffer &);
};

View file

@ -68,6 +68,8 @@ namespace ircd
#include "query.h"
#include "cursor.h"
#include "room.h"
#include "request.h"
#include "session.h"
#include "io.h"
#include "vm.h"
#include "user.h"

129
include/ircd/m/request.h Normal file
View file

@ -0,0 +1,129 @@
// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 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. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_M_REQUEST_H
namespace ircd::m
{
struct request;
}
/// Composes a matrix protocol request. This is a JSON tuple because the
/// protocol uses a JSON authorization object to create the X-Matrix
/// authorization header on federation requests.
///
struct ircd::m::request
:json::tuple
<
json::property<name::content, json::object>,
json::property<name::destination, string_view>,
json::property<name::method, string_view>,
json::property<name::origin, string_view>,
json::property<name::uri, string_view>
>
{
struct x_matrix;
static bool verify(const ed25519::pk &, const ed25519::sig &, const json::object &);
bool verify(const ed25519::pk &, const ed25519::sig &) const;
bool verify(const string_view &key, const string_view &sig) const;
string_view generate(const mutable_buffer &out, const ed25519::sk &, const string_view &pkid) const;
string_view operator()(const mutable_buffer &out, const vector_view<const http::header> & = {}) const;
request(const string_view &origin,
const string_view &destination,
const string_view &method,
const string_view &uri,
const json::object &content);
request(const string_view &method,
const string_view &uri,
const json::object &content);
request(const string_view &method,
const string_view &uri);
// stringifies content members into buf
request(const string_view &method,
const string_view &uri,
const mutable_buffer &body_buf,
const json::members &body);
using super_type::tuple;
request() = default;
};
struct ircd::m::request::x_matrix
{
string_view origin;
string_view key;
string_view sig;
x_matrix(const string_view &);
x_matrix() = default;
};
inline
ircd::m::request::request(const string_view &method,
const string_view &uri,
const mutable_buffer &body_buf,
const json::members &body)
:request
{
my_host(),
string_view{},
method,
uri,
json::stringify(mutable_buffer{body_buf}, body)
}
{}
inline
ircd::m::request::request(const string_view &method,
const string_view &uri)
:request
{
my_host(),
string_view{},
method,
uri,
json::object{}
}
{}
inline
ircd::m::request::request(const string_view &method,
const string_view &uri,
const json::object &content)
:request
{
my_host(),
string_view{},
method,
uri,
content
}
{}
inline
ircd::m::request::request(const string_view &origin,
const string_view &destination,
const string_view &method,
const string_view &uri,
const json::object &content)
{
json::get<"origin"_>(*this) = origin;
json::get<"destination"_>(*this) = destination;
json::get<"method"_>(*this) = method;
json::get<"uri"_>(*this) = uri;
json::get<"content"_>(*this) = content;
}

88
include/ircd/m/session.h Normal file
View file

@ -0,0 +1,88 @@
// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 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. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_M_SESSION_H
namespace ircd::m
{
struct session;
}
struct ircd::m::session
{
net::remote remote;
std::string access_token;
server::request operator()(const server::out &out, const server::in &in) const;
template<class... args> server::request operator()(const mutable_buffer &buf, args&&...) const;
session(const net::remote &remote)
:remote{remote}
{}
session(const net::hostport &remote)
:remote{remote}
{}
session() = default;
};
template<class... args>
ircd::server::request
ircd::m::session::operator()(const mutable_buffer &buf,
args&&... a)
const
{
m::request request
{
std::forward<args>(a)...
};
if(!defined(json::get<"destination"_>(request)))
json::get<"destination"_>(request) = remote.hostname;
if(!defined(json::get<"origin"_>(request)))
json::get<"origin"_>(request) = my_host();
const auto head
{
request(buf)
};
const server::out out
{
head
};
const auto in_max
{
std::max(ssize_t(size(buf) - size(head)), ssize_t(0))
};
assert(in_max >= ssize_t(size(buf) / 2));
const server::in in
{
{ data(buf) + size(head), size_t(in_max) }
};
return operator()(out, in);
}
inline ircd::server::request
ircd::m::session::operator()(const server::out &out,
const server::in &in)
const
{
return
{
remote, out, in
};
}

View file

@ -78,6 +78,7 @@ libircd_la_SOURCES = \
m/id.cc \
m/io.cc \
m/keys.cc \
m/request.cc \
m/room.cc \
m/vm.cc \
m/m.cc \

View file

@ -524,269 +524,3 @@ catch(const std::exception &e)
tab.error = std::make_exception_ptr(e);
return false;
}
///////////////////////////////////////////////////////////////////////////////
//
// m/session.h
//
ircd::m::io::session::session(const net::remote &remote)
:server{remote}
,destination{remote.hostname}
{
}
ircd::json::object
ircd::m::io::session::operator()(parse::buffer &pb,
request &request)
{
request.destination = destination;
request(server);
return response
{
server, pb
};
}
ircd::m::io::response::response(server &server,
parse::buffer &pb)
{
http::code status;
json::object &object
{
static_cast<json::object &>(*this)
};
parse::capstan pc
{
pb, read_closure(server)
};
http::response
{
pc,
nullptr,
[&pc, &status, &object](const http::response::head &head)
{
status = http::status(head.status);
object = http::response::content{pc, head};
},
[](const auto &header)
{
//std::cout << header.first << " :" << header.second << std::endl;
}
};
if(status < 200 || status >= 300)
throw m::error(status, object);
}
void
ircd::m::io::request::operator()(const vector_view<const http::header> &addl_headers)
const
{
}
void
ircd::m::io::request::operator()(server &server,
const vector_view<const http::header> &addl_headers)
const
{
const size_t addl_headers_size
{
std::min(addl_headers.size(), size_t(64UL))
};
size_t headers{1};
http::line::header header[headers + addl_headers_size]
{
{ "User-Agent", BRANDING_NAME " (IRCd " BRANDING_VERSION ")" },
};
for(size_t i(0); i < addl_headers_size; ++i)
header[headers++] = addl_headers.at(i);
char x_matrix[1024];
if(startswith(path, "_matrix/federation"))
header[headers++] =
{
"Authorization", generate_authorization(x_matrix)
};
static const auto content_type
{
"application/json; charset=utf-8"
};
const unique_buffer<mutable_buffer> head_buf{8192};
stream_buffer head{head_buf};
http::request
{
head,
destination,
method,
path,
query,
content.size(),
content_type,
{ header, headers }
};
const ilist<const_buffer> vector
{
head.completed(),
content
};
write_closure(server)(vector);
}
namespace ircd::m::name
{
// constexpr const char *const content {"content"};
constexpr const char *const destination {"destination"};
constexpr const char *const method {"method"};
// constexpr const char *const origin {"origin"};
constexpr const char *const uri {"uri"};
}
struct ircd::m::io::request::authorization
:json::tuple
<
json::property<name::content, string_view>,
json::property<name::destination, string_view>,
json::property<name::method, string_view>,
json::property<name::origin, string_view>,
json::property<name::uri, string_view>
>
{
string_view generate(const mutable_buffer &out);
using super_type::tuple;
};
ircd::string_view
ircd::m::io::request::generate_authorization(const mutable_buffer &out)
const
{
const fmt::bsprintf<2048> uri
{
"/%s%s%s", lstrip(path, '/'), query? "?" : "", query
};
request::authorization authorization
{
json::members
{
{ "destination", destination },
{ "method", method },
{ "origin", my_host() },
{ "uri", uri },
}
};
if(string_view{content}.size() > 2)
json::get<"content"_>(authorization) = content;
return authorization.generate(out);
}
ircd::string_view
ircd::m::io::request::authorization::generate(const mutable_buffer &out)
{
// Any buffers here can be comfortably large if they're not on a stack and
// nothing in this procedure has a yield which risks decohering static
// buffers; the assertion is tripped if so.
ctx::critical_assertion ca;
static fixed_buffer<mutable_buffer, 131072> request_object_buf;
const auto request_object
{
json::stringify(request_object_buf, *this)
};
const ed25519::sig sig
{
self::secret_key.sign(request_object)
};
static fixed_buffer<mutable_buffer, 128> signature_buf;
const auto x_matrix_len
{
fmt::sprintf(out, "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"",
unquote(string_view{at<"origin"_>(*this)}),
self::public_key_id,
b64encode_unpadded(signature_buf, sig))
};
return
{
data(out), size_t(x_matrix_len)
};
}
bool
ircd::m::io::verify_x_matrix_authorization(const string_view &x_matrix,
const string_view &method,
const string_view &uri,
const string_view &content)
{
string_view tokens[3], origin, key, sig;
if(ircd::tokens(split(x_matrix, ' ').second, ',', tokens) != 3)
return false;
for(const auto &token : tokens)
{
const auto &kv{split(token, '=')};
const auto &val{unquote(kv.second)};
switch(hash(kv.first))
{
case hash("origin"): origin = val; break;
case hash("key"): key = val; break;
case hash("sig"): sig = val; break;
}
}
request::authorization authorization
{
json::members
{
{ "destination", my_host() },
{ "method", method },
{ "origin", origin },
{ "uri", uri },
}
};
if(content.size() > 2)
json::get<"content"_>(authorization) = content;
//TODO: XXX
const json::strung request_object
{
authorization
};
const ed25519::sig _sig
{
[&sig](auto &buf)
{
b64decode(buf, sig);
}
};
const ed25519::pk pk
{
[&origin, &key](auto &buf)
{
m::keys::get(origin, key, [&buf]
(const string_view &key)
{
b64decode(buf, unquote(key));
});
}
};
return pk.verify(const_raw_buffer{request_object}, _sig);
}

198
ircd/m/request.cc Normal file
View file

@ -0,0 +1,198 @@
/*
* 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/m/m.h>
//
// request
//
ircd::string_view
ircd::m::request::operator()(const mutable_buffer &out,
const vector_view<const http::header> &addl_headers)
const
{
const size_t addl_headers_size
{
std::min(addl_headers.size(), size_t(64UL))
};
size_t headers{1};
http::header header[headers + addl_headers_size]
{
{ "User-Agent", BRANDING_NAME " (IRCd " BRANDING_VERSION ")" },
};
for(size_t i(0); i < addl_headers_size; ++i)
header[headers++] = addl_headers.at(i);
thread_local char x_matrix[1_KiB];
if(startswith(at<"uri"_>(*this), "/_matrix/federation"))
{
const auto &sk{self::secret_key};
const auto &pkid{self::public_key_id};
header[headers++] =
{
"Authorization", generate(x_matrix, sk, pkid)
};
}
static const string_view content_type
{
"application/json; charset=utf-8"_sv
};
const auto content_length
{
string_view(json::get<"content"_>(*this)).size()
};
stream_buffer sb{out};
http::request
{
sb,
at<"destination"_>(*this),
at<"method"_>(*this),
at<"uri"_>(*this),
content_length,
content_type,
{ header, headers }
};
return sb.completed();
}
ircd::string_view
ircd::m::request::generate(const mutable_buffer &out,
const ed25519::sk &sk,
const string_view &pkid)
const
{
const json::strung object
{
*this,
};
const ed25519::sig sig
{
self::secret_key.sign(const_buffer{object})
};
const auto &origin
{
unquote(string_view{at<"origin"_>(*this)})
};
thread_local char sigb64[1_KiB];
return fmt::sprintf
{
out, "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"",
origin,
pkid,
b64encode_unpadded(sigb64, sig)
};
}
bool
ircd::m::request::verify(const string_view &key,
const string_view &sig)
const
{
const ed25519::sig _sig
{
[&sig](auto &buf)
{
b64decode(buf, sig);
}
};
const auto &origin
{
unquote(at<"origin"_>(*this))
};
const ed25519::pk pk
{
[&origin, &key](auto &buf)
{
m::keys::get(origin, key, [&buf]
(const string_view &key)
{
b64decode(buf, unquote(key));
});
}
};
return verify(pk, _sig);
}
bool
ircd::m::request::verify(const ed25519::pk &pk,
const ed25519::sig &sig)
const
{
const json::strung object
{
*this
};
return verify(pk, sig, object);
}
bool
ircd::m::request::verify(const ed25519::pk &pk,
const ed25519::sig &sig,
const json::object &object)
{
return pk.verify(object, sig);
}
//
// x_matrix
//
ircd::m::request::x_matrix::x_matrix(const string_view &input)
{
string_view tokens[3];
if(ircd::tokens(split(input, ' ').second, ',', tokens) != 3)
throw std::out_of_range{"The x_matrix header is malformed"};
for(const auto &token : tokens)
{
const auto &kv{split(token, '=')};
const auto &val{unquote(kv.second)};
switch(hash(kv.first))
{
case hash("origin"): origin = val; break;
case hash("key"): key = val; break;
case hash("sig"): sig = val; break;
}
}
if(empty(origin))
throw std::out_of_range{"The x_matrix header is missing 'origin='"};
if(empty(key))
throw std::out_of_range{"The x_matrix header is missing 'key='"};
if(empty(sig))
throw std::out_of_range{"The x_matrix header is missing 'sig='"};
}