/* Copyright (c) 2007 Arash Partow (http://www.partow.net) URL: http://www.partow.net/programming/tcpproxy/index.html Modified and adapted by Pwnzer0tt1 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; boost::asio::io_service *ios_loop = nullptr; bool unhexlify(string const &hex, string &newString) { try{ int len = hex.length(); for(int i=0; i< len; i+=2) { std::string byte = hex.substr(i,2); char chr = (char) (int)strtol(byte.c_str(), NULL, 16); newString.push_back(chr); } return true; } catch (...){ return false; } } typedef vector> regex_rule_vector; struct regex_rules{ regex_rule_vector regex_s_c_w, regex_c_s_w, regex_s_c_b, regex_c_s_b; regex_rule_vector* getByCode(char code){ switch(code){ case 'C': // Client to server Blacklist return ®ex_c_s_b; break; case 'c': // Client to server Whitelist return ®ex_c_s_w; break; case 'S': // Server to client Blacklist return ®ex_s_c_b; break; case 's': // Server to client Whitelist return ®ex_s_c_w; break; } throw invalid_argument( "Expected 'C' 'c' 'S' or 's'" ); } void add(const char* arg){ //Integrity checks size_t arg_len = strlen(arg); if (arg_len < 2 || arg_len%2 != 0) return; if (arg[0] != '0' && arg[0] != '1') return; if (arg[1] != 'C' && arg[1] != 'c' && arg[1] != 'S' && arg[1] != 's') return; string hex(arg+2), expr; if (!unhexlify(hex, expr)) return; try{ //Push regex if (arg[0] == '1'){ regex regex(expr); #ifdef DEBUG cerr << "Added case sensitive regex " << expr << endl; #endif getByCode(arg[1])->push_back(make_pair(string(arg), regex)); } else { regex regex(expr,regex_constants::icase); #ifdef DEBUG cerr << "Added case insensitive regex " << expr << endl; #endif getByCode(arg[1])->push_back(make_pair(string(arg), regex)); } } catch(...){ cerr << "Regex " << arg << " was not compiled successfully" << endl; } } }; shared_ptr regex_config; mutex update_mutex; #ifdef MULTI_THREAD mutex stdout_mutex; #endif bool filter_data(unsigned char* data, const size_t& bytes_transferred, vector> const &blacklist, vector> const &whitelist){ #ifdef DEBUG_PACKET cerr << "---------------- Packet ----------------" << endl; for(int i=0;i ele:blacklist){ try{ if(regex_search(reinterpret_cast(data), reinterpret_cast(data)+bytes_transferred, ele.second)){ #ifdef MULTI_THREAD std::unique_lock lck(stdout_mutex); #endif cout << "BLOCKED " << ele.first << endl; return false; } } catch(...){ cerr << "Error while matching regex: " << ele.first << endl; } } for (pair ele:whitelist){ try{ if(!regex_search(reinterpret_cast(data),reinterpret_cast(data)+bytes_transferred, ele.second)){ #ifdef MULTI_THREAD std::unique_lock lck(stdout_mutex); #endif cout << "BLOCKED " << ele.first << endl; return false; } } catch(...){ cerr << "Error while matching regex: " << ele.first << endl; } } #ifdef DEBUG cerr << "Packet Accepted!" << endl; #endif return true; } namespace tcp_proxy { namespace ip = boost::asio::ip; class bridge : public boost::enable_shared_from_this { public: typedef ip::tcp::socket socket_type; typedef boost::shared_ptr ptr_type; bridge(boost::asio::io_service& ios) : downstream_socket_(ios), upstream_socket_ (ios), thread_safety(ios) {} socket_type& downstream_socket() { // Client socket return downstream_socket_; } socket_type& upstream_socket() { // Remote server socket return upstream_socket_; } void start(const string& upstream_host, unsigned short upstream_port) { // Attempt connection to remote server (upstream side) upstream_socket_.async_connect( ip::tcp::endpoint( boost::asio::ip::address::from_string(upstream_host), upstream_port), boost::asio::bind_executor(thread_safety, boost::bind( &bridge::handle_upstream_connect, shared_from_this(), boost::asio::placeholders::error))); } void handle_upstream_connect(const boost::system::error_code& error) { if (!error) { // Setup async read from remote server (upstream) upstream_socket_.async_read_some( boost::asio::buffer(upstream_data_,max_data_length), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_upstream_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); // Setup async read from client (downstream) downstream_socket_.async_read_some( boost::asio::buffer(downstream_data_,max_data_length), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_downstream_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); } else close(); } private: /* Section A: Remote Server --> Proxy --> Client Process data recieved from remote sever then send to client. */ // Read from remote server complete, now send data to client void handle_upstream_read(const boost::system::error_code& error, const size_t& bytes_transferred) // Da Server a Client { if (!error) { shared_ptr regex_old_config = regex_config; if (filter_data(upstream_data_, bytes_transferred, regex_old_config->regex_s_c_b, regex_old_config->regex_s_c_w)){ async_write(downstream_socket_, boost::asio::buffer(upstream_data_,bytes_transferred), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_downstream_write, shared_from_this(), boost::asio::placeholders::error))); }else{ close(); } } else close(); } // Write to client complete, Async read from remote server void handle_downstream_write(const boost::system::error_code& error) { if (!error) { upstream_socket_.async_read_some( boost::asio::buffer(upstream_data_,max_data_length), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_upstream_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); } else close(); } // *** End Of Section A *** /* Section B: Client --> Proxy --> Remove Server Process data recieved from client then write to remove server. */ // Read from client complete, now send data to remote server void handle_downstream_read(const boost::system::error_code& error, const size_t& bytes_transferred) // Da Client a Server { if (!error) { shared_ptr regex_old_config = regex_config; if (filter_data(downstream_data_, bytes_transferred, regex_old_config->regex_c_s_b, regex_old_config->regex_c_s_w)){ async_write(upstream_socket_, boost::asio::buffer(downstream_data_,bytes_transferred), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_upstream_write, shared_from_this(), boost::asio::placeholders::error))); }else{ close(); } } else close(); } // Write to remote server complete, Async read from client void handle_upstream_write(const boost::system::error_code& error) { if (!error) { downstream_socket_.async_read_some( boost::asio::buffer(downstream_data_,max_data_length), boost::asio::bind_executor(thread_safety, boost::bind(&bridge::handle_downstream_read, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); } else close(); } // *** End Of Section B *** void close() { boost::mutex::scoped_lock lock(mutex_); if (downstream_socket_.is_open()) { downstream_socket_.close(); } if (upstream_socket_.is_open()) { upstream_socket_.close(); } } socket_type downstream_socket_; socket_type upstream_socket_; enum { max_data_length = 8192 }; //8KB unsigned char downstream_data_[max_data_length]; unsigned char upstream_data_ [max_data_length]; boost::asio::io_service::strand thread_safety; boost::mutex mutex_; public: class acceptor { public: acceptor(boost::asio::io_service& io_service, const string& local_host, unsigned short local_port, const string& upstream_host, unsigned short upstream_port) : io_service_(io_service), localhost_address(boost::asio::ip::address_v4::from_string(local_host)), acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)), upstream_port_(upstream_port), upstream_host_(upstream_host) {} bool accept_connections() { try { session_ = boost::shared_ptr(new bridge(io_service_)); acceptor_.async_accept(session_->downstream_socket(), boost::asio::bind_executor(session_->thread_safety, boost::bind(&acceptor::handle_accept, this, boost::asio::placeholders::error))); } catch(exception& e) { cerr << "acceptor exception: " << e.what() << endl; return false; } return true; } private: void handle_accept(const boost::system::error_code& error) { if (!error) { session_->start(upstream_host_,upstream_port_); if (!accept_connections()) { cerr << "Failure during call to accept." << endl; } } else { cerr << "Error: " << error.message() << endl; } } boost::asio::io_service& io_service_; ip::address_v4 localhost_address; ip::tcp::acceptor acceptor_; ptr_type session_; unsigned short upstream_port_; string upstream_host_; }; }; } void update_regex(){ #ifdef DEBUG cerr << "Updating configuration" << endl; #endif std::unique_lock lck(update_mutex); regex_rules *regex_new_config = new regex_rules(); string data; while(true){ cin >> data; if (data == "END") break; regex_new_config->add(data.c_str()); } regex_config.reset(regex_new_config); } void signal_handler(int signal_num) { if (signal_num == SIGUSR1){ update_regex(); }else if(signal_num == SIGTERM){ if (ios_loop != nullptr) ios_loop->stop(); #ifdef DEBUG cerr << "Close Requested" << endl; #endif exit(0); } } int main(int argc, char* argv[]) { if (argc < 5) { cerr << "usage: tcpproxy_server " << endl; return 1; } const unsigned short local_port = static_cast(::atoi(argv[2])); const unsigned short forward_port = static_cast(::atoi(argv[4])); const string local_host = argv[1]; const string forward_host = argv[3]; update_regex(); signal(SIGUSR1, signal_handler); boost::asio::io_service ios; ios_loop = &ios; signal(SIGTERM, signal_handler); #ifdef DEBUG cerr << "Starting Proxy" << endl; #endif try { tcp_proxy::bridge::acceptor acceptor(ios, local_host, local_port, forward_host, forward_port); acceptor.accept_connections(); #ifdef MULTI_THREAD boost::thread_group tg; #ifdef THREAD_NUM for (unsigned i = 0; i < THREAD_NUM; ++i) #else for (unsigned i = 0; i < thread::hardware_concurrency(); ++i) #endif tg.create_thread(boost::bind(&boost::asio::io_service::run, &ios)); tg.join_all(); #else ios.run(); #endif } catch(exception& e) { cerr << "Error: " << e.what() << endl; return 1; } #ifdef DEBUG cerr << "Proxy stopped!" << endl; #endif return 0; }