Electroneum
Loading...
Searching...
No Matches
transport.hpp
Go to the documentation of this file.
1// Copyright (c) 2017-Present, Electroneum
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification, are
6// permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this list of
9// conditions and the following disclaimer.
10//
11// 2. Redistributions in binary form must reproduce the above copyright notice, this list
12// of conditions and the following disclaimer in the documentation and/or other
13// materials provided with the distribution.
14//
15// 3. Neither the name of the copyright holder nor the names of its contributors may be
16// used to endorse or promote products derived from this software without specific
17// prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29
30#ifndef ELECTRONEUM_TRANSPORT_H
31#define ELECTRONEUM_TRANSPORT_H
32
33
34#include <boost/asio.hpp>
35#include <boost/asio/deadline_timer.hpp>
36#include <boost/array.hpp>
37#include <boost/utility/string_ref.hpp>
38
39#include <typeinfo>
40#include <type_traits>
41#include "net/http_client.h"
42
43#include "rapidjson/document.h"
44#include "rapidjson/writer.h"
46
47#include "exceptions.hpp"
48#include "trezor_defs.hpp"
49#include "messages_map.hpp"
50
51#include "messages/messages.pb.h"
52#include "messages/messages-common.pb.h"
53#include "messages/messages-management.pb.h"
54#include "messages/messages-electroneum.pb.h"
55
56namespace hw {
57namespace trezor {
58
59 using json = rapidjson::Document;
60 using json_val = rapidjson::Value;
61 namespace http = epee::net_utils::http;
62
63 const std::string DEFAULT_BRIDGE = "127.0.0.1:21325";
64
65 uint64_t pack_version(uint32_t major, uint32_t minor=0, uint32_t patch=0);
66
67 // Base HTTP comm serialization.
68 bool t_serialize(const std::string & in, std::string & out);
69 bool t_serialize(const json_val & in, std::string & out);
70 std::string t_serialize(const json_val & in);
71
72 bool t_deserialize(const std::string & in, std::string & out);
73 bool t_deserialize(const std::string & in, json & out);
74
75 // Flexible json serialization. HTTP client tailored for bridge API
76 template<class t_req, class t_res, class t_transport>
77 bool invoke_bridge_http(const boost::string_ref uri, const t_req & out_struct, t_res & result_struct, t_transport& transport, const boost::string_ref method = "POST", std::chrono::milliseconds timeout = std::chrono::seconds(180))
78 {
79 std::string req_param;
80 t_serialize(out_struct, req_param);
81
82 http::fields_list additional_params;
83 additional_params.push_back(std::make_pair("Origin","https://electroneum.trezor.io"));
84 additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8"));
85
86 const http::http_response_info* pri = nullptr;
87 if(!transport.invoke(uri, method, req_param, timeout, &pri, std::move(additional_params)))
88 {
89 MERROR("Failed to invoke http request to " << uri);
90 return false;
91 }
92
93 if(!pri)
94 {
95 MERROR("Failed to invoke http request to " << uri << ", internal error (null response ptr)");
96 return false;
97 }
98
99 if(pri->m_response_code != 200)
100 {
101 MERROR("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code
102 << " Response Body: " << pri->m_body);
103 return false;
104 }
105
106 return t_deserialize(pri->m_body, result_struct);
107 }
108
109 // Forward decl
110 class Transport;
111 class Protocol;
112
113 // Communication protocol
114 class Protocol {
115 public:
116 Protocol() = default;
117 virtual ~Protocol() = default;
119 virtual void session_end(Transport & transport){ };
120 virtual void write(Transport & transport, const google::protobuf::Message & req)= 0;
121 virtual void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr)= 0;
122 };
123
124 class ProtocolV1 : public Protocol {
125 public:
126 ProtocolV1() = default;
127 virtual ~ProtocolV1() = default;
128
129 void write(Transport & transport, const google::protobuf::Message & req) override;
130 void read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
131 };
132
133
134 // Base transport
135 typedef std::vector<std::shared_ptr<Transport>> t_transport_vect;
136
137 class Transport {
138 public:
139 Transport();
140 virtual ~Transport() = default;
141
142 virtual bool ping() { return false; };
143 virtual std::string get_path() const { return ""; };
144 virtual void enumerate(t_transport_vect & res){};
145 virtual void open(){};
146 virtual void close(){};
147 virtual void write(const google::protobuf::Message & req) =0;
148 virtual void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) =0;
149 virtual std::shared_ptr<Transport> find_debug() { return nullptr; };
150
151 virtual void write_chunk(const void * buff, size_t size) { };
152 virtual size_t read_chunk(void * buff, size_t size) { return 0; };
153 virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; }
154 protected:
156
157 virtual bool pre_open();
158 virtual bool pre_close();
159 };
160
161 // Bridge transport
162 class BridgeTransport : public Transport {
163 public:
165 boost::optional<std::string> device_path = boost::none,
166 boost::optional<std::string> bridge_host = boost::none);
167
168 virtual ~BridgeTransport() = default;
169
170 static const char * PATH_PREFIX;
171
172 std::string get_path() const override;
173 void enumerate(t_transport_vect & res) override;
174
175 void open() override;
176 void close() override;
177
178 void write(const google::protobuf::Message &req) override;
179 void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
180
181 const boost::optional<json> & device_info() const;
182 std::ostream& dump(std::ostream& o) const override;
183
184 private:
186 std::string m_bridge_host;
187 boost::optional<std::string> m_device_path;
188 boost::optional<std::string> m_session;
189 boost::optional<std::string> m_response;
190 boost::optional<json> m_device_info;
191 };
192
193 // UdpTransport transport
194 using boost::asio::ip::udp;
195
196 class UdpTransport : public Transport {
197 public:
198
199 explicit UdpTransport(
200 boost::optional<std::string> device_path=boost::none,
201 boost::optional<std::shared_ptr<Protocol>> proto=boost::none);
202
203 virtual ~UdpTransport() = default;
204
205 static const char * PATH_PREFIX;
206 static const char * DEFAULT_HOST;
207 static const int DEFAULT_PORT;
208
209 bool ping() override;
210 std::string get_path() const override;
211 void enumerate(t_transport_vect & res) override;
212
213 void open() override;
214 void close() override;
215 std::shared_ptr<Transport> find_debug() override;
216
217 void write(const google::protobuf::Message &req) override;
218 void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
219
220 void write_chunk(const void * buff, size_t size) override;
221 size_t read_chunk(void * buff, size_t size) override;
222
223 std::ostream& dump(std::ostream& o) const override;
224
225 private:
226 void require_socket();
227 ssize_t receive(void * buff, size_t size, boost::system::error_code * error_code=nullptr, bool no_throw=false, boost::posix_time::time_duration timeout=boost::posix_time::seconds(10));
228 void check_deadline();
229 static void handle_receive(const boost::system::error_code& ec, std::size_t length,
230 boost::system::error_code* out_ec, std::size_t* out_length);
231 bool ping_int(boost::posix_time::time_duration timeout=boost::posix_time::milliseconds(1500));
232
233 std::shared_ptr<Protocol> m_proto;
234 std::string m_device_host;
235 int m_device_port;
236
237 std::unique_ptr<udp::socket> m_socket;
238 boost::asio::io_service m_io_service;
239 boost::asio::deadline_timer m_deadline;
240 udp::endpoint m_endpoint;
241 };
242
243#ifdef WITH_DEVICE_TREZOR_WEBUSB
244#include <libusb.h>
245
246 class WebUsbTransport : public Transport {
247 public:
248
249 explicit WebUsbTransport(
250 boost::optional<libusb_device_descriptor*> descriptor = boost::none,
251 boost::optional<std::shared_ptr<Protocol>> proto = boost::none
252 );
253
254 virtual ~WebUsbTransport();
255
256 static const char * PATH_PREFIX;
257
258 std::string get_path() const override;
259 void enumerate(t_transport_vect & res) override;
260
261 void open() override;
262 void close() override;
263 std::shared_ptr<Transport> find_debug() override;
264
265 void write(const google::protobuf::Message &req) override;
266 void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override;
267
268 void write_chunk(const void * buff, size_t size) override;
269 size_t read_chunk(void * buff, size_t size) override;
270
271 std::ostream& dump(std::ostream& o) const override;
272
273 private:
274 void require_device() const;
275 void require_connected() const;
276 int get_interface() const;
277 unsigned char get_endpoint() const;
278
279 std::shared_ptr<Protocol> m_proto;
280
281 libusb_context *m_usb_session;
282 libusb_device *m_usb_device;
283 libusb_device_handle *m_usb_device_handle;
284 std::unique_ptr<libusb_device_descriptor> m_usb_device_desc;
285 std::vector<uint8_t> m_port_numbers;
286 int m_bus_id;
287 int m_device_addr;
288
289#ifdef WITH_TREZOR_DEBUGGING
290 bool m_debug_mode;
291#endif
292 };
293
294#endif
295
296 //
297 // General helpers
298 //
299
304
309
313 std::shared_ptr<Transport> transport(const std::string & path);
314
318 template<class t_transport=Transport>
319 std::shared_ptr<t_transport> transport_typed(const std::string & path){
320 auto t = transport(path);
321 if (!t){
322 return nullptr;
323 }
324
325 return std::dynamic_pointer_cast<t_transport>(t);
326 }
327
328 // Exception carries unexpected message being received
329 namespace exc {
331 protected:
332 hw::trezor::messages::MessageType recvType;
333 std::shared_ptr<google::protobuf::Message> recvMsg;
334
335 public:
337 UnexpectedMessageException(): ProtocolException("Trezor returned unexpected message") {};
338 UnexpectedMessageException(hw::trezor::messages::MessageType recvType,
339 const std::shared_ptr<google::protobuf::Message> & recvMsg)
341 reason = std::string("Trezor returned unexpected message: ") + std::to_string(recvType);
342 }
343 };
344 }
345
349 [[ noreturn ]] void throw_failure_exception(const messages::common::Failure * failure);
350
355 public:
357 GenericMessage(messages::MessageType m_type, const std::shared_ptr<google::protobuf::Message> &m_msg);
358 bool empty() const { return m_empty; }
359
360 hw::trezor::messages::MessageType m_type;
361 std::shared_ptr<google::protobuf::Message> m_msg;
363 };
364
371 template<class t_message=google::protobuf::Message>
372 std::shared_ptr<t_message>
373 exchange_message(Transport & transport, const google::protobuf::Message & req,
374 boost::optional<messages::MessageType> resp_type = boost::none)
375 {
376 // Require strictly protocol buffers response in the template.
377 BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value);
378
379 // Write the request
380 transport.write(req);
381
382 // Read the response
383 std::shared_ptr<google::protobuf::Message> msg_resp;
384 hw::trezor::messages::MessageType msg_resp_type;
385 transport.read(msg_resp, &msg_resp_type);
386
387 // Determine type of expected message response
388 messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>();
389
390 if (msg_resp_type == required_type) {
391 return message_ptr_retype<t_message>(msg_resp);
392 } else if (msg_resp_type == messages::MessageType_Failure){
393 throw_failure_exception(dynamic_cast<messages::common::Failure*>(msg_resp.get()));
394 } else {
395 throw exc::UnexpectedMessageException(msg_resp_type, msg_resp);
396 }
397 }
398
399 std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t);
400 std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t);
401}}
402
403
404#endif //ELECTRONEUM_TRANSPORT_H
virtual ~BridgeTransport()=default
static const char * PATH_PREFIX
void write(const google::protobuf::Message &req) override
BridgeTransport(boost::optional< std::string > device_path=boost::none, boost::optional< std::string > bridge_host=boost::none)
const boost::optional< json > & device_info() const
std::string get_path() const override
void enumerate(t_transport_vect &res) override
void read(std::shared_ptr< google::protobuf::Message > &msg, messages::MessageType *msg_type=nullptr) override
std::ostream & dump(std::ostream &o) const override
std::shared_ptr< google::protobuf::Message > m_msg
hw::trezor::messages::MessageType m_type
static messages::MessageType get_message_wire_number()
virtual void session_end(Transport &transport)
virtual void session_begin(Transport &transport)
virtual ~Protocol()=default
virtual void read(Transport &transport, std::shared_ptr< google::protobuf::Message > &msg, messages::MessageType *msg_type=nullptr)=0
virtual void write(Transport &transport, const google::protobuf::Message &req)=0
void write(Transport &transport, const google::protobuf::Message &req) override
void read(Transport &transport, std::shared_ptr< google::protobuf::Message > &msg, messages::MessageType *msg_type=nullptr) override
virtual ~ProtocolV1()=default
virtual void close()
virtual void enumerate(t_transport_vect &res)
virtual void read(std::shared_ptr< google::protobuf::Message > &msg, messages::MessageType *msg_type=nullptr)=0
virtual void write_chunk(const void *buff, size_t size)
virtual size_t read_chunk(void *buff, size_t size)
virtual bool pre_open()
virtual std::ostream & dump(std::ostream &o) const
virtual std::string get_path() const
virtual bool pre_close()
virtual void open()
virtual bool ping()
virtual void write(const google::protobuf::Message &req)=0
virtual std::shared_ptr< Transport > find_debug()
virtual ~Transport()=default
void write(const google::protobuf::Message &req) override
static const char * PATH_PREFIX
void enumerate(t_transport_vect &res) override
std::ostream & dump(std::ostream &o) const override
void write_chunk(const void *buff, size_t size) override
static const char * DEFAULT_HOST
size_t read_chunk(void *buff, size_t size) override
void close() override
virtual ~UdpTransport()=default
static const int DEFAULT_PORT
std::string get_path() const override
std::shared_ptr< Transport > find_debug() override
UdpTransport(boost::optional< std::string > device_path=boost::none, boost::optional< std::shared_ptr< Protocol > > proto=boost::none)
void read(std::shared_ptr< google::protobuf::Message > &msg, messages::MessageType *msg_type=nullptr) override
boost::optional< std::string > reason
hw::trezor::messages::MessageType recvType
std::shared_ptr< google::protobuf::Message > recvMsg
UnexpectedMessageException(hw::trezor::messages::MessageType recvType, const std::shared_ptr< google::protobuf::Message > &recvMsg)
const char * res
#define MERROR(x)
Definition misc_log_ex.h:73
http_simple_client_template< blocked_mode_client > http_simple_client
void sort_transports_by_env(t_transport_vect &res)
std::ostream & operator<<(std::ostream &o, hw::trezor::Transport const &t)
void throw_failure_exception(const messages::common::Failure *failure)
std::shared_ptr< t_message > message_ptr_retype(std::shared_ptr< google::protobuf::Message > &in)
bool t_serialize(const std::string &in, std::string &out)
Definition transport.cpp:55
bool t_deserialize(const std::string &in, std::string &out)
Definition transport.cpp:74
std::shared_ptr< t_message > exchange_message(Transport &transport, const google::protobuf::Message &req, boost::optional< messages::MessageType > resp_type=boost::none)
const std::string DEFAULT_BRIDGE
Definition transport.hpp:63
std::shared_ptr< t_transport > transport_typed(const std::string &path)
std::shared_ptr< Transport > transport(const std::string &path)
uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch)
Definition transport.cpp:90
bool invoke_bridge_http(const boost::string_ref uri, const t_req &out_struct, t_res &result_struct, t_transport &transport, const boost::string_ref method="POST", std::chrono::milliseconds timeout=std::chrono::seconds(180))
Definition transport.hpp:77
rapidjson::Document json
Definition transport.hpp:59
rapidjson::Value json_val
Definition transport.hpp:60
std::vector< std::shared_ptr< Transport > > t_transport_vect
void enumerate(t_transport_vect &res)
Definition device.cpp:38
#define true
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136