1#pragma once
2
3#ifndef _WIN32_WINNT
4#define _WIN32_WINNT 0x501
5#endif
6
7#include <cstdint>
8#include <boost/asio.hpp>
9#include <memory>
10#include <string>
11#include <list>
12#include <thread>
13#include <functional>
14#include <map>
15#include <chrono>
16#include <chrono>
17#include <vector>
18#include <random>
19#include <set>
20
21using ProxyPacket = std::vector<uint8_t>;
22using ProxyPacketPtr = std::shared_ptr<ProxyPacket>;
23
24class Session;
25using SessionPtr = std::shared_ptr<Session>;
26
27class Proxy : public std::enable_shared_from_this<Proxy> {
28 static constexpr int CHECK_INTERVAL = 2500; // also timeout for ping
29 static constexpr int BUFFER_SIZE = 65535;
30 enum ProxyState {
31 STATE_NOT_CONNECTED,
32 STATE_CONNECTING,
33 STATE_CONNECTING_WAIT_FOR_PING,
34 STATE_CONNECTED
35 };
36public:
37 Proxy(boost::asio::io_context& io, const std::string& host, uint16_t port, int priority)
38 : m_io(io), m_timer(io), m_socket(io), m_resolver(io), m_state(STATE_NOT_CONNECTED)
39 {
40 m_host = host;
41 m_port = port;
42 m_priority = priority;
43 }
44
45 // thread-safe
46 void start();
47 void terminate();
48
49 uint32_t getPing() { return m_ping + m_priority; }
50 uint32_t getRealPing() { return m_ping; }
51 uint32_t getPriority() { return m_priority; }
52 bool isConnected() { return m_state == STATE_CONNECTED; }
53 std::string getHost() { return m_host; }
54 uint16_t getPort() { return m_port; }
55 std::string getDebugInfo();
56 bool isActive() { return m_sessions > 0; }
57
58 // not thread-safe
59 void addSession(uint32_t id, int m_port);
60 void removeSession(uint32_t id);
61
62 void send(const ProxyPacketPtr& packet);
63
64private:
65 void check(const boost::system::error_code& ec = boost::system::error_code());
66 void connect();
67 void disconnect();
68
69 void ping();
70 void onPing(uint32_t packetId);
71
72 void readHeader();
73 void onHeader(const boost::system::error_code& ec, std::size_t bytes_transferred);
74 void onPacket(const boost::system::error_code& ec, std::size_t bytes_transferred);
75 void onSent(const boost::system::error_code& ec, std::size_t bytes_transferred);
76
77 boost::asio::io_context& m_io;
78 boost::asio::steady_timer m_timer;
79 boost::asio::ip::tcp::socket m_socket;
80 boost::asio::ip::tcp::resolver m_resolver;
81
82 ProxyState m_state;
83
84 std::string m_host;
85 uint16_t m_port;
86
87 bool m_terminated = false;
88
89 uint8_t m_buffer[BUFFER_SIZE];
90 std::list<ProxyPacketPtr> m_sendQueue;
91
92 bool m_waitingForPing;
93 uint32_t m_ping = 0;
94 int m_priority = 0;
95 std::chrono::time_point<std::chrono::high_resolution_clock> m_lastPingSent;
96
97 int m_packetsRecived = 0;
98 int m_packetsSent = 0;
99 int m_bytesRecived = 0;
100 int m_bytesSent = 0;
101 int m_connections = 0;
102 int m_sessions = 0;
103 std::string m_resolvedIp;
104};
105
106using ProxyPtr = std::shared_ptr<Proxy>;
107
108class Session : public std::enable_shared_from_this<Session> {
109 static constexpr int CHECK_INTERVAL = 500;
110 static constexpr int BUFFER_SIZE = 65535;
111 static constexpr int TIMEOUT = 30000;
112public:
113 Session(boost::asio::io_context& io, boost::asio::ip::tcp::socket socket, int port)
114 : m_io(io), m_timer(io), m_socket(std::move(socket))
115 {
116 m_id = (std::chrono::high_resolution_clock::now().time_since_epoch().count()) & 0xFFFFFFFF;
117 if (m_id == 0) m_id = 1;
118 m_port = port;
119 m_useSocket = true;
120 }
121
122 Session(boost::asio::io_context& io, int port, std::function<void(ProxyPacketPtr)> recvCallback, std::function<void(boost::system::error_code)> disconnectCallback)
123 : m_io(io), m_timer(io), m_socket(io)
124 {
125 m_id = (std::chrono::high_resolution_clock::now().time_since_epoch().count()) & 0xFFFFFFFF;
126 if (m_id == 0) m_id = 1;
127 m_port = port;
128 m_useSocket = false;
129 m_recvCallback = recvCallback;
130 m_disconnectCallback = disconnectCallback;
131 }
132
133 // thread safe
134 uint32_t getId() { return m_id; }
135 void start(int maxConnections = 3);
136 void terminate(boost::system::error_code ec = boost::asio::error::eof);
137 void onPacket(const ProxyPacketPtr& packet);
138
139 // not thread safe
140 void onProxyPacket(uint32_t packetId, uint32_t lastRecivedPacketId, const ProxyPacketPtr& packet);
141
142private:
143 void check(const boost::system::error_code& ec);
144 void selectProxies();
145
146 void readTibia12Header();
147 void readHeader();
148 void onHeader(const boost::system::error_code& ec, std::size_t bytes_transferred);
149 void onBody(const boost::system::error_code& ec, std::size_t bytes_transferred);
150 void onSent(const boost::system::error_code& ec, std::size_t bytes_transferred);
151
152 boost::asio::io_context& m_io;
153 boost::asio::steady_timer m_timer;
154 boost::asio::ip::tcp::socket m_socket;
155
156 uint32_t m_id;
157 uint16_t m_port;
158 bool m_useSocket;
159 bool m_terminated = false;
160
161 std::function<void(ProxyPacketPtr)> m_recvCallback = nullptr;
162 std::function<void(boost::system::error_code)> m_disconnectCallback = nullptr;
163
164 std::set<ProxyPtr> m_proxies;
165
166 int m_maxConnections;
167
168 uint8_t m_buffer[BUFFER_SIZE];
169 std::map<uint32_t, ProxyPacketPtr> m_sendQueue;
170 std::map<uint32_t, ProxyPacketPtr> m_proxySendQueue;
171
172 uint32_t m_inputPacketId = 1;
173 uint32_t m_outputPacketId = 1;
174
175 std::chrono::time_point<std::chrono::high_resolution_clock> m_lastPacket;
176};