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