Botcraft 1.21.5
Loading...
Searching...
No Matches
TCP_Com.cpp
Go to the documentation of this file.
1#include <functional>
2#include <asio/connect.hpp>
3#include <asio/write.hpp>
4#include <asio/ip/udp.hpp>
5
7
11#ifdef USE_ENCRYPTION
13#endif
14
17
18namespace Botcraft
19{
20 TCP_Com::TCP_Com(const std::string& address,
21 std::function<void(const std::vector<unsigned char>&)> callback)
22 : socket(io_context), initialized(false)
23 {
24 NewPacketCallback = callback;
25
27
28 asio::ip::tcp::resolver resolver(io_context);
29 asio::ip::tcp::resolver::results_type results = resolver.resolve(ip, std::to_string(port));
30 LOG_INFO("Trying to connect to " << ip << ":" << port);
31 asio::async_connect(socket, results,
32 std::bind(&TCP_Com::handle_connect, this,
33 std::placeholders::_1));
34
35 thread_com = std::thread([&] { io_context.run(); });
36 Logger::GetInstance().RegisterThread(thread_com.get_id(), "NetworkIOService");
37 }
38
40 {
41 if (thread_com.joinable())
42 {
44 thread_com.join();
45 }
46 }
47
49 {
50 return initialized;
51 }
52
53 void TCP_Com::SendPacket(const std::vector<unsigned char>& bytes)
54 {
55 std::vector<unsigned char> sized_packet;
56 sized_packet.reserve(bytes.size() + 5);
57 ProtocolCraft::WriteData<ProtocolCraft::VarInt>(static_cast<int>(bytes.size()), sized_packet);
58 sized_packet.insert(sized_packet.end(), bytes.begin(), bytes.end());
59
60#ifdef USE_ENCRYPTION
61 if (encrypter != nullptr)
62 {
63 std::vector<unsigned char> encrypted = encrypter->Encrypt(sized_packet);
64 asio::post(io_context, std::bind(&TCP_Com::do_write, this, encrypted));
65 }
66 else
67 {
68 asio::post(io_context, std::bind(&TCP_Com::do_write, this, sized_packet));
69 }
70#else
71 asio::post(io_context, std::bind(&TCP_Com::do_write, this, sized_packet));
72#endif
73 }
74
75#ifdef USE_ENCRYPTION
76 void TCP_Com::SetEncrypter(const std::shared_ptr<AESEncrypter> encrypter_)
77 {
78 encrypter = encrypter_;
79 }
80#endif
81
82 const std::string& TCP_Com::GetIp() const
83 {
84 return ip;
85 }
86
87 const unsigned short TCP_Com::GetPort() const
88 {
89 return port;
90 }
91
93 {
94 asio::post(io_context, std::bind(&TCP_Com::do_close, this));
95 }
96
97 void TCP_Com::handle_connect(const asio::error_code& error)
98 {
99 if (!error)
100 {
101 LOG_INFO("Connection to server established.");
102 initialized = true;
103 socket.async_read_some(asio::buffer(read_packet.data(), read_packet.size()),
104 std::bind(&TCP_Com::handle_read, this,
105 std::placeholders::_1, std::placeholders::_2));
106 }
107 else
108 {
109 LOG_ERROR("Error when connecting to server. Error code :" << error);
110 }
111 }
112
113 void TCP_Com::handle_read(const asio::error_code& error, std::size_t bytes_transferred)
114 {
115 if (!error)
116 {
117#ifdef USE_ENCRYPTION
118 if (encrypter != nullptr)
119 {
120 std::vector<unsigned char> decrypted(bytes_transferred);
121 std::copy_n(read_packet.begin(), bytes_transferred, decrypted.data());
122 decrypted = encrypter->Decrypt(decrypted);
123 for (int i = 0; i < decrypted.size(); ++i)
124 {
125 input_packet.push_back(decrypted[i]);
126 }
127 }
128 else
129 {
130 for (int i = 0; i < bytes_transferred; ++i)
131 {
132 input_packet.push_back(read_packet[i]);
133 }
134 }
135#else
136 for (int i = 0; i < bytes_transferred; ++i)
137 {
138 input_packet.push_back(read_packet[i]);
139 }
140#endif
141
142 while (input_packet.size() != 0)
143 {
144 std::vector<unsigned char>::const_iterator read_iter = input_packet.begin();
145 size_t max_length = input_packet.size();
146 int packet_length;
147 try
148 {
149 packet_length = ProtocolCraft::ReadData<ProtocolCraft::VarInt>(read_iter, max_length);
150 }
151 catch (const std::runtime_error)
152 {
153 break;
154 }
155 const int bytes_read = static_cast<int>(std::distance<std::vector<unsigned char>::const_iterator>(input_packet.begin(), read_iter));
156 std::vector<unsigned char> data_packet;
157
158 if (packet_length > 0 && input_packet.size() >= bytes_read + packet_length)
159 {
160 data_packet = std::vector<unsigned char>(input_packet.begin() + bytes_read, input_packet.begin() + bytes_read + packet_length);
161
162 NewPacketCallback(data_packet);
163 input_packet.erase(input_packet.begin(), input_packet.begin() + bytes_read + packet_length);
164 }
165 else
166 {
167 break;
168 }
169 }
170
171 socket.async_read_some(asio::buffer(read_packet.data(), read_packet.size()),
172 std::bind(&TCP_Com::handle_read, this,
173 std::placeholders::_1, std::placeholders::_2));
174 }
175 else
176 {
177 do_close();
178 }
179 }
180
181 void TCP_Com::do_write(const std::vector<unsigned char>& bytes)
182 {
183 mutex_output.lock();
184 bool write_in_progress = !output_packet.empty();
185 output_packet.push_back(bytes);
186 mutex_output.unlock();
187
188 if (!write_in_progress)
189 {
190 asio::async_write(socket,
191 asio::buffer(output_packet.front().data(),
192 output_packet.front().size()),
193 std::bind(&TCP_Com::handle_write, this,
194 std::placeholders::_1));
195 }
196 }
197
198 void TCP_Com::handle_write(const asio::error_code& error)
199 {
200 if (!error)
201 {
202 mutex_output.lock();
203 output_packet.pop_front();
204 mutex_output.unlock();
205
206 if (!output_packet.empty())
207 {
208 asio::async_write(socket,
209 asio::buffer(output_packet.front().data(),
210 output_packet.front().size()),
211 std::bind(&TCP_Com::handle_write, this,
212 std::placeholders::_1));
213 }
214 }
215 else
216 {
217 do_close();
218 }
219 }
220
222 {
223 socket.close();
224 }
225
226 void TCP_Com::SetIPAndPortFromAddress(const std::string& address)
227 {
228 std::string addressOnly;
229
230 const std::vector<std::string> splitted_port = Utilities::SplitString(address, ':');
231 // address:port format
232 if (splitted_port.size() > 1)
233 {
234 try
235 {
236 port = std::stoi(splitted_port[1]);
237 ip = splitted_port[0];
238 return;
239 }
240 catch (const std::exception&)
241 {
242 port = 0;
243 }
244 addressOnly = splitted_port[0];
245 }
246 // address only format
247 else
248 {
249 addressOnly = address;
250 port = 0;
251 }
252
253 // If port is unknown we first try a SRV DNS lookup
254 LOG_INFO("Performing SRV DNS lookup on " << "_minecraft._tcp." << address << " to find an endpoint");
255 asio::ip::udp::socket udp_socket(io_context);
256
257 // Create the query
258 DNSMessage query;
259 // Random identification
260 query.SetIdentification({ 0x42, 0x42 });
261 query.SetFlagQR(0);
262 query.SetFlagOPCode(0);
263 query.SetFlagAA(0);
264 query.SetFlagTC(0);
265 query.SetFlagRD(1);
266 query.SetFlagRA(0);
267 query.SetFlagZ(0);
268 query.SetFlagRCode(0);
269 query.SetNumberQuestion(1);
270 query.SetNumberAnswer(0);
271 query.SetNumberAuthority(0);
272 query.SetNumberAdditionalRR(0);
273 DNSQuestion question;
274 // SRV type
275 question.SetTypeCode(33);
276 question.SetClassCode(1);
277 question.SetNameLabels(Utilities::SplitString("_minecraft._tcp." + address, '.'));
278 query.SetQuestions({ question });
279
280 // Write the request and send it to google DNS
281 std::vector<unsigned char> encoded_query;
282 query.Write(encoded_query);
283 udp_socket.open(asio::ip::udp::v4());
284 asio::ip::udp::endpoint endpoint(asio::ip::make_address("8.8.8.8"), 53);
285 udp_socket.send_to(asio::buffer(encoded_query), endpoint);
286
287 // Wait for the answer
288 std::vector<unsigned char> answer_buffer(512);
289 asio::ip::udp::endpoint sender_endpoint;
290 const size_t len = udp_socket.receive_from(asio::buffer(answer_buffer), sender_endpoint);
291
292 ProtocolCraft::ReadIterator iter = answer_buffer.begin();
293 size_t remaining = len;
294
295 // Read answer
296 DNSMessage answer;
297 answer.Read(iter, remaining);
298
299 // If there is an answer and it's a SRV one (as it should be)
300 if (answer.GetNumberAnswer() > 0
301 && answer.GetAnswers()[0].GetTypeCode() == 0x21)
302 {
303 DNSSrvData data;
304 auto iter2 = answer.GetAnswers()[0].GetRData().begin();
305 size_t len2 = answer.GetAnswers()[0].GetRDLength();
306 data.Read(iter2, len2);
307 ip = "";
308 for (int i = 0; i < data.GetNameLabels().size(); ++i)
309 {
310 ip += data.GetNameLabels()[i] + (i == data.GetNameLabels().size() - 1 ? "" : ".");
311 }
312 port = data.GetPort();
313
314 LOG_INFO("SRV DNS lookup successful!");
315 return;
316 }
317 LOG_WARNING("SRV DNS lookup failed to find an address");
318
319 // If we are here either the port was given or the SRV failed
320 // In both cases we need to assume the given address is the correct one
321 port = (port == 0) ? 25565 : port;
322 ip = addressOnly;
323 }
324} //Botcraft
#define LOG_ERROR(osstream)
Definition Logger.hpp:45
#define LOG_WARNING(osstream)
Definition Logger.hpp:44
#define LOG_INFO(osstream)
Definition Logger.hpp:43
void SetIdentification(const std::vector< unsigned char > &identification_)
void SetFlagOPCode(const char opcode_)
void SetQuestions(const std::vector< DNSQuestion > &questions_)
void SetFlagRA(const char ra_)
void SetFlagQR(const char qr_)
void SetFlagRCode(const char rcode_)
short GetNumberAnswer() const
void SetNumberAdditionalRR(const short number_additional_rr_)
void SetFlagAA(const char aa_)
void SetNumberAnswer(const short number_answer_)
void SetFlagZ(const char z_)
void SetFlagRD(const char rd_)
const std::vector< DNSResourceRecord > & GetAnswers() const
void SetFlagTC(const char tc_)
void SetNumberQuestion(const short number_question_)
void SetNumberAuthority(const short number_authority_)
void SetClassCode(const unsigned short class_code_)
void SetNameLabels(const std::vector< std::string > &name_labels_)
void SetTypeCode(const unsigned short type_code_)
const unsigned short GetPort() const
const std::vector< std::string > & GetNameLabels() const
void UnregisterThread(const std::thread::id id)
Remove a thread from the map.
Definition Logger.cpp:130
void RegisterThread(const std::string &name)
Register the current thread in the map.
Definition Logger.cpp:104
static Logger & GetInstance()
Definition Logger.cpp:36
void do_write(const std::vector< unsigned char > &bytes)
Definition TCP_Com.cpp:181
void SendPacket(const std::vector< unsigned char > &bytes)
Definition TCP_Com.cpp:53
asio::ip::tcp::socket socket
Definition TCP_Com.hpp:54
std::function< void(const std::vector< unsigned char > &)> NewPacketCallback
Definition TCP_Com.hpp:62
std::shared_ptr< AESEncrypter > encrypter
Definition TCP_Com.hpp:69
std::vector< unsigned char > input_packet
Definition TCP_Com.hpp:59
TCP_Com(const std::string &address, std::function< void(const std::vector< unsigned char > &)> callback)
Definition TCP_Com.cpp:20
void SetEncrypter(const std::shared_ptr< AESEncrypter > encrypter_)
Definition TCP_Com.cpp:76
void handle_write(const asio::error_code &error)
Definition TCP_Com.cpp:198
void SetIPAndPortFromAddress(const std::string &address)
Definition TCP_Com.cpp:226
asio::io_context io_context
Definition TCP_Com.hpp:53
std::mutex mutex_output
Definition TCP_Com.hpp:63
std::string ip
Definition TCP_Com.hpp:65
void handle_connect(const asio::error_code &error)
Definition TCP_Com.cpp:97
std::thread thread_com
Definition TCP_Com.hpp:56
unsigned short port
Definition TCP_Com.hpp:66
void handle_read(const asio::error_code &error, std::size_t bytes_transferred)
Definition TCP_Com.cpp:113
bool IsInitialized() const
Definition TCP_Com.cpp:48
const std::string & GetIp() const
Definition TCP_Com.cpp:82
std::atomic< bool > initialized
Definition TCP_Com.hpp:72
const unsigned short GetPort() const
Definition TCP_Com.cpp:87
std::deque< std::vector< unsigned char > > output_packet
Definition TCP_Com.hpp:60
std::array< unsigned char, 512 > read_packet
Definition TCP_Com.hpp:58
virtual void Write(WriteContainer &container) const
virtual void Read(ReadIterator &iter, size_t &length)
std::vector< std::string > SplitString(const std::string &s, const char delimiter)
std::vector< unsigned char >::const_iterator ReadIterator