forked from amjuarez/bytecoin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLevinProtocol.cpp
145 lines (117 loc) · 4.02 KB
/
LevinProtocol.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright (c) 2012-2017, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "LevinProtocol.h"
#include <System/TcpConnection.h>
using namespace CryptoNote;
namespace {
const uint64_t LEVIN_SIGNATURE = 0x0101010101012101LL; //Bender's nightmare
const uint32_t LEVIN_PACKET_REQUEST = 0x00000001;
const uint32_t LEVIN_PACKET_RESPONSE = 0x00000002;
const uint32_t LEVIN_DEFAULT_MAX_PACKET_SIZE = 100000000; //100MB by default
const uint32_t LEVIN_PROTOCOL_VER_1 = 1;
#pragma pack(push)
#pragma pack(1)
struct bucket_head2
{
uint64_t m_signature;
uint64_t m_cb;
bool m_have_to_return_data;
uint32_t m_command;
int32_t m_return_code;
uint32_t m_flags;
uint32_t m_protocol_version;
};
#pragma pack(pop)
}
bool LevinProtocol::Command::needReply() const {
return !(isNotify || isResponse);
}
LevinProtocol::LevinProtocol(System::TcpConnection& connection)
: m_conn(connection) {}
void LevinProtocol::sendMessage(uint32_t command, const BinaryArray& out, bool needResponse) {
bucket_head2 head = { 0 };
head.m_signature = LEVIN_SIGNATURE;
head.m_cb = out.size();
head.m_have_to_return_data = needResponse;
head.m_command = command;
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
head.m_flags = LEVIN_PACKET_REQUEST;
// write header and body in one operation
BinaryArray writeBuffer;
writeBuffer.reserve(sizeof(head) + out.size());
Common::VectorOutputStream stream(writeBuffer);
stream.writeSome(&head, sizeof(head));
stream.writeSome(out.data(), out.size());
writeStrict(writeBuffer.data(), writeBuffer.size());
}
bool LevinProtocol::readCommand(Command& cmd) {
bucket_head2 head = { 0 };
if (!readStrict(reinterpret_cast<uint8_t*>(&head), sizeof(head))) {
return false;
}
if (head.m_signature != LEVIN_SIGNATURE) {
throw std::runtime_error("Levin signature mismatch");
}
if (head.m_cb > LEVIN_DEFAULT_MAX_PACKET_SIZE) {
throw std::runtime_error("Levin packet size is too big");
}
BinaryArray buf;
if (head.m_cb != 0) {
buf.resize(head.m_cb);
if (!readStrict(&buf[0], head.m_cb)) {
return false;
}
}
cmd.command = head.m_command;
cmd.buf = std::move(buf);
cmd.isNotify = !head.m_have_to_return_data;
cmd.isResponse = (head.m_flags & LEVIN_PACKET_RESPONSE) == LEVIN_PACKET_RESPONSE;
return true;
}
void LevinProtocol::sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode) {
bucket_head2 head = { 0 };
head.m_signature = LEVIN_SIGNATURE;
head.m_cb = out.size();
head.m_have_to_return_data = false;
head.m_command = command;
head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
head.m_flags = LEVIN_PACKET_RESPONSE;
head.m_return_code = returnCode;
BinaryArray writeBuffer;
writeBuffer.reserve(sizeof(head) + out.size());
Common::VectorOutputStream stream(writeBuffer);
stream.writeSome(&head, sizeof(head));
stream.writeSome(out.data(), out.size());
writeStrict(writeBuffer.data(), writeBuffer.size());
}
void LevinProtocol::writeStrict(const uint8_t* ptr, size_t size) {
size_t offset = 0;
while (offset < size) {
offset += m_conn.write(ptr + offset, size - offset);
}
}
bool LevinProtocol::readStrict(uint8_t* ptr, size_t size) {
size_t offset = 0;
while (offset < size) {
size_t read = m_conn.read(ptr + offset, size - offset);
if (read == 0) {
return false;
}
offset += read;
}
return true;
}