├── readme.txt ├── tcpproxy_server_v01.cpp ├── tcpproxy_server_v02.cpp ├── tcpproxy_server_v03.cpp └── tcpproxy_server_v04.cpp /readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArashPartow/tcpproxy-variations/8be0a82d2c93a5eefcc09a4be490314c23ab3c92/readme.txt -------------------------------------------------------------------------------- /tcpproxy_server_v01.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // tcpproxy_server_v01.cpp 3 | // ~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2007 Arash Partow (http://www.partow.net) 6 | // URL: http://www.partow.net/programming/tcpproxy/index.html 7 | // 8 | // Distributed under the Boost Software License, Version 1.0. 9 | // 10 | // 11 | // Variation: Multi-threaded I/O service 12 | // In this example, multiple threads will be instantiated and associated 13 | // with the io_service. This will allow the io_service to handle bridges 14 | // using seperate threads, allowing for the more efficient use of the 15 | // processor's capabilities. 16 | // 17 | // 18 | 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | namespace tcp_proxy 35 | { 36 | namespace ip = boost::asio::ip; 37 | 38 | class bridge : public boost::enable_shared_from_this 39 | { 40 | public: 41 | 42 | typedef ip::tcp::socket socket_type; 43 | typedef boost::shared_ptr ptr_type; 44 | 45 | bridge(boost::asio::io_service& ios) 46 | : downstream_socket_(ios), 47 | upstream_socket_(ios) 48 | {} 49 | 50 | socket_type& downstream_socket() 51 | { 52 | return downstream_socket_; 53 | } 54 | 55 | socket_type& upstream_socket() 56 | { 57 | return upstream_socket_; 58 | } 59 | 60 | void start(const std::string& upstream_host, unsigned short upstream_port) 61 | { 62 | upstream_socket_.async_connect( 63 | ip::tcp::endpoint( 64 | boost::asio::ip::address::from_string(upstream_host), 65 | upstream_port), 66 | boost::bind(&bridge::handle_upstream_connect, 67 | shared_from_this(), 68 | boost::asio::placeholders::error)); 69 | } 70 | 71 | void handle_upstream_connect(const boost::system::error_code& error) 72 | { 73 | if (!error) 74 | { 75 | upstream_socket_.async_read_some( 76 | boost::asio::buffer(upstream_data_,max_data_length), 77 | boost::bind(&bridge::handle_upstream_read, 78 | shared_from_this(), 79 | boost::asio::placeholders::error, 80 | boost::asio::placeholders::bytes_transferred)); 81 | 82 | downstream_socket_.async_read_some( 83 | boost::asio::buffer(downstream_data_,max_data_length), 84 | boost::bind(&bridge::handle_downstream_read, 85 | shared_from_this(), 86 | boost::asio::placeholders::error, 87 | boost::asio::placeholders::bytes_transferred)); 88 | } 89 | else 90 | close(); 91 | } 92 | 93 | private: 94 | 95 | void handle_downstream_write(const boost::system::error_code& error) 96 | { 97 | if (!error) 98 | { 99 | upstream_socket_.async_read_some( 100 | boost::asio::buffer(upstream_data_,max_data_length), 101 | boost::bind(&bridge::handle_upstream_read, 102 | shared_from_this(), 103 | boost::asio::placeholders::error, 104 | boost::asio::placeholders::bytes_transferred)); 105 | } 106 | else 107 | close(); 108 | } 109 | 110 | void handle_downstream_read(const boost::system::error_code& error, 111 | const size_t& bytes_transferred) 112 | { 113 | if (!error) 114 | { 115 | async_write(upstream_socket_, 116 | boost::asio::buffer(downstream_data_,bytes_transferred), 117 | boost::bind(&bridge::handle_upstream_write, 118 | shared_from_this(), 119 | boost::asio::placeholders::error)); 120 | } 121 | else 122 | close(); 123 | } 124 | 125 | void handle_upstream_write(const boost::system::error_code& error) 126 | { 127 | if (!error) 128 | { 129 | downstream_socket_.async_read_some( 130 | boost::asio::buffer(downstream_data_,max_data_length), 131 | boost::bind(&bridge::handle_downstream_read, 132 | shared_from_this(), 133 | boost::asio::placeholders::error, 134 | boost::asio::placeholders::bytes_transferred)); 135 | } 136 | else 137 | close(); 138 | } 139 | 140 | void handle_upstream_read(const boost::system::error_code& error, 141 | const size_t& bytes_transferred) 142 | { 143 | if (!error) 144 | { 145 | async_write(downstream_socket_, 146 | boost::asio::buffer(upstream_data_,bytes_transferred), 147 | boost::bind(&bridge::handle_downstream_write, 148 | shared_from_this(), 149 | boost::asio::placeholders::error)); 150 | } 151 | else 152 | close(); 153 | } 154 | 155 | void close() 156 | { 157 | boost::mutex::scoped_lock lock(mutex_); 158 | 159 | if (downstream_socket_.is_open()) 160 | { 161 | downstream_socket_.close(); 162 | } 163 | 164 | if (upstream_socket_.is_open()) 165 | { 166 | upstream_socket_.close(); 167 | } 168 | } 169 | 170 | socket_type downstream_socket_; 171 | socket_type upstream_socket_; 172 | 173 | enum { max_data_length = 8192 }; //8KB 174 | unsigned char downstream_data_[max_data_length]; 175 | unsigned char upstream_data_[max_data_length]; 176 | 177 | boost::mutex mutex_; 178 | 179 | public: 180 | 181 | class acceptor 182 | { 183 | public: 184 | 185 | acceptor(boost::asio::io_service& io_service, 186 | const std::string& local_host, unsigned short local_port, 187 | const std::string& upstream_host, unsigned short upstream_port) 188 | : io_service_(io_service), 189 | localhost_address(boost::asio::ip::address_v4::from_string(local_host)), 190 | acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)), 191 | upstream_port_(upstream_port), 192 | upstream_host_(upstream_host) 193 | {} 194 | 195 | bool accept_connections() 196 | { 197 | try 198 | { 199 | session_ = boost::shared_ptr(new bridge(io_service_)); 200 | 201 | acceptor_.async_accept(session_->downstream_socket(), 202 | boost::bind(&acceptor::handle_accept, 203 | this, 204 | boost::asio::placeholders::error)); 205 | } 206 | catch(std::exception& e) 207 | { 208 | std::cerr << "acceptor exception: " << e.what() << std::endl; 209 | return false; 210 | } 211 | 212 | return true; 213 | } 214 | 215 | private: 216 | 217 | void handle_accept(const boost::system::error_code& error) 218 | { 219 | if (!error) 220 | { 221 | session_->start(upstream_host_,upstream_port_); 222 | 223 | if (!accept_connections()) 224 | { 225 | std::cerr << "Failure during call to accept." << std::endl; 226 | } 227 | } 228 | else 229 | { 230 | std::cerr << "Error: " << error.message() << std::endl; 231 | } 232 | } 233 | 234 | boost::asio::io_service& io_service_; 235 | ip::address_v4 localhost_address; 236 | ip::tcp::acceptor acceptor_; 237 | ptr_type session_; 238 | unsigned short upstream_port_; 239 | std::string upstream_host_; 240 | }; 241 | 242 | }; 243 | 244 | class ios_thread_pool 245 | { 246 | public: 247 | 248 | typedef boost::shared_ptr thread_t; 249 | 250 | ios_thread_pool() 251 | : thread_count_(0) 252 | {} 253 | 254 | ios_thread_pool& set_thread_count(const std::size_t thread_count) 255 | { 256 | thread_count_ = thread_count; 257 | return *this; 258 | } 259 | 260 | void run(boost::asio::io_service& ios) 261 | { 262 | for (std::size_t i = 0; i < thread_count_; ++i) 263 | { 264 | thread_t thread(new boost::thread(boost::bind(&boost::asio::io_service::run,&ios))); 265 | thread_pool_.push_back(thread); 266 | } 267 | 268 | for (std::size_t i = 0; i < thread_pool_.size(); ++i) 269 | { 270 | thread_pool_[i]->join(); 271 | } 272 | } 273 | 274 | private: 275 | 276 | std::size_t thread_count_; 277 | std::deque thread_pool_; 278 | }; 279 | } 280 | 281 | int main(int argc, char* argv[]) 282 | { 283 | if (argc != 6) 284 | { 285 | std::cerr << "usage: tcpproxy_server " << std::endl; 286 | return 1; 287 | } 288 | 289 | const unsigned short local_port = static_cast(::atoi(argv[2])); 290 | const unsigned short forward_port = static_cast(::atoi(argv[4])); 291 | const std::string local_host = argv[1]; 292 | const std::string forward_host = argv[3]; 293 | const unsigned int number_of_threads = static_cast(::atoi(argv[5])); 294 | 295 | boost::asio::io_service ios; 296 | 297 | try 298 | { 299 | tcp_proxy::bridge::acceptor acceptor(ios, 300 | local_host, local_port, 301 | forward_host, forward_port); 302 | 303 | acceptor.accept_connections(); 304 | 305 | tcp_proxy::ios_thread_pool() 306 | .set_thread_count(number_of_threads) 307 | .run(ios); 308 | } 309 | catch(std::exception& e) 310 | { 311 | std::cerr << "Error: " << e.what() << std::endl; 312 | return 1; 313 | } 314 | 315 | return 0; 316 | } 317 | 318 | /* 319 | * [Note] On posix systems the tcp proxy server build command is as follows: 320 | * c++ -pedantic -ansi -Wall -Werror -O3 -o tcpproxy_server_v01 tcpproxy_server_v01.cpp -L/usr/lib -lstdc++ -lpthread -lboost_thread -lboost_system 321 | */ 322 | -------------------------------------------------------------------------------- /tcpproxy_server_v02.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // tcpproxy_server_v02.cpp 3 | // ~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2007 Arash Partow (http://www.partow.net) 6 | // URL: http://www.partow.net/programming/tcpproxy/index.html 7 | // 8 | // Distributed under the Boost Software License, Version 1.0. 9 | // 10 | // 11 | // Variation: Limit upstream data flow 12 | // In this example, a maximum limit on the number of bytes sent upstream 13 | // is provided, and the limit itself is enforced in the bridge. Once the 14 | // limit has been reached, a shutdown of the bridge will occur. 15 | // 16 | // 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | namespace tcp_proxy 32 | { 33 | namespace ip = boost::asio::ip; 34 | 35 | class bridge : public boost::enable_shared_from_this 36 | { 37 | public: 38 | 39 | typedef ip::tcp::socket socket_type; 40 | typedef boost::shared_ptr ptr_type; 41 | 42 | bridge(boost::asio::io_service& ios) 43 | : downstream_socket_(ios), 44 | upstream_socket_(ios) 45 | {} 46 | 47 | socket_type& downstream_socket() 48 | { 49 | return downstream_socket_; 50 | } 51 | 52 | socket_type& upstream_socket() 53 | { 54 | return upstream_socket_; 55 | } 56 | 57 | void start(const std::size_t& max_bytes, 58 | const std::string& upstream_host, unsigned short upstream_port) 59 | { 60 | bytes_left_to_send_ = max_bytes; 61 | 62 | upstream_socket_.async_connect( 63 | ip::tcp::endpoint( 64 | boost::asio::ip::address::from_string(upstream_host), 65 | upstream_port), 66 | boost::bind(&bridge::handle_upstream_connect, 67 | shared_from_this(), 68 | boost::asio::placeholders::error)); 69 | } 70 | 71 | void handle_upstream_connect(const boost::system::error_code& error) 72 | { 73 | if (!error) 74 | { 75 | upstream_socket_.async_read_some( 76 | boost::asio::buffer(upstream_data_,max_data_length), 77 | boost::bind(&bridge::handle_upstream_read, 78 | shared_from_this(), 79 | boost::asio::placeholders::error, 80 | boost::asio::placeholders::bytes_transferred)); 81 | 82 | downstream_socket_.async_read_some( 83 | boost::asio::buffer(downstream_data_,max_data_length), 84 | boost::bind(&bridge::handle_downstream_read, 85 | shared_from_this(), 86 | boost::asio::placeholders::error, 87 | boost::asio::placeholders::bytes_transferred)); 88 | } 89 | else 90 | close(); 91 | } 92 | 93 | private: 94 | 95 | void handle_downstream_write(const boost::system::error_code& error) 96 | { 97 | if (!error) 98 | { 99 | upstream_socket_.async_read_some( 100 | boost::asio::buffer(upstream_data_,max_data_length), 101 | boost::bind(&bridge::handle_upstream_read, 102 | shared_from_this(), 103 | boost::asio::placeholders::error, 104 | boost::asio::placeholders::bytes_transferred)); 105 | } 106 | else 107 | close(); 108 | } 109 | 110 | void handle_downstream_read(const boost::system::error_code& error, 111 | const size_t& bytes_transferred) 112 | { 113 | if (!error) 114 | { 115 | std::size_t bytes_to_transfer = bytes_transferred; 116 | 117 | if (bytes_left_to_send_ >= bytes_transferred) 118 | bytes_left_to_send_ -= bytes_transferred; 119 | else 120 | { 121 | bytes_to_transfer = bytes_left_to_send_; 122 | bytes_left_to_send_ = 0; 123 | } 124 | 125 | async_write(upstream_socket_, 126 | boost::asio::buffer(downstream_data_,bytes_to_transfer), 127 | boost::bind(&bridge::handle_upstream_write, 128 | shared_from_this(), 129 | boost::asio::placeholders::error)); 130 | } 131 | else 132 | close(); 133 | } 134 | 135 | void handle_upstream_write(const boost::system::error_code& error) 136 | { 137 | if (!error) 138 | { 139 | if (bytes_left_to_send_) 140 | { 141 | downstream_socket_.async_read_some( 142 | boost::asio::buffer(downstream_data_,max_data_length), 143 | boost::bind(&bridge::handle_downstream_read, 144 | shared_from_this(), 145 | boost::asio::placeholders::error, 146 | boost::asio::placeholders::bytes_transferred)); 147 | } 148 | else 149 | close(); 150 | } 151 | else 152 | close(); 153 | } 154 | 155 | void handle_upstream_read(const boost::system::error_code& error, 156 | const size_t& bytes_transferred) 157 | { 158 | if (!error) 159 | { 160 | async_write(downstream_socket_, 161 | boost::asio::buffer(upstream_data_,bytes_transferred), 162 | boost::bind(&bridge::handle_downstream_write, 163 | shared_from_this(), 164 | boost::asio::placeholders::error)); 165 | } 166 | else 167 | close(); 168 | } 169 | 170 | void close() 171 | { 172 | boost::mutex::scoped_lock lock(mutex_); 173 | 174 | if (downstream_socket_.is_open()) 175 | { 176 | downstream_socket_.close(); 177 | } 178 | 179 | if (upstream_socket_.is_open()) 180 | { 181 | upstream_socket_.close(); 182 | } 183 | } 184 | 185 | socket_type downstream_socket_; 186 | socket_type upstream_socket_; 187 | 188 | enum { max_data_length = 8192 }; //8KB 189 | unsigned char downstream_data_[max_data_length]; 190 | unsigned char upstream_data_[max_data_length]; 191 | 192 | boost::mutex mutex_; 193 | 194 | std::size_t bytes_left_to_send_; 195 | 196 | public: 197 | 198 | class acceptor 199 | { 200 | public: 201 | 202 | acceptor(boost::asio::io_service& io_service, 203 | const std::size_t& max_bytes_per_connection, 204 | const std::string& local_host, unsigned short local_port, 205 | const std::string& upstream_host, unsigned short upstream_port) 206 | : io_service_(io_service), 207 | localhost_address(boost::asio::ip::address_v4::from_string(local_host)), 208 | acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)), 209 | upstream_port_(upstream_port), 210 | upstream_host_(upstream_host), 211 | max_bytes_per_connection_(max_bytes_per_connection) 212 | {} 213 | 214 | bool accept_connections() 215 | { 216 | try 217 | { 218 | session_ = boost::shared_ptr(new bridge(io_service_)); 219 | 220 | acceptor_.async_accept(session_->downstream_socket(), 221 | boost::bind(&acceptor::handle_accept, 222 | this, 223 | boost::asio::placeholders::error)); 224 | } 225 | catch(std::exception& e) 226 | { 227 | std::cerr << "acceptor exception: " << e.what() << std::endl; 228 | return false; 229 | } 230 | 231 | return true; 232 | } 233 | 234 | private: 235 | 236 | void handle_accept(const boost::system::error_code& error) 237 | { 238 | if (!error) 239 | { 240 | session_->start(max_bytes_per_connection_,upstream_host_,upstream_port_); 241 | 242 | if (!accept_connections()) 243 | { 244 | std::cerr << "Failure during call to accept." << std::endl; 245 | } 246 | } 247 | else 248 | { 249 | std::cerr << "Error: " << error.message() << std::endl; 250 | } 251 | } 252 | 253 | boost::asio::io_service& io_service_; 254 | ip::address_v4 localhost_address; 255 | ip::tcp::acceptor acceptor_; 256 | ptr_type session_; 257 | unsigned short upstream_port_; 258 | std::string upstream_host_; 259 | std::size_t max_bytes_per_connection_; 260 | }; 261 | 262 | }; 263 | } 264 | 265 | int main(int argc, char* argv[]) 266 | { 267 | if (argc != 6) 268 | { 269 | std::cerr << "usage: tcpproxy_server " << std::endl; 270 | return 1; 271 | } 272 | 273 | const unsigned short local_port = static_cast(::atoi(argv[2])); 274 | const unsigned short forward_port = static_cast(::atoi(argv[4])); 275 | const std::string local_host = argv[1]; 276 | const std::string forward_host = argv[3]; 277 | const std::size_t max_bytes = static_cast(::atoi(argv[5])); 278 | 279 | boost::asio::io_service ios; 280 | 281 | try 282 | { 283 | tcp_proxy::bridge::acceptor acceptor(ios, 284 | max_bytes, 285 | local_host, local_port, 286 | forward_host, forward_port); 287 | 288 | acceptor.accept_connections(); 289 | 290 | ios.run(); 291 | } 292 | catch(std::exception& e) 293 | { 294 | std::cerr << "Error: " << e.what() << std::endl; 295 | return 1; 296 | } 297 | 298 | return 0; 299 | } 300 | 301 | /* 302 | * [Note] On posix systems the tcp proxy server build command is as follows: 303 | * c++ -pedantic -ansi -Wall -Werror -O3 -o tcpproxy_server_v02 tcpproxy_server_v02.cpp -L/usr/lib -lstdc++ -lpthread -lboost_thread -lboost_system 304 | */ 305 | -------------------------------------------------------------------------------- /tcpproxy_server_v03.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // tcpproxy_server_v03.cpp 3 | // ~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2007 Arash Partow (http://www.partow.net) 6 | // URL: http://www.partow.net/programming/tcpproxy/index.html 7 | // 8 | // Distributed under the Boost Software License, Version 1.0. 9 | // 10 | // 11 | // Variation: Upstream and downstream logging. 12 | // In this example the upstream and downstream flows will be logged to 13 | // disk. The naming convention of the log files will be "(us|ds)_(client ip)_(time).log" 14 | // 15 | // 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | namespace tcp_proxy 34 | { 35 | namespace ip = boost::asio::ip; 36 | 37 | class bridge : public boost::enable_shared_from_this 38 | { 39 | public: 40 | 41 | typedef ip::tcp::socket socket_type; 42 | typedef boost::shared_ptr ptr_type; 43 | 44 | bridge(boost::asio::io_service& ios) 45 | : downstream_socket_(ios), 46 | upstream_socket_(ios) 47 | {} 48 | 49 | socket_type& downstream_socket() 50 | { 51 | return downstream_socket_; 52 | } 53 | 54 | socket_type& upstream_socket() 55 | { 56 | return upstream_socket_; 57 | } 58 | 59 | void start(const std::string& upstream_host, unsigned short upstream_port) 60 | { 61 | upstream_socket_.async_connect( 62 | ip::tcp::endpoint( 63 | boost::asio::ip::address::from_string(upstream_host), 64 | upstream_port), 65 | boost::bind(&bridge::handle_upstream_connect, 66 | shared_from_this(), 67 | boost::asio::placeholders::error)); 68 | } 69 | 70 | void handle_upstream_connect(const boost::system::error_code& error) 71 | { 72 | if (!error) 73 | { 74 | std::string datetime = format_datetime(boost::posix_time::microsec_clock::universal_time()); 75 | 76 | std::string upstream_file_name = "us_" + upstream_socket_ .remote_endpoint().address().to_string()+ "_" + datetime + ".log"; 77 | std::string downstream_file_name = "ds_" + downstream_socket_.remote_endpoint().address().to_string()+ "_" + datetime + ".log"; 78 | 79 | upstream_log.open(upstream_file_name.c_str(),std::ios::binary); 80 | downstream_log.open(downstream_file_name.c_str(),std::ios::binary); 81 | 82 | upstream_socket_.async_read_some( 83 | boost::asio::buffer(upstream_data_,max_data_length), 84 | boost::bind(&bridge::handle_upstream_read, 85 | shared_from_this(), 86 | boost::asio::placeholders::error, 87 | boost::asio::placeholders::bytes_transferred)); 88 | 89 | downstream_socket_.async_read_some( 90 | boost::asio::buffer(downstream_data_,max_data_length), 91 | boost::bind(&bridge::handle_downstream_read, 92 | shared_from_this(), 93 | boost::asio::placeholders::error, 94 | boost::asio::placeholders::bytes_transferred)); 95 | } 96 | else 97 | close(); 98 | } 99 | 100 | private: 101 | 102 | void handle_downstream_write(const boost::system::error_code& error) 103 | { 104 | if (!error) 105 | { 106 | upstream_socket_.async_read_some( 107 | boost::asio::buffer(upstream_data_,max_data_length), 108 | boost::bind(&bridge::handle_upstream_read, 109 | shared_from_this(), 110 | boost::asio::placeholders::error, 111 | boost::asio::placeholders::bytes_transferred)); 112 | } 113 | else 114 | close(); 115 | } 116 | 117 | void handle_downstream_read(const boost::system::error_code& error, 118 | const size_t& bytes_transferred) 119 | { 120 | if (!error) 121 | { 122 | if (downstream_log) 123 | downstream_log.write(reinterpret_cast(&downstream_data_[0]),static_cast(bytes_transferred)); 124 | 125 | async_write(upstream_socket_, 126 | boost::asio::buffer(downstream_data_,bytes_transferred), 127 | boost::bind(&bridge::handle_upstream_write, 128 | shared_from_this(), 129 | boost::asio::placeholders::error)); 130 | } 131 | else 132 | close(); 133 | } 134 | 135 | void handle_upstream_write(const boost::system::error_code& error) 136 | { 137 | if (!error) 138 | { 139 | downstream_socket_.async_read_some( 140 | boost::asio::buffer(downstream_data_,max_data_length), 141 | boost::bind(&bridge::handle_downstream_read, 142 | shared_from_this(), 143 | boost::asio::placeholders::error, 144 | boost::asio::placeholders::bytes_transferred)); 145 | } 146 | else 147 | close(); 148 | } 149 | 150 | void handle_upstream_read(const boost::system::error_code& error, 151 | const size_t& bytes_transferred) 152 | { 153 | if (!error) 154 | { 155 | if (upstream_log) 156 | upstream_log.write(reinterpret_cast(&upstream_data_[0]),static_cast(bytes_transferred)); 157 | 158 | async_write(downstream_socket_, 159 | boost::asio::buffer(upstream_data_,bytes_transferred), 160 | boost::bind(&bridge::handle_downstream_write, 161 | shared_from_this(), 162 | boost::asio::placeholders::error)); 163 | } 164 | else 165 | close(); 166 | } 167 | 168 | void close() 169 | { 170 | boost::mutex::scoped_lock lock(mutex_); 171 | 172 | if (downstream_socket_.is_open()) 173 | { 174 | downstream_socket_.close(); 175 | } 176 | 177 | if (upstream_socket_.is_open()) 178 | { 179 | upstream_socket_.close(); 180 | } 181 | 182 | downstream_log.close(); 183 | upstream_log.close(); 184 | } 185 | 186 | std::string format_datetime(boost::posix_time::ptime pt) 187 | { 188 | std::string s; 189 | std::ostringstream datetime_ss; 190 | boost::posix_time::time_facet * p_time_output = new boost::posix_time::time_facet; 191 | std::locale special_locale (std::locale(""), p_time_output); 192 | datetime_ss.imbue (special_locale); 193 | (*p_time_output).format("%Y%m%d%H%M%S.%f"); 194 | datetime_ss << pt; 195 | s = datetime_ss.str().c_str(); 196 | 197 | return s; 198 | } 199 | 200 | socket_type downstream_socket_; 201 | socket_type upstream_socket_; 202 | 203 | enum { max_data_length = 8192 }; //8KB 204 | unsigned char downstream_data_[max_data_length]; 205 | unsigned char upstream_data_[max_data_length]; 206 | 207 | boost::mutex mutex_; 208 | 209 | std::ofstream downstream_log; 210 | std::ofstream upstream_log; 211 | 212 | public: 213 | 214 | class acceptor 215 | { 216 | public: 217 | 218 | acceptor(boost::asio::io_service& io_service, 219 | const std::string& local_host, unsigned short local_port, 220 | const std::string& upstream_host, unsigned short upstream_port) 221 | : io_service_(io_service), 222 | localhost_address(boost::asio::ip::address_v4::from_string(local_host)), 223 | acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)), 224 | upstream_port_(upstream_port), 225 | upstream_host_(upstream_host) 226 | {} 227 | 228 | bool accept_connections() 229 | { 230 | try 231 | { 232 | session_ = boost::shared_ptr(new bridge(io_service_)); 233 | 234 | acceptor_.async_accept(session_->downstream_socket(), 235 | boost::bind(&acceptor::handle_accept, 236 | this, 237 | boost::asio::placeholders::error)); 238 | } 239 | catch(std::exception& e) 240 | { 241 | std::cerr << "acceptor exception: " << e.what() << std::endl; 242 | return false; 243 | } 244 | 245 | return true; 246 | } 247 | 248 | private: 249 | 250 | void handle_accept(const boost::system::error_code& error) 251 | { 252 | if (!error) 253 | { 254 | session_->start(upstream_host_,upstream_port_); 255 | 256 | if (!accept_connections()) 257 | { 258 | std::cerr << "Failure during call to accept." << std::endl; 259 | } 260 | } 261 | else 262 | { 263 | std::cerr << "Error: " << error.message() << std::endl; 264 | } 265 | } 266 | 267 | boost::asio::io_service& io_service_; 268 | ip::address_v4 localhost_address; 269 | ip::tcp::acceptor acceptor_; 270 | ptr_type session_; 271 | unsigned short upstream_port_; 272 | std::string upstream_host_; 273 | }; 274 | 275 | }; 276 | } 277 | 278 | int main(int argc, char* argv[]) 279 | { 280 | if (argc != 5) 281 | { 282 | std::cerr << "usage: tcpproxy_server " << std::endl; 283 | return 1; 284 | } 285 | 286 | const unsigned short local_port = static_cast(::atoi(argv[2])); 287 | const unsigned short forward_port = static_cast(::atoi(argv[4])); 288 | const std::string local_host = argv[1]; 289 | const std::string forward_host = argv[3]; 290 | 291 | boost::asio::io_service ios; 292 | 293 | try 294 | { 295 | tcp_proxy::bridge::acceptor acceptor(ios, 296 | local_host, local_port, 297 | forward_host, forward_port); 298 | 299 | acceptor.accept_connections(); 300 | 301 | ios.run(); 302 | } 303 | catch(std::exception& e) 304 | { 305 | std::cerr << "Error: " << e.what() << std::endl; 306 | return 1; 307 | } 308 | 309 | return 0; 310 | } 311 | 312 | /* 313 | * [Note] On posix systems the tcp proxy server build command is as follows: 314 | * c++ -pedantic -ansi -Wall -Werror -O3 -o tcpproxy_server_v03 tcpproxy_server_v03.cpp -L/usr/lib -lstdc++ -lpthread -lboost_thread -lboost_system 315 | */ 316 | -------------------------------------------------------------------------------- /tcpproxy_server_v04.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // tcpproxy_server_v04.cpp 3 | // ~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2007 Arash Partow (http://www.partow.net) 6 | // URL: http://www.partow.net/programming/tcpproxy/index.html 7 | // 8 | // Distributed under the Boost Software License, Version 1.0. 9 | // 10 | // 11 | // Variation: Limit the number of concurrent client connections 12 | // In this example the acceptor will limit the number of concurrent 13 | // client connections made to the server. When the limit has been 14 | // reached, any subsequent connections made will be immediately 15 | // disconnected, until such time as the concurrent client count 16 | // has decreased to below the user specified limit. 17 | // 18 | // 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | namespace tcp_proxy 34 | { 35 | namespace ip = boost::asio::ip; 36 | 37 | class safe_counter 38 | { 39 | public: 40 | 41 | safe_counter(const std::size_t& max_val, 42 | const std::size_t& counter = 0) 43 | : counter_(counter), 44 | max_value_(max_val) 45 | {} 46 | 47 | bool operator < (const std::size_t& v) 48 | { 49 | boost::mutex::scoped_lock lock(mutex_); 50 | return counter_ < v; 51 | } 52 | 53 | void operator++() 54 | { 55 | boost::mutex::scoped_lock lock(mutex_); 56 | ++counter_; 57 | } 58 | 59 | void operator--() 60 | { 61 | boost::mutex::scoped_lock lock(mutex_); 62 | --counter_; 63 | } 64 | 65 | operator std::size_t() 66 | { 67 | boost::mutex::scoped_lock lock(mutex_); 68 | return counter_; 69 | } 70 | 71 | std::size_t max_value() const 72 | { 73 | return max_value_; 74 | } 75 | 76 | bool compare_and_inc() 77 | { 78 | boost::mutex::scoped_lock lock(mutex_); 79 | 80 | if (counter_ >= max_value_) 81 | return false; 82 | 83 | ++counter_; 84 | 85 | return true; 86 | } 87 | 88 | private: 89 | 90 | safe_counter(const safe_counter&); 91 | safe_counter& operator=(const safe_counter&); 92 | 93 | volatile std::size_t counter_; 94 | std::size_t max_value_; 95 | boost::mutex mutex_; 96 | }; 97 | 98 | class bridge : public boost::enable_shared_from_this 99 | { 100 | public: 101 | 102 | typedef ip::tcp::socket socket_type; 103 | typedef boost::shared_ptr ptr_type; 104 | 105 | bridge(boost::asio::io_service& ios, safe_counter& connection_count) 106 | : downstream_socket_(ios), 107 | upstream_socket_(ios), 108 | connection_count_(connection_count), 109 | upstream_connected_(false) 110 | {} 111 | 112 | socket_type& downstream_socket() 113 | { 114 | return downstream_socket_; 115 | } 116 | 117 | socket_type& upstream_socket() 118 | { 119 | return upstream_socket_; 120 | } 121 | 122 | void start(const std::string& upstream_host, unsigned short upstream_port) 123 | { 124 | upstream_socket_.async_connect( 125 | ip::tcp::endpoint( 126 | boost::asio::ip::address::from_string(upstream_host), 127 | upstream_port), 128 | boost::bind(&bridge::handle_upstream_connect, 129 | shared_from_this(), 130 | boost::asio::placeholders::error)); 131 | } 132 | 133 | void handle_upstream_connect(const boost::system::error_code& error) 134 | { 135 | if (!error) 136 | { 137 | if (connection_count_.compare_and_inc()) 138 | { 139 | upstream_connected_ = true; 140 | 141 | upstream_socket_.async_read_some( 142 | boost::asio::buffer(upstream_data_,max_data_length), 143 | boost::bind(&bridge::handle_upstream_read, 144 | shared_from_this(), 145 | boost::asio::placeholders::error, 146 | boost::asio::placeholders::bytes_transferred)); 147 | 148 | downstream_socket_.async_read_some( 149 | boost::asio::buffer(downstream_data_,max_data_length), 150 | boost::bind(&bridge::handle_downstream_read, 151 | shared_from_this(), 152 | boost::asio::placeholders::error, 153 | boost::asio::placeholders::bytes_transferred)); 154 | } 155 | else 156 | close(); 157 | } 158 | else 159 | close(); 160 | } 161 | 162 | private: 163 | 164 | void handle_downstream_write(const boost::system::error_code& error) 165 | { 166 | if (!error) 167 | { 168 | upstream_socket_.async_read_some( 169 | boost::asio::buffer(upstream_data_,max_data_length), 170 | boost::bind(&bridge::handle_upstream_read, 171 | shared_from_this(), 172 | boost::asio::placeholders::error, 173 | boost::asio::placeholders::bytes_transferred)); 174 | } 175 | else 176 | close(); 177 | } 178 | 179 | void handle_downstream_read(const boost::system::error_code& error, 180 | const size_t& bytes_transferred) 181 | { 182 | if (!error) 183 | { 184 | async_write(upstream_socket_, 185 | boost::asio::buffer(downstream_data_,bytes_transferred), 186 | boost::bind(&bridge::handle_upstream_write, 187 | shared_from_this(), 188 | boost::asio::placeholders::error)); 189 | } 190 | else 191 | close(); 192 | } 193 | 194 | void handle_upstream_write(const boost::system::error_code& error) 195 | { 196 | if (!error) 197 | { 198 | downstream_socket_.async_read_some( 199 | boost::asio::buffer(downstream_data_,max_data_length), 200 | boost::bind(&bridge::handle_downstream_read, 201 | shared_from_this(), 202 | boost::asio::placeholders::error, 203 | boost::asio::placeholders::bytes_transferred)); 204 | } 205 | else 206 | close(); 207 | } 208 | 209 | void handle_upstream_read(const boost::system::error_code& error, 210 | const size_t& bytes_transferred) 211 | { 212 | if (!error) 213 | { 214 | async_write(downstream_socket_, 215 | boost::asio::buffer(upstream_data_,bytes_transferred), 216 | boost::bind(&bridge::handle_downstream_write, 217 | shared_from_this(), 218 | boost::asio::placeholders::error)); 219 | } 220 | else 221 | close(); 222 | } 223 | 224 | void close() 225 | { 226 | boost::mutex::scoped_lock lock(mutex_); 227 | 228 | if (upstream_connected_) 229 | { 230 | --connection_count_; 231 | upstream_connected_ = false; 232 | } 233 | 234 | if (downstream_socket_.is_open()) 235 | { 236 | downstream_socket_.close(); 237 | } 238 | 239 | if (upstream_socket_.is_open()) 240 | { 241 | upstream_socket_.close(); 242 | } 243 | } 244 | 245 | socket_type downstream_socket_; 246 | socket_type upstream_socket_; 247 | 248 | enum { max_data_length = 8192 }; //8KB 249 | unsigned char downstream_data_[max_data_length]; 250 | unsigned char upstream_data_[max_data_length]; 251 | 252 | boost::mutex mutex_; 253 | safe_counter& connection_count_; 254 | bool upstream_connected_; 255 | 256 | public: 257 | 258 | class acceptor 259 | { 260 | public: 261 | 262 | acceptor(boost::asio::io_service& io_service, 263 | const std::size_t& max_connections, 264 | const std::string& local_host, unsigned short local_port, 265 | const std::string& upstream_host, unsigned short upstream_port) 266 | : io_service_(io_service), 267 | localhost_address(boost::asio::ip::address_v4::from_string(local_host)), 268 | acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)), 269 | upstream_port_(upstream_port), 270 | upstream_host_(upstream_host), 271 | connection_count_(max_connections,0) 272 | {} 273 | 274 | bool accept_connections() 275 | { 276 | try 277 | { 278 | session_ = boost::shared_ptr(new bridge(io_service_,connection_count_)); 279 | 280 | acceptor_.async_accept(session_->downstream_socket(), 281 | boost::bind(&acceptor::handle_accept, 282 | this, 283 | boost::asio::placeholders::error)); 284 | } 285 | catch(std::exception& e) 286 | { 287 | std::cerr << "acceptor exception: " << e.what() << std::endl; 288 | return false; 289 | } 290 | 291 | return true; 292 | } 293 | 294 | private: 295 | 296 | void handle_accept(const boost::system::error_code& error) 297 | { 298 | if (!error) 299 | { 300 | if (connection_count_ < connection_count_.max_value()) 301 | session_->start(upstream_host_,upstream_port_); 302 | else 303 | session_->downstream_socket().close(); 304 | 305 | if (!accept_connections()) 306 | { 307 | std::cerr << "Failure during call to accept." << std::endl; 308 | } 309 | } 310 | else 311 | { 312 | std::cerr << "Error: " << error.message() << std::endl; 313 | } 314 | } 315 | 316 | boost::asio::io_service& io_service_; 317 | ip::address_v4 localhost_address; 318 | ip::tcp::acceptor acceptor_; 319 | ptr_type session_; 320 | unsigned short upstream_port_; 321 | std::string upstream_host_; 322 | safe_counter connection_count_; 323 | }; 324 | 325 | }; 326 | } 327 | 328 | int main(int argc, char* argv[]) 329 | { 330 | if (argc != 6) 331 | { 332 | std::cerr << "usage: tcpproxy_server " << std::endl; 333 | return 1; 334 | } 335 | 336 | const unsigned short local_port = static_cast(::atoi(argv[2])); 337 | const unsigned short forward_port = static_cast(::atoi(argv[4])); 338 | const std::string local_host = argv[1]; 339 | const std::string forward_host = argv[3]; 340 | const std::size_t max_connections = static_cast(::atoi(argv[5])); 341 | 342 | boost::asio::io_service ios; 343 | 344 | try 345 | { 346 | tcp_proxy::bridge::acceptor acceptor(ios, 347 | max_connections, 348 | local_host, local_port, 349 | forward_host, forward_port); 350 | 351 | acceptor.accept_connections(); 352 | 353 | ios.run(); 354 | } 355 | catch(std::exception& e) 356 | { 357 | std::cerr << "Error: " << e.what() << std::endl; 358 | return 1; 359 | } 360 | 361 | return 0; 362 | } 363 | 364 | /* 365 | * [Note] On posix systems the tcp proxy server build command is as follows: 366 | * c++ -pedantic -ansi -Wall -Werror -O3 -o tcpproxy_server_v04 tcpproxy_server_v04.cpp -L/usr/lib -lstdc++ -lpthread -lboost_thread -lboost_system 367 | */ 368 | --------------------------------------------------------------------------------