BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
stcp_socket.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include <assert.h>
25 
26 #include <algorithm>
27 
28 #include <fc/crypto/hex.hpp>
29 #include <fc/crypto/aes.hpp>
30 #include <fc/crypto/city.hpp>
31 #include <fc/log/logger.hpp>
32 #include <fc/network/ip.hpp>
34 
36 
37 namespace graphene { namespace net {
38 
40 //:_buf_len(0)
41 #ifndef NDEBUG
42  : _read_buffer_in_use(false),
43  _write_buffer_in_use(false)
44 #endif
45 {
46 }
48 {
49 }
50 
51 void stcp_socket::do_key_exchange()
52 {
53  _priv_key = fc::ecc::private_key::generate();
54  fc::ecc::public_key pub = _priv_key.get_public_key();
56  std::shared_ptr<char> serialized_key_buffer(new char[sizeof(fc::ecc::public_key_data)], [](char* p){ delete[] p; });
57  memcpy(serialized_key_buffer.get(), (char*)&s, sizeof(fc::ecc::public_key_data));
58  _sock.write( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );
59  _sock.read( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );
61  memcpy((char*)&rpub, serialized_key_buffer.get(), sizeof(fc::ecc::public_key_data));
62 
63  _shared_secret = _priv_key.get_shared_secret( rpub );
64 // ilog("shared secret ${s}", ("s", shared_secret) );
65  _send_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ),
66  fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );
67  _recv_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ),
68  fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );
69 }
70 
71 
72 void stcp_socket::connect_to( const fc::ip::endpoint& remote_endpoint )
73 {
74  _sock.connect_to( remote_endpoint );
75  do_key_exchange();
76 }
77 
78 void stcp_socket::bind( const fc::ip::endpoint& local_endpoint )
79 {
80  _sock.bind(local_endpoint);
81 }
82 
88 size_t stcp_socket::readsome( char* buffer, size_t len )
89 { try {
90  assert( len > 0 && (len % 16) == 0 );
91 
92 #ifndef NDEBUG
93  // This code was written with the assumption that you'd only be making one call to readsome
94  // at a time so it reuses _read_buffer. If you really need to make concurrent calls to
95  // readsome(), you'll need to prevent reusing _read_buffer here
96  struct check_buffer_in_use {
97  bool& _buffer_in_use;
98  check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }
99  ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }
100  } buffer_in_use_checker(_read_buffer_in_use);
101 #endif
102 
103  const size_t read_buffer_length = 4096;
104  if (!_read_buffer)
105  _read_buffer.reset(new char[read_buffer_length], [](char* p){ delete[] p; });
106 
107  len = std::min<size_t>(read_buffer_length, len);
108 
109  size_t s = _sock.readsome( _read_buffer, len, 0 );
110  if( s % 16 )
111  {
112  _sock.read(_read_buffer, 16 - (s%16), s);
113  s += 16-(s%16);
114  }
115  _recv_aes.decode( _read_buffer.get(), s, buffer );
116  return s;
117 } FC_RETHROW_EXCEPTIONS( warn, "", ("len",len) ) }
118 
119 size_t stcp_socket::readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset )
120 {
121  return readsome(buf.get() + offset, len);
122 }
123 
124 bool stcp_socket::eof()const
125 {
126  return _sock.eof();
127 }
128 
129 size_t stcp_socket::writesome( const char* buffer, size_t len )
130 { try {
131  assert( len > 0 && (len % 16) == 0 );
132 
133 #ifndef NDEBUG
134  // This code was written with the assumption that you'd only be making one call to writesome
135  // at a time so it reuses _write_buffer. If you really need to make concurrent calls to
136  // writesome(), you'll need to prevent reusing _write_buffer here
137  struct check_buffer_in_use {
138  bool& _buffer_in_use;
139  check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }
140  ~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }
141  } buffer_in_use_checker(_write_buffer_in_use);
142 #endif
143 
144  const std::size_t write_buffer_length = 4096;
145  if (!_write_buffer)
146  _write_buffer.reset(new char[write_buffer_length], [](char* p){ delete[] p; });
147  len = std::min<size_t>(write_buffer_length, len);
148  memset(_write_buffer.get(), 0, len); // just in case aes.encode screws up
155  uint32_t ciphertext_len = _send_aes.encode( buffer, len, _write_buffer.get() );
156  assert(ciphertext_len == len);
157  _sock.write( _write_buffer, ciphertext_len );
158  return ciphertext_len;
159 } FC_RETHROW_EXCEPTIONS( warn, "", ("len",len) ) }
160 
161 size_t stcp_socket::writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset )
162 {
163  return writesome(buf.get() + offset, len);
164 }
165 
167 {
168  _sock.flush();
169 }
170 
171 
173 {
174  try
175  {
176  _sock.close();
177  }FC_RETHROW_EXCEPTIONS( warn, "error closing stcp socket" );
178 }
179 
181 {
182  do_key_exchange();
183 }
184 
185 
186 }} // namespace graphene::net
187 
void init(const fc::sha256 &key, const uint128_t &init_value)
Definition: aes.cpp:41
istream & read(char *buf, size_t len)
Definition: iostream.cpp:274
public_key_data serialize() const
uint128_t city_hash_crc_128(const char *s, size_t len)
Definition: city.cpp:649
void connect_to(const fc::ip::endpoint &remote_endpoint)
Definition: stcp_socket.cpp:72
Definition: api.cpp:56
void bind(const fc::ip::endpoint &local_endpoint)
Definition: tcp_socket.cpp:160
#define FC_RETHROW_EXCEPTIONS(LOG_LEVEL, FORMAT,...)
Catchs all exception&#39;s, std::exceptions, and ... and rethrows them after appending the provided log m...
Definition: exception.hpp:463
public_key get_public_key() const
static sha256 hash(const char *d, uint32_t dlen)
Definition: sha256.cpp:41
virtual size_t writesome(const char *buffer, size_t len)
static private_key generate()
contains only the public point of an elliptic curve key.
Definition: elliptic.hpp:35
fc::sha512 get_shared_secret(const public_key &pub) const
void init(const fc::sha256 &key, const uint128_t &init_value)
Definition: aes.cpp:109
void connect_to(const fc::ip::endpoint &remote_endpoint)
Definition: tcp_socket.cpp:156
virtual size_t readsome(char *buffer, size_t max)
Definition: stcp_socket.cpp:88
Defines exception&#39;s used by fc.
zero_initialized_array< unsigned char, 33 > public_key_data
Definition: elliptic.hpp:23
virtual bool eof() const
void bind(const fc::ip::endpoint &local_endpoint)
Definition: stcp_socket.cpp:78
ostream & write(const char *buf, size_t len)
Definition: iostream.cpp:290