Electroneum
Loading...
Searching...
No Matches
transport.cpp
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#ifdef WITH_DEVICE_TREZOR_WEBUSB
31#include <libusb.h>
32#endif
33
34#include <algorithm>
35#include <functional>
36#include <boost/endian/conversion.hpp>
37#include <boost/asio/io_service.hpp>
38#include <boost/asio/ip/udp.hpp>
39#include <boost/date_time/posix_time/posix_time_types.hpp>
40#include <boost/format.hpp>
42#include "transport.hpp"
43#include "messages/messages-common.pb.h"
44
45#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
46#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "device.trezor.transport"
47
48using namespace std;
49using json = rapidjson::Document;
50
51
52namespace hw{
53namespace trezor{
54
55 bool t_serialize(const std::string & in, std::string & out){
56 out = in;
57 return true;
58 }
59
60 bool t_serialize(const json_val & in, std::string & out){
61 rapidjson::StringBuffer sb;
62 rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
63 in.Accept(writer);
64 out = sb.GetString();
65 return true;
66 }
67
68 std::string t_serialize(const json_val & in){
69 std::string ret;
70 t_serialize(in, ret);
71 return ret;
72 }
73
74 bool t_deserialize(const std::string & in, std::string & out){
75 out = in;
76 return true;
77 }
78
79 bool t_deserialize(const std::string & in, json & out){
80 if (out.Parse(in.c_str()).HasParseError()) {
81 throw exc::CommunicationException("JSON parse error");
82 }
83 return true;
84 }
85
86 static std::string json_get_string(const rapidjson::Value & in){
87 return std::string(in.GetString());
88 }
89
91 {
92 // packing (major, minor, patch) to 64 B: 16 B | 24 B | 24 B
93 const unsigned bits_1 = 16;
94 const unsigned bits_2 = 24;
95 const uint32_t mask_1 = (1 << bits_1) - 1;
96 const uint32_t mask_2 = (1 << bits_2) - 1;
97 CHECK_AND_ASSERT_THROW_MES(major <= mask_1 && minor <= mask_2 && patch <= mask_2, "Version numbers overflow packing scheme");
98 return patch | (((uint64_t)minor) << bits_2) | (((uint64_t)major) << (bits_1 + bits_2));
99 }
100
106
107 static trezor_usb_desc_t TREZOR_DESC_T1 = {1, 0x534C, 0x0001};
108 static trezor_usb_desc_t TREZOR_DESC_T2 = {2, 0x1209, 0x53C1};
109 static trezor_usb_desc_t TREZOR_DESC_T2_BL = {3, 0x1209, 0x53C0};
110
111 static trezor_usb_desc_t TREZOR_DESCS[] = {
112 TREZOR_DESC_T1,
113 TREZOR_DESC_T2,
114 TREZOR_DESC_T2_BL,
115 };
116
117 static size_t TREZOR_DESCS_LEN = sizeof(TREZOR_DESCS)/sizeof(TREZOR_DESCS[0]);
118
119 static ssize_t get_device_idx(uint16_t id_vendor, uint16_t id_product){
120 for(size_t i = 0; i < TREZOR_DESCS_LEN; ++i){
121 if (TREZOR_DESCS[i].id_vendor == id_vendor && TREZOR_DESCS[i].id_product == id_product){
122 return i;
123 }
124 }
125
126 return -1;
127 }
128
129 static bool is_device_supported(ssize_t device_idx){
130 CHECK_AND_ASSERT_THROW_MES(device_idx < (ssize_t)TREZOR_DESCS_LEN, "Device desc idx too big");
131 if (device_idx < 0){
132 return false;
133 }
134
135#ifdef TREZOR_1_SUPPORTED
136 return true;
137#else
138 return TREZOR_DESCS[device_idx].trezor_type != 1;
139#endif
140 }
141
142 //
143 // Helpers
144 //
145
146#define PROTO_HEADER_SIZE 6
147
148 static size_t message_size(const google::protobuf::Message &req){
149 return static_cast<size_t>(req.ByteSize());
150 }
151
152 static size_t serialize_message_buffer_size(size_t msg_size) {
153 return PROTO_HEADER_SIZE + msg_size; // tag 2B + len 4B
154 }
155
156 static void serialize_message_header(void * buff, uint16_t tag, uint32_t len){
157 uint16_t wire_tag = boost::endian::native_to_big(static_cast<uint16_t>(tag));
158 uint32_t wire_len = boost::endian::native_to_big(static_cast<uint32_t>(len));
159 memcpy(buff, (void *) &wire_tag, 2);
160 memcpy((uint8_t*)buff + 2, (void *) &wire_len, 4);
161 }
162
163 static void deserialize_message_header(const void * buff, uint16_t & tag, uint32_t & len){
164 uint16_t wire_tag;
165 uint32_t wire_len;
166 memcpy(&wire_tag, buff, 2);
167 memcpy(&wire_len, (uint8_t*)buff + 2, 4);
168
169 tag = boost::endian::big_to_native(wire_tag);
170 len = boost::endian::big_to_native(wire_len);
171 }
172
173 static void serialize_message(const google::protobuf::Message &req, size_t msg_size, uint8_t * buff, size_t buff_size) {
174 auto msg_wire_num = MessageMapper::get_message_wire_number(req);
175 const auto req_buffer_size = serialize_message_buffer_size(msg_size);
176 if (req_buffer_size > buff_size){
177 throw std::invalid_argument("Buffer too small");
178 }
179
180 serialize_message_header(buff, msg_wire_num, msg_size);
181 if (!req.SerializeToArray(buff + 6, msg_size)){
182 throw exc::EncodingException("Message serialization error");
183 }
184 }
185
186 //
187 // Communication protocol
188 //
189
190#define REPLEN 64
191
192 void ProtocolV1::write(Transport & transport, const google::protobuf::Message & req){
193 const auto msg_size = message_size(req);
194 const auto buff_size = serialize_message_buffer_size(msg_size) + 2;
195
196 std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
197 uint8_t * req_buff_raw = req_buff.get();
198 req_buff_raw[0] = '#';
199 req_buff_raw[1] = '#';
200
201 serialize_message(req, msg_size, req_buff_raw + 2, buff_size - 2);
202
203 size_t offset = 0;
204 uint8_t chunk_buff[REPLEN];
205
206 // Chunk by chunk upload
207 while(offset < buff_size){
208 auto to_copy = std::min((size_t)(buff_size - offset), (size_t)(REPLEN - 1));
209
210 chunk_buff[0] = '?';
211 memcpy(chunk_buff + 1, req_buff_raw + offset, to_copy);
212
213 // Pad with zeros
214 if (to_copy < REPLEN - 1){
215 memset(chunk_buff + 1 + to_copy, 0, REPLEN - 1 - to_copy);
216 }
217
218 transport.write_chunk(chunk_buff, REPLEN);
219 offset += REPLEN - 1;
220 }
221 }
222
223 void ProtocolV1::read(Transport & transport, std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type){
224 char chunk[REPLEN];
225
226 // Initial chunk read
227 size_t nread = transport.read_chunk(chunk, REPLEN);
228 if (nread != REPLEN){
229 throw exc::CommunicationException("Read chunk has invalid size");
230 }
231
232 if (strncmp(chunk, "?##", 3) != 0){
233 throw exc::CommunicationException("Malformed chunk");
234 }
235
236 uint16_t tag;
237 uint32_t len;
238 nread -= 3 + 6;
239 deserialize_message_header(chunk + 3, tag, len);
240
241 std::string data_acc(chunk + 3 + 6, nread);
242 data_acc.reserve(len);
243
244 while(nread < len){
245 const size_t cur = transport.read_chunk(chunk, REPLEN);
246 if (chunk[0] != '?'){
247 throw exc::CommunicationException("Chunk malformed");
248 }
249
250 data_acc.append(chunk + 1, cur - 1);
251 nread += cur - 1;
252 }
253
254 if (msg_type){
255 *msg_type = static_cast<messages::MessageType>(tag);
256 }
257
258 if (nread < len){
259 throw exc::CommunicationException("Response incomplete");
260 }
261
262 std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(tag));
263 if (!msg_wrap->ParseFromArray(data_acc.c_str(), len)){
264 throw exc::CommunicationException("Message could not be parsed");
265 }
266
267 msg = msg_wrap;
268 }
269
270 static void assert_port_number(uint32_t port)
271 {
272 CHECK_AND_ASSERT_THROW_MES(port >= 1024 && port < 65535, "Invalid port number: " << port);
273 }
274
278
280 if (m_open_counter > 0){
281 MTRACE("Already opened, count: " << m_open_counter);
282 m_open_counter += 1;
283 return false;
284
285 } else if (m_open_counter < 0){
286 MTRACE("Negative open value: " << m_open_counter);
287
288 }
289
290 // Caller should set m_open_counter to 1 after open
291 m_open_counter = 0;
292 return true;
293 }
294
296 m_open_counter -= 1;
297
298 if (m_open_counter < 0){
299 MDEBUG("Already closed. Counter " << m_open_counter);
300
301 } else if (m_open_counter == 0) {
302 return true;
303
304 }
305
306 return false;
307 }
308
309 //
310 // Bridge transport
311 //
312
313 const char * BridgeTransport::PATH_PREFIX = "bridge:";
314
316 boost::optional<std::string> device_path,
317 boost::optional<std::string> bridge_host):
318 m_device_path(device_path),
319 m_bridge_host(bridge_host ? bridge_host.get() : DEFAULT_BRIDGE),
320 m_response(boost::none),
321 m_session(boost::none),
322 m_device_info(boost::none)
323 {
324 const char *env_bridge_port = nullptr;
325 if (!bridge_host && (env_bridge_port = getenv("TREZOR_BRIDGE_PORT")) != nullptr)
326 {
327 uint16_t bridge_port;
328 CHECK_AND_ASSERT_THROW_MES(epee::string_tools::get_xtype_from_string(bridge_port, env_bridge_port), "Invalid bridge port: " << env_bridge_port);
329 assert_port_number(bridge_port);
330
331 m_bridge_host = std::string("127.0.0.1:") + boost::lexical_cast<std::string>(env_bridge_port);
332 MDEBUG("Bridge host: " << m_bridge_host);
333 }
334
335 m_http_client.set_server(m_bridge_host, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
336 }
337
338 std::string BridgeTransport::get_path() const {
339 if (!m_device_path){
340 return "";
341 }
342
343 std::string path(PATH_PREFIX);
344 return path + m_device_path.get();
345 }
346
348 json bridge_res;
349 std::string req;
350
351 bool req_status = invoke_bridge_http("/enumerate", req, bridge_res, m_http_client);
352 if (!req_status){
353 throw exc::CommunicationException("Bridge enumeration failed");
354 }
355
356 for(rapidjson::Value::ConstValueIterator itr = bridge_res.Begin(); itr != bridge_res.End(); ++itr){
357 auto element = itr->GetObject();
358 auto t = std::make_shared<BridgeTransport>(boost::make_optional(json_get_string(element["path"])));
359
360 auto itr_vendor = element.FindMember("vendor");
361 auto itr_product = element.FindMember("product");
362 if (itr_vendor != element.MemberEnd() && itr_product != element.MemberEnd()
363 && itr_vendor->value.IsNumber() && itr_product->value.IsNumber()){
364 try {
365 const auto id_vendor = (uint16_t) itr_vendor->value.GetUint64();
366 const auto id_product = (uint16_t) itr_product->value.GetUint64();
367 const auto device_idx = get_device_idx(id_vendor, id_product);
368 if (!is_device_supported(device_idx)){
369 MDEBUG("Device with idx " << device_idx << " is not supported. Vendor: " << id_vendor << ", product: " << id_product);
370 continue;
371 }
372 } catch(const std::exception &e){
373 MERROR("Could not detect vendor & product: " << e.what());
374 }
375 }
376
377 t->m_device_info.emplace();
378 t->m_device_info->CopyFrom(*itr, t->m_device_info->GetAllocator());
379 res.push_back(t);
380 }
381 }
382
384 if (!pre_open()){
385 return;
386 }
387
388 if (!m_device_path){
389 throw exc::CommunicationException("Coud not open, empty device path");
390 }
391
392 std::string uri = "/acquire/" + m_device_path.get() + "/null";
393 std::string req;
394 json bridge_res;
395 bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
396 if (!req_status){
397 throw exc::CommunicationException("Failed to acquire device");
398 }
399
400 m_session = boost::make_optional(json_get_string(bridge_res["session"]));
401 m_open_counter = 1;
402 }
403
405 if (!pre_close()){
406 return;
407 }
408
409 MTRACE("Closing Trezor:BridgeTransport");
410 if (!m_device_path || !m_session){
411 throw exc::CommunicationException("Device not open");
412 }
413
414 std::string uri = "/release/" + m_session.get();
415 std::string req;
416 json bridge_res;
417 bool req_status = invoke_bridge_http(uri, req, bridge_res, m_http_client);
418 if (!req_status){
419 throw exc::CommunicationException("Failed to release device");
420 }
421
422 m_session = boost::none;
423 }
424
425 void BridgeTransport::write(const google::protobuf::Message &req) {
426 m_response = boost::none;
427
428 const auto msg_size = message_size(req);
429 const auto buff_size = serialize_message_buffer_size(msg_size);
430
431 std::unique_ptr<uint8_t[]> req_buff(new uint8_t[buff_size]);
432 uint8_t * req_buff_raw = req_buff.get();
433
434 serialize_message(req, msg_size, req_buff_raw, buff_size);
435
436 std::string uri = "/call/" + m_session.get();
437 std::string req_hex = epee::to_hex::string(epee::span<const std::uint8_t>(req_buff_raw, buff_size));
438 std::string res_hex;
439
440 bool req_status = invoke_bridge_http(uri, req_hex, res_hex, m_http_client);
441 if (!req_status){
442 throw exc::CommunicationException("Call method failed");
443 }
444
445 m_response = res_hex;
446 }
447
448 void BridgeTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
449 if (!m_response){
450 throw exc::CommunicationException("Could not read, no response stored");
451 }
452
453 std::string bin_data;
454 if (!epee::string_tools::parse_hexstr_to_binbuff(m_response.get(), bin_data)){
455 throw exc::CommunicationException("Response is not well hexcoded");
456 }
457
458 uint16_t msg_tag;
459 uint32_t msg_len;
460 deserialize_message_header(bin_data.c_str(), msg_tag, msg_len);
461 if (bin_data.size() != msg_len + 6){
462 throw exc::CommunicationException("Response is not well hexcoded");
463 }
464
465 if (msg_type){
466 *msg_type = static_cast<messages::MessageType>(msg_tag);
467 }
468
469 std::shared_ptr<google::protobuf::Message> msg_wrap(MessageMapper::get_message(msg_tag));
470 if (!msg_wrap->ParseFromArray(bin_data.c_str() + 6, msg_len)){
471 throw exc::EncodingException("Response is not well hexcoded");
472 }
473 msg = msg_wrap;
474 }
475
476 const boost::optional<json> & BridgeTransport::device_info() const {
477 return m_device_info;
478 }
479
480 std::ostream& BridgeTransport::dump(std::ostream& o) const {
481 return o << "BridgeTransport<path=" << (m_device_path ? get_path() : "None")
482 << ", info=" << (m_device_info ? t_serialize(m_device_info.get()) : "None")
483 << ", session=" << (m_session ? m_session.get() : "None")
484 << ">";
485 }
486
487 //
488 // UdpTransport
489 //
490 const char * UdpTransport::PATH_PREFIX = "udp:";
491 const char * UdpTransport::DEFAULT_HOST = "127.0.0.1";
492 const int UdpTransport::DEFAULT_PORT = 21324;
493
494 static void parse_udp_path(std::string &host, int &port, std::string path)
495 {
496 if (boost::starts_with(path, UdpTransport::PATH_PREFIX))
497 {
498 path = path.substr(strlen(UdpTransport::PATH_PREFIX));
499 }
500
501 auto delim = path.find(':');
502 if (delim == std::string::npos) {
503 host = path;
504 } else {
505 host = path.substr(0, delim);
506 port = std::stoi(path.substr(delim + 1));
507 }
508 }
509
510 UdpTransport::UdpTransport(boost::optional<std::string> device_path,
511 boost::optional<std::shared_ptr<Protocol>> proto) :
512 m_io_service(), m_deadline(m_io_service)
513 {
514 m_device_host = DEFAULT_HOST;
515 m_device_port = DEFAULT_PORT;
516 const char *env_trezor_path = nullptr;
517
518 if (device_path) {
519 parse_udp_path(m_device_host, m_device_port, device_path.get());
520 } else if ((env_trezor_path = getenv("TREZOR_PATH")) != nullptr && boost::starts_with(env_trezor_path, UdpTransport::PATH_PREFIX)){
521 parse_udp_path(m_device_host, m_device_port, std::string(env_trezor_path));
522 MDEBUG("Applied TREZOR_PATH: " << m_device_host << ":" << m_device_port);
523 } else {
524 m_device_host = DEFAULT_HOST;
525 }
526
527 assert_port_number((uint32_t)m_device_port);
528 if (m_device_host != "localhost" && m_device_host != DEFAULT_HOST){
529 throw std::invalid_argument("Local endpoint allowed only");
530 }
531
532 m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
533 }
534
535 std::string UdpTransport::get_path() const {
536 std::string path(PATH_PREFIX);
537 return path + m_device_host + ":" + std::to_string(m_device_port);
538 }
539
540 void UdpTransport::require_socket(){
541 if (!m_socket){
542 throw exc::NotConnectedException("Socket not connected");
543 }
544 }
545
547 return ping_int();
548 }
549
550 bool UdpTransport::ping_int(boost::posix_time::time_duration timeout){
551 require_socket();
552 try {
553 std::string req = "PINGPING";
554 char res[8];
555
556 m_socket->send_to(boost::asio::buffer(req.c_str(), req.size()), m_endpoint);
557 receive(res, 8, nullptr, false, timeout);
558
559 return memcmp(res, "PONGPONG", 8) == 0;
560
561 } catch(...){
562 return false;
563 }
564 }
565
567 std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>();
568 bool t_works = false;
569
570 try{
571 t->open();
572 t_works = t->ping();
573 } catch(...) {
574
575 }
576 t->close();
577 if (t_works){
578 res.push_back(t);
579 }
580 }
581
583 if (!pre_open()){
584 return;
585 }
586
587 udp::resolver resolver(m_io_service);
588 udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port));
589 m_endpoint = *resolver.resolve(query);
590
591 m_socket.reset(new udp::socket(m_io_service));
592 m_socket->open(udp::v4());
593
594 m_deadline.expires_at(boost::posix_time::pos_infin);
595 check_deadline();
596
597 m_proto->session_begin(*this);
598 m_open_counter = 1;
599 }
600
602 if (!pre_close()){
603 return;
604 }
605
606 MTRACE("Closing Trezor:UdpTransport");
607 if (!m_socket) {
608 throw exc::CommunicationException("Socket is already closed");
609 }
610
611 m_proto->session_end(*this);
612 m_socket->close();
613 m_socket = nullptr;
614 }
615
616 std::shared_ptr<Transport> UdpTransport::find_debug() {
617#ifdef WITH_TREZOR_DEBUGGING
618 std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>();
619 t->m_proto = std::make_shared<ProtocolV1>();
620 t->m_device_host = m_device_host;
621 t->m_device_port = m_device_port + 1;
622 return t;
623#else
624 MINFO("Debug link is disabled in production");
625 return nullptr;
626#endif
627 }
628
629 void UdpTransport::write_chunk(const void * buff, size_t size){
630 require_socket();
631
632 if (size != 64){
633 throw exc::CommunicationException("Invalid chunk size");
634 }
635
636 auto written = m_socket->send_to(boost::asio::buffer(buff, size), m_endpoint);
637 if (size != written){
638 throw exc::CommunicationException("Could not send the whole chunk");
639 }
640 }
641
642 size_t UdpTransport::read_chunk(void * buff, size_t size){
643 require_socket();
644 if (size < 64){
645 throw std::invalid_argument("Buffer too small");
646 }
647
648 ssize_t len;
649 while(true) {
650 try {
651 boost::system::error_code ec;
652 len = receive(buff, size, &ec, true);
653 if (ec == boost::asio::error::operation_aborted) {
654 continue;
655 } else if (ec) {
656 throw exc::CommunicationException(std::string("Comm error: ") + ec.message());
657 }
658
659 if (len != 64) {
660 throw exc::CommunicationException("Invalid chunk size");
661 }
662
663 break;
664
665 } catch(exc::CommunicationException const& e){
666 throw;
667 } catch(std::exception const& e){
668 MWARNING("Error reading chunk, reason: " << e.what());
669 throw exc::CommunicationException(std::string("Chunk read error: ") + std::string(e.what()));
670 }
671 }
672
673 return static_cast<size_t>(len);
674 }
675
676 ssize_t UdpTransport::receive(void * buff, size_t size, boost::system::error_code * error_code, bool no_throw, boost::posix_time::time_duration timeout){
677 boost::system::error_code ec;
678 boost::asio::mutable_buffer buffer = boost::asio::buffer(buff, size);
679
680 require_socket();
681
682 // Set a deadline for the asynchronous operation.
683 m_deadline.expires_from_now(timeout);
684
685 // Set up the variables that receive the result of the asynchronous
686 // operation. The error code is set to would_block to signal that the
687 // operation is incomplete. Asio guarantees that its asynchronous
688 // operations will never fail with would_block, so any other value in
689 // ec indicates completion.
690 ec = boost::asio::error::would_block;
691 std::size_t length = 0;
692
693 // Start the asynchronous operation itself. The handle_receive function
694 // used as a callback will update the ec and length variables.
695 m_socket->async_receive_from(boost::asio::buffer(buffer), m_endpoint,
696 std::bind(&UdpTransport::handle_receive, std::placeholders::_1, std::placeholders::_2, &ec, &length));
697
698 // Block until the asynchronous operation has completed.
699 do {
700 m_io_service.run_one();
701 }
702 while (ec == boost::asio::error::would_block);
703
704 if (error_code){
705 *error_code = ec;
706 }
707
708 if (no_throw){
709 return length;
710 }
711
712 // Operation result
713 if (ec == boost::asio::error::operation_aborted){
714 throw exc::TimeoutException();
715
716 } else if (ec) {
717 MWARNING("Reading from UDP socket failed: " << ec.message());
718 throw exc::CommunicationException();
719
720 }
721
722 return length;
723 }
724
725 void UdpTransport::write(const google::protobuf::Message &req) {
726 m_proto->write(*this, req);
727 }
728
729 void UdpTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
730 m_proto->read(*this, msg, msg_type);
731 }
732
733 void UdpTransport::check_deadline(){
734 if (!m_socket){
735 return; // no active socket.
736 }
737
738 // Check whether the deadline has passed. We compare the deadline against
739 // the current time since a new asynchronous operation may have moved the
740 // deadline before this actor had a chance to run.
741 if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
742 {
743 // The deadline has passed. The outstanding asynchronous operation needs
744 // to be cancelled so that the blocked receive() function will return.
745 //
746 // Please note that cancel() has portability issues on some versions of
747 // Microsoft Windows, and it may be necessary to use close() instead.
748 // Consult the documentation for cancel() for further information.
749 m_socket->cancel();
750
751 // There is no longer an active deadline. The expiry is set to positive
752 // infinity so that the actor takes no action until a new deadline is set.
753 m_deadline.expires_at(boost::posix_time::pos_infin);
754 }
755
756 // Put the actor back to sleep.
757 m_deadline.async_wait(boost::bind(&UdpTransport::check_deadline, this));
758 }
759
760 void UdpTransport::handle_receive(const boost::system::error_code &ec, std::size_t length,
761 boost::system::error_code *out_ec, std::size_t *out_length) {
762 *out_ec = ec;
763 *out_length = length;
764 }
765
766 std::ostream& UdpTransport::dump(std::ostream& o) const {
767 return o << "UdpTransport<path=" << get_path()
768 << ", socket_alive=" << (m_socket ? "true" : "false")
769 << ">";
770 }
771
772#ifdef WITH_DEVICE_TREZOR_WEBUSB
773
774 static bool is_trezor1(libusb_device_descriptor * info){
775 return info->idVendor == TREZOR_DESC_T1.id_vendor && info->idProduct == TREZOR_DESC_T1.id_product;
776 }
777
778 static bool is_trezor2(libusb_device_descriptor * info){
779 return info->idVendor == TREZOR_DESC_T2.id_vendor && info->idProduct == TREZOR_DESC_T2.id_product;
780 }
781
782 static bool is_trezor2_bl(libusb_device_descriptor * info){
783 return info->idVendor == TREZOR_DESC_T2_BL.id_vendor && info->idProduct == TREZOR_DESC_T2_BL.id_product;
784 }
785
786 static ssize_t get_trezor_dev_id(libusb_device_descriptor *info){
787 CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor");
788 return get_device_idx(info->idVendor, info->idProduct);
789 }
790
791 static void set_libusb_log(libusb_context *ctx){
792 CHECK_AND_ASSERT_THROW_MES(ctx, "Null libusb context");
793
794 // http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html
795#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
796# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
797#else
798# define TREZOR_LIBUSB_SET_DEBUG(ctx, level) libusb_set_debug(ctx, level)
799#endif
800
801 if (ELPP->vRegistry()->allowed(el::Level::Debug, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
802 TREZOR_LIBUSB_SET_DEBUG(ctx, 3);
803 else if (ELPP->vRegistry()->allowed(el::Level::Warning, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
804 TREZOR_LIBUSB_SET_DEBUG(ctx, 2);
805 else if (ELPP->vRegistry()->allowed(el::Level::Error, ELECTRONEUM_DEFAULT_LOG_CATEGORY))
806 TREZOR_LIBUSB_SET_DEBUG(ctx, 1);
807
808#undef TREZOR_LIBUSB_SET_DEBUG
809 }
810
811 static int get_libusb_ports(libusb_device *dev, std::vector<uint8_t> &path){
812 uint8_t tmp_path[16];
813 int r = libusb_get_port_numbers(dev, tmp_path, sizeof(tmp_path));
814 CHECK_AND_ASSERT_MES(r != LIBUSB_ERROR_OVERFLOW, -1, "Libusb path array too small");
815 CHECK_AND_ASSERT_MES(r >= 0, -1, "Libusb path array error");
816
817 path.resize(r);
818 for (int i = 0; i < r; i++){
819 path[i] = tmp_path[i];
820 }
821
822 return 0;
823 }
824
825 static std::string get_usb_path(uint8_t bus_id, const std::vector<uint8_t> &path){
826 std::stringstream ss;
827 ss << WebUsbTransport::PATH_PREFIX << (boost::format("%03d") % ((int)bus_id));
828 for(uint8_t port : path){
829 ss << ":" << ((int) port);
830 }
831 return ss.str();
832 }
833
834 const char * WebUsbTransport::PATH_PREFIX = "webusb:";
835
836 WebUsbTransport::WebUsbTransport(
837 boost::optional<libusb_device_descriptor*> descriptor,
838 boost::optional<std::shared_ptr<Protocol>> proto
839 ): m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr),
840 m_bus_id(-1), m_device_addr(-1)
841 {
842 if (descriptor){
843 libusb_device_descriptor * desc = new libusb_device_descriptor;
844 memcpy(desc, descriptor.get(), sizeof(libusb_device_descriptor));
845 this->m_usb_device_desc.reset(desc);
846 }
847
848 m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>();
849
850#ifdef WITH_TREZOR_DEBUGGING
851 m_debug_mode = false;
852#endif
853 }
854
855 WebUsbTransport::~WebUsbTransport(){
856 if (m_usb_device){
857 close();
858 }
859
860 if (m_usb_session) {
861 libusb_exit(m_usb_session);
862 m_usb_session = nullptr;
863 }
864 }
865
866 void WebUsbTransport::require_device() const{
867 if (!m_usb_device_desc){
868 throw std::runtime_error("No USB device specified");
869 }
870 }
871
872 void WebUsbTransport::require_connected() const{
873 require_device();
874 if (!m_usb_device_handle){
875 throw std::runtime_error("USB Device not opened");
876 }
877 }
878
879 void WebUsbTransport::enumerate(t_transport_vect & res) {
880 int r;
881 libusb_device **devs;
882 libusb_context *ctx = nullptr;
883
884 r = libusb_init(&ctx);
885 CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
886
887 set_libusb_log(ctx);
888
889 ssize_t cnt = libusb_get_device_list(ctx, &devs);
890 if (cnt < 0){
891 libusb_exit(ctx);
892 throw std::runtime_error("Unable to enumerate libusb devices");
893 }
894
895 MTRACE("Libusb devices: " << cnt);
896
897 for(ssize_t i = 0; i < cnt; i++) {
898 libusb_device_descriptor desc{};
899 r = libusb_get_device_descriptor(devs[i], &desc);
900 if (r < 0){
901 MERROR("Unable to get libusb device descriptor " << i);
902 continue;
903 }
904
905 const auto trezor_dev_idx = get_trezor_dev_id(&desc);
906 if (!is_device_supported(trezor_dev_idx)){
907 continue;
908 }
909
910 MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " dev_idx " << (int)trezor_dev_idx);
911
912 auto t = std::make_shared<WebUsbTransport>(boost::make_optional(&desc));
913 t->m_bus_id = libusb_get_bus_number(devs[i]);
914 t->m_device_addr = libusb_get_device_address(devs[i]);
915
916 // Port resolution may fail. Non-critical error, just addressing precision is decreased.
917 get_libusb_ports(devs[i], t->m_port_numbers);
918
919 res.push_back(t);
920 }
921
922 libusb_free_device_list(devs, 1);
923 libusb_exit(ctx);
924 }
925
926 std::string WebUsbTransport::get_path() const {
927 if (!m_usb_device_desc){
928 return "";
929 }
930
931 return get_usb_path(static_cast<uint8_t>(m_bus_id), m_port_numbers);
932 };
933
934 void WebUsbTransport::open() {
935 if (!pre_open()){
936 return;
937 }
938 const int interface = get_interface();
939
940#define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0)
941
942 int r;
943 libusb_device **devs = nullptr;
944
945 if (m_usb_session) {
946 TREZOR_DESTROY_SESSION();
947 }
948
949 r = libusb_init(&m_usb_session);
950 CHECK_AND_ASSERT_THROW_MES(r >= 0, "Unable to init libusb");
951 set_libusb_log(m_usb_session);
952
953 bool found = false;
954 int open_res = 0;
955
956 ssize_t cnt = libusb_get_device_list(m_usb_session, &devs);
957 if (cnt < 0){
958 TREZOR_DESTROY_SESSION();
959 throw std::runtime_error("Unable to enumerate libusb devices");
960 }
961
962 for (ssize_t i = 0; i < cnt; i++) {
963 libusb_device_descriptor desc{};
964 r = libusb_get_device_descriptor(devs[i], &desc);
965 if (r < 0){
966 MERROR("Unable to get libusb device descriptor " << i);
967 continue;
968 }
969
970 const auto trezor_dev_idx = get_trezor_dev_id(&desc);
971 if (!is_device_supported(trezor_dev_idx)){
972 continue;
973 }
974
975 auto bus_id = libusb_get_bus_number(devs[i]);
976 std::vector<uint8_t> path;
977
978 // Port resolution may fail. Non-critical error, just addressing precision is decreased.
979 get_libusb_ports(devs[i], path);
980
981 MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct
982 << ", dev_idx: " << (int)trezor_dev_idx
983 << ". path: " << get_usb_path(bus_id, path));
984
985 if (bus_id == m_bus_id && path == m_port_numbers) {
986 found = true;
987 m_usb_device = devs[i];
988 open_res = libusb_open(m_usb_device, &m_usb_device_handle);
989 break;
990 }
991 }
992
993 libusb_free_device_list(devs, 1);
994
995 if (!found){
996 TREZOR_DESTROY_SESSION();
997 throw exc::DeviceAcquireException("Device not found");
998
999 } else if (found && open_res != 0) {
1000 m_usb_device_handle = nullptr;
1001 m_usb_device = nullptr;
1002 TREZOR_DESTROY_SESSION();
1003 throw exc::DeviceAcquireException("Unable to open libusb device");
1004 }
1005
1006 r = libusb_claim_interface(m_usb_device_handle, interface);
1007
1008 if (r != 0){
1009 libusb_close(m_usb_device_handle);
1010 m_usb_device_handle = nullptr;
1011 m_usb_device = nullptr;
1012 TREZOR_DESTROY_SESSION();
1013 throw exc::DeviceAcquireException("Unable to claim libusb device");
1014 }
1015
1016 m_open_counter = 1;
1017 m_proto->session_begin(*this);
1018
1019#undef TREZOR_DESTROY_SESSION
1020 };
1021
1022 void WebUsbTransport::close() {
1023 if (!pre_close()){
1024 return;
1025 }
1026
1027 MTRACE("Closing Trezor:WebUsbTransport");
1028 m_proto->session_end(*this);
1029
1030 int r = libusb_release_interface(m_usb_device_handle, get_interface());
1031 if (r != 0){
1032 MERROR("Could not release libusb interface: " << r);
1033 }
1034
1035 m_usb_device = nullptr;
1036 if (m_usb_device_handle) {
1037 libusb_close(m_usb_device_handle);
1038 m_usb_device_handle = nullptr;
1039 }
1040
1041 if (m_usb_session) {
1042 libusb_exit(m_usb_session);
1043 m_usb_session = nullptr;
1044 }
1045 };
1046
1047 std::shared_ptr<Transport> WebUsbTransport::find_debug() {
1048#ifdef WITH_TREZOR_DEBUGGING
1049 require_device();
1050 auto t = std::make_shared<WebUsbTransport>(boost::make_optional(m_usb_device_desc.get()));
1051 t->m_bus_id = m_bus_id;
1052 t->m_device_addr = m_device_addr;
1053 t->m_port_numbers = m_port_numbers;
1054 t->m_debug_mode = true;
1055 return t;
1056#else
1057 MINFO("Debug link is disabled in production");
1058 return nullptr;
1059#endif
1060 }
1061
1062 int WebUsbTransport::get_interface() const{
1063 const int INTERFACE_NORMAL = 0;
1064#ifdef WITH_TREZOR_DEBUGGING
1065 const int INTERFACE_DEBUG = 1;
1066 return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL;
1067#else
1068 return INTERFACE_NORMAL;
1069#endif
1070 }
1071
1072 unsigned char WebUsbTransport::get_endpoint() const{
1073 const unsigned char ENDPOINT_NORMAL = 1;
1074#ifdef WITH_TREZOR_DEBUGGING
1075 const unsigned char ENDPOINT_DEBUG = 2;
1076 return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL;
1077#else
1078 return ENDPOINT_NORMAL;
1079#endif
1080 }
1081
1082 void WebUsbTransport::write(const google::protobuf::Message &req) {
1083 m_proto->write(*this, req);
1084 };
1085
1086 void WebUsbTransport::read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type) {
1087 m_proto->read(*this, msg, msg_type);
1088 };
1089
1090 void WebUsbTransport::write_chunk(const void * buff, size_t size) {
1091 require_connected();
1092 if (size != REPLEN){
1093 throw exc::CommunicationException("Invalid chunk size: ");
1094 }
1095
1096 unsigned char endpoint = get_endpoint();
1097 endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_OUT;
1098
1099 int transferred = 0;
1100 int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
1101 CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
1102 if (transferred != (int)size){
1103 throw exc::CommunicationException("Could not transfer chunk");
1104 }
1105 };
1106
1107 size_t WebUsbTransport::read_chunk(void * buff, size_t size) {
1108 require_connected();
1109 unsigned char endpoint = get_endpoint();
1110 endpoint = (endpoint & ~LIBUSB_ENDPOINT_DIR_MASK) | LIBUSB_ENDPOINT_IN;
1111
1112 int transferred = 0;
1113 int r = libusb_interrupt_transfer(m_usb_device_handle, endpoint, (unsigned char*)buff, (int)size, &transferred, 0);
1114 CHECK_AND_ASSERT_THROW_MES(r == 0, "Unable to transfer, r: " << r);
1115 if (transferred != (int)size){
1116 throw exc::CommunicationException("Could not read the chunk");
1117 }
1118
1119 return transferred;
1120 };
1121
1122 std::ostream& WebUsbTransport::dump(std::ostream& o) const {
1123 o << "WebUsbTransport<path=" << get_path()
1124 << ", vendorId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idVendor) : "?")
1125 << ", productId=" << (m_usb_device_desc ? std::to_string(m_usb_device_desc->idProduct) : "?")
1126 << ", deviceType=";
1127
1128 if (m_usb_device_desc){
1129 if (is_trezor1(m_usb_device_desc.get()))
1130 o << "TrezorOne";
1131 else if (is_trezor2(m_usb_device_desc.get()))
1132 o << "TrezorT";
1133 else if (is_trezor2_bl(m_usb_device_desc.get()))
1134 o << "TrezorT BL";
1135 } else {
1136 o << "?";
1137 }
1138
1139 return o << ">";
1140 };
1141
1142#endif // WITH_DEVICE_TREZOR_WEBUSB
1143
1145 BridgeTransport bt;
1146 try{
1147 bt.enumerate(res);
1148 } catch (const std::exception & e){
1149 MERROR("BridgeTransport enumeration failed:" << e.what());
1150 }
1151
1152#ifdef WITH_DEVICE_TREZOR_WEBUSB
1153 hw::trezor::WebUsbTransport btw;
1154 try{
1155 btw.enumerate(res);
1156 } catch (const std::exception & e){
1157 MERROR("WebUsbTransport enumeration failed:" << e.what());
1158 }
1159#endif
1160
1161#ifdef WITH_DEVICE_TREZOR_UDP
1163 try{
1164 btu.enumerate(res);
1165 } catch (const std::exception & e){
1166 MERROR("UdpTransport enumeration failed:" << e.what());
1167 }
1168#endif
1169 }
1170
1172 const char *env_trezor_path = getenv("TREZOR_PATH");
1173 if (!env_trezor_path){
1174 return;
1175 }
1176
1177 // Sort transports by the longest matching prefix with TREZOR_PATH
1178 std::string trezor_path(env_trezor_path);
1179 std::vector<size_t> match_idx(res.size());
1180 std::vector<size_t> path_permutation(res.size());
1181
1182 for(size_t i = 0; i < res.size(); ++i){
1183 auto cpath = res[i]->get_path();
1184 std::string * s1 = &trezor_path;
1185 std::string * s2 = &cpath;
1186
1187 // first has to be shorter in std::mismatch(). Returns first non-matching iterators.
1188 if (s1->size() >= s2->size()){
1189 std::swap(s1, s2);
1190 }
1191
1192 const auto mism = std::mismatch(s1->begin(), s1->end(), s2->begin());
1193 match_idx[i] = mism.first - s1->begin();
1194 path_permutation[i] = i;
1195 }
1196
1197 std::sort(path_permutation.begin(), path_permutation.end(), [&](const size_t i0, const size_t i1) {
1198 return match_idx[i0] > match_idx[i1];
1199 });
1200
1201 tools::apply_permutation(path_permutation, res);
1202 }
1203
1204 std::shared_ptr<Transport> transport(const std::string & path){
1205 if (boost::starts_with(path, BridgeTransport::PATH_PREFIX)){
1206 return std::make_shared<BridgeTransport>(path.substr(strlen(BridgeTransport::PATH_PREFIX)));
1207
1208 } else if (boost::starts_with(path, UdpTransport::PATH_PREFIX)){
1209 return std::make_shared<UdpTransport>(path.substr(strlen(UdpTransport::PATH_PREFIX)));
1210
1211 } else {
1212 throw std::invalid_argument("Unknown Trezor device path: " + path);
1213
1214 }
1215 }
1216
1217 void throw_failure_exception(const messages::common::Failure * failure) {
1218 if (failure == nullptr){
1219 throw std::invalid_argument("Failure message cannot be null");
1220 }
1221
1222 boost::optional<std::string> message = failure->has_message() ? boost::make_optional(failure->message()) : boost::none;
1223 boost::optional<uint32_t> code = failure->has_code() ? boost::make_optional(static_cast<uint32_t>(failure->code())) : boost::none;
1224 if (!code){
1226 }
1227
1228 auto ecode = failure->code();
1229 if (ecode == messages::common::Failure_FailureType_Failure_UnexpectedMessage){
1231 } else if (ecode == messages::common::Failure_FailureType_Failure_ActionCancelled){
1233 } else if (ecode == messages::common::Failure_FailureType_Failure_PinExpected){
1235 } else if (ecode == messages::common::Failure_FailureType_Failure_PinInvalid){
1237 } else if (ecode == messages::common::Failure_FailureType_Failure_NotEnoughFunds){
1239 } else if (ecode == messages::common::Failure_FailureType_Failure_NotInitialized){
1241 } else if (ecode == messages::common::Failure_FailureType_Failure_FirmwareError){
1243 } else {
1245 }
1246 }
1247
1248 GenericMessage::GenericMessage(messages::MessageType m_type, const shared_ptr<google::protobuf::Message> &m_msg)
1250
1251 std::ostream& operator<<(std::ostream& o, hw::trezor::Transport const& t){
1252 return t.dump(o);
1253 }
1254
1255 std::ostream& operator<<(std::ostream& o, std::shared_ptr<hw::trezor::Transport> const& t){
1256 if (!t){
1257 return o << "None";
1258 }
1259
1260 return t->dump(o);
1261 }
1262
1263}
1264}
1265
const GenericValue * ConstValueIterator
Definition document.h:586
Non-owning sequence of data. Does not deep copy.
Definition span.h:57
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()
::google::protobuf::Message * get_message(int wire_number)
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 bool pre_open()
virtual std::ostream & dump(std::ostream &o) const
virtual bool pre_close()
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
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
virtual const char * what() const
std::string message("Message requiring signing")
#define ELPP
void * memcpy(void *a, const void *b, size_t c)
const char * res
void get(std::istream &input, bool &res)
Definition io.h:62
#define MERROR(x)
Definition misc_log_ex.h:73
#define ELECTRONEUM_DEFAULT_LOG_CATEGORY
Definition misc_log_ex.h:36
#define MWARNING(x)
Definition misc_log_ex.h:74
#define MDEBUG(x)
Definition misc_log_ex.h:76
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
#define MTRACE(x)
Definition misc_log_ex.h:77
#define MINFO(x)
Definition misc_log_ex.h:75
@ Warning
Useful when application has potentially harmful situtaions.
@ Error
Information representing errors in application but application will keep running.
@ Debug
Informational events most useful for developers to debug application.
std::string to_string(t_connection_type type)
bool parse_hexstr_to_binbuff(const epee::span< const char > s, epee::span< char > &res)
PUSH_WARNINGS bool get_xtype_from_string(OUT XType &val, const std::string &str_id)
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)
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
const std::string DEFAULT_BRIDGE
Definition transport.hpp:63
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
STL namespace.
void apply_permutation(std::vector< size_t > permutation, const F &swap)
#define false
CXA_THROW_INFO_T * info
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
static std::string string(const span< const std::uint8_t > src)
Definition hex.cpp:68
#define REPLEN
#define PROTO_HEADER_SIZE
rapidjson::Document json
Definition transport.cpp:49