Monero
levin_abstract_invoke2.h
Go to the documentation of this file.
1 // Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of the Andrey N. Sabelnikov nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 //
26 
27 #pragma once
28 
30 #include <boost/utility/string_ref.hpp>
31 #include <boost/utility/value_init.hpp>
32 #include <functional>
33 #include "byte_slice.h"
34 #include "span.h"
35 #include "net/levin_base.h"
36 
37 #undef MONERO_DEFAULT_LOG_CATEGORY
38 #define MONERO_DEFAULT_LOG_CATEGORY "net"
39 
40 template<typename context_t>
41 void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, const char *category);
42 
43 template<typename context_t>
44 void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, int command);
45 
46 namespace
47 {
48  static const constexpr epee::serialization::portable_storage::limits_t default_levin_limits = {
49  8192, // objects
50  16384, // fields
51  16384, // strings
52  };
53 }
54 
55 namespace epee
56 {
57  namespace net_utils
58  {
59  template<class t_arg, class t_result, class t_transport>
60  bool invoke_remote_command2(const epee::net_utils::connection_context_base context, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport)
61  {
62  const boost::uuids::uuid &conn_id = context.m_connection_id;
64  out_struct.store(stg);
65  levin::message_writer to_send{16 * 1024};
66  std::string buff_to_recv;
67  stg.store_to_binary(to_send.buffer);
68 
69  int res = transport.invoke(command, std::move(to_send), buff_to_recv, conn_id);
70  if( res <=0 )
71  {
72  LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res);
73  return false;
74  }
75  typename serialization::portable_storage stg_ret;
76  if(!stg_ret.load_from_binary(buff_to_recv, &default_levin_limits))
77  {
78  on_levin_traffic(context, true, false, true, buff_to_recv.size(), command);
79  LOG_ERROR("Failed to load_from_binary on command " << command);
80  return false;
81  }
82  on_levin_traffic(context, true, false, false, buff_to_recv.size(), command);
83  return result_struct.load(stg_ret);
84  }
85 
86  template<class t_result, class t_arg, class callback_t, class t_transport>
87  bool async_invoke_remote_command2(const epee::net_utils::connection_context_base &context, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
88  {
89  const boost::uuids::uuid &conn_id = context.m_connection_id;
91  const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation
92  levin::message_writer to_send{16 * 1024};
93  stg.store_to_binary(to_send.buffer);
94  int res = transport.invoke_async(command, std::move(to_send), conn_id, [cb, command](int code, const epee::span<const uint8_t> buff, typename t_transport::connection_context& context)->bool
95  {
96  t_result result_struct = AUTO_VAL_INIT(result_struct);
97  if( code <=0 )
98  {
99  if (!buff.empty())
100  on_levin_traffic(context, true, false, true, buff.size(), command);
101  LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code);
102  cb(code, result_struct, context);
103  return false;
104  }
106  if(!stg_ret.load_from_binary(buff, &default_levin_limits))
107  {
108  on_levin_traffic(context, true, false, true, buff.size(), command);
109  LOG_ERROR("Failed to load_from_binary on command " << command);
110  cb(LEVIN_ERROR_FORMAT, result_struct, context);
111  return false;
112  }
113  if (!result_struct.load(stg_ret))
114  {
115  on_levin_traffic(context, true, false, true, buff.size(), command);
116  LOG_ERROR("Failed to load result struct on command " << command);
117  cb(LEVIN_ERROR_FORMAT, result_struct, context);
118  return false;
119  }
120  on_levin_traffic(context, true, false, false, buff.size(), command);
121  cb(code, result_struct, context);
122  return true;
123  }, inv_timeout);
124  if( res <=0 )
125  {
126  LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res);
127  return false;
128  }
129  return true;
130  }
131 
132  template<class t_arg, class t_transport>
133  bool notify_remote_command2(const typename t_transport::connection_context &context, int command, const t_arg& out_struct, t_transport& transport)
134  {
135  const boost::uuids::uuid &conn_id = context.m_connection_id;
137  out_struct.store(stg);
138  levin::message_writer to_send;
139  stg.store_to_binary(to_send.buffer);
140 
141  int res = transport.send(to_send.finalize_notify(command), conn_id);
142  if(res <=0 )
143  {
144  MERROR("Failed to notify command " << command << " return code " << res);
145  return false;
146  }
147  return true;
148  }
149  //----------------------------------------------------------------------------------------------------
150  //----------------------------------------------------------------------------------------------------
151  template<class t_owner, class t_in_type, class t_out_type, class t_context, class callback_t>
152  int buff_to_t_adapter(int command, const epee::span<const uint8_t> in_buff, byte_stream& buff_out, callback_t cb, t_context& context )
153  {
155  if(!strg.load_from_binary(in_buff, &default_levin_limits))
156  {
157  on_levin_traffic(context, false, false, true, in_buff.size(), command);
158  LOG_ERROR("Failed to load_from_binary in command " << command);
159  return -1;
160  }
161  boost::value_initialized<t_in_type> in_struct;
162  boost::value_initialized<t_out_type> out_struct;
163 
164  if (!static_cast<t_in_type&>(in_struct).load(strg))
165  {
166  on_levin_traffic(context, false, false, true, in_buff.size(), command);
167  LOG_ERROR("Failed to load in_struct in command " << command);
168  return -1;
169  }
170  on_levin_traffic(context, false, false, false, in_buff.size(), command);
171  int res = cb(command, static_cast<t_in_type&>(in_struct), static_cast<t_out_type&>(out_struct), context);
173  static_cast<t_out_type&>(out_struct).store(strg_out);
174 
175  if(!strg_out.store_to_binary(buff_out))
176  {
177  LOG_ERROR("Failed to store_to_binary in command" << command);
178  return -1;
179  }
180 
181  return res;
182  }
183 
184  template<class t_owner, class t_in_type, class t_context, class callback_t>
185  int buff_to_t_adapter(t_owner* powner, int command, const epee::span<const uint8_t> in_buff, callback_t cb, t_context& context)
186  {
188  if(!strg.load_from_binary(in_buff, &default_levin_limits))
189  {
190  on_levin_traffic(context, false, false, true, in_buff.size(), command);
191  LOG_ERROR("Failed to load_from_binary in notify " << command);
192  return -1;
193  }
194  boost::value_initialized<t_in_type> in_struct;
195  if (!static_cast<t_in_type&>(in_struct).load(strg))
196  {
197  on_levin_traffic(context, false, false, true, in_buff.size(), command);
198  LOG_ERROR("Failed to load in_struct in notify " << command);
199  return -1;
200  }
201  on_levin_traffic(context, false, false, false, in_buff.size(), command);
202  return cb(command, in_struct, context);
203  }
204 
205 #define CHAIN_LEVIN_INVOKE_MAP2(context_type) \
206  int invoke(int command, const epee::span<const uint8_t> in_buff, epee::byte_stream& buff_out, context_type& context) \
207  { \
208  bool handled = false; \
209  return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \
210  }
211 
212 #define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \
213  int notify(int command, const epee::span<const uint8_t> in_buff, context_type& context) \
214  { \
215  bool handled = false; epee::byte_stream fake_str; \
216  return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \
217  }
218 
219 
220 #define CHAIN_LEVIN_INVOKE_MAP() \
221  int invoke(int command, const epee::span<const uint8_t> in_buff, epee::byte_stream& buff_out, epee::net_utils::connection_context_base& context) \
222  { \
223  bool handled = false; \
224  return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \
225  }
226 
227 #define CHAIN_LEVIN_NOTIFY_MAP() \
228  int notify(int command, const epee::span<const uint8_t> in_buff, epee::net_utils::connection_context_base& context) \
229  { \
230  bool handled = false; std::string fake_str;\
231  return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \
232  }
233 
234 #define CHAIN_LEVIN_NOTIFY_STUB() \
235  int notify(int command, const epee::span<const uint8_t> in_buff, epee::net_utils::connection_context_base& context) \
236  { \
237  return -1; \
238  }
239 
240 #define BEGIN_INVOKE_MAP2(owner_type) \
241  template <class t_context> int handle_invoke_map(bool is_notify, int command, const epee::span<const uint8_t> in_buff, epee::byte_stream& buff_out, t_context& context, bool& handled) \
242  { \
243  try { \
244  typedef owner_type internal_owner_type_name;
245 
246 #define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \
247  if(!is_notify && command_id == command) \
248  {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in, typename_out>(this, command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);}
249 
250 #define HANDLE_INVOKE_T2(COMMAND, func) \
251  if(!is_notify && COMMAND::ID == command) \
252  {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename COMMAND::request, typename COMMAND::response>(command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);}
253 
254 
255 #define HANDLE_NOTIFY2(command_id, func, type_name_in) \
256  if(is_notify && command_id == command) \
257  {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in>(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);}
258 
259 #define HANDLE_NOTIFY_T2(NOTIFY, func) \
260  if(is_notify && NOTIFY::ID == command) \
261  {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename NOTIFY::request>(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);}
262 
263 
264 #define CHAIN_INVOKE_MAP2(func) \
265  { \
266  int res = func(is_notify, command, in_buff, buff_out, context, handled); \
267  if(handled) \
268  return res; \
269  }
270 
271 #define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \
272  { \
273  int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \
274  if(handled) \
275  return res; \
276  }
277 
278 #define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \
279  { \
280  int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast<context_type>(context), handled); \
281  if(handled) return res; \
282  }
283 
284 
285 #define END_INVOKE_MAP2() \
286  LOG_ERROR("Unknown command:" << command); \
287  on_levin_traffic(context, false, false, true, in_buff.size(), "invalid-command"); \
288  return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \
289  } \
290  catch (const std::exception &e) { \
291  MERROR("Error in handle_invoke_map: " << e.what()); \
292  return LEVIN_ERROR_CONNECTION_TIMEDOUT; /* seems kinda appropriate */ \
293  } \
294  }
295 
296  }
297 }
298 
const char * res
Definition: hmac_keccak.cpp:42
bool invoke_remote_command2(const epee::net_utils::connection_context_base context, int command, const t_arg &out_struct, t_result &result_struct, t_transport &transport)
Definition: levin_abstract_invoke2.h:60
bool notify_remote_command2(const typename t_transport::connection_context &context, int command, const t_arg &out_struct, t_transport &transport)
Definition: levin_abstract_invoke2.h:133
#define LEVIN_ERROR_FORMAT
Definition: levin_base.h:109
int buff_to_t_adapter(int command, const epee::span< const uint8_t > in_buff, byte_stream &buff_out, callback_t cb, t_context &context)
Definition: levin_abstract_invoke2.h:152
::std::string string
Definition: gtest-port.h:1097
void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, const char *category)
Definition: levin_protocol_handler_async.h:54
A partial drop-in replacement for std::ostream.
Definition: byte_stream.h:57
Definition: net_utils_base.h:366
Non-owning sequence of data. Does not deep copy.
Definition: span.h:54
Definition: portable_storage.h:45
Provides space for levin (p2p) header, so that payload can be sent without copy.
Definition: levin_base.h:131
bool async_invoke_remote_command2(const epee::net_utils::connection_context_base &context, int command, const t_arg &out_struct, t_transport &transport, const callback_t &cb, size_t inv_timeout=LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
Definition: levin_abstract_invoke2.h:87
constexpr std::size_t size() const noexcept
Definition: span.h:109
const portMappingElt code
Definition: portlistingparse.c:22
const char * uuid
Definition: minissdp.c:598
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
Definition: zmq.h:105
void load(Archive &a, std::unordered_map< h_key, hval > &x, const boost::serialization::version_type ver)
Definition: unordered_containers_boost_serialization.h:54
byte_slice finalize_notify(uint32_t command)
Definition: levin_base.h:152
TODO: (mj-xmr) This will be reduced in an another PR.
Definition: byte_slice.h:39
const T & move(const T &t)
Definition: gtest-port.h:1317
#define AUTO_VAL_INIT(v)
Definition: misc_language.h:36
constexpr bool empty() const noexcept
Definition: span.h:107
Definition: portable_storage.h:52
bool store_to_binary(byte_slice &target, std::size_t initial_buffer_size=8192)
Definition: portable_storage.cpp:46
#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED
Definition: levin_base.h:75
bool load_from_binary(const epee::span< const uint8_t > target, const limits_t *limits=nullptr)
Definition: portable_storage.cpp:87
error
Tracks LMDB error codes.
Definition: error.h:44
std::shared_ptr< Transport > transport(const std::string &path)
Definition: transport.cpp:1231
byte_stream buffer
Has space for levin header until a finalize method is used.
Definition: levin_base.h:159