mirror of https://github.com/oxen-io/oxen-core.git
Wallet RPC server modernization
- Replaces the wallet RPC classes with ones like the core RPC server (with serialization code moved into a new .cpp file). - Restricted commands are now carried through the RPC serialization types (by inheriting from RESTRICTED instead of RPC_COMMAND) and restrictions are handled in one place rather than being handled in each of the 49 restricted endpoints. This differs a little from how the core http server works (which has a PUBLIC base class) because for the wallet rpc server unrestricted really doesn't mean "public", it means something closer to view-only. - GET_TRANSFERS_CSV is now restricted (it looks like an oversight that it wasn't before since GET_TRANSFERS is restricted) - GET_ADDRESS_BOOK_ENTRY is now restricted. Since restricted mode is meant to provide something like view-only access, it doesn't make much sense that address book entries were available. - Use uWebSockets to provide the wallet RPC server HTTP functionality. This version is quite a bit simpler than the core RPC version since it doesn't support multithreaded (parallel) requests, and so we don't have to worry about queuing jobs. - Converted all the numeric wallet rpc error codes defines to constexprs - Changed how endpoints get called; previous this was called: bool on_some_endpoint(const wallet_rpc::COMMAND_RPC_SOME_ENDPOINT::request& req, wallet_rpc::COMMAND_RPC_SOME_ENDPOINT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL) This PR changes it similarly to how core_rpc_server's endpoints work: wallet_rpc::SOME_ENDPOINT invoke(wallet_rpc::COMMAND_RPC_SOME_ENDPOINT::request&& req); That is: - the response is now returned - the request is provided by mutable rvalue reference - the error object is gone (just throw instead) - the connection_context is gone (it was not used at all by any wallet rpc endpoint). - Consolidated most of the (identical) exception handling to the RPC method invocation point rather than needing to repeat it in each individual endpoint. This means each endpoint's `invoke` method can now just throw (or not catch) exceptions. Some try/catches are still there because they are converting one type of exception into another, but the generic ones that return a generic error are gone. - Removed all remaining epee http code. - DRYed out some wallet rpc code.
This commit is contained in:
parent
8de0cc5553
commit
899d1e4eaf
|
@ -1,173 +0,0 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include "wipeable_string.h"
|
||||
#include "http_base.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
struct login
|
||||
{
|
||||
login() : username(), password() {}
|
||||
login(std::string username_, wipeable_string password_)
|
||||
: username(std::move(username_)), password(std::move(password_))
|
||||
{}
|
||||
|
||||
std::string username;
|
||||
wipeable_string password;
|
||||
};
|
||||
|
||||
//! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added.
|
||||
class http_server_auth
|
||||
{
|
||||
public:
|
||||
struct session
|
||||
{
|
||||
session(login credentials_)
|
||||
: credentials(std::move(credentials_)), nonce(), counter(0)
|
||||
{}
|
||||
|
||||
login credentials;
|
||||
std::string nonce;
|
||||
std::uint32_t counter;
|
||||
};
|
||||
|
||||
http_server_auth() : user(), rng() {}
|
||||
http_server_auth(login credentials, std::function<void(size_t, uint8_t*)> r);
|
||||
|
||||
//! \return Auth response, or `std::nullopt` iff `request` had valid auth.
|
||||
std::optional<http_response_info> get_response(const http_request_info& request)
|
||||
{
|
||||
if (user)
|
||||
return do_get_response(request);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<http_response_info> do_get_response(const http_request_info& request);
|
||||
|
||||
std::optional<session> user;
|
||||
|
||||
std::function<void(size_t, uint8_t*)> rng;
|
||||
};
|
||||
|
||||
//! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added.
|
||||
class http_client_auth
|
||||
{
|
||||
public:
|
||||
enum status : std::uint8_t { kSuccess = 0, kBadPassword, kParseFailure };
|
||||
|
||||
struct session
|
||||
{
|
||||
session(login credentials_)
|
||||
: credentials(std::move(credentials_)), server(), counter(0)
|
||||
{}
|
||||
|
||||
struct keys
|
||||
{
|
||||
using algorithm =
|
||||
std::function<std::string(const session&, std::string_view, std::string_view)>;
|
||||
|
||||
keys() : nonce(), opaque(), realm(), generator() {}
|
||||
keys(std::string nonce_, std::string opaque_, std::string realm_, algorithm generator_)
|
||||
: nonce(std::move(nonce_))
|
||||
, opaque(std::move(opaque_))
|
||||
, realm(std::move(realm_))
|
||||
, generator(std::move(generator_))
|
||||
{}
|
||||
|
||||
std::string nonce;
|
||||
std::string opaque;
|
||||
std::string realm;
|
||||
algorithm generator;
|
||||
};
|
||||
|
||||
login credentials;
|
||||
keys server;
|
||||
std::uint32_t counter;
|
||||
};
|
||||
|
||||
http_client_auth() : user() {}
|
||||
http_client_auth(login credentials);
|
||||
|
||||
/*!
|
||||
Clients receiving a 401 response code from the server should call this
|
||||
function to process the server auth. Then, before every client request,
|
||||
`get_auth_field()` should be called to retrieve the newest
|
||||
authorization request.
|
||||
|
||||
\return `kBadPassword` if client will never be able to authenticate,
|
||||
`kParseFailure` if all server authentication responses were invalid,
|
||||
and `kSuccess` if `get_auth_field` is ready to generate authorization
|
||||
fields.
|
||||
*/
|
||||
status handle_401(const http_response_info& response)
|
||||
{
|
||||
if (user)
|
||||
return do_handle_401(response);
|
||||
return kBadPassword;
|
||||
}
|
||||
|
||||
/*!
|
||||
After calling `handle_401`, clients should call this function to
|
||||
generate an authentication field for every request.
|
||||
|
||||
\return A HTTP "Authorization" field if `handle_401(...)` previously
|
||||
returned `kSuccess`.
|
||||
*/
|
||||
std::optional<std::pair<std::string, std::string>> get_auth_field(
|
||||
std::string_view method, std::string_view uri)
|
||||
{
|
||||
if (user)
|
||||
return do_get_auth_field(method, uri);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
status do_handle_401(const http_response_info&);
|
||||
std::optional<std::pair<std::string, std::string>> do_get_auth_field(std::string_view, std::string_view);
|
||||
|
||||
std::optional<session> user;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <list>
|
||||
|
||||
#include "memwipe.h"
|
||||
#include "string_tools.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
namespace epee::net_utils::http
|
||||
{
|
||||
|
||||
enum http_method{
|
||||
http_method_options,
|
||||
http_method_get,
|
||||
http_method_post,
|
||||
http_method_put,
|
||||
http_method_head,
|
||||
http_method_etc,
|
||||
http_method_unknown
|
||||
};
|
||||
|
||||
enum http_content_type
|
||||
{
|
||||
http_content_type_text_html,
|
||||
http_content_type_image_gif,
|
||||
http_content_type_other,
|
||||
http_content_type_not_set
|
||||
};
|
||||
|
||||
typedef std::list<std::pair<std::string, std::string> > fields_list;
|
||||
|
||||
inline
|
||||
std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields)
|
||||
{
|
||||
fields_list::const_iterator it = fields.begin();
|
||||
for(; it != fields.end(); it++)
|
||||
if(!string_tools::compare_no_case(param_name, it->first))
|
||||
break;
|
||||
|
||||
if(it==fields.end())
|
||||
return std::string();
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static inline void add_field(std::string& out, std::string_view name, std::string_view value)
|
||||
{
|
||||
out.append(name).append(": "sv).append(value).append("\r\n"sv);
|
||||
}
|
||||
static inline void add_field(std::string& out, const std::pair<std::string, std::string>& field)
|
||||
{
|
||||
add_field(out, field.first, field.second);
|
||||
}
|
||||
|
||||
|
||||
struct http_header_info
|
||||
{
|
||||
std::string m_connection; //"Connection:"
|
||||
std::string m_referer; //"Referer:"
|
||||
std::string m_content_length; //"Content-Length:"
|
||||
std::string m_content_type; //"Content-Type:"
|
||||
std::string m_transfer_encoding;//"Transfer-Encoding:"
|
||||
std::string m_content_encoding; //"Content-Encoding:"
|
||||
std::string m_host; //"Host:"
|
||||
std::string m_cookie; //"Cookie:"
|
||||
std::string m_user_agent; //"User-Agent:"
|
||||
std::string m_origin; //"Origin:"
|
||||
fields_list m_etc_fields;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_connection.clear();
|
||||
m_referer.clear();
|
||||
m_content_length.clear();
|
||||
m_content_type.clear();
|
||||
m_transfer_encoding.clear();
|
||||
m_content_encoding.clear();
|
||||
m_host.clear();
|
||||
m_cookie.clear();
|
||||
m_user_agent.clear();
|
||||
m_origin.clear();
|
||||
m_etc_fields.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct uri_content
|
||||
{
|
||||
std::string m_path;
|
||||
std::string m_query;
|
||||
std::string m_fragment;
|
||||
std::list<std::pair<std::string, std::string> > m_query_params;
|
||||
};
|
||||
|
||||
struct url_content
|
||||
{
|
||||
std::string schema;
|
||||
std::string host;
|
||||
std::string uri;
|
||||
uint64_t port;
|
||||
uri_content m_uri_content;
|
||||
};
|
||||
|
||||
|
||||
struct http_request_info
|
||||
{
|
||||
http_request_info():m_http_method(http_method_unknown),
|
||||
m_http_ver_hi(0),
|
||||
m_http_ver_lo(0),
|
||||
m_have_to_block(false),
|
||||
m_full_request_buf_size(0)
|
||||
{}
|
||||
|
||||
http_method m_http_method;
|
||||
std::string m_URI;
|
||||
std::string m_http_method_str;
|
||||
std::string m_full_request_str;
|
||||
std::string m_replace_html;
|
||||
std::string m_request_head;
|
||||
int m_http_ver_hi;
|
||||
int m_http_ver_lo;
|
||||
bool m_have_to_block;
|
||||
http_header_info m_header_info;
|
||||
uri_content m_uri_content;
|
||||
size_t m_full_request_buf_size;
|
||||
std::string m_body;
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->~http_request_info();
|
||||
new(this) http_request_info();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct http_response_info
|
||||
{
|
||||
int m_response_code;
|
||||
std::string m_response_comment;
|
||||
fields_list m_additional_fields;
|
||||
std::string m_body;
|
||||
std::vector<std::string> m_body_pieces; // For specifying outgoing responses in pieces (this takes priority over m_body when sending)
|
||||
std::string m_mime_type;
|
||||
http_header_info m_header_info;
|
||||
int m_http_ver_hi;// OUT paramter only
|
||||
int m_http_ver_lo;// OUT paramter only
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->~http_response_info();
|
||||
new(this) http_response_info();
|
||||
}
|
||||
|
||||
void wipe()
|
||||
{
|
||||
memwipe(&m_body[0], m_body.size());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef _HTTP_SERVER_H_
|
||||
#define _HTTP_SERVER_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "net_utils_base.h"
|
||||
#include "to_nonconst_iterator.h"
|
||||
#include "http_auth.h"
|
||||
#include "http_base.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
struct http_server_config
|
||||
{
|
||||
std::string m_folder;
|
||||
std::vector<std::string> m_access_control_origins;
|
||||
std::optional<login> m_user;
|
||||
std::mutex m_config_mutex;
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
template<class t_connection_context = net_utils::connection_context_base>
|
||||
class simple_http_connection_handler
|
||||
{
|
||||
public:
|
||||
typedef t_connection_context connection_context;//t_connection_context net_utils::connection_context_base connection_context;
|
||||
typedef http_server_config config_type;
|
||||
|
||||
simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
|
||||
virtual ~simple_http_connection_handler(){}
|
||||
|
||||
bool release_protocol()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool thread_init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool thread_deinit()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool after_init_connection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool handle_recv(const void* ptr, size_t cb);
|
||||
virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
|
||||
|
||||
private:
|
||||
enum machine_state{
|
||||
http_state_retriving_comand_line,
|
||||
http_state_retriving_header,
|
||||
http_state_retriving_body,
|
||||
http_state_connection_close,
|
||||
http_state_error
|
||||
};
|
||||
|
||||
enum body_transfer_type{
|
||||
http_body_transfer_chunked,
|
||||
http_body_transfer_measure,//mean "Content-Length" valid
|
||||
http_body_transfer_chunked_instead_measure,
|
||||
http_body_transfer_connection_close,
|
||||
http_body_transfer_multipart,
|
||||
http_body_transfer_undefined
|
||||
};
|
||||
|
||||
bool handle_buff_in(std::string& buf);
|
||||
|
||||
bool analyze_cached_request_header_and_invoke_state(size_t pos);
|
||||
|
||||
bool handle_invoke_query_line();
|
||||
bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos);
|
||||
std::string::size_type match_end_of_header(const std::string& buf);
|
||||
bool get_len_from_content_length(const std::string& str, size_t& len);
|
||||
bool handle_retriving_query_body();
|
||||
bool handle_query_measure();
|
||||
bool set_ready_state();
|
||||
bool slash_to_back_slash(std::string& str);
|
||||
std::string get_file_mime_type(const std::string& path);
|
||||
std::string get_response_header(const http_response_info& response);
|
||||
|
||||
//major function
|
||||
inline bool handle_request_and_send_response(const http::http_request_info& query_info);
|
||||
|
||||
|
||||
std::string get_not_found_response_body(const std::string& URI);
|
||||
|
||||
std::string m_root_path;
|
||||
std::string m_cache;
|
||||
machine_state m_state;
|
||||
body_transfer_type m_body_transfer_type;
|
||||
bool m_is_stop_handling;
|
||||
http::http_request_info m_query_info;
|
||||
size_t m_len_summary, m_len_remain;
|
||||
config_type& m_config;
|
||||
bool m_want_close;
|
||||
size_t m_newlines;
|
||||
protected:
|
||||
i_service_endpoint* m_psnd_hndlr;
|
||||
t_connection_context& m_conn_context;
|
||||
};
|
||||
|
||||
template<class t_connection_context>
|
||||
struct i_http_server_handler
|
||||
{
|
||||
virtual ~i_http_server_handler(){}
|
||||
virtual bool handle_http_request(const http_request_info& query_info,
|
||||
http_response_info& response,
|
||||
t_connection_context& m_conn_context) = 0;
|
||||
virtual bool init_server_thread(){return true;}
|
||||
virtual bool deinit_server_thread(){return true;}
|
||||
};
|
||||
|
||||
template<class t_connection_context>
|
||||
struct custum_handler_config: public http_server_config
|
||||
{
|
||||
i_http_server_handler<t_connection_context>* m_phandler;
|
||||
std::function<void(size_t, uint8_t*)> rng;
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
template<class t_connection_context = net_utils::connection_context_base>
|
||||
class http_custom_handler: public simple_http_connection_handler<t_connection_context>
|
||||
{
|
||||
public:
|
||||
typedef custum_handler_config<t_connection_context> config_type;
|
||||
|
||||
http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context)
|
||||
: simple_http_connection_handler<t_connection_context>(psnd_hndlr, config, conn_context),
|
||||
m_config(config),
|
||||
m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{})
|
||||
{}
|
||||
inline bool handle_request(const http_request_info& query_info, http_response_info& response)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!");
|
||||
|
||||
const auto auth_response = m_auth.get_response(query_info);
|
||||
if (auth_response)
|
||||
{
|
||||
response = std::move(*auth_response);
|
||||
return true;
|
||||
}
|
||||
|
||||
//fill with default values
|
||||
response.m_mime_type = "text/plain";
|
||||
response.m_response_code = 200;
|
||||
response.m_response_comment = "OK";
|
||||
response.m_body.clear();
|
||||
|
||||
return m_config.m_phandler->handle_http_request(query_info, response, this->m_conn_context);
|
||||
}
|
||||
|
||||
virtual bool thread_init()
|
||||
{
|
||||
return m_config.m_phandler->init_server_thread();
|
||||
}
|
||||
|
||||
virtual bool thread_deinit()
|
||||
{
|
||||
return m_config.m_phandler->deinit_server_thread();
|
||||
}
|
||||
void handle_qued_callback()
|
||||
{}
|
||||
bool after_init_connection()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
//simple_http_connection_handler::config_type m_stub_config;
|
||||
config_type& m_config;
|
||||
http_server_auth m_auth;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "http_protocol_handler.inl"
|
||||
|
||||
#endif //_HTTP_SERVER_H_
|
|
@ -1,745 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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/lexical_cast.hpp>
|
||||
#include <regex>
|
||||
#include <cassert>
|
||||
#include "http_protocol_handler.h"
|
||||
#include "string_tools.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "net_parse_helpers.h"
|
||||
#include "time_helper.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
#define HTTP_MAX_URI_LEN 9000
|
||||
#define HTTP_MAX_HEADER_LEN 100000
|
||||
#define HTTP_MAX_STARTING_NEWLINES 8
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
|
||||
struct multipart_entry
|
||||
{
|
||||
std::list<std::pair<std::string, std::string> > m_etc_header_fields;
|
||||
std::string m_content_disposition;
|
||||
std::string m_content_type;
|
||||
std::string m_body;
|
||||
};
|
||||
|
||||
inline const std::regex rexp_boundary{R"(boundary=([^;\s,]*))", std::regex::icase};
|
||||
|
||||
inline
|
||||
bool match_boundary(const std::string& content_type, std::string& boundary)
|
||||
{
|
||||
if(std::smatch result; std::regex_search(content_type, result, rexp_boundary))
|
||||
{
|
||||
boundary = result[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_content_header{R"(\n?(?:(Content-Disposition)|(Content-Type)|([\w-]+)) ?: ?(.*?)\r?\n[^\t ])", std::regex::icase};
|
||||
|
||||
inline
|
||||
bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
|
||||
{
|
||||
std::string::const_iterator it_current_bound = it_begin;
|
||||
std::string::const_iterator it_end_bound = it_end;
|
||||
|
||||
//lookup all fields and fill well-known fields
|
||||
for (std::smatch result;
|
||||
std::regex_search(it_current_bound, it_end_bound, result, rexp_content_header);
|
||||
it_current_bound = result.suffix().first)
|
||||
{
|
||||
std::string value = result[4];
|
||||
|
||||
if(result[1].matched)//"Content-Disposition"
|
||||
entry.m_content_disposition = result[4];
|
||||
else if(result[2].matched)//"Content-Type"
|
||||
entry.m_content_type = result[4];
|
||||
else {
|
||||
assert(result[3].matched); //e.t.c (HAVE TO BE MATCHED!)
|
||||
entry.m_etc_header_fields.emplace_back(result[3], result[4]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool handle_part_of_multipart(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
|
||||
{
|
||||
std::string end_str = "\r\n\r\n";
|
||||
std::string::const_iterator end_header_it = std::search(it_begin, it_end, end_str.begin(), end_str.end());
|
||||
if(end_header_it == it_end)
|
||||
{
|
||||
//header not matched
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!parse_header(it_begin, end_header_it+4, entry))
|
||||
{
|
||||
LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2));
|
||||
return false;
|
||||
}
|
||||
|
||||
entry.m_body.assign(end_header_it+4, it_end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool parse_multipart_body(const std::string& content_type, const std::string& body, std::list<multipart_entry>& out_values)
|
||||
{
|
||||
//bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body);
|
||||
|
||||
std::string boundary;
|
||||
if(!match_boundary(content_type, boundary))
|
||||
{
|
||||
MERROR("Failed to match boundary in content type: " << content_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
boundary+="\r\n";
|
||||
bool is_stop = false;
|
||||
bool first_step = true;
|
||||
|
||||
std::string::const_iterator it_begin = body.begin();
|
||||
std::string::const_iterator it_end;
|
||||
while(!is_stop)
|
||||
{
|
||||
std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin));
|
||||
|
||||
if(std::string::npos == pos)
|
||||
{
|
||||
is_stop = true;
|
||||
boundary.erase(boundary.size()-2, 2);
|
||||
boundary+= "--";
|
||||
pos = body.find(boundary, std::distance(body.begin(), it_begin));
|
||||
if(std::string::npos == pos)
|
||||
{
|
||||
MERROR("Error: Filed to match closing multipart tag");
|
||||
it_end = body.end();
|
||||
}else
|
||||
{
|
||||
it_end = body.begin() + pos;
|
||||
}
|
||||
}else
|
||||
it_end = body.begin() + pos;
|
||||
|
||||
|
||||
if(first_step && !is_stop)
|
||||
{
|
||||
first_step = false;
|
||||
it_begin = it_end + boundary.size();
|
||||
std::string temp = "\r\n--";
|
||||
boundary = temp + boundary;
|
||||
continue;
|
||||
}
|
||||
|
||||
out_values.push_back(multipart_entry());
|
||||
if(!handle_part_of_multipart(it_begin, it_end, out_values.back()))
|
||||
{
|
||||
MERROR("Failed to handle_part_of_multipart");
|
||||
return false;
|
||||
}
|
||||
|
||||
it_begin = it_end + boundary.size();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
simple_http_connection_handler<t_connection_context>::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context):
|
||||
m_state(http_state_retriving_comand_line),
|
||||
m_body_transfer_type(http_body_transfer_undefined),
|
||||
m_is_stop_handling(false),
|
||||
m_len_summary(0),
|
||||
m_len_remain(0),
|
||||
m_config(config),
|
||||
m_want_close(false),
|
||||
m_newlines(0),
|
||||
m_psnd_hndlr(psnd_hndlr),
|
||||
m_conn_context(conn_context)
|
||||
{
|
||||
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::set_ready_state()
|
||||
{
|
||||
m_is_stop_handling = false;
|
||||
m_state = http_state_retriving_comand_line;
|
||||
m_body_transfer_type = http_body_transfer_undefined;
|
||||
m_query_info.clear();
|
||||
m_len_summary = 0;
|
||||
m_newlines = 0;
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_recv(const void* ptr, size_t cb)
|
||||
{
|
||||
std::string buf((const char*)ptr, cb);
|
||||
//LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf);
|
||||
//file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast<std::string>(ptr), std::string((const char*)ptr, cb));
|
||||
|
||||
bool res = handle_buff_in(buf);
|
||||
if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/)
|
||||
return false;
|
||||
return res;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_buff_in(std::string& buf)
|
||||
{
|
||||
|
||||
size_t ndel;
|
||||
|
||||
if(m_cache.size())
|
||||
m_cache += buf;
|
||||
else
|
||||
m_cache.swap(buf);
|
||||
|
||||
m_is_stop_handling = false;
|
||||
while(!m_is_stop_handling)
|
||||
{
|
||||
switch(m_state)
|
||||
{
|
||||
case http_state_retriving_comand_line:
|
||||
//The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616
|
||||
//but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely
|
||||
if(!m_cache.size())
|
||||
break;
|
||||
|
||||
//check_and_handle_fake_response();
|
||||
ndel = m_cache.find_first_not_of("\r\n");
|
||||
if (ndel != 0)
|
||||
{
|
||||
//some times it could be that before query line cold be few line breaks
|
||||
//so we have to be calm without panic with assers
|
||||
m_newlines += std::string::npos == ndel ? m_cache.size() : ndel;
|
||||
if (m_newlines > HTTP_MAX_STARTING_NEWLINES)
|
||||
{
|
||||
LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too many starting newlines");
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
m_cache.erase(0, ndel);
|
||||
break;
|
||||
}
|
||||
|
||||
if(m_cache.find('\n') != std::string::npos)
|
||||
handle_invoke_query_line();
|
||||
else
|
||||
{
|
||||
m_is_stop_handling = true;
|
||||
if(m_cache.size() > HTTP_MAX_URI_LEN)
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_buff_out: Too long URI line");
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case http_state_retriving_header:
|
||||
{
|
||||
std::string::size_type pos = match_end_of_header(m_cache);
|
||||
if(std::string::npos == pos)
|
||||
{
|
||||
m_is_stop_handling = true;
|
||||
if(m_cache.size() > HTTP_MAX_HEADER_LEN)
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_buff_in: Too long header area");
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!analyze_cached_request_header_and_invoke_state(pos))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case http_state_retriving_body:
|
||||
return handle_retriving_query_body();
|
||||
case http_state_connection_close:
|
||||
return false;
|
||||
default:
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_char_out: Wrong state: " << m_state);
|
||||
return false;
|
||||
case http_state_error:
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler::handle_char_out: Error state!!!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!m_cache.size())
|
||||
m_is_stop_handling = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_http_request{R"(^((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|DELETE|TRACE) (\S+) HTTP/(\d+)\.(\d+)\r?\n)", std::regex::icase};
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
inline bool analyze_http_method(const std::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor)
|
||||
{
|
||||
if (!boost::conversion::try_lexical_convert<int>(result[8], http_ver_major))
|
||||
return false;
|
||||
if (!boost::conversion::try_lexical_convert<int>(result[9], http_ver_minor))
|
||||
return false;
|
||||
|
||||
if(result[2].matched)
|
||||
method = http::http_method_options;
|
||||
else if(result[3].matched)
|
||||
method = http::http_method_get;
|
||||
else if(result[4].matched)
|
||||
method = http::http_method_head;
|
||||
else if(result[5].matched)
|
||||
method = http::http_method_post;
|
||||
else if(result[6].matched)
|
||||
method = http::http_method_put;
|
||||
else
|
||||
method = http::http_method_etc;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_invoke_query_line()
|
||||
{
|
||||
std::smatch result;
|
||||
if(std::regex_search(m_cache, result, rexp_http_request))
|
||||
{
|
||||
if (!analyze_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi))
|
||||
{
|
||||
m_state = http_state_error;
|
||||
MERROR("Failed to analyze method");
|
||||
return false;
|
||||
}
|
||||
m_query_info.m_URI = result[7];
|
||||
if (!parse_uri(m_query_info.m_URI, m_query_info.m_uri_content))
|
||||
{
|
||||
m_state = http_state_error;
|
||||
MERROR("Failed to parse URI: m_query_info.m_URI");
|
||||
return false;
|
||||
}
|
||||
m_query_info.m_http_method_str = result[1];
|
||||
m_query_info.m_full_request_str = result[0];
|
||||
|
||||
m_cache = result.suffix();
|
||||
|
||||
m_state = http_state_retriving_header;
|
||||
|
||||
return true;
|
||||
}else
|
||||
{
|
||||
m_state = http_state_error;
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_invoke_query_line(): Failed to match first line: " << m_cache);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
std::string::size_type simple_http_connection_handler<t_connection_context>::match_end_of_header(const std::string& buf)
|
||||
{
|
||||
|
||||
//Here we returning head size, including terminating sequence (\r\n\r\n or \n\n)
|
||||
std::string::size_type res = buf.find("\r\n\r\n");
|
||||
if(std::string::npos != res)
|
||||
return res+4;
|
||||
res = buf.find("\n\n");
|
||||
if(std::string::npos != res)
|
||||
return res+2;
|
||||
return res;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::analyze_cached_request_header_and_invoke_state(size_t pos)
|
||||
{
|
||||
LOG_PRINT_L3("HTTP HEAD:\r\n" << m_cache.substr(0, pos));
|
||||
|
||||
m_query_info.m_full_request_buf_size = pos;
|
||||
m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos);
|
||||
|
||||
if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos))
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analyze_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache);
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cache.erase(0, pos);
|
||||
|
||||
std::string req_command_str = m_query_info.m_full_request_str;
|
||||
//if we have POST or PUT command, it is very possible tha we will get body
|
||||
//but now, we suppose than we have body only in case of we have "ContentLength"
|
||||
if(m_query_info.m_header_info.m_content_length.size())
|
||||
{
|
||||
m_state = http_state_retriving_body;
|
||||
m_body_transfer_type = http_body_transfer_measure;
|
||||
if(!get_len_from_content_length(m_query_info.m_header_info.m_content_length, m_len_summary))
|
||||
{
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::analyze_cached_request_header_and_invoke_state(): Failed to get_len_from_content_length();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length);
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
if(0 == m_len_summary)
|
||||
{ //current query finished, next will be next query
|
||||
if(handle_request_and_send_response(m_query_info))
|
||||
set_ready_state();
|
||||
else
|
||||
m_state = http_state_error;
|
||||
}
|
||||
m_len_remain = m_len_summary;
|
||||
}else
|
||||
{//current query finished, next will be next query
|
||||
handle_request_and_send_response(m_query_info);
|
||||
set_ready_state();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_retriving_query_body()
|
||||
{
|
||||
switch(m_body_transfer_type)
|
||||
{
|
||||
case http_body_transfer_measure:
|
||||
return handle_query_measure();
|
||||
case http_body_transfer_chunked:
|
||||
case http_body_transfer_connection_close:
|
||||
case http_body_transfer_multipart:
|
||||
case http_body_transfer_undefined:
|
||||
default:
|
||||
LOG_ERROR_CC(m_conn_context, "simple_http_connection_handler<t_connection_context>::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type);
|
||||
m_state = http_state_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_query_measure()
|
||||
{
|
||||
|
||||
if(m_len_remain >= m_cache.size())
|
||||
{
|
||||
m_len_remain -= m_cache.size();
|
||||
m_query_info.m_body += m_cache;
|
||||
m_cache.clear();
|
||||
}else
|
||||
{
|
||||
m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain);
|
||||
m_cache.erase(0, m_len_remain);
|
||||
m_len_remain = 0;
|
||||
}
|
||||
|
||||
if(!m_len_remain)
|
||||
{
|
||||
if(handle_request_and_send_response(m_query_info))
|
||||
set_ready_state();
|
||||
else
|
||||
m_state = http_state_error;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_http_header{
|
||||
"\n?(?:"
|
||||
"(Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)|([\\w-]+?)"
|
||||
") ?: ?(.*?)\r?\n(?=[^\t ])", std::regex::icase};
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
|
||||
{
|
||||
std::string::const_iterator it_current_bound = m_cache_to_process.begin();
|
||||
std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos;
|
||||
|
||||
body_info.clear();
|
||||
|
||||
constexpr size_t field_val = 12;
|
||||
|
||||
//lookup all fields and fill well-known fields
|
||||
for (std::smatch result;
|
||||
std::regex_search(it_current_bound, it_end_bound, result, rexp_http_header);
|
||||
it_current_bound = result.suffix().first)
|
||||
{
|
||||
if(result[1].matched)//"Connection"
|
||||
body_info.m_connection = result[field_val];
|
||||
else if(result[2].matched)//"Referer"
|
||||
body_info.m_referer = result[field_val];
|
||||
else if(result[3].matched)//"Content-Length"
|
||||
body_info.m_content_length = result[field_val];
|
||||
else if(result[4].matched)//"Content-Type"
|
||||
body_info.m_content_type = result[field_val];
|
||||
else if(result[5].matched)//"Transfer-Encoding"
|
||||
body_info.m_transfer_encoding = result[field_val];
|
||||
else if(result[6].matched)//"Content-Encoding"
|
||||
body_info.m_content_encoding = result[field_val];
|
||||
else if(result[7].matched)//"Host"
|
||||
body_info.m_host = result[field_val];
|
||||
else if(result[8].matched)//"Cookie"
|
||||
body_info.m_cookie = result[field_val];
|
||||
else if(result[9].matched)//"User-Agent"
|
||||
body_info.m_user_agent = result[field_val];
|
||||
else if(result[10].matched)//"Origin"
|
||||
body_info.m_origin = result[field_val];
|
||||
else
|
||||
{
|
||||
assert(result[11].matched);
|
||||
body_info.m_etc_fields.emplace_back(result[11], result[field_val]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_digits{"\\d+"};
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::get_len_from_content_length(const std::string& str, size_t& len)
|
||||
{
|
||||
std::string res;
|
||||
std::smatch result;
|
||||
if(!std::regex_search(str, result, rexp_digits))
|
||||
return false;
|
||||
|
||||
try { len = boost::lexical_cast<size_t>(result[0]); }
|
||||
catch(...) { return false; }
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_request_and_send_response(const http::http_request_info& query_info)
|
||||
{
|
||||
http_response_info response{};
|
||||
//CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" );
|
||||
bool res = true;
|
||||
|
||||
if (query_info.m_http_method != http::http_method_options)
|
||||
{
|
||||
res = handle_request(query_info, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.m_response_code = 200;
|
||||
response.m_response_comment = "OK";
|
||||
}
|
||||
|
||||
if (response.m_body_pieces.empty())
|
||||
response.m_body_pieces.push_back(std::move(response.m_body));
|
||||
|
||||
std::string response_data = get_response_header(response);
|
||||
//LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body);
|
||||
|
||||
LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data);
|
||||
|
||||
m_psnd_hndlr->do_send(shared_sv{std::move(response_data)});
|
||||
if (query_info.m_http_method != http::http_method_head)
|
||||
for (auto& body_piece : response.m_body_pieces)
|
||||
m_psnd_hndlr->do_send(shared_sv{std::move(body_piece)});
|
||||
|
||||
m_psnd_hndlr->send_done();
|
||||
return res;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_request(const http::http_request_info& query_info, http_response_info& response)
|
||||
{
|
||||
|
||||
std::string uri_to_path = query_info.m_uri_content.m_path;
|
||||
if("/" == uri_to_path)
|
||||
uri_to_path = "/index.html";
|
||||
|
||||
//slash_to_back_slash(uri_to_path);
|
||||
std::string destination_file_path;
|
||||
{
|
||||
std::lock_guard lock{m_config.m_config_mutex};
|
||||
destination_file_path = m_config.m_folder + uri_to_path;
|
||||
}
|
||||
if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body))
|
||||
{
|
||||
MWARNING("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )");
|
||||
response.m_body = get_not_found_response_body(query_info.m_URI);
|
||||
response.m_response_code = 404;
|
||||
response.m_response_comment = "Not found";
|
||||
response.m_mime_type = "text/html";
|
||||
return true;
|
||||
}
|
||||
|
||||
MDEBUG(" -->> " << query_info.m_full_request_str << "\r\n<<--OK");
|
||||
response.m_response_code = 200;
|
||||
response.m_response_comment = "OK";
|
||||
response.m_mime_type = get_file_mime_type(uri_to_path);
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
std::string simple_http_connection_handler<t_connection_context>::get_response_header(const http_response_info& response)
|
||||
{
|
||||
std::string buf;
|
||||
buf.reserve(200); // Typical length seems to be around 145 bytes
|
||||
buf += "HTTP/1.1 ";
|
||||
buf += std::to_string(response.m_response_code);
|
||||
buf += ' ';
|
||||
buf += response.m_response_comment;
|
||||
buf += "\r\n"
|
||||
"Server: Epee-based\r\n"
|
||||
"Content-Length: ";
|
||||
|
||||
size_t size = 0;
|
||||
for (auto& piece : response.m_body_pieces)
|
||||
size += piece.size();
|
||||
|
||||
buf += std::to_string(size);
|
||||
buf += "\r\n";
|
||||
|
||||
if(!response.m_mime_type.empty())
|
||||
{
|
||||
buf += "Content-Type: ";
|
||||
buf += response.m_mime_type;
|
||||
buf += "\r\n";
|
||||
}
|
||||
|
||||
buf += "Last-Modified: "; //Wed, 01 Dec 2010 03:27:41 GMT"
|
||||
buf += misc_utils::get_internet_time_str(std::time(nullptr));
|
||||
buf += "\r\nAccept-Ranges: bytes\r\n";
|
||||
|
||||
string_tools::trim(m_query_info.m_header_info.m_connection);
|
||||
if(m_query_info.m_header_info.m_connection.size())
|
||||
{
|
||||
if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection))
|
||||
{
|
||||
//closing connection after sending
|
||||
buf += "Connection: close\r\n";
|
||||
m_state = http_state_connection_close;
|
||||
m_want_close = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-origin resource sharing
|
||||
if(m_query_info.m_header_info.m_origin.size())
|
||||
{
|
||||
if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin))
|
||||
{
|
||||
buf += "Access-Control-Allow-Origin: ";
|
||||
buf += m_query_info.m_header_info.m_origin;
|
||||
buf += "\r\n";
|
||||
buf += "Access-Control-Expose-Headers: www-authenticate\r\n";
|
||||
if (m_query_info.m_http_method == http::http_method_options)
|
||||
buf += "Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With\r\n";
|
||||
buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
//add additional fields, if it is
|
||||
for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++)
|
||||
buf += it->first + ": " + it->second + "\r\n";
|
||||
|
||||
buf+="\r\n";
|
||||
|
||||
return buf;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
std::string simple_http_connection_handler<t_connection_context>::get_file_mime_type(const std::string& path)
|
||||
{
|
||||
std::string result;
|
||||
std::string ext = string_tools::get_extension(path);
|
||||
if(!string_tools::compare_no_case(ext, "gif"))
|
||||
result = "image/gif";
|
||||
else if(!string_tools::compare_no_case(ext, "jpg"))
|
||||
result = "image/jpeg";
|
||||
else if(!string_tools::compare_no_case(ext, "html"))
|
||||
result = "text/html";
|
||||
else if(!string_tools::compare_no_case(ext, "htm"))
|
||||
result = "text/html";
|
||||
else if(!string_tools::compare_no_case(ext, "js"))
|
||||
result = "application/x-javascript";
|
||||
else if(!string_tools::compare_no_case(ext, "css"))
|
||||
result = "text/css";
|
||||
else if(!string_tools::compare_no_case(ext, "xml"))
|
||||
result = "application/xml";
|
||||
else if(!string_tools::compare_no_case(ext, "svg"))
|
||||
result = "image/svg+xml";
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
std::string simple_http_connection_handler<t_connection_context>::get_not_found_response_body(const std::string& URI)
|
||||
{
|
||||
std::string body =
|
||||
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
|
||||
"<html><head>\r\n"
|
||||
"<title>404 Not Found</title>\r\n"
|
||||
"</head><body>\r\n"
|
||||
"<h1>Not Found</h1>\r\n"
|
||||
"<p>The requested URL \r\n";
|
||||
body += URI;
|
||||
body += "was not found on this server.</p>\r\n"
|
||||
"</body></html>\r\n";
|
||||
|
||||
return body;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------
|
||||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::slash_to_back_slash(std::string& str)
|
||||
{
|
||||
for(std::string::iterator it = str.begin(); it!=str.end(); it++)
|
||||
if('/' == *it)
|
||||
*it = '\\';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------------------------
|
|
@ -1,240 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
#include "http_base.h"
|
||||
#include "jsonrpc_structs.h"
|
||||
#include "storages/portable_storage.h"
|
||||
#include "storages/portable_storage_template_helper.h"
|
||||
#include "misc_os_dependent.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
|
||||
#define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \
|
||||
epee::net_utils::http::http_response_info& response, \
|
||||
context_type& m_conn_context) \
|
||||
{\
|
||||
MINFO("HTTP [" << m_conn_context.m_remote_address.host_str() << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
|
||||
response.m_response_code = 200; \
|
||||
response.m_response_comment = "Ok"; \
|
||||
if(!handle_http_request_map(query_info, response, m_conn_context)) \
|
||||
{response.m_response_code = 404;response.m_response_comment = "Not found";} \
|
||||
return true; \
|
||||
}
|
||||
|
||||
|
||||
#define BEGIN_URI_MAP2() template<class t_context> bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \
|
||||
epee::net_utils::http::http_response_info& response_info, \
|
||||
t_context& m_conn_context) { \
|
||||
bool handled = false; \
|
||||
if(false) return true; //just a stub to have "else if"
|
||||
|
||||
#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, &m_conn_context);
|
||||
|
||||
#define MAP_URI_AUTO_JON2_IF(s_pattern, callback_f, command_type, cond) \
|
||||
else if((query_info.m_URI == s_pattern) && (cond)) \
|
||||
{ \
|
||||
response_info.m_mime_type = "application/json"; \
|
||||
response_info.m_header_info.m_content_type = " application/json"; \
|
||||
handled = true; \
|
||||
uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||
command_type::request req{}; \
|
||||
bool parse_res = epee::serialization::load_t_from_json(req, query_info.m_body); \
|
||||
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \
|
||||
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||
command_type::response resp{};\
|
||||
MINFO(m_conn_context << "calling " << s_pattern); \
|
||||
if(!callback_f(req, resp, &m_conn_context)) \
|
||||
{ \
|
||||
LOG_ERROR("Failed to " << #callback_f << "()"); \
|
||||
response_info.m_response_code = 500; \
|
||||
response_info.m_response_comment = "Internal Server Error"; \
|
||||
return true; \
|
||||
} \
|
||||
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||
epee::serialization::store_t_to_json(resp, response_info.m_body); \
|
||||
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||
MDEBUG( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms"); \
|
||||
}
|
||||
|
||||
#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) MAP_URI_AUTO_JON2_IF(s_pattern, callback_f, command_type, true)
|
||||
|
||||
#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \
|
||||
else if(query_info.m_URI == s_pattern) \
|
||||
{ \
|
||||
response_info.m_mime_type = " application/octet-stream"; \
|
||||
response_info.m_header_info.m_content_type = " application/octet-stream"; \
|
||||
handled = true; \
|
||||
uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||
command_type::request req{}; \
|
||||
bool parse_res = epee::serialization::load_t_from_binary(req, epee::strspan<uint8_t>(query_info.m_body)); \
|
||||
CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \
|
||||
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||
command_type::response resp{};\
|
||||
MINFO(m_conn_context << "calling " << s_pattern); \
|
||||
if(!callback_f(req, resp, &m_conn_context)) \
|
||||
{ \
|
||||
LOG_ERROR("Failed to " << #callback_f << "()"); \
|
||||
response_info.m_response_code = 500; \
|
||||
response_info.m_response_comment = "Internal Server Error"; \
|
||||
return true; \
|
||||
} \
|
||||
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||
epee::serialization::store_t_to_binary(resp, response_info.m_body); \
|
||||
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||
MDEBUG( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms"); \
|
||||
}
|
||||
|
||||
#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;}
|
||||
|
||||
#define END_URI_MAP2() return handled;}
|
||||
|
||||
|
||||
#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \
|
||||
{ \
|
||||
uint64_t ticks = epee::misc_utils::get_tick_count(); \
|
||||
response_info.m_mime_type = "application/json"; \
|
||||
epee::serialization::portable_storage ps; \
|
||||
if(!ps.load_from_json(query_info.m_body)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response rsp{}; \
|
||||
rsp.jsonrpc = "2.0"; \
|
||||
rsp.error.code = -32700; \
|
||||
rsp.error.message = "Parse error"; \
|
||||
epee::serialization::store_t_to_json(rsp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
epee::serialization::storage_entry id_; \
|
||||
id_ = epee::serialization::storage_entry(std::string()); \
|
||||
ps.get_value("id", id_, nullptr); \
|
||||
std::string callback_name; \
|
||||
if(!ps.get_value("method", callback_name, nullptr)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response rsp; \
|
||||
rsp.jsonrpc = "2.0"; \
|
||||
rsp.error.code = -32600; \
|
||||
rsp.error.message = "Invalid Request"; \
|
||||
epee::serialization::store_t_to_json(rsp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
if(false) return true; //just a stub to have "else if"
|
||||
|
||||
|
||||
#define PREPARE_OBJECTS_FROM_JSON(command_type) \
|
||||
handled = true; \
|
||||
epee::json_rpc::request<command_type::request> req{}; \
|
||||
if(!req.load(ps)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response fail_resp{}; \
|
||||
fail_resp.jsonrpc = "2.0"; \
|
||||
fail_resp.id = req.id; \
|
||||
fail_resp.error.code = -32602; \
|
||||
fail_resp.error.message = "Invalid params"; \
|
||||
epee::serialization::store_t_to_json(fail_resp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
|
||||
epee::json_rpc::response<command_type::response> resp{}; \
|
||||
resp.jsonrpc = "2.0"; \
|
||||
resp.id = req.id;
|
||||
|
||||
#define FINALIZE_OBJECTS_TO_JSON(method_name) \
|
||||
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
|
||||
epee::serialization::store_t_to_json(resp, response_info.m_body); \
|
||||
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
|
||||
response_info.m_mime_type = "application/json"; \
|
||||
response_info.m_header_info.m_content_type = " application/json"; \
|
||||
MDEBUG( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms");
|
||||
|
||||
#define MAP_JON_RPC_WE_IF(method_name, callback_f, command_type, cond) \
|
||||
else if((callback_name == method_name) && (cond)) \
|
||||
{ \
|
||||
PREPARE_OBJECTS_FROM_JSON(command_type) \
|
||||
epee::json_rpc::error_response fail_resp{}; \
|
||||
fail_resp.jsonrpc = "2.0"; \
|
||||
fail_resp.id = req.id; \
|
||||
MINFO(m_conn_context << "Calling RPC method " << method_name); \
|
||||
if(!callback_f(req.params, resp.result, fail_resp.error, &m_conn_context)) \
|
||||
{ \
|
||||
epee::serialization::store_t_to_json(fail_resp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
FINALIZE_OBJECTS_TO_JSON(method_name) \
|
||||
return true;\
|
||||
}
|
||||
|
||||
#define MAP_JON_RPC_WE(method_name, callback_f, command_type) MAP_JON_RPC_WE_IF(method_name, callback_f, command_type, true)
|
||||
|
||||
#define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \
|
||||
else if(callback_name == method_name) \
|
||||
{ \
|
||||
PREPARE_OBJECTS_FROM_JSON(command_type) \
|
||||
epee::json_rpc::error_response fail_resp{}; \
|
||||
fail_resp.jsonrpc = "2.0"; \
|
||||
fail_resp.id = req.id; \
|
||||
MINFO(m_conn_context << "calling RPC method " << method_name); \
|
||||
if(!callback_f(req.params, resp.result, fail_resp.error, response_info, &m_conn_context)) \
|
||||
{ \
|
||||
epee::serialization::store_t_to_json(fail_resp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
FINALIZE_OBJECTS_TO_JSON(method_name) \
|
||||
return true;\
|
||||
}
|
||||
|
||||
#define MAP_JON_RPC(method_name, callback_f, command_type) \
|
||||
else if(callback_name == method_name) \
|
||||
{ \
|
||||
PREPARE_OBJECTS_FROM_JSON(command_type) \
|
||||
MINFO(m_conn_context << "calling RPC method " << method_name); \
|
||||
if(!callback_f(req.params, resp.result, &m_conn_context)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response fail_resp{}; \
|
||||
fail_resp.jsonrpc = "2.0"; \
|
||||
fail_resp.id = req.id; \
|
||||
fail_resp.error.code = -32603; \
|
||||
fail_resp.error.message = "Internal error"; \
|
||||
epee::serialization::store_t_to_json(fail_resp, response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
FINALIZE_OBJECTS_TO_JSON(method_name) \
|
||||
return true;\
|
||||
}
|
||||
|
||||
#define END_JSON_RPC_MAP() \
|
||||
epee::json_rpc::error_response rsp{}; \
|
||||
rsp.id = id_; \
|
||||
rsp.jsonrpc = "2.0"; \
|
||||
rsp.error.code = -32601; \
|
||||
rsp.error.message = "Method not found"; \
|
||||
epee::serialization::store_t_to_json(rsp, response_info.m_body); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "net/abstract_tcp_server2.h"
|
||||
#include "http_protocol_handler.h"
|
||||
#include "net/http_server_handlers_map2.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net.http"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
template<class t_child_class, class t_connection_context = epee::net_utils::connection_context_base>
|
||||
class http_server_impl_base: public net_utils::http::i_http_server_handler<t_connection_context>
|
||||
{
|
||||
|
||||
public:
|
||||
http_server_impl_base()
|
||||
: m_net_server(epee::net_utils::e_connection_type_RPC)
|
||||
{}
|
||||
|
||||
explicit http_server_impl_base(boost::asio::io_service& external_io_service)
|
||||
: m_net_server(external_io_service)
|
||||
{}
|
||||
|
||||
bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
|
||||
const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
|
||||
std::vector<std::string> access_control_origins = std::vector<std::string>(),
|
||||
std::optional<net_utils::http::login> user = std::nullopt)
|
||||
{
|
||||
|
||||
//set self as callback handler
|
||||
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
|
||||
m_net_server.get_config_object().rng = std::move(rng);
|
||||
|
||||
//here set folder for hosting reqests
|
||||
m_net_server.get_config_object().m_folder = "";
|
||||
|
||||
//set access control allow origins if configured
|
||||
std::sort(access_control_origins.begin(), access_control_origins.end());
|
||||
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
||||
|
||||
m_net_server.get_config_object().m_user = std::move(user);
|
||||
|
||||
MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
|
||||
if (use_ipv6)
|
||||
{
|
||||
MGINFO("Binding on " << bind_ipv6_address << " (IPv6):" << bind_port);
|
||||
}
|
||||
bool res = m_net_server.init_server(bind_port, bind_ip, bind_port, bind_ipv6_address, use_ipv6, require_ipv4);
|
||||
if(!res)
|
||||
{
|
||||
LOG_ERROR("Failed to bind server");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool run(size_t threads_count, bool wait = true)
|
||||
{
|
||||
//go to loop
|
||||
MINFO("Run net_service loop( " << threads_count << " threads)...");
|
||||
if(!m_net_server.run_server(threads_count, wait))
|
||||
{
|
||||
LOG_ERROR("Failed to run net tcp server!");
|
||||
}
|
||||
|
||||
if(wait)
|
||||
MINFO("net_service loop stopped.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deinit()
|
||||
{
|
||||
return m_net_server.deinit_server();
|
||||
}
|
||||
|
||||
bool server_stop()
|
||||
{
|
||||
return m_net_server.server_stop();
|
||||
}
|
||||
|
||||
bool send_stop_signal()
|
||||
{
|
||||
m_net_server.send_stop_signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_binded_port()
|
||||
{
|
||||
return m_net_server.get_binded_port();
|
||||
}
|
||||
|
||||
long get_connections_count() const
|
||||
{
|
||||
return m_net_server.get_connections_count();
|
||||
}
|
||||
|
||||
protected:
|
||||
net_utils::boosted_tcp_server<net_utils::http::http_custom_handler<t_connection_context> > m_net_server;
|
||||
};
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#include "http_base.h"
|
||||
#include <regex>
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "net"
|
||||
|
||||
namespace epee::net_utils
|
||||
{
|
||||
|
||||
inline bool parse_uri_query(const std::string& query, std::list<std::pair<std::string, std::string> >& params)
|
||||
{
|
||||
enum state
|
||||
{
|
||||
st_param_name,
|
||||
st_param_val
|
||||
};
|
||||
state st = st_param_name;
|
||||
std::string::const_iterator start_it = query.begin();
|
||||
std::pair<std::string, std::string> e;
|
||||
for(std::string::const_iterator it = query.begin(); it != query.end(); it++)
|
||||
{
|
||||
switch(st)
|
||||
{
|
||||
case st_param_name:
|
||||
if(*it == '=')
|
||||
{
|
||||
e.first.assign(start_it, it);
|
||||
start_it = it;++start_it;
|
||||
st = st_param_val;
|
||||
}
|
||||
break;
|
||||
case st_param_val:
|
||||
if(*it == '&')
|
||||
{
|
||||
e.second.assign(start_it, it);
|
||||
start_it = it;++start_it;
|
||||
params.push_back(e);
|
||||
e.first.clear();e.second.clear();
|
||||
st = st_param_name;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown state " << (int)st);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(st == st_param_name)
|
||||
{
|
||||
if(start_it != query.end())
|
||||
{
|
||||
e.first.assign(start_it, query.end());
|
||||
params.push_back(e);
|
||||
}
|
||||
}else
|
||||
{
|
||||
if(start_it != query.end())
|
||||
e.second.assign(start_it, query.end());
|
||||
|
||||
if(e.first.size())
|
||||
params.push_back(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_match_uri{R"(([^?#]*)(?:\?([^#]*))?(?:#(.*))?)", std::regex::optimize};
|
||||
|
||||
inline
|
||||
bool parse_uri(const std::string& uri, http::uri_content& content)
|
||||
{
|
||||
|
||||
///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
|
||||
content.m_query_params.clear();
|
||||
|
||||
std::smatch result;
|
||||
if(!std::regex_match(uri, result, rexp_match_uri))
|
||||
{
|
||||
LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << uri);
|
||||
content.m_path = uri;
|
||||
return true;
|
||||
}
|
||||
content.m_path = result[1];
|
||||
if(result[2].matched)
|
||||
content.m_query = result[2];
|
||||
if(result[3].matched)
|
||||
content.m_fragment = result[3];
|
||||
if(content.m_query.size())
|
||||
parse_uri_query(content.m_query, content.m_query_params);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const std::regex rexp_match_url{R"(^(?:(.*?)://)?(?:\[([0-9a-fA-F:.]*)\]|([^\[/:]*))(?::(\d+))?)", std::regex::optimize};
|
||||
// proto ipv6 ipv4/host port
|
||||
|
||||
inline
|
||||
bool parse_url(const std::string& url_str, http::url_content& content)
|
||||
{
|
||||
content.port = 0;
|
||||
std::smatch result;
|
||||
if(!std::regex_search(url_str, result, rexp_match_url))
|
||||
{
|
||||
LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << url_str);
|
||||
return false;
|
||||
}
|
||||
if(result[1].matched)
|
||||
content.schema = result[1];
|
||||
content.host = result[result[2].matched ? 2 : 3];
|
||||
if(result[4].matched)
|
||||
content.port = boost::lexical_cast<uint64_t>(result[4]);
|
||||
content.uri = result.suffix().str();
|
||||
return parse_uri(content.uri, content.m_uri_content);
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
|
||||
// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the Andrey N. Sabelnikov nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include "portable_storage_template_helper.h"
|
||||
#include "net/http_base.h"
|
||||
#include "net/http_server_handlers_map2.h"
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
template<class t_request, class t_response, class t_transport>
|
||||
bool invoke_http_json(const std::string_view uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = 15s, const std::string_view method = "POST"sv)
|
||||
{
|
||||
std::string req_param;
|
||||
if(!serialization::store_t_to_json(out_struct, req_param))
|
||||
return false;
|
||||
|
||||
http::fields_list additional_params;
|
||||
additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8"));
|
||||
|
||||
const http::http_response_info* pri = NULL;
|
||||
if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri), std::move(additional_params)))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!pri)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri << ", internal error (null response ptr)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pri->m_response_code != 200)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
return serialization::load_t_from_json(result_struct, pri->m_body);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class t_request, class t_response, class t_transport>
|
||||
bool invoke_http_bin(const std::string_view uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = 15s, const std::string_view method = "POST"sv)
|
||||
{
|
||||
std::string req_param;
|
||||
if(!serialization::store_t_to_binary(out_struct, req_param))
|
||||
return false;
|
||||
|
||||
const http::http_response_info* pri = NULL;
|
||||
if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri)))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!pri)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri << ", internal error (null response ptr)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pri->m_response_code != 200)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
return serialization::load_t_from_binary(result_struct, epee::strspan<uint8_t>(pri->m_body));
|
||||
}
|
||||
|
||||
template<class t_request, class t_response, class t_transport>
|
||||
bool invoke_http_json_rpc(const std::string_view uri, std::string method_name, const t_request& out_struct, t_response& result_struct, epee::json_rpc::error &error_struct, t_transport& transport, std::chrono::milliseconds timeout = 15s, const std::string_view http_method = "POST"sv, const std::string& req_id = "0"s)
|
||||
{
|
||||
epee::json_rpc::request<t_request> req_t{};
|
||||
req_t.jsonrpc = "2.0";
|
||||
req_t.id = req_id;
|
||||
req_t.method = std::move(method_name);
|
||||
req_t.params = out_struct;
|
||||
epee::json_rpc::response<t_response, true> resp_t{};
|
||||
if(!epee::net_utils::invoke_http_json(uri, req_t, resp_t, transport, timeout, http_method))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(resp_t.error.code || resp_t.error.message.size())
|
||||
{
|
||||
LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message);
|
||||
return false;
|
||||
}
|
||||
result_struct = resp_t.result;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class t_request, class t_response, class t_transport>
|
||||
bool invoke_http_json_rpc(const std::string_view uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const std::string_view http_method = "POST", const std::string& req_id = "0")
|
||||
{
|
||||
epee::json_rpc::error error_struct;
|
||||
return invoke_http_json_rpc(uri, method_name, out_struct, result_struct, error_struct, transport, timeout, http_method, req_id);
|
||||
}
|
||||
|
||||
template<class t_command, class t_transport>
|
||||
bool invoke_http_json_rpc(const std::string_view uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = 15s, const std::string_view http_method = "POST"sv, const std::string_view req_id = "0"sv)
|
||||
{
|
||||
return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ add_library(epee
|
|||
buffer.cpp
|
||||
connection_basic.cpp
|
||||
hex.cpp
|
||||
http_auth.cpp
|
||||
levin_base.cpp
|
||||
memwipe.c
|
||||
mlocker.cpp
|
||||
|
|
|
@ -1,779 +0,0 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "net/http_auth.h"
|
||||
|
||||
#include <array>
|
||||
#include <boost/algorithm/string/find_iterator.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||
#include <boost/fusion/algorithm/iteration/for_each.hpp>
|
||||
#include <boost/fusion/algorithm/iteration/iter_fold.hpp>
|
||||
#include <boost/fusion/algorithm/query/any.hpp>
|
||||
#include <boost/fusion/iterator/distance.hpp>
|
||||
#include <boost/fusion/iterator/value_of.hpp>
|
||||
#include <boost/fusion/sequence/intrinsic/begin.hpp>
|
||||
#include <boost/fusion/sequence/intrinsic/size.hpp>
|
||||
#include <boost/range/algorithm/copy.hpp>
|
||||
#include <boost/range/algorithm/find_if.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/range/join.hpp>
|
||||
#include <boost/spirit/include/karma_generate.hpp>
|
||||
#include <boost/spirit/include/karma_uint.hpp>
|
||||
#include <boost/spirit/include/qi_alternative.hpp>
|
||||
#include <boost/spirit/include/qi_and_predicate.hpp>
|
||||
#include <boost/spirit/include/qi_char.hpp>
|
||||
#include <boost/spirit/include/qi_char_class.hpp>
|
||||
#include <boost/spirit/include/qi_difference.hpp>
|
||||
#include <boost/spirit/include/qi_kleene.hpp>
|
||||
#include <boost/spirit/include/qi_parse.hpp>
|
||||
#include <boost/spirit/include/qi_plus.hpp>
|
||||
#include <boost/spirit/include/qi_no_case.hpp>
|
||||
#include <boost/spirit/include/qi_not_predicate.hpp>
|
||||
#include <boost/spirit/include/qi_raw.hpp>
|
||||
#include <boost/spirit/include/qi_rule.hpp>
|
||||
#include <boost/spirit/include/qi_sequence.hpp>
|
||||
#include <boost/spirit/include/qi_string.hpp>
|
||||
#include <boost/spirit/include/qi_symbols.hpp>
|
||||
#include <boost/spirit/include/qi_uint.hpp>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "hex.h"
|
||||
#include "md5_l.h"
|
||||
#include "string_coding.h"
|
||||
|
||||
/* This file uses the `u8` prefix for (a paranoid attempt at) maximum portability:
|
||||
C++ does not actually specify ASCII
|
||||
as the encoding type for unprefixed string literals, etc. Although rare, the
|
||||
effort required to support rare compiler encoding types is low.
|
||||
|
||||
Also be careful of qi::ascii character classes (`qi::asci::alpha`, etc.) -
|
||||
non-ASCII characters will cause undefined behavior in the table lookup until
|
||||
boost 1.60. The expression `&qi::ascii::char_` will fail on non-ASCII
|
||||
characters without "consuming" the input character. */
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace http = epee::net_utils::http;
|
||||
|
||||
constexpr auto client_auth_field = u8"Authorization";
|
||||
constexpr auto server_auth_field = u8"WWW-authenticate";
|
||||
constexpr auto auth_realm = u8"monero-rpc";
|
||||
constexpr auto sess_algo = u8"-sess"sv;
|
||||
|
||||
constexpr unsigned client_reserve_size = 512; //!< std::string::reserve size for clients
|
||||
|
||||
//// Digest Algorithms
|
||||
|
||||
struct md5_
|
||||
{
|
||||
static constexpr auto name = u8"MD5"sv;
|
||||
|
||||
struct update
|
||||
{
|
||||
void operator()(const std::string_view arg) const
|
||||
{
|
||||
md5::MD5Update(
|
||||
std::addressof(ctx),
|
||||
reinterpret_cast<const std::uint8_t*>(arg.data()),
|
||||
arg.size()
|
||||
);
|
||||
}
|
||||
void operator()(const boost::iterator_range<const char*> arg) const
|
||||
{
|
||||
operator()(std::string_view{arg.begin(), arg.size()});
|
||||
}
|
||||
template <size_t N>
|
||||
void operator()(const std::array<char, N>& arg) const
|
||||
{
|
||||
operator()(std::string_view{arg.data(), arg.size()});
|
||||
}
|
||||
// Template indirection for wipeable_string to prevent implicit conversion from std::string/char.
|
||||
template <typename T, std::enable_if_t<std::is_same_v<T, epee::wipeable_string>, int> = 0>
|
||||
void operator()(const T& arg) const
|
||||
{
|
||||
operator()(std::string_view{arg.data(), arg.size()});
|
||||
}
|
||||
|
||||
md5::MD5_CTX& ctx;
|
||||
};
|
||||
|
||||
template<typename... T>
|
||||
std::array<char, 32> operator()(const T&... args) const
|
||||
{
|
||||
md5::MD5_CTX ctx{};
|
||||
md5::MD5Init(std::addressof(ctx));
|
||||
update updater{ctx};
|
||||
(updater(args), ...);
|
||||
|
||||
std::array<std::uint8_t, 16> digest{{}};
|
||||
md5::MD5Final(digest.data(), std::addressof(ctx));
|
||||
return epee::to_hex::array(digest);
|
||||
}
|
||||
};
|
||||
|
||||
//! Digest Algorithms available for HTTP Digest Auth. Sort better algos to the left
|
||||
constexpr const std::tuple<md5_> digest_algorithms{};
|
||||
|
||||
//// Various String Utilities
|
||||
|
||||
struct ascii_tolower_
|
||||
{
|
||||
template<typename Char>
|
||||
constexpr Char operator()(Char value) const noexcept
|
||||
{
|
||||
static_assert(std::is_integral<Char>::value, "only integral types supported");
|
||||
return (u8'A' <= value && value <= u8'Z') ? (value + (u8'a' - u8'A')) : value;
|
||||
}
|
||||
};
|
||||
constexpr const ascii_tolower_ ascii_tolower{};
|
||||
|
||||
struct ascii_iequal_
|
||||
{
|
||||
template<typename Char>
|
||||
constexpr bool operator()(Char left, Char right) const noexcept
|
||||
{
|
||||
return ascii_tolower(left) == ascii_tolower(right);
|
||||
}
|
||||
};
|
||||
constexpr const ascii_iequal_ ascii_iequal{};
|
||||
|
||||
struct http_list_separator_
|
||||
{
|
||||
template<typename Char>
|
||||
bool operator()(Char value) const noexcept
|
||||
{
|
||||
static_assert(std::is_integral<Char>::value, "only integral types supported");
|
||||
return boost::spirit::char_encoding::ascii::isascii_(value) &&
|
||||
(value == u8',' || boost::spirit::char_encoding::ascii::isspace(value));
|
||||
}
|
||||
};
|
||||
constexpr const http_list_separator_ http_list_separator{};
|
||||
|
||||
std::string to_string(boost::iterator_range<const char*> source)
|
||||
{
|
||||
return {source.begin(), source.size()};
|
||||
}
|
||||
|
||||
void add_first_field(std::string& str, const std::string_view name, const std::string_view value, bool add_quotes = false)
|
||||
{
|
||||
str.append(name);
|
||||
str.push_back(u8'=');
|
||||
if (add_quotes) str.push_back(u8'"');
|
||||
boost::copy(value, std::back_inserter(str));
|
||||
if (add_quotes) str.push_back(u8'"');
|
||||
}
|
||||
|
||||
void add_field(std::string& str, const std::string_view name, const std::string_view value, bool add_quotes = false)
|
||||
{
|
||||
str.push_back(u8',');
|
||||
add_first_field(str, name, value, add_quotes);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void add_field(std::string& str, const std::string_view name, const std::array<char, N>& value, bool add_quotes = false)
|
||||
{
|
||||
add_field(str, name, std::string_view{value.data(), N}, add_quotes);
|
||||
}
|
||||
|
||||
//// Digest Authentication
|
||||
|
||||
template<typename Digest>
|
||||
std::result_of_t<Digest()> generate_a1(
|
||||
Digest digest, const http::login& creds, const std::string_view realm)
|
||||
{
|
||||
return digest(creds.username, u8":", realm, u8":", creds.password);
|
||||
}
|
||||
|
||||
template<typename Digest>
|
||||
std::result_of_t<Digest()> generate_a1(
|
||||
Digest digest, const http::http_client_auth::session& user)
|
||||
{
|
||||
return generate_a1(std::move(digest), user.credentials, user.server.realm);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void init_client_value(std::string& str,
|
||||
const std::string_view algorithm, const http::http_client_auth::session& user,
|
||||
const std::string_view uri, const T& response)
|
||||
{
|
||||
str.append(u8"Digest ");
|
||||
constexpr bool add_quotes = true;
|
||||
add_first_field(str, u8"algorithm", algorithm);
|
||||
add_field(str, u8"nonce", user.server.nonce, add_quotes);
|
||||
add_field(str, u8"realm", user.server.realm, add_quotes);
|
||||
add_field(str, u8"response", response, add_quotes);
|
||||
add_field(str, u8"uri", uri, add_quotes);
|
||||
add_field(str, u8"username", user.credentials.username, add_quotes);
|
||||
if (!user.server.opaque.empty())
|
||||
add_field(str, u8"opaque", user.server.opaque, add_quotes);
|
||||
}
|
||||
|
||||
//! Implements superseded algorithm specified in RFC 2069
|
||||
template<typename Digest>
|
||||
struct old_algorithm
|
||||
{
|
||||
explicit old_algorithm(Digest digest_) : digest(std::move(digest_)) {}
|
||||
|
||||
std::string operator()(const http::http_client_auth::session& user,
|
||||
const std::string_view method, const std::string_view uri) const
|
||||
{
|
||||
const auto response = digest(
|
||||
generate_a1(digest, user), u8":", user.server.nonce, u8":", digest(method, u8":", uri)
|
||||
);
|
||||
std::string out{};
|
||||
out.reserve(client_reserve_size);
|
||||
init_client_value(out, Digest::name, user, uri, response);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
Digest digest;
|
||||
};
|
||||
|
||||
//! Implements the `qop=auth` algorithm specified in RFC 2617
|
||||
template<typename Digest>
|
||||
struct auth_algorithm
|
||||
{
|
||||
explicit auth_algorithm(Digest digest_) : digest(std::move(digest_)) {}
|
||||
|
||||
std::string operator()(const http::http_client_auth::session& user,
|
||||
const std::string_view method, const std::string_view uri) const
|
||||
{
|
||||
namespace karma = boost::spirit::karma;
|
||||
using counter_type = decltype(user.counter);
|
||||
static_assert(
|
||||
std::numeric_limits<counter_type>::radix == 2, "unexpected radix for counter"
|
||||
);
|
||||
static_assert(
|
||||
std::numeric_limits<counter_type>::digits <= 32,
|
||||
"number of digits will cause underflow on padding below"
|
||||
);
|
||||
|
||||
std::string out{};
|
||||
out.reserve(client_reserve_size);
|
||||
|
||||
karma::generate(std::back_inserter(out), karma::hex(user.counter));
|
||||
out.insert(out.begin(), 8 - out.size(), u8'0'); // zero left pad
|
||||
if (out.size() != 8)
|
||||
return {};
|
||||
|
||||
std::array<char, 8> nc{{}};
|
||||
boost::copy(out, nc.data());
|
||||
const auto response = digest(
|
||||
generate_a1(digest, user), u8":", user.server.nonce, u8":", nc, u8"::auth:", digest(method, u8":", uri)
|
||||
);
|
||||
out.clear();
|
||||
init_client_value(out, Digest::name, user, uri, response);
|
||||
add_field(out, u8"qop", u8"auth"sv);
|
||||
add_field(out, u8"nc", nc);
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
Digest digest;
|
||||
};
|
||||
|
||||
//! Processes client "Authorization" and server "WWW-authenticate" HTTP fields
|
||||
struct auth_message
|
||||
{
|
||||
using iterator = const char*;
|
||||
enum status{ kFail = 0, kStale, kPass };
|
||||
|
||||
//! \return Status of the `response` field from the client
|
||||
static status verify(const std::string_view method, const std::string_view request,
|
||||
const http::http_server_auth::session& user)
|
||||
{
|
||||
const auto parsed = parse(request);
|
||||
if (parsed &&
|
||||
boost::equals(parsed->username, user.credentials.username) &&
|
||||
boost::fusion::any(digest_algorithms, has_valid_response{*parsed, user, method}))
|
||||
{
|
||||
if (boost::equals(parsed->nonce, user.nonce))
|
||||
{
|
||||
// RFC 2069 format does not verify nc value - allow just once
|
||||
if (user.counter == 1 || (!parsed->qop.empty() && parsed->counter() == user.counter))
|
||||
{
|
||||
return kPass;
|
||||
}
|
||||
}
|
||||
return kStale;
|
||||
}
|
||||
return kFail;
|
||||
}
|
||||
|
||||
//! \return Information needed to generate client authentication `response`s.
|
||||
static http::http_client_auth::session::keys extract(
|
||||
const http::http_response_info& response, const bool is_first)
|
||||
{
|
||||
using field = std::pair<std::string, std::string>;
|
||||
|
||||
server_parameters best{};
|
||||
|
||||
const std::list<field>& fields = response.m_header_info.m_etc_fields;
|
||||
auto current = fields.begin();
|
||||
const auto end = fields.end();
|
||||
while (true)
|
||||
{
|
||||
current = std::find_if(current, end, [] (const field& value) {
|
||||
return boost::equals(server_auth_field, value.first, ascii_iequal);
|
||||
});
|
||||
if (current == end)
|
||||
break;
|
||||
|
||||
const auto parsed = parse(current->second);
|
||||
if (parsed)
|
||||
{
|
||||
server_parameters local_best = parsed->algorithm.empty() ?
|
||||
server_parameters{*parsed, boost::fusion::find<md5_>(digest_algorithms)} :
|
||||
boost::fusion::iter_fold(digest_algorithms, server_parameters{}, matches_algorithm{*parsed});
|
||||
|
||||
if (local_best.index < best.index)
|
||||
best = std::move(local_best);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
if (is_first || boost::equals(best.stale, u8"true"sv, ascii_iequal))
|
||||
return best.take();
|
||||
return {}; // authentication failed with bad user/pass
|
||||
}
|
||||
|
||||
private:
|
||||
explicit auth_message()
|
||||
: algorithm()
|
||||
, cnonce()
|
||||
, nc()
|
||||
, nonce()
|
||||
, qop()
|
||||
, realm()
|
||||
, response()
|
||||
, stale()
|
||||
, uri()
|
||||
, username() {
|
||||
}
|
||||
|
||||
static std::optional<auth_message> parse(const std::string_view request)
|
||||
{
|
||||
struct parser
|
||||
{
|
||||
using field_parser = std::function<bool(const parser&, iterator&, iterator, auth_message&)>;
|
||||
|
||||
explicit parser() : field_table(), skip_whitespace(), header(), quoted_string(), token(), fields() {
|
||||
using namespace std::placeholders;
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
struct parse_nc
|
||||
{
|
||||
bool operator()(const parser&, iterator& current, const iterator end, auth_message& result) const
|
||||
{
|
||||
return qi::parse(
|
||||
current, end,
|
||||
(qi::raw[qi::uint_parser<std::uint32_t, 16, 8, 8>{}]),
|
||||
result.nc
|
||||
);
|
||||
}
|
||||
};
|
||||
struct parse_token
|
||||
{
|
||||
bool operator()(const parser& parse, iterator& current, const iterator end,
|
||||
boost::iterator_range<iterator>& result) const
|
||||
{
|
||||
return qi::parse(current, end, parse.token, result);
|
||||
}
|
||||
};
|
||||
struct parse_string
|
||||
{
|
||||
bool operator()(const parser& parse, iterator& current, const iterator end,
|
||||
boost::iterator_range<iterator>& result) const
|
||||
{
|
||||
return qi::parse(current, end, parse.quoted_string, result);
|
||||
}
|
||||
bool operator()(const parser& parse, iterator& current, const iterator end) const
|
||||
{
|
||||
return qi::parse(current, end, parse.quoted_string);
|
||||
}
|
||||
};
|
||||
struct parse_response
|
||||
{
|
||||
bool operator()(const parser&, iterator& current, const iterator end, auth_message& result) const
|
||||
{
|
||||
using byte = qi::uint_parser<std::uint8_t, 16, 2, 2>;
|
||||
return qi::parse(
|
||||
current, end,
|
||||
(qi::lit(u8'"') >> qi::raw[+(byte{})] >> qi::lit(u8'"')),
|
||||
result.response
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
field_table.add
|
||||
(u8"algorithm", std::bind(parse_token{}, _1, _2, _3, std::bind(&auth_message::algorithm, _4)))
|
||||
(u8"cnonce", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::cnonce, _4)))
|
||||
(u8"domain", std::bind(parse_string{}, _1, _2, _3)) // ignore field
|
||||
(u8"nc", parse_nc{})
|
||||
(u8"nonce", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::nonce, _4)))
|
||||
(u8"opaque", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::opaque, _4)))
|
||||
(u8"qop", std::bind(parse_token{}, _1, _2, _3, std::bind(&auth_message::qop, _4)))
|
||||
(u8"realm", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::realm, _4)))
|
||||
(u8"response", parse_response{})
|
||||
(u8"stale", std::bind(parse_token{}, _1, _2, _3, std::bind(&auth_message::stale, _4)))
|
||||
(u8"uri", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::uri, _4)))
|
||||
(u8"username", std::bind(parse_string{}, _1, _2, _3, std::bind(&auth_message::username, _4)));
|
||||
|
||||
skip_whitespace = *(&qi::ascii::char_ >> qi::ascii::space);
|
||||
header = skip_whitespace >> qi::ascii::no_case[u8"digest"] >> skip_whitespace;
|
||||
quoted_string = (qi::lit(u8'"') >> qi::raw[+(u8"\\\"" | (qi::ascii::char_ - u8'"'))] >> qi::lit(u8'"'));
|
||||
token =
|
||||
(!qi::lit(u8'"') >> qi::raw[+(&qi::ascii::char_ >> (qi::ascii::graph - qi::ascii::char_(u8"()<>@,;:\\\"/[]?={}")))]) |
|
||||
quoted_string;
|
||||
fields = field_table >> skip_whitespace >> u8'=' >> skip_whitespace;
|
||||
}
|
||||
|
||||
std::optional<auth_message> operator()(const std::string_view request) const
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
iterator current = request.begin();
|
||||
const iterator end = request.end();
|
||||
|
||||
if (!qi::parse(current, end, header))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auth_message info{};
|
||||
field_parser null_parser{};
|
||||
std::reference_wrapper<const field_parser> field = null_parser;
|
||||
|
||||
do // require at least one field; require field after `,` character
|
||||
{
|
||||
if (!qi::parse(current, end, fields, field) || !field(*this, current, end, info))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
qi::parse(current, end, skip_whitespace);
|
||||
} while (qi::parse(current, end, qi::char_(u8',') >> skip_whitespace));
|
||||
if (current == end)
|
||||
return info;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::spirit::qi::symbols<
|
||||
char, field_parser, boost::spirit::qi::tst<char, field_parser>, ascii_tolower_
|
||||
> field_table;
|
||||
boost::spirit::qi::rule<iterator> skip_whitespace;
|
||||
boost::spirit::qi::rule<iterator> header;
|
||||
boost::spirit::qi::rule<iterator, boost::iterator_range<iterator>()> quoted_string;
|
||||
boost::spirit::qi::rule<iterator, boost::iterator_range<iterator>()> token;
|
||||
boost::spirit::qi::rule<iterator, std::reference_wrapper<const field_parser>()> fields;
|
||||
}; // parser
|
||||
|
||||
static const parser parse_;
|
||||
return parse_(request);
|
||||
}
|
||||
|
||||
struct has_valid_response
|
||||
{
|
||||
template<typename Digest, typename Result>
|
||||
Result generate_old_response(Digest digest, const Result& key, const Result& auth) const
|
||||
{
|
||||
return digest(key, u8":", request.nonce, u8":", auth);
|
||||
}
|
||||
|
||||
template<typename Digest, typename Result>
|
||||
Result generate_new_response(Digest digest, const Result& key, const Result& auth) const
|
||||
{
|
||||
return digest(
|
||||
key, u8":", request.nonce, u8":", request.nc, u8":", request.cnonce, u8":", request.qop, u8":", auth
|
||||
);
|
||||
}
|
||||
|
||||
template<typename Result>
|
||||
bool check(const Result& result) const
|
||||
{
|
||||
return boost::equals(request.response, result, ascii_iequal);
|
||||
}
|
||||
|
||||
template<typename Digest>
|
||||
bool operator()(const Digest& digest) const
|
||||
{
|
||||
if (boost::starts_with(request.algorithm, Digest::name, ascii_iequal) ||
|
||||
(request.algorithm.empty() && std::is_same<md5_, Digest>::value))
|
||||
{
|
||||
auto key = generate_a1(digest, user.credentials, auth_realm);
|
||||
if (boost::ends_with(request.algorithm, sess_algo, ascii_iequal))
|
||||
{
|
||||
key = digest(key, u8":", request.nonce, u8":", request.cnonce);
|
||||
}
|
||||
|
||||
auto auth = digest(method, u8":", request.uri);
|
||||
if (request.qop.empty())
|
||||
{
|
||||
return check(generate_old_response(std::move(digest), std::move(key), std::move(auth)));
|
||||
}
|
||||
else if (boost::equals(u8"auth"sv, request.qop, ascii_iequal))
|
||||
{
|
||||
return check(generate_new_response(std::move(digest), std::move(key), std::move(auth)));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const auth_message& request;
|
||||
const http::http_server_auth::session& user;
|
||||
const std::string_view method;
|
||||
};
|
||||
|
||||
std::optional<std::uint32_t> counter() const
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
using hex = qi::uint_parser<std::uint32_t, 16>;
|
||||
std::uint32_t value = 0;
|
||||
const bool converted = qi::parse(nc.begin(), nc.end(), hex{}, value);
|
||||
if (converted) return value;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
struct server_parameters
|
||||
{
|
||||
server_parameters()
|
||||
: nonce(), opaque(), realm(), stale(), value_generator()
|
||||
, index(boost::fusion::size(digest_algorithms))
|
||||
{}
|
||||
|
||||
template<typename DigestIter>
|
||||
explicit server_parameters(const auth_message& request, const DigestIter& digest)
|
||||
: nonce(request.nonce)
|
||||
, opaque(request.opaque)
|
||||
, realm(request.realm)
|
||||
, stale(request.stale)
|
||||
, value_generator()
|
||||
, index(boost::fusion::distance(boost::fusion::begin(digest_algorithms), digest))
|
||||
{
|
||||
using digest_type = typename boost::fusion::result_of::value_of<DigestIter>::type;
|
||||
|
||||
// debug check internal state of the auth_message class
|
||||
assert(
|
||||
(std::is_same<digest_type, md5_>::value) ||
|
||||
boost::equals((*digest).name, request.algorithm, ascii_iequal)
|
||||
);
|
||||
if (request.qop.empty())
|
||||
value_generator = old_algorithm<digest_type>{*digest};
|
||||
else
|
||||
{
|
||||
for (auto elem = boost::make_split_iterator(request.qop, boost::token_finder(http_list_separator));
|
||||
!elem.eof();
|
||||
++elem)
|
||||
{
|
||||
if (boost::equals(u8"auth"sv, *elem, ascii_iequal))
|
||||
{
|
||||
value_generator = auth_algorithm<digest_type>{*digest};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!value_generator) // no supported qop mode
|
||||
index = boost::fusion::size(digest_algorithms);
|
||||
}
|
||||
}
|
||||
|
||||
http::http_client_auth::session::keys take()
|
||||
{
|
||||
return {to_string(nonce), to_string(opaque), to_string(realm), std::move(value_generator)};
|
||||
}
|
||||
|
||||
boost::iterator_range<iterator> nonce;
|
||||
boost::iterator_range<iterator> opaque;
|
||||
boost::iterator_range<iterator> realm;
|
||||
boost::iterator_range<iterator> stale;
|
||||
http::http_client_auth::session::keys::algorithm value_generator;
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
struct matches_algorithm
|
||||
{
|
||||
template<typename DigestIter>
|
||||
server_parameters operator()(server_parameters current, const DigestIter& digest) const
|
||||
{
|
||||
if (!current.value_generator)
|
||||
{
|
||||
if (boost::equals(response.algorithm, (*digest).name, ascii_iequal))
|
||||
{
|
||||
current = server_parameters{response, digest};
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
const auth_message& response;
|
||||
};
|
||||
|
||||
|
||||
boost::iterator_range<iterator> algorithm;
|
||||
boost::iterator_range<iterator> cnonce;
|
||||
boost::iterator_range<iterator> nc;
|
||||
boost::iterator_range<iterator> nonce;
|
||||
boost::iterator_range<iterator> opaque;
|
||||
boost::iterator_range<iterator> qop;
|
||||
boost::iterator_range<iterator> realm;
|
||||
boost::iterator_range<iterator> response;
|
||||
boost::iterator_range<iterator> stale;
|
||||
boost::iterator_range<iterator> uri;
|
||||
boost::iterator_range<iterator> username;
|
||||
}; // auth_message
|
||||
|
||||
struct add_challenge
|
||||
{
|
||||
template<typename Digest>
|
||||
void operator()(const Digest& digest) const
|
||||
{
|
||||
static constexpr auto fvalue = u8"Digest qop=\"auth\""sv;
|
||||
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
std::string out(fvalue);
|
||||
|
||||
std::string algorithm{Digest::name};
|
||||
if (i > 0) algorithm += sess_algo;
|
||||
|
||||
add_field(out, u8"algorithm", algorithm);
|
||||
constexpr bool add_quotes = true;
|
||||
add_field(out, u8"realm", auth_realm, add_quotes);
|
||||
add_field(out, u8"nonce", nonce, add_quotes);
|
||||
add_field(out, u8"stale", is_stale ? "true"sv : "false"sv);
|
||||
|
||||
fields.push_back(std::make_pair(std::string(server_auth_field), std::move(out)));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string_view nonce;
|
||||
std::list<std::pair<std::string, std::string>>& fields;
|
||||
const bool is_stale;
|
||||
};
|
||||
|
||||
http::http_response_info create_digest_response(const std::string_view nonce, const bool is_stale)
|
||||
{
|
||||
epee::net_utils::http::http_response_info rc{};
|
||||
rc.m_response_code = 401;
|
||||
rc.m_response_comment = u8"Unauthorized";
|
||||
rc.m_mime_type = u8"text/html";
|
||||
rc.m_body =
|
||||
u8"<html><head><title>Unauthorized Access</title></head><body><h1>401 Unauthorized</h1></body></html>";
|
||||
|
||||
boost::fusion::for_each(
|
||||
digest_algorithms, add_challenge{nonce, rc.m_additional_fields, is_stale}
|
||||
);
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
namespace epee
|
||||
{
|
||||
namespace net_utils
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
http_server_auth::http_server_auth(login credentials, std::function<void(size_t, uint8_t*)> r)
|
||||
: user(session{std::move(credentials)}), rng(std::move(r)) {
|
||||
}
|
||||
|
||||
std::optional<http_response_info> http_server_auth::do_get_response(const http_request_info& request)
|
||||
{
|
||||
assert(user);
|
||||
using field = std::pair<std::string, std::string>;
|
||||
|
||||
const std::list<field>& fields = request.m_header_info.m_etc_fields;
|
||||
const auto auth = boost::find_if(fields, [] (const field& value) {
|
||||
return boost::equals(client_auth_field, value.first, ascii_iequal);
|
||||
});
|
||||
|
||||
bool is_stale = false;
|
||||
if (auth != fields.end())
|
||||
{
|
||||
++(user->counter);
|
||||
switch (auth_message::verify(request.m_http_method_str, auth->second, *user))
|
||||
{
|
||||
case auth_message::kPass:
|
||||
return std::nullopt;
|
||||
|
||||
case auth_message::kStale:
|
||||
is_stale = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
case auth_message::kFail:
|
||||
break;
|
||||
}
|
||||
}
|
||||
user->counter = 0;
|
||||
{
|
||||
std::array<std::uint8_t, 16> rand_128bit{{}};
|
||||
rng(rand_128bit.size(), rand_128bit.data());
|
||||
user->nonce = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size());
|
||||
}
|
||||
return create_digest_response(user->nonce, is_stale);
|
||||
}
|
||||
|
||||
http_client_auth::http_client_auth(login credentials)
|
||||
: user(session{std::move(credentials)}) {
|
||||
}
|
||||
|
||||
http_client_auth::status http_client_auth::do_handle_401(const http_response_info& response)
|
||||
{
|
||||
assert(user);
|
||||
const bool first_auth = (user->counter == 0);
|
||||
user->server = auth_message::extract(response, first_auth);
|
||||
if (user->server.generator)
|
||||
{
|
||||
user->counter = 0;
|
||||
return kSuccess;
|
||||
}
|
||||
return first_auth ? kParseFailure : kBadPassword;
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> http_client_auth::do_get_auth_field(
|
||||
const std::string_view method, const std::string_view uri)
|
||||
{
|
||||
assert(user);
|
||||
if (user->server.generator)
|
||||
{
|
||||
++(user->counter);
|
||||
return std::make_pair(std::string(client_auth_field), user->server.generator(*user, method, uri));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include "cryptonote_core/blockchain.h"
|
||||
#include "common/command_line.h"
|
||||
#include "loki_economy.h"
|
||||
#include "common/hex.h"
|
||||
#include "version.h"
|
||||
#include <lokimq/hex.h>
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "cryptonote_core/tx_sanity_check.h"
|
||||
#include "misc_language.h"
|
||||
#include "net/parse.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "rpc/rpc_args.h"
|
||||
#include "rpc/rpc_handler.h"
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
#include "cryptonote_core/loki_name_system.h"
|
||||
#include "simplewallet.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "crypto/crypto.h" // for crypto::secret_key definition
|
||||
#include "mnemonics/electrum-words.h"
|
||||
|
@ -3170,12 +3169,12 @@ Pending or Failed: "failed"|"pending", "out", Lock, Checkpointed, Time, Amount*
|
|||
m_cmd_binder.set_handler("lns_buy_mapping",
|
||||
[this](const auto& x) { return lns_buy_mapping(x); },
|
||||
tr(USAGE_LNS_BUY_MAPPING),
|
||||
tr(tools::wallet_rpc::COMMAND_RPC_LNS_BUY_MAPPING::description));
|
||||
tr(tools::wallet_rpc::LNS_BUY_MAPPING::description));
|
||||
|
||||
m_cmd_binder.set_handler("lns_update_mapping",
|
||||
[this](const auto& x) { return lns_update_mapping(x); },
|
||||
tr(USAGE_LNS_UPDATE_MAPPING),
|
||||
tr(tools::wallet_rpc::COMMAND_RPC_LNS_UPDATE_MAPPING::description));
|
||||
tr(tools::wallet_rpc::LNS_UPDATE_MAPPING::description));
|
||||
|
||||
m_cmd_binder.set_handler("lns_print_owners_to_names",
|
||||
[this](const auto& x) { return lns_print_owners_to_names(x); },
|
||||
|
@ -3190,7 +3189,7 @@ Pending or Failed: "failed"|"pending", "out", Lock, Checkpointed, Time, Amount*
|
|||
m_cmd_binder.set_handler("lns_make_update_mapping_signature",
|
||||
[this](const auto& x) { return lns_make_update_mapping_signature(x); },
|
||||
tr(USAGE_LNS_MAKE_UPDATE_MAPPING_SIGNATURE),
|
||||
tr(tools::wallet_rpc::COMMAND_RPC_LNS_MAKE_UPDATE_SIGNATURE::description));
|
||||
tr(tools::wallet_rpc::LNS_MAKE_UPDATE_SIGNATURE::description));
|
||||
}
|
||||
|
||||
simple_wallet::~simple_wallet()
|
||||
|
|
|
@ -41,7 +41,7 @@ target_link_libraries(wallet
|
|||
PUBLIC
|
||||
multisig
|
||||
rpc_commands
|
||||
rpc_args
|
||||
rpc_server_base
|
||||
cryptonote_core
|
||||
mnemonics
|
||||
device_trezor
|
||||
|
@ -58,10 +58,12 @@ target_link_libraries(wallet
|
|||
|
||||
loki_add_executable(wallet_rpc_server "loki-wallet-rpc"
|
||||
wallet_rpc_server.cpp
|
||||
wallet_rpc_server_commands_defs.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(wallet_rpc_server
|
||||
PRIVATE
|
||||
rpc_server_base
|
||||
wallet
|
||||
daemonizer
|
||||
Boost::program_options
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -34,263 +34,170 @@
|
|||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <string>
|
||||
|
||||
#include <uWebSockets/App.h>
|
||||
|
||||
#include "common/util.h"
|
||||
#include "net/http_server_impl_base.h"
|
||||
#include "common/periodic_task.h"
|
||||
#include "wallet_rpc_server_commands_defs.h"
|
||||
#include "wallet2.h"
|
||||
#include "rpc/http_server_base.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "wallet.rpc"
|
||||
|
||||
namespace tools
|
||||
{
|
||||
using HttpRequest = uWS::HttpRequest;
|
||||
using HttpResponse = uWS::HttpResponse<false/*SSL*/>;
|
||||
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class wallet_rpc_server: public epee::http_server_impl_base<wallet_rpc_server>
|
||||
class wallet_rpc_server final : public cryptonote::rpc::http_server_base
|
||||
{
|
||||
public:
|
||||
typedef epee::net_utils::connection_context_base connection_context;
|
||||
|
||||
static const char* tr(const char* str);
|
||||
|
||||
wallet_rpc_server(boost::program_options::variables_map vm);
|
||||
|
||||
bool init();
|
||||
bool run(bool /*interactive - ignored (rpc wallet is always non-interactive) */);
|
||||
void stop();
|
||||
void set_wallet(std::unique_ptr<wallet2> cr);
|
||||
std::atomic<bool> m_long_poll_disabled;
|
||||
|
||||
/// Thrown if we get invalid/unparseable JSON data.
|
||||
struct parse_error : std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
//json_rpc
|
||||
wallet_rpc::GET_BALANCE::response invoke(wallet_rpc::GET_BALANCE::request&& req);
|
||||
wallet_rpc::GET_ADDRESS::response invoke(wallet_rpc::GET_ADDRESS::request&& req);
|
||||
wallet_rpc::GET_ADDRESS_INDEX::response invoke(wallet_rpc::GET_ADDRESS_INDEX::request&& req);
|
||||
wallet_rpc::CREATE_ADDRESS::response invoke(wallet_rpc::CREATE_ADDRESS::request&& req);
|
||||
wallet_rpc::LABEL_ADDRESS::response invoke(wallet_rpc::LABEL_ADDRESS::request&& req);
|
||||
wallet_rpc::GET_ACCOUNTS::response invoke(wallet_rpc::GET_ACCOUNTS::request&& req);
|
||||
wallet_rpc::CREATE_ACCOUNT::response invoke(wallet_rpc::CREATE_ACCOUNT::request&& req);
|
||||
wallet_rpc::LABEL_ACCOUNT::response invoke(wallet_rpc::LABEL_ACCOUNT::request&& req);
|
||||
wallet_rpc::GET_ACCOUNT_TAGS::response invoke(wallet_rpc::GET_ACCOUNT_TAGS::request&& req);
|
||||
wallet_rpc::TAG_ACCOUNTS::response invoke(wallet_rpc::TAG_ACCOUNTS::request&& req);
|
||||
wallet_rpc::UNTAG_ACCOUNTS::response invoke(wallet_rpc::UNTAG_ACCOUNTS::request&& req);
|
||||
wallet_rpc::SET_ACCOUNT_TAG_DESCRIPTION::response invoke(wallet_rpc::SET_ACCOUNT_TAG_DESCRIPTION::request&& req);
|
||||
wallet_rpc::GET_HEIGHT::response invoke(wallet_rpc::GET_HEIGHT::request&& req);
|
||||
wallet_rpc::TRANSFER::response invoke(wallet_rpc::TRANSFER::request&& req);
|
||||
wallet_rpc::TRANSFER_SPLIT::response invoke(wallet_rpc::TRANSFER_SPLIT::request&& req);
|
||||
wallet_rpc::SIGN_TRANSFER::response invoke(wallet_rpc::SIGN_TRANSFER::request&& req);
|
||||
wallet_rpc::DESCRIBE_TRANSFER::response invoke(wallet_rpc::DESCRIBE_TRANSFER::request&& req);
|
||||
wallet_rpc::SUBMIT_TRANSFER::response invoke(wallet_rpc::SUBMIT_TRANSFER::request&& req);
|
||||
wallet_rpc::SWEEP_DUST::response invoke(wallet_rpc::SWEEP_DUST::request&& req);
|
||||
wallet_rpc::SWEEP_ALL::response invoke(wallet_rpc::SWEEP_ALL::request&& req);
|
||||
wallet_rpc::SWEEP_SINGLE::response invoke(wallet_rpc::SWEEP_SINGLE::request&& req);
|
||||
wallet_rpc::RELAY_TX::response invoke(wallet_rpc::RELAY_TX::request&& req);
|
||||
wallet_rpc::MAKE_INTEGRATED_ADDRESS::response invoke(wallet_rpc::MAKE_INTEGRATED_ADDRESS::request&& req);
|
||||
wallet_rpc::SPLIT_INTEGRATED_ADDRESS::response invoke(wallet_rpc::SPLIT_INTEGRATED_ADDRESS::request&& req);
|
||||
wallet_rpc::STORE::response invoke(wallet_rpc::STORE::request&& req);
|
||||
wallet_rpc::GET_PAYMENTS::response invoke(wallet_rpc::GET_PAYMENTS::request&& req);
|
||||
wallet_rpc::GET_BULK_PAYMENTS::response invoke(wallet_rpc::GET_BULK_PAYMENTS::request&& req);
|
||||
wallet_rpc::INCOMING_TRANSFERS::response invoke(wallet_rpc::INCOMING_TRANSFERS::request&& req);
|
||||
wallet_rpc::STOP_WALLET::response invoke(wallet_rpc::STOP_WALLET::request&& req);
|
||||
wallet_rpc::RESCAN_BLOCKCHAIN::response invoke(wallet_rpc::RESCAN_BLOCKCHAIN::request&& req);
|
||||
wallet_rpc::SET_TX_NOTES::response invoke(wallet_rpc::SET_TX_NOTES::request&& req);
|
||||
wallet_rpc::GET_TX_NOTES::response invoke(wallet_rpc::GET_TX_NOTES::request&& req);
|
||||
wallet_rpc::SET_ATTRIBUTE::response invoke(wallet_rpc::SET_ATTRIBUTE::request&& req);
|
||||
wallet_rpc::GET_ATTRIBUTE::response invoke(wallet_rpc::GET_ATTRIBUTE::request&& req);
|
||||
wallet_rpc::GET_TX_KEY::response invoke(wallet_rpc::GET_TX_KEY::request&& req);
|
||||
wallet_rpc::CHECK_TX_KEY::response invoke(wallet_rpc::CHECK_TX_KEY::request&& req);
|
||||
wallet_rpc::GET_TX_PROOF::response invoke(wallet_rpc::GET_TX_PROOF::request&& req);
|
||||
wallet_rpc::CHECK_TX_PROOF::response invoke(wallet_rpc::CHECK_TX_PROOF::request&& req);
|
||||
wallet_rpc::GET_SPEND_PROOF::response invoke(wallet_rpc::GET_SPEND_PROOF::request&& req);
|
||||
wallet_rpc::CHECK_SPEND_PROOF::response invoke(wallet_rpc::CHECK_SPEND_PROOF::request&& req);
|
||||
wallet_rpc::GET_RESERVE_PROOF::response invoke(wallet_rpc::GET_RESERVE_PROOF::request&& req);
|
||||
wallet_rpc::CHECK_RESERVE_PROOF::response invoke(wallet_rpc::CHECK_RESERVE_PROOF::request&& req);
|
||||
wallet_rpc::GET_TRANSFERS::response invoke(wallet_rpc::GET_TRANSFERS::request&& req);
|
||||
wallet_rpc::GET_TRANSFERS_CSV::response invoke(wallet_rpc::GET_TRANSFERS_CSV::request&& req);
|
||||
wallet_rpc::GET_TRANSFER_BY_TXID::response invoke(wallet_rpc::GET_TRANSFER_BY_TXID::request&& req);
|
||||
wallet_rpc::SIGN::response invoke(wallet_rpc::SIGN::request&& req);
|
||||
wallet_rpc::VERIFY::response invoke(wallet_rpc::VERIFY::request&& req);
|
||||
wallet_rpc::EXPORT_OUTPUTS::response invoke(wallet_rpc::EXPORT_OUTPUTS::request&& req);
|
||||
wallet_rpc::IMPORT_OUTPUTS::response invoke(wallet_rpc::IMPORT_OUTPUTS::request&& req);
|
||||
wallet_rpc::EXPORT_KEY_IMAGES::response invoke(wallet_rpc::EXPORT_KEY_IMAGES::request&& req);
|
||||
wallet_rpc::IMPORT_KEY_IMAGES::response invoke(wallet_rpc::IMPORT_KEY_IMAGES::request&& req);
|
||||
wallet_rpc::MAKE_URI::response invoke(wallet_rpc::MAKE_URI::request&& req);
|
||||
wallet_rpc::PARSE_URI::response invoke(wallet_rpc::PARSE_URI::request&& req);
|
||||
wallet_rpc::GET_ADDRESS_BOOK_ENTRY::response invoke(wallet_rpc::GET_ADDRESS_BOOK_ENTRY::request&& req);
|
||||
wallet_rpc::ADD_ADDRESS_BOOK_ENTRY::response invoke(wallet_rpc::ADD_ADDRESS_BOOK_ENTRY::request&& req);
|
||||
wallet_rpc::EDIT_ADDRESS_BOOK_ENTRY::response invoke(wallet_rpc::EDIT_ADDRESS_BOOK_ENTRY::request&& req);
|
||||
wallet_rpc::DELETE_ADDRESS_BOOK_ENTRY::response invoke(wallet_rpc::DELETE_ADDRESS_BOOK_ENTRY::request&& req);
|
||||
wallet_rpc::REFRESH::response invoke(wallet_rpc::REFRESH::request&& req);
|
||||
wallet_rpc::AUTO_REFRESH::response invoke(wallet_rpc::AUTO_REFRESH::request&& req);
|
||||
wallet_rpc::RESCAN_SPENT::response invoke(wallet_rpc::RESCAN_SPENT::request&& req);
|
||||
wallet_rpc::START_MINING::response invoke(wallet_rpc::START_MINING::request&& req);
|
||||
wallet_rpc::STOP_MINING::response invoke(wallet_rpc::STOP_MINING::request&& req);
|
||||
wallet_rpc::GET_LANGUAGES::response invoke(wallet_rpc::GET_LANGUAGES::request&& req);
|
||||
wallet_rpc::CREATE_WALLET::response invoke(wallet_rpc::CREATE_WALLET::request&& req);
|
||||
wallet_rpc::OPEN_WALLET::response invoke(wallet_rpc::OPEN_WALLET::request&& req);
|
||||
wallet_rpc::CLOSE_WALLET::response invoke(wallet_rpc::CLOSE_WALLET::request&& req);
|
||||
wallet_rpc::CHANGE_WALLET_PASSWORD::response invoke(wallet_rpc::CHANGE_WALLET_PASSWORD::request&& req);
|
||||
wallet_rpc::GENERATE_FROM_KEYS::response invoke(wallet_rpc::GENERATE_FROM_KEYS::request&& req);
|
||||
wallet_rpc::RESTORE_DETERMINISTIC_WALLET::response invoke(wallet_rpc::RESTORE_DETERMINISTIC_WALLET::request&& req);
|
||||
wallet_rpc::IS_MULTISIG::response invoke(wallet_rpc::IS_MULTISIG::request&& req);
|
||||
wallet_rpc::PREPARE_MULTISIG::response invoke(wallet_rpc::PREPARE_MULTISIG::request&& req);
|
||||
wallet_rpc::MAKE_MULTISIG::response invoke(wallet_rpc::MAKE_MULTISIG::request&& req);
|
||||
wallet_rpc::EXPORT_MULTISIG::response invoke(wallet_rpc::EXPORT_MULTISIG::request&& req);
|
||||
wallet_rpc::IMPORT_MULTISIG::response invoke(wallet_rpc::IMPORT_MULTISIG::request&& req);
|
||||
wallet_rpc::FINALIZE_MULTISIG::response invoke(wallet_rpc::FINALIZE_MULTISIG::request&& req);
|
||||
wallet_rpc::EXCHANGE_MULTISIG_KEYS::response invoke(wallet_rpc::EXCHANGE_MULTISIG_KEYS::request&& req);
|
||||
wallet_rpc::SIGN_MULTISIG::response invoke(wallet_rpc::SIGN_MULTISIG::request&& req);
|
||||
wallet_rpc::SUBMIT_MULTISIG::response invoke(wallet_rpc::SUBMIT_MULTISIG::request&& req);
|
||||
wallet_rpc::VALIDATE_ADDRESS::response invoke(wallet_rpc::VALIDATE_ADDRESS::request&& req);
|
||||
wallet_rpc::SET_DAEMON::response invoke(wallet_rpc::SET_DAEMON::request&& req);
|
||||
wallet_rpc::SET_LOG_LEVEL::response invoke(wallet_rpc::SET_LOG_LEVEL::request&& req);
|
||||
wallet_rpc::SET_LOG_CATEGORIES::response invoke(wallet_rpc::SET_LOG_CATEGORIES::request&& req);
|
||||
wallet_rpc::GET_VERSION::response invoke(wallet_rpc::GET_VERSION::request&& req);
|
||||
wallet_rpc::STAKE::response invoke(wallet_rpc::STAKE::request&& req);
|
||||
wallet_rpc::REGISTER_SERVICE_NODE::response invoke(wallet_rpc::REGISTER_SERVICE_NODE::request&& req);
|
||||
wallet_rpc::CAN_REQUEST_STAKE_UNLOCK::response invoke(wallet_rpc::CAN_REQUEST_STAKE_UNLOCK::request&& req);
|
||||
wallet_rpc::REQUEST_STAKE_UNLOCK::response invoke(wallet_rpc::REQUEST_STAKE_UNLOCK::request&& req);
|
||||
wallet_rpc::LNS_BUY_MAPPING::response invoke(wallet_rpc::LNS_BUY_MAPPING::request&& req);
|
||||
wallet_rpc::LNS_UPDATE_MAPPING::response invoke(wallet_rpc::LNS_UPDATE_MAPPING::request&& req);
|
||||
wallet_rpc::LNS_MAKE_UPDATE_SIGNATURE::response invoke(wallet_rpc::LNS_MAKE_UPDATE_SIGNATURE::request&& req);
|
||||
wallet_rpc::LNS_HASH_NAME::response invoke(wallet_rpc::LNS_HASH_NAME::request&& req);
|
||||
wallet_rpc::LNS_DECRYPT_VALUE::response invoke(wallet_rpc::LNS_DECRYPT_VALUE::request&& req);
|
||||
wallet_rpc::QUERY_KEY::response invoke(wallet_rpc::QUERY_KEY::request&& req);
|
||||
|
||||
private:
|
||||
bool run_server_threads();
|
||||
|
||||
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
|
||||
/// Handles a POST request to /json_rpc.
|
||||
void handle_json_rpc_request(HttpResponse& res, HttpRequest& req);
|
||||
|
||||
BEGIN_URI_MAP2()
|
||||
BEGIN_JSON_RPC_MAP("/json_rpc")
|
||||
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
||||
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
||||
MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
|
||||
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
||||
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
||||
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
|
||||
MAP_JON_RPC_WE("label_address", on_label_address, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS)
|
||||
MAP_JON_RPC_WE("get_accounts", on_get_accounts, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS)
|
||||
MAP_JON_RPC_WE("create_account", on_create_account, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT)
|
||||
MAP_JON_RPC_WE("label_account", on_label_account, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT)
|
||||
MAP_JON_RPC_WE("get_account_tags", on_get_account_tags, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS)
|
||||
MAP_JON_RPC_WE("tag_accounts", on_tag_accounts, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS)
|
||||
MAP_JON_RPC_WE("untag_accounts", on_untag_accounts, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS)
|
||||
MAP_JON_RPC_WE("set_account_tag_description", on_set_account_tag_description, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION)
|
||||
MAP_JON_RPC_WE("get_height", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
|
||||
MAP_JON_RPC_WE("getheight", on_getheight, wallet_rpc::COMMAND_RPC_GET_HEIGHT)
|
||||
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
||||
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
|
||||
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
|
||||
MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER)
|
||||
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
|
||||
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
|
||||
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
|
||||
MAP_JON_RPC_WE("sweep_all", on_sweep_all, wallet_rpc::COMMAND_RPC_SWEEP_ALL)
|
||||
MAP_JON_RPC_WE("sweep_single", on_sweep_single, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE)
|
||||
MAP_JON_RPC_WE("relay_tx", on_relay_tx, wallet_rpc::COMMAND_RPC_RELAY_TX)
|
||||
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
|
||||
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
|
||||
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
|
||||
MAP_JON_RPC_WE("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS)
|
||||
MAP_JON_RPC_WE("query_key", on_query_key, wallet_rpc::COMMAND_RPC_QUERY_KEY)
|
||||
MAP_JON_RPC_WE("make_integrated_address", on_make_integrated_address, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS)
|
||||
MAP_JON_RPC_WE("split_integrated_address", on_split_integrated_address, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS)
|
||||
MAP_JON_RPC_WE("stop_wallet", on_stop_wallet, wallet_rpc::COMMAND_RPC_STOP_WALLET)
|
||||
MAP_JON_RPC_WE("rescan_blockchain", on_rescan_blockchain, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN)
|
||||
MAP_JON_RPC_WE("set_tx_notes", on_set_tx_notes, wallet_rpc::COMMAND_RPC_SET_TX_NOTES)
|
||||
MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES)
|
||||
MAP_JON_RPC_WE("set_attribute", on_set_attribute, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE)
|
||||
MAP_JON_RPC_WE("get_attribute", on_get_attribute, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE)
|
||||
MAP_JON_RPC_WE("get_tx_key", on_get_tx_key, wallet_rpc::COMMAND_RPC_GET_TX_KEY)
|
||||
MAP_JON_RPC_WE("check_tx_key", on_check_tx_key, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY)
|
||||
MAP_JON_RPC_WE("get_tx_proof", on_get_tx_proof, wallet_rpc::COMMAND_RPC_GET_TX_PROOF)
|
||||
MAP_JON_RPC_WE("check_tx_proof", on_check_tx_proof, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF)
|
||||
MAP_JON_RPC_WE("get_spend_proof", on_get_spend_proof, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF)
|
||||
MAP_JON_RPC_WE("check_spend_proof", on_check_spend_proof, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF)
|
||||
MAP_JON_RPC_WE("get_reserve_proof", on_get_reserve_proof, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF)
|
||||
MAP_JON_RPC_WE("check_reserve_proof", on_check_reserve_proof, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF)
|
||||
MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS)
|
||||
MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID)
|
||||
MAP_JON_RPC_WE("get_transfers_csv", on_get_transfers_csv, wallet_rpc::COMMAND_RPC_GET_TRANSFERS_CSV)
|
||||
MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN)
|
||||
MAP_JON_RPC_WE("verify", on_verify, wallet_rpc::COMMAND_RPC_VERIFY)
|
||||
MAP_JON_RPC_WE("export_outputs", on_export_outputs, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS)
|
||||
MAP_JON_RPC_WE("import_outputs", on_import_outputs, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS)
|
||||
MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES)
|
||||
MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES)
|
||||
MAP_JON_RPC_WE("make_uri", on_make_uri, wallet_rpc::COMMAND_RPC_MAKE_URI)
|
||||
MAP_JON_RPC_WE("parse_uri", on_parse_uri, wallet_rpc::COMMAND_RPC_PARSE_URI)
|
||||
MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("edit_address_book", on_edit_address_book, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
|
||||
MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
|
||||
MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
|
||||
MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
|
||||
MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
|
||||
MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
|
||||
MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES)
|
||||
MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET)
|
||||
MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
|
||||
MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET)
|
||||
MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD)
|
||||
MAP_JON_RPC_WE("generate_from_keys", on_generate_from_keys, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS)
|
||||
MAP_JON_RPC_WE("restore_deterministic_wallet", on_restore_deterministic_wallet, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET)
|
||||
MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG)
|
||||
MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG)
|
||||
MAP_JON_RPC_WE("make_multisig", on_make_multisig, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG)
|
||||
MAP_JON_RPC_WE("export_multisig_info", on_export_multisig, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG)
|
||||
MAP_JON_RPC_WE("import_multisig_info", on_import_multisig, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG)
|
||||
MAP_JON_RPC_WE("finalize_multisig", on_finalize_multisig, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
|
||||
MAP_JON_RPC_WE("exchange_multisig_keys", on_exchange_multisig_keys, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS)
|
||||
MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
|
||||
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
|
||||
MAP_JON_RPC_WE("validate_address", on_validate_address, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS)
|
||||
MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON)
|
||||
MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL)
|
||||
MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES)
|
||||
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
|
||||
|
||||
//
|
||||
// Loki
|
||||
//
|
||||
MAP_JON_RPC_WE("stake", on_stake, wallet_rpc::COMMAND_RPC_STAKE)
|
||||
MAP_JON_RPC_WE("register_service_node", on_register_service_node, wallet_rpc::COMMAND_RPC_REGISTER_SERVICE_NODE)
|
||||
MAP_JON_RPC_WE("can_request_stake_unlock", on_can_request_stake_unlock, wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK)
|
||||
MAP_JON_RPC_WE("request_stake_unlock", on_request_stake_unlock, wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK)
|
||||
MAP_JON_RPC_WE("lns_buy_mapping", on_lns_buy_mapping, wallet_rpc::COMMAND_RPC_LNS_BUY_MAPPING)
|
||||
MAP_JON_RPC_WE("lns_update_mapping", on_lns_update_mapping, wallet_rpc::COMMAND_RPC_LNS_UPDATE_MAPPING)
|
||||
MAP_JON_RPC_WE("lns_make_update_mapping_signature", on_lns_make_update_mapping_signature, wallet_rpc::COMMAND_RPC_LNS_MAKE_UPDATE_SIGNATURE)
|
||||
MAP_JON_RPC_WE("lns_hash_name", on_lns_hash_name, wallet_rpc::COMMAND_RPC_LNS_HASH_NAME)
|
||||
MAP_JON_RPC_WE("lns_decrypt_value", on_lns_decrypt_value, wallet_rpc::COMMAND_RPC_LNS_DECRYPT_VALUE)
|
||||
END_JSON_RPC_MAP()
|
||||
END_URI_MAP2()
|
||||
|
||||
//json_rpc
|
||||
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sweep_single(const wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::request& req, wallet_rpc::COMMAND_RPC_SWEEP_SINGLE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_relay_tx(const wallet_rpc::COMMAND_RPC_RELAY_TX::request& req, wallet_rpc::COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_check_tx_key(const wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_check_tx_proof(const wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_spend_proof(const wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_check_spend_proof(const wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_SPEND_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_check_reserve_proof(const wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_CHECK_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_transfers_csv(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS_CSV::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS_CSV::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_export_outputs(const wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request& req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request& req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_make_multisig(const wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_MAKE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_export_multisig(const wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_EXPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_stake(const wallet_rpc::COMMAND_RPC_STAKE::request& req, wallet_rpc::COMMAND_RPC_STAKE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_register_service_node(const wallet_rpc::COMMAND_RPC_REGISTER_SERVICE_NODE::request& req, wallet_rpc::COMMAND_RPC_REGISTER_SERVICE_NODE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_can_request_stake_unlock(const wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK::request& req, wallet_rpc::COMMAND_RPC_CAN_REQUEST_STAKE_UNLOCK::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_request_stake_unlock(const wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK::request& req, wallet_rpc::COMMAND_RPC_REQUEST_STAKE_UNLOCK::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_lns_buy_mapping(const wallet_rpc::COMMAND_RPC_LNS_BUY_MAPPING::request& req, wallet_rpc::COMMAND_RPC_LNS_BUY_MAPPING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_lns_update_mapping(const wallet_rpc::COMMAND_RPC_LNS_UPDATE_MAPPING::request& req, wallet_rpc::COMMAND_RPC_LNS_UPDATE_MAPPING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_lns_make_update_mapping_signature(const wallet_rpc::COMMAND_RPC_LNS_MAKE_UPDATE_SIGNATURE::request& req, wallet_rpc::COMMAND_RPC_LNS_MAKE_UPDATE_SIGNATURE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_lns_hash_name(const wallet_rpc::COMMAND_RPC_LNS_HASH_NAME::request& req, wallet_rpc::COMMAND_RPC_LNS_HASH_NAME::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
bool on_lns_decrypt_value(const wallet_rpc::COMMAND_RPC_LNS_DECRYPT_VALUE::request& req, wallet_rpc::COMMAND_RPC_LNS_DECRYPT_VALUE::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
|
||||
//json rpc v2
|
||||
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
|
||||
|
||||
// helpers
|
||||
bool not_open(epee::json_rpc::error& er);
|
||||
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
|
||||
// Checks that a wallet is open; if not, throws an error.
|
||||
void require_open();
|
||||
|
||||
template<typename Ts, typename Tu>
|
||||
bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
|
||||
void fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
|
||||
bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, std::string &unsigned_txset, bool do_not_relay, bool blink,
|
||||
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er);
|
||||
Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata);
|
||||
|
||||
bool validate_transfer(const std::list<wallet::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er);
|
||||
void validate_transfer(const std::list<wallet::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination);
|
||||
|
||||
// Parse options and opens the wallet. Returns nullptr if in directory mode (i.e. no wallet
|
||||
// gets opened). Throws on error.
|
||||
std::unique_ptr<tools::wallet2> load_wallet();
|
||||
|
||||
// Sets up the RPC endpoints (called before listening).
|
||||
void create_rpc_endpoints(uWS::App& http);
|
||||
|
||||
// Runs the server event loop; does not return until the server is shut down (by a signal or a
|
||||
// remote STOP command).
|
||||
void run_loop();
|
||||
|
||||
std::unique_ptr<wallet2> m_wallet;
|
||||
std::string m_wallet_dir;
|
||||
std::vector<std::tuple<std::string /*ip*/, uint16_t /*port*/, bool /*required*/>> m_bind;
|
||||
tools::private_file rpc_login_file;
|
||||
std::atomic<bool> m_stop;
|
||||
bool m_restricted;
|
||||
boost::program_options::variables_map m_vm;
|
||||
std::chrono::milliseconds m_auto_refresh_period;
|
||||
std::chrono::steady_clock::time_point m_last_auto_refresh_time;
|
||||
std::thread m_long_poll_thread;
|
||||
std::atomic<bool> m_long_poll_new_changes;
|
||||
std::atomic<bool> m_long_poll_disabled;
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -29,60 +29,64 @@
|
|||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace tools::wallet_rpc::error_code {
|
||||
|
||||
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR -1
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS -2
|
||||
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3
|
||||
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID -5
|
||||
#define WALLET_RPC_ERROR_CODE_TRANSFER_TYPE -6
|
||||
#define WALLET_RPC_ERROR_CODE_DENIED -7
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_TXID -8
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE -9
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE -10
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_URI -11
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13
|
||||
#define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUT_OF_BOUNDS -14
|
||||
#define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS -15
|
||||
#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE -16
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -17
|
||||
#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE -18
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX -19
|
||||
#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION -20
|
||||
#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS -21
|
||||
#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD -22
|
||||
#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23
|
||||
#define WALLET_RPC_ERROR_CODE_NO_TXKEY -24
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_KEY -25
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_HEX -26
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_TX_METADATA -27
|
||||
#define WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG -28
|
||||
#define WALLET_RPC_ERROR_CODE_WATCH_ONLY -29
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_INFO -30
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_MULTISIG -31
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_LR -32
|
||||
#define WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED -33
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA -34
|
||||
#define WALLET_RPC_ERROR_CODE_MULTISIG_SIGNATURE -35
|
||||
#define WALLET_RPC_ERROR_CODE_MULTISIG_SUBMISSION -36
|
||||
#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_UNLOCKED_MONEY -37
|
||||
#define WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION -38
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA -39
|
||||
#define WALLET_RPC_ERROR_CODE_BAD_SIGNED_TX_DATA -40
|
||||
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41
|
||||
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42
|
||||
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43
|
||||
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL -44
|
||||
#define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45
|
||||
constexpr int16_t UNKNOWN_ERROR = -1;
|
||||
constexpr int16_t WRONG_ADDRESS = -2;
|
||||
constexpr int16_t DAEMON_IS_BUSY = -3;
|
||||
constexpr int16_t GENERIC_TRANSFER_ERROR = -4;
|
||||
constexpr int16_t WRONG_PAYMENT_ID = -5;
|
||||
constexpr int16_t TRANSFER_TYPE = -6;
|
||||
constexpr int16_t DENIED = -7;
|
||||
constexpr int16_t WRONG_TXID = -8;
|
||||
constexpr int16_t WRONG_SIGNATURE = -9;
|
||||
constexpr int16_t WRONG_KEY_IMAGE = -10;
|
||||
constexpr int16_t WRONG_URI = -11;
|
||||
constexpr int16_t WRONG_INDEX = -12;
|
||||
constexpr int16_t NOT_OPEN = -13;
|
||||
constexpr int16_t ACCOUNT_INDEX_OUT_OF_BOUNDS = -14;
|
||||
constexpr int16_t ADDRESS_INDEX_OUT_OF_BOUNDS = -15;
|
||||
constexpr int16_t TX_NOT_POSSIBLE = -16;
|
||||
constexpr int16_t NOT_ENOUGH_MONEY = -17;
|
||||
constexpr int16_t TX_TOO_LARGE = -18;
|
||||
constexpr int16_t NOT_ENOUGH_OUTS_TO_MIX = -19;
|
||||
constexpr int16_t ZERO_DESTINATION = -20;
|
||||
constexpr int16_t WALLET_ALREADY_EXISTS = -21;
|
||||
constexpr int16_t INVALID_PASSWORD = -22;
|
||||
constexpr int16_t NO_WALLET_DIR = -23;
|
||||
constexpr int16_t NO_TXKEY = -24;
|
||||
constexpr int16_t WRONG_KEY = -25;
|
||||
constexpr int16_t BAD_HEX = -26;
|
||||
constexpr int16_t BAD_TX_METADATA = -27;
|
||||
constexpr int16_t ALREADY_MULTISIG = -28;
|
||||
constexpr int16_t WATCH_ONLY = -29;
|
||||
constexpr int16_t BAD_MULTISIG_INFO = -30;
|
||||
constexpr int16_t NOT_MULTISIG = -31;
|
||||
constexpr int16_t WRONG_LR = -32;
|
||||
constexpr int16_t THRESHOLD_NOT_REACHED = -33;
|
||||
constexpr int16_t BAD_MULTISIG_TX_DATA = -34;
|
||||
constexpr int16_t MULTISIG_SIGNATURE = -35;
|
||||
constexpr int16_t MULTISIG_SUBMISSION = -36;
|
||||
constexpr int16_t NOT_ENOUGH_UNLOCKED_MONEY = -37;
|
||||
constexpr int16_t NO_DAEMON_CONNECTION = -38;
|
||||
constexpr int16_t BAD_UNSIGNED_TX_DATA = -39;
|
||||
constexpr int16_t BAD_SIGNED_TX_DATA = -40;
|
||||
constexpr int16_t SIGNED_SUBMISSION = -41;
|
||||
constexpr int16_t SIGN_UNSIGNED = -42;
|
||||
constexpr int16_t NON_DETERMINISTIC = -43;
|
||||
constexpr int16_t INVALID_LOG_LEVEL = -44;
|
||||
constexpr int16_t ATTRIBUTE_NOT_FOUND = -45;
|
||||
|
||||
// Loki:
|
||||
#define WALLET_RPC_ERROR_CODE_BLINK_FAILED -1000
|
||||
#define WALLET_RPC_ERROR_CODE_HF_QUERY_FAILED -1001
|
||||
#define WALLET_RPC_ERROR_CODE_WRONG_LNS_TYPE -1002
|
||||
#define WALLET_RPC_ERROR_CODE_LNS_BAD_NAME -1003
|
||||
#define WALLET_RPC_ERROR_CODE_LNS_VALUE_TOO_LONG -1004
|
||||
#define WALLET_RPC_ERROR_CODE_LNS_VALUE_NOT_HEX -1005
|
||||
#define WALLET_RPC_ERROR_CODE_LNS_VALUE_LENGTH_NOT_EVEN -1006
|
||||
#define WALLET_RPC_ERROR_CODE_LNS_VALUE_DECRYPT_FAILED -1007
|
||||
constexpr int16_t BLINK_FAILED = -1000;
|
||||
constexpr int16_t HF_QUERY_FAILED = -1001;
|
||||
constexpr int16_t WRONG_LNS_TYPE = -1002;
|
||||
constexpr int16_t LNS_BAD_NAME = -1003;
|
||||
constexpr int16_t LNS_VALUE_TOO_LONG = -1004;
|
||||
constexpr int16_t LNS_VALUE_NOT_HEX = -1005;
|
||||
constexpr int16_t LNS_VALUE_LENGTH_NOT_EVEN = -1006;
|
||||
constexpr int16_t LNS_VALUE_DECRYPT_FAILED = -1007;
|
||||
|
||||
}
|
||||
|
|
|
@ -122,18 +122,6 @@ set_property(TARGET base58_fuzz_tests
|
|||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
add_executable(parse-url_fuzz_tests parse_url.cpp fuzzer.cpp)
|
||||
target_link_libraries(parse-url_fuzz_tests
|
||||
PRIVATE
|
||||
common
|
||||
epee
|
||||
Boost::program_options
|
||||
Boost::system
|
||||
extra)
|
||||
set_property(TARGET parse-url_fuzz_tests
|
||||
PROPERTY
|
||||
FOLDER "tests")
|
||||
|
||||
add_executable(levin_fuzz_tests levin.cpp fuzzer.cpp)
|
||||
target_link_libraries(levin_fuzz_tests
|
||||
PRIVATE
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright (c) 2017-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "include_base_utils.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "net/net_parse_helpers.h"
|
||||
#include "fuzzer.h"
|
||||
|
||||
class ParseURLFuzzer: public Fuzzer
|
||||
{
|
||||
public:
|
||||
ParseURLFuzzer() {}
|
||||
virtual int init();
|
||||
virtual int run(const std::string &filename);
|
||||
};
|
||||
|
||||
int ParseURLFuzzer::init()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ParseURLFuzzer::run(const std::string &filename)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
if (!epee::file_io_utils::load_file_to_string(filename, s))
|
||||
{
|
||||
std::cout << "Error: failed to load file " << filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
try
|
||||
{
|
||||
epee::net_utils::http::url_content url;
|
||||
epee::net_utils::parse_url(s, url);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Failed to load from binary: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
TRY_ENTRY();
|
||||
ParseURLFuzzer fuzzer;
|
||||
return run_fuzzer(argc, argv, fuzzer);
|
||||
CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
|
@ -49,7 +49,6 @@ add_executable(unit_tests
|
|||
get_xtype_from_string.cpp
|
||||
hashchain.cpp
|
||||
hmac_keccak.cpp
|
||||
http.cpp
|
||||
keccak.cpp
|
||||
levin.cpp
|
||||
logging.cpp
|
||||
|
|
|
@ -1,745 +0,0 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "gtest/gtest.h"
|
||||
#include "net/http_auth.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/fusion/adapted/std_pair.hpp>
|
||||
#include <boost/range/algorithm/find_if.hpp>
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/spirit/include/karma_char.hpp>
|
||||
#include <boost/spirit/include/karma_list.hpp>
|
||||
#include <boost/spirit/include/karma_generate.hpp>
|
||||
#include <boost/spirit/include/karma_right_alignment.hpp>
|
||||
#include <boost/spirit/include/karma_sequence.hpp>
|
||||
#include <boost/spirit/include/karma_string.hpp>
|
||||
#include <boost/spirit/include/karma_uint.hpp>
|
||||
#include <boost/spirit/include/qi_alternative.hpp>
|
||||
#include <boost/spirit/include/qi_char.hpp>
|
||||
#include <boost/spirit/include/qi_char_class.hpp>
|
||||
#include <boost/spirit/include/qi_difference.hpp>
|
||||
#include <boost/spirit/include/qi_eoi.hpp>
|
||||
#include <boost/spirit/include/qi_list.hpp>
|
||||
#include <boost/spirit/include/qi_parse.hpp>
|
||||
#include <boost/spirit/include/qi_plus.hpp>
|
||||
#include <boost/spirit/include/qi_sequence.hpp>
|
||||
#include <boost/spirit/include/qi_string.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "md5_l.h"
|
||||
#include "string_tools.h"
|
||||
#include "crypto/crypto.h"
|
||||
|
||||
namespace {
|
||||
namespace http = epee::net_utils::http;
|
||||
using fields = std::unordered_map<std::string, std::string>;
|
||||
using auth_responses = std::vector<fields>;
|
||||
|
||||
void rng(size_t len, uint8_t *ptr)
|
||||
{
|
||||
crypto::rand(len, ptr);
|
||||
}
|
||||
|
||||
std::string quoted(std::string str)
|
||||
{
|
||||
str.insert(str.begin(), '"');
|
||||
str.push_back('"');
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_fields(std::string& out, const fields& args)
|
||||
{
|
||||
namespace karma = boost::spirit::karma;
|
||||
karma::generate(
|
||||
std::back_inserter(out),
|
||||
(karma::string << " = " << karma::string) % " , ",
|
||||
args);
|
||||
}
|
||||
|
||||
std::string write_fields(const fields& args)
|
||||
{
|
||||
std::string out{};
|
||||
write_fields(out, args);
|
||||
return out;
|
||||
}
|
||||
|
||||
http::http_request_info make_request(const fields& args)
|
||||
{
|
||||
std::string out{" DIGEST "};
|
||||
write_fields(out, args);
|
||||
|
||||
http::http_request_info request{};
|
||||
request.m_http_method_str = "NOP";
|
||||
request.m_header_info.m_etc_fields.push_back(
|
||||
std::make_pair(u8"authorization", std::move(out))
|
||||
);
|
||||
return request;
|
||||
}
|
||||
|
||||
http::http_response_info make_response(const auth_responses& choices)
|
||||
{
|
||||
http::http_response_info response{};
|
||||
for (const auto& choice : choices)
|
||||
{
|
||||
std::string out{" DIGEST "};
|
||||
write_fields(out, choice);
|
||||
|
||||
response.m_header_info.m_etc_fields.push_back(
|
||||
std::make_pair(u8"WWW-authenticate", std::move(out))
|
||||
);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
bool has_same_fields(const auth_responses& in)
|
||||
{
|
||||
const std::vector<std::string> check{u8"nonce", u8"qop", u8"realm", u8"stale"};
|
||||
|
||||
auto current = in.begin();
|
||||
const auto end = in.end();
|
||||
if (current == end)
|
||||
return true;
|
||||
|
||||
++current;
|
||||
for ( ; current != end; ++current )
|
||||
{
|
||||
for (const auto& field : check)
|
||||
{
|
||||
const std::string& expected = in[0].at(field);
|
||||
const std::string& actual = current->at(field);
|
||||
EXPECT_EQ(expected, actual);
|
||||
if (expected != actual)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_unauthorized(const http::http_response_info& response)
|
||||
{
|
||||
EXPECT_EQ(401, response.m_response_code);
|
||||
EXPECT_STREQ(u8"Unauthorized", response.m_response_comment.c_str());
|
||||
EXPECT_STREQ(u8"text/html", response.m_mime_type.c_str());
|
||||
return response.m_response_code == 401 &&
|
||||
response.m_response_comment == u8"Unauthorized" &&
|
||||
response.m_mime_type == u8"text/html";
|
||||
}
|
||||
|
||||
fields parse_fields(const std::string& value)
|
||||
{
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
fields out{};
|
||||
const bool rc = qi::parse(
|
||||
value.begin(), value.end(),
|
||||
qi::lit(u8"Digest ") >> ((
|
||||
+qi::ascii::alpha >>
|
||||
qi::lit('=') >> (
|
||||
(qi::lit('"') >> +(qi::ascii::char_ - '"') >> qi::lit('"')) |
|
||||
+(qi::ascii::graph - qi::ascii::char_(u8"()<>@,;:\\\"/[]?={}"))
|
||||
)
|
||||
) % ','
|
||||
) >> qi::eoi,
|
||||
out
|
||||
);
|
||||
if (!rc)
|
||||
throw std::runtime_error{"Bad field given in HTTP header"};
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
auth_responses parse_response(const http::http_response_info& response)
|
||||
{
|
||||
auth_responses result{};
|
||||
|
||||
const auto end = response.m_additional_fields.end();
|
||||
for (auto current = response.m_additional_fields.begin();; ++current)
|
||||
{
|
||||
current = std::find_if(current, end, [] (const std::pair<std::string, std::string>& field) {
|
||||
return boost::equals(u8"WWW-authenticate", field.first);
|
||||
});
|
||||
|
||||
if (current == end)
|
||||
return result;
|
||||
|
||||
result.push_back(parse_fields(current->second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string md5_hex(const std::string& in)
|
||||
{
|
||||
md5::MD5_CTX ctx{};
|
||||
md5::MD5Init(std::addressof(ctx));
|
||||
md5::MD5Update(
|
||||
std::addressof(ctx),
|
||||
reinterpret_cast<const std::uint8_t*>(in.data()),
|
||||
in.size()
|
||||
);
|
||||
|
||||
std::array<std::uint8_t, 16> digest{{}};
|
||||
md5::MD5Final(digest.data(), std::addressof(ctx));
|
||||
return epee::string_tools::pod_to_hex(digest);
|
||||
}
|
||||
|
||||
std::string get_a1(const http::login& user, const fields& src)
|
||||
{
|
||||
const std::string& realm = src.at(u8"realm");
|
||||
return boost::join(
|
||||
std::vector<std::string>{user.username, realm, std::string(user.password.data(), user.password.size())}, u8":"
|
||||
);
|
||||
}
|
||||
|
||||
std::string get_a1(const http::login& user, const auth_responses& responses)
|
||||
{
|
||||
return get_a1(user, responses.at(0));
|
||||
}
|
||||
|
||||
std::string get_a1_sess(const http::login& user, const std::string& cnonce, const auth_responses& responses)
|
||||
{
|
||||
const std::string& nonce = responses.at(0).at(u8"nonce");
|
||||
return boost::join(
|
||||
std::vector<std::string>{md5_hex(get_a1(user, responses)), nonce, cnonce}, u8":"
|
||||
);
|
||||
}
|
||||
|
||||
std::string get_a2(const std::string& uri)
|
||||
{
|
||||
return boost::join(std::vector<std::string>{"NOP", uri}, u8":");
|
||||
}
|
||||
|
||||
std::string get_nc(std::uint32_t count)
|
||||
{
|
||||
namespace karma = boost::spirit::karma;
|
||||
std::string out;
|
||||
karma::generate(
|
||||
std::back_inserter(out),
|
||||
karma::right_align(8, '0')[karma::uint_generator<std::uint32_t, 16>{}],
|
||||
count
|
||||
);
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, NotRequired)
|
||||
{
|
||||
http::http_server_auth auth{}; // no rng here
|
||||
EXPECT_FALSE(auth.get_response(http::http_request_info{}));
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, MissingAuth)
|
||||
{
|
||||
http::http_server_auth auth{{"foo", "bar"}, rng};
|
||||
EXPECT_TRUE(bool(auth.get_response(http::http_request_info{})));
|
||||
{
|
||||
http::http_request_info request{};
|
||||
request.m_header_info.m_etc_fields.push_back({"\xFF", "\xFF"});
|
||||
EXPECT_TRUE(bool(auth.get_response(request)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, BadSyntax)
|
||||
{
|
||||
http::http_server_auth auth{{"foo", "bar"}, rng};
|
||||
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"algorithm", "fo\xFF"}}))));
|
||||
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce", "\"000\xFF\""}}))));
|
||||
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce \xFF =", "\"000\xFF\""}}))));
|
||||
EXPECT_TRUE(bool(auth.get_response(make_request({{u8" \xFF cnonce", "\"000\xFF\""}}))));
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, MD5)
|
||||
{
|
||||
http::login user{"foo", "bar"};
|
||||
http::http_server_auth auth{user, rng};
|
||||
|
||||
const auto response = auth.get_response(make_request(fields{}));
|
||||
ASSERT_TRUE(bool(response));
|
||||
EXPECT_TRUE(is_unauthorized(*response));
|
||||
|
||||
const auto fields = parse_response(*response);
|
||||
ASSERT_LE(2u, fields.size());
|
||||
EXPECT_TRUE(has_same_fields(fields));
|
||||
|
||||
const std::string& nonce = fields[0].at(u8"nonce");
|
||||
EXPECT_EQ(24, nonce.size());
|
||||
|
||||
const std::string uri{"/some_foo_thing"};
|
||||
|
||||
const std::string a1 = get_a1(user, fields);
|
||||
const std::string a2 = get_a2(uri);
|
||||
|
||||
const std::string auth_code = md5_hex(
|
||||
boost::join(std::vector<std::string>{md5_hex(a1), nonce, md5_hex(a2)}, u8":")
|
||||
);
|
||||
|
||||
const auto request = make_request({
|
||||
{u8"nonce", quoted(nonce)},
|
||||
{u8"realm", quoted(fields[0].at(u8"realm"))},
|
||||
{u8"response", quoted(auth_code)},
|
||||
{u8"uri", quoted(uri)},
|
||||
{u8"username", quoted(user.username)}
|
||||
});
|
||||
|
||||
EXPECT_FALSE(bool(auth.get_response(request)));
|
||||
|
||||
const auto response2 = auth.get_response(request);
|
||||
ASSERT_TRUE(bool(response2));
|
||||
EXPECT_TRUE(is_unauthorized(*response2));
|
||||
|
||||
const auto fields2 = parse_response(*response2);
|
||||
ASSERT_LE(2u, fields2.size());
|
||||
EXPECT_TRUE(has_same_fields(fields2));
|
||||
|
||||
EXPECT_NE(nonce, fields2[0].at(u8"nonce"));
|
||||
EXPECT_STREQ(u8"true", fields2[0].at(u8"stale").c_str());
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, MD5_sess)
|
||||
{
|
||||
constexpr const char cnonce[] = "not a good cnonce";
|
||||
|
||||
http::login user{"foo", "bar"};
|
||||
http::http_server_auth auth{user, rng};
|
||||
|
||||
const auto response = auth.get_response(make_request(fields{}));
|
||||
ASSERT_TRUE(bool(response));
|
||||
EXPECT_TRUE(is_unauthorized(*response));
|
||||
|
||||
const auto fields = parse_response(*response);
|
||||
ASSERT_LE(2u, fields.size());
|
||||
EXPECT_TRUE(has_same_fields(fields));
|
||||
|
||||
const std::string& nonce = fields[0].at(u8"nonce");
|
||||
EXPECT_EQ(24, nonce.size());
|
||||
|
||||
const std::string uri{"/some_foo_thing"};
|
||||
|
||||
const std::string a1 = get_a1_sess(user, cnonce, fields);
|
||||
const std::string a2 = get_a2(uri);
|
||||
|
||||
const std::string auth_code = md5_hex(
|
||||
boost::join(std::vector<std::string>{md5_hex(a1), nonce, md5_hex(a2)}, u8":")
|
||||
);
|
||||
|
||||
const auto request = make_request({
|
||||
{u8"algorithm", u8"md5-sess"},
|
||||
{u8"cnonce", quoted(cnonce)},
|
||||
{u8"nonce", quoted(nonce)},
|
||||
{u8"realm", quoted(fields[0].at(u8"realm"))},
|
||||
{u8"response", quoted(auth_code)},
|
||||
{u8"uri", quoted(uri)},
|
||||
{u8"username", quoted(user.username)}
|
||||
});
|
||||
|
||||
EXPECT_FALSE(bool(auth.get_response(request)));
|
||||
|
||||
const auto response2 = auth.get_response(request);
|
||||
ASSERT_TRUE(bool(response2));
|
||||
EXPECT_TRUE(is_unauthorized(*response2));
|
||||
|
||||
const auto fields2 = parse_response(*response2);
|
||||
ASSERT_LE(2u, fields2.size());
|
||||
EXPECT_TRUE(has_same_fields(fields2));
|
||||
|
||||
EXPECT_NE(nonce, fields2[0].at(u8"nonce"));
|
||||
EXPECT_STREQ(u8"true", fields2[0].at(u8"stale").c_str());
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, MD5_auth)
|
||||
{
|
||||
constexpr const char cnonce[] = "not a nonce";
|
||||
constexpr const char qop[] = "auth";
|
||||
|
||||
http::login user{"foo", "bar"};
|
||||
http::http_server_auth auth{user, rng};
|
||||
|
||||
const auto response = auth.get_response(make_request(fields{}));
|
||||
ASSERT_TRUE(bool(response));
|
||||
EXPECT_TRUE(is_unauthorized(*response));
|
||||
|
||||
const auto parsed = parse_response(*response);
|
||||
ASSERT_LE(2u, parsed.size());
|
||||
EXPECT_TRUE(has_same_fields(parsed));
|
||||
|
||||
const std::string& nonce = parsed[0].at(u8"nonce");
|
||||
EXPECT_EQ(24, nonce.size());
|
||||
|
||||
const std::string uri{"/some_foo_thing"};
|
||||
|
||||
const std::string a1 = get_a1(user, parsed);
|
||||
const std::string a2 = get_a2(uri);
|
||||
std::string nc = get_nc(1);
|
||||
|
||||
const auto generate_auth = [&] {
|
||||
return md5_hex(
|
||||
boost::join(
|
||||
std::vector<std::string>{md5_hex(a1), nonce, nc, cnonce, qop, md5_hex(a2)}, u8":"
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
fields args{
|
||||
{u8"algorithm", quoted(u8"md5")},
|
||||
{u8"cnonce", quoted(cnonce)},
|
||||
{u8"nc", nc},
|
||||
{u8"nonce", quoted(nonce)},
|
||||
{u8"qop", quoted(qop)},
|
||||
{u8"realm", quoted(parsed[0].at(u8"realm"))},
|
||||
{u8"response", quoted(generate_auth())},
|
||||
{u8"uri", quoted(uri)},
|
||||
{u8"username", quoted(user.username)}
|
||||
};
|
||||
|
||||
const auto request = make_request(args);
|
||||
EXPECT_FALSE(bool(auth.get_response(request)));
|
||||
|
||||
for (unsigned i = 2; i < 20; ++i)
|
||||
{
|
||||
nc = get_nc(i);
|
||||
args.at(u8"nc") = nc;
|
||||
args.at(u8"response") = quoted(generate_auth());
|
||||
EXPECT_FALSE(auth.get_response(make_request(args)));
|
||||
}
|
||||
|
||||
const auto replay = auth.get_response(request);
|
||||
ASSERT_TRUE(bool(replay));
|
||||
EXPECT_TRUE(is_unauthorized(*replay));
|
||||
|
||||
const auto parsed_replay = parse_response(*replay);
|
||||
ASSERT_LE(2u, parsed_replay.size());
|
||||
EXPECT_TRUE(has_same_fields(parsed_replay));
|
||||
|
||||
EXPECT_NE(nonce, parsed_replay[0].at(u8"nonce"));
|
||||
EXPECT_STREQ(u8"true", parsed_replay[0].at(u8"stale").c_str());
|
||||
}
|
||||
|
||||
TEST(HTTP_Server_Auth, MD5_sess_auth)
|
||||
{
|
||||
constexpr const char cnonce[] = "not a nonce";
|
||||
constexpr const char qop[] = "auth";
|
||||
|
||||
http::login user{"foo", "bar"};
|
||||
http::http_server_auth auth{user, rng};
|
||||
|
||||
const auto response = auth.get_response(make_request(fields{}));
|
||||
ASSERT_TRUE(bool(response));
|
||||
EXPECT_TRUE(is_unauthorized(*response));
|
||||
|
||||
const auto parsed = parse_response(*response);
|
||||
ASSERT_LE(2u, parsed.size());
|
||||
EXPECT_TRUE(has_same_fields(parsed));
|
||||
|
||||
const std::string& nonce = parsed[0].at(u8"nonce");
|
||||
EXPECT_EQ(24, nonce.size());
|
||||
|
||||
const std::string uri{"/some_foo_thing"};
|
||||
|
||||
const std::string a1 = get_a1_sess(user, cnonce, parsed);
|
||||
const std::string a2 = get_a2(uri);
|
||||
std::string nc = get_nc(1);
|
||||
|
||||
const auto generate_auth = [&] {
|
||||
return md5_hex(
|
||||
boost::join(
|
||||
std::vector<std::string>{md5_hex(a1), nonce, nc, cnonce, qop, md5_hex(a2)}, u8":"
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
fields args{
|
||||
{u8"algorithm", u8"md5-sess"},
|
||||
{u8"cnonce", quoted(cnonce)},
|
||||
{u8"nc", nc},
|
||||
{u8"nonce", quoted(nonce)},
|
||||
{u8"qop", qop},
|
||||
{u8"realm", quoted(parsed[0].at(u8"realm"))},
|
||||
{u8"response", quoted(generate_auth())},
|
||||
{u8"uri", quoted(uri)},
|
||||
{u8"username", quoted(user.username)}
|
||||
};
|
||||
|
||||
const auto request = make_request(args);
|
||||
EXPECT_FALSE(bool(auth.get_response(request)));
|
||||
|
||||
for (unsigned i = 2; i < 20; ++i)
|
||||
{
|
||||
nc = get_nc(i);
|
||||
args.at(u8"nc") = nc;
|
||||
args.at(u8"response") = quoted(generate_auth());
|
||||
EXPECT_FALSE(auth.get_response(make_request(args)));
|
||||
}
|
||||
|
||||
const auto replay = auth.get_response(request);
|
||||
ASSERT_TRUE(bool(replay));
|
||||
EXPECT_TRUE(is_unauthorized(*replay));
|
||||
|
||||
const auto parsed_replay = parse_response(*replay);
|
||||
ASSERT_LE(2u, parsed_replay.size());
|
||||
EXPECT_TRUE(has_same_fields(parsed_replay));
|
||||
|
||||
EXPECT_NE(nonce, parsed_replay[0].at(u8"nonce"));
|
||||
EXPECT_STREQ(u8"true", parsed_replay[0].at(u8"stale").c_str());
|
||||
}
|
||||
|
||||
|
||||
TEST(HTTP_Auth, DogFood)
|
||||
{
|
||||
const auto add_auth_field = [] (http::http_request_info& request, http::http_client_auth& client)
|
||||
{
|
||||
auto field = client.get_auth_field(request.m_http_method_str, request.m_URI);
|
||||
EXPECT_TRUE(bool(field));
|
||||
if (!field)
|
||||
return false;
|
||||
request.m_header_info.m_etc_fields.push_back(std::move(*field));
|
||||
return true;
|
||||
};
|
||||
|
||||
const http::login user{"some_user", "ultimate password"};
|
||||
|
||||
http::http_server_auth server{user, rng};
|
||||
http::http_client_auth client{user};
|
||||
|
||||
http::http_request_info request{};
|
||||
request.m_http_method_str = "GET";
|
||||
request.m_URI = "/FOO";
|
||||
|
||||
auto response = server.get_response(request);
|
||||
ASSERT_TRUE(bool(response));
|
||||
EXPECT_TRUE(is_unauthorized(*response));
|
||||
EXPECT_TRUE(response->m_header_info.m_etc_fields.empty());
|
||||
response->m_header_info.m_etc_fields = response->m_additional_fields;
|
||||
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response));
|
||||
EXPECT_TRUE(add_auth_field(request, client));
|
||||
EXPECT_FALSE(bool(server.get_response(request)));
|
||||
|
||||
for (unsigned i = 0; i < 1000; ++i)
|
||||
{
|
||||
request.m_http_method_str += std::to_string(i);
|
||||
request.m_header_info.m_etc_fields.clear();
|
||||
EXPECT_TRUE(add_auth_field(request, client));
|
||||
EXPECT_FALSE(bool(server.get_response(request)));
|
||||
}
|
||||
|
||||
// resetting counter should be rejected by server
|
||||
request.m_header_info.m_etc_fields.clear();
|
||||
client = http::http_client_auth{user};
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response));
|
||||
EXPECT_TRUE(add_auth_field(request, client));
|
||||
|
||||
auto response2 = server.get_response(request);
|
||||
ASSERT_TRUE(bool(response2));
|
||||
EXPECT_TRUE(is_unauthorized(*response2));
|
||||
EXPECT_TRUE(response2->m_header_info.m_etc_fields.empty());
|
||||
response2->m_header_info.m_etc_fields = response2->m_additional_fields;
|
||||
|
||||
const auth_responses parsed1 = parse_response(*response);
|
||||
const auth_responses parsed2 = parse_response(*response2);
|
||||
ASSERT_LE(1u, parsed1.size());
|
||||
ASSERT_LE(1u, parsed2.size());
|
||||
EXPECT_NE(parsed1[0].at(u8"nonce"), parsed2[0].at(u8"nonce"));
|
||||
|
||||
// with stale=true client should reset
|
||||
request.m_header_info.m_etc_fields.clear();
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response2));
|
||||
EXPECT_TRUE(add_auth_field(request, client));
|
||||
EXPECT_FALSE(bool(server.get_response(request)));
|
||||
|
||||
// client should give up if stale=false
|
||||
EXPECT_EQ(http::http_client_auth::kBadPassword, client.handle_401(*response));
|
||||
}
|
||||
|
||||
TEST(HTTP_Client_Auth, Unavailable)
|
||||
{
|
||||
http::http_client_auth auth{};
|
||||
EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(http::http_response_info{}));
|
||||
EXPECT_FALSE(bool(auth.get_auth_field("GET", "/file")));
|
||||
}
|
||||
|
||||
TEST(HTTP_Client_Auth, MissingAuthenticate)
|
||||
{
|
||||
http::http_client_auth auth{{"foo", "bar"}};
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(http::http_response_info{}));
|
||||
EXPECT_FALSE(bool(auth.get_auth_field("POST", "/\xFFname")));
|
||||
{
|
||||
http::http_response_info response{};
|
||||
response.m_additional_fields.push_back({"\xFF", "\xFF"});
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(response));
|
||||
}
|
||||
EXPECT_FALSE(bool(auth.get_auth_field("DELETE", "/file/does/not/exist")));
|
||||
}
|
||||
|
||||
TEST(HTTP_Client_Auth, BadSyntax)
|
||||
{
|
||||
http::http_client_auth auth{{"foo", "bar"}};
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(make_response({{{u8"realm", "fo\xFF"}}})));
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(make_response({{{u8"domain", "fo\xFF"}}})));
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(make_response({{{u8"nonce", "fo\xFF"}}})));
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(make_response({{{u8"nonce \xFF =", "fo\xFF"}}})));
|
||||
EXPECT_EQ(http::http_client_auth::kParseFailure, auth.handle_401(make_response({{{u8" \xFF nonce", "fo\xFF"}}})));
|
||||
}
|
||||
|
||||
TEST(HTTP_Client_Auth, MD5)
|
||||
{
|
||||
constexpr char method[] = "NOP";
|
||||
constexpr char nonce[] = "some crazy nonce";
|
||||
constexpr char realm[] = "the only realm";
|
||||
constexpr char uri[] = "/some_file";
|
||||
|
||||
const http::login user{"foo", "bar"};
|
||||
http::http_client_auth auth{user};
|
||||
|
||||
auto response = make_response({
|
||||
{
|
||||
{u8"domain", quoted("ignored")},
|
||||
{u8"nonce", quoted(nonce)},
|
||||
{u8"REALM", quoted(realm)}
|
||||
},
|
||||
{
|
||||
{u8"algorithm", "null"},
|
||||
{u8"domain", quoted("ignored")},
|
||||
{u8"nonce", quoted(std::string{"e"} + nonce)},
|
||||
{u8"realm", quoted(std::string{"e"} + realm)}
|
||||
},
|
||||
});
|
||||
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
|
||||
const auto auth_field = auth.get_auth_field(method, uri);
|
||||
ASSERT_TRUE(bool(auth_field));
|
||||
|
||||
const auto parsed = parse_fields(auth_field->second);
|
||||
EXPECT_STREQ(u8"Authorization", auth_field->first.c_str());
|
||||
EXPECT_EQ(parsed.end(), parsed.find(u8"opaque"));
|
||||
EXPECT_EQ(parsed.end(), parsed.find(u8"qop"));
|
||||
EXPECT_EQ(parsed.end(), parsed.find(u8"nc"));
|
||||
EXPECT_STREQ(u8"MD5", parsed.at(u8"algorithm").c_str());
|
||||
EXPECT_STREQ(nonce, parsed.at(u8"nonce").c_str());
|
||||
EXPECT_STREQ(uri, parsed.at(u8"uri").c_str());
|
||||
EXPECT_EQ(user.username, parsed.at(u8"username"));
|
||||
EXPECT_STREQ(realm, parsed.at(u8"realm").c_str());
|
||||
|
||||
const std::string a1 = get_a1(user, parsed);
|
||||
const std::string a2 = get_a2(uri);
|
||||
const std::string auth_code = md5_hex(
|
||||
boost::join(std::vector<std::string>{md5_hex(a1), nonce, md5_hex(a2)}, u8":")
|
||||
);
|
||||
EXPECT_TRUE(boost::iequals(auth_code, parsed.at(u8"response")));
|
||||
{
|
||||
const auto auth_field_dup = auth.get_auth_field(method, uri);
|
||||
ASSERT_TRUE(bool(auth_field_dup));
|
||||
EXPECT_EQ(*auth_field, *auth_field_dup);
|
||||
}
|
||||
|
||||
|
||||
EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(response));
|
||||
response.m_header_info.m_etc_fields.front().second.append(u8"," + write_fields({{u8"stale", u8"TRUE"}}));
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
|
||||
}
|
||||
|
||||
TEST(HTTP_Client_Auth, MD5_auth)
|
||||
{
|
||||
constexpr char cnonce[] = "";
|
||||
constexpr char method[] = "NOP";
|
||||
constexpr char nonce[] = "some crazy nonce";
|
||||
constexpr char opaque[] = "this is the opaque";
|
||||
constexpr char qop[] = u8"ignore,auth,ignore";
|
||||
constexpr char realm[] = "the only realm";
|
||||
constexpr char uri[] = "/some_file";
|
||||
|
||||
const http::login user{"foo", "bar"};
|
||||
http::http_client_auth auth{user};
|
||||
|
||||
auto response = make_response({
|
||||
{
|
||||
{u8"algorithm", u8"MD5"},
|
||||
{u8"domain", quoted("ignored")},
|
||||
{u8"nonce", quoted(std::string{"e"} + nonce)},
|
||||
{u8"realm", quoted(std::string{"e"} + realm)},
|
||||
{u8"qop", quoted("some,thing,to,ignore")}
|
||||
},
|
||||
{
|
||||
{u8"algorIthm", quoted(u8"md5")},
|
||||
{u8"domain", quoted("ignored")},
|
||||
{u8"noNce", quoted(nonce)},
|
||||
{u8"opaque", quoted(opaque)},
|
||||
{u8"realm", quoted(realm)},
|
||||
{u8"QoP", quoted(qop)}
|
||||
}
|
||||
});
|
||||
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
|
||||
|
||||
for (unsigned i = 1; i < 1000; ++i)
|
||||
{
|
||||
const std::string nc = get_nc(i);
|
||||
|
||||
const auto auth_field = auth.get_auth_field(method, uri);
|
||||
ASSERT_TRUE(bool(auth_field));
|
||||
|
||||
const auto parsed = parse_fields(auth_field->second);
|
||||
EXPECT_STREQ(u8"Authorization", auth_field->first.c_str());
|
||||
EXPECT_STREQ(u8"MD5", parsed.at(u8"algorithm").c_str());
|
||||
EXPECT_STREQ(nonce, parsed.at(u8"nonce").c_str());
|
||||
EXPECT_STREQ(opaque, parsed.at(u8"opaque").c_str());
|
||||
EXPECT_STREQ(u8"auth", parsed.at(u8"qop").c_str());
|
||||
EXPECT_STREQ(uri, parsed.at(u8"uri").c_str());
|
||||
EXPECT_EQ(user.username, parsed.at(u8"username"));
|
||||
EXPECT_STREQ(realm, parsed.at(u8"realm").c_str());
|
||||
EXPECT_EQ(nc, parsed.at(u8"nc"));
|
||||
|
||||
const std::string a1 = get_a1(user, parsed);
|
||||
const std::string a2 = get_a2(uri);
|
||||
const std::string auth_code = md5_hex(
|
||||
boost::join(std::vector<std::string>{md5_hex(a1), nonce, nc, cnonce, u8"auth", md5_hex(a2)}, u8":")
|
||||
);
|
||||
EXPECT_TRUE(boost::iequals(auth_code, parsed.at(u8"response")));
|
||||
}
|
||||
|
||||
EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(response));
|
||||
response.m_header_info.m_etc_fields.back().second.append(u8"," + write_fields({{u8"stale", u8"trUe"}}));
|
||||
EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response));
|
||||
}
|
||||
|
||||
|
||||
TEST(HTTP, Add_Field)
|
||||
{
|
||||
std::string str{"leading text"};
|
||||
epee::net_utils::http::add_field(str, "foo", "bar");
|
||||
epee::net_utils::http::add_field(str, std::string("bar"), std::string("foo"));
|
||||
epee::net_utils::http::add_field(str, {"moarbars", "moarfoo"});
|
||||
|
||||
EXPECT_STREQ("leading textfoo: bar\r\nbar: foo\r\nmoarbars: moarfoo\r\n", str.c_str());
|
||||
}
|
Loading…
Reference in New Issue