From 2ed2758f14a76b0450ff13b60e3339d36d559ba4 Mon Sep 17 00:00:00 2001 From: Alexander Duryagin Date: Wed, 20 Mar 2019 17:10:48 +0300 Subject: [PATCH 1/2] incorporate dpuchkin's changes --- conf/fastproxy.conf | 5 ++ debian/changelog | 60 +++++++++++++++- debian/control | 4 +- debian/upstart | 1 + src/channel.cpp | 2 +- src/channel.hpp | 2 +- src/fastproxy.cpp | 53 ++------------ src/fastproxy.hpp | 2 +- src/headers.hpp | 40 +++-------- src/proxy.cpp | 53 ++------------ src/proxy.hpp | 15 ++-- src/resolver.cpp | 156 ++++++----------------------------------- src/resolver.hpp | 48 +++---------- src/session.cpp | 89 +++++++++-------------- src/session.hpp | 13 ++-- src/wscript | 7 +- test/fastproxy_test.py | 66 +++++------------ wscript | 38 ++-------- 18 files changed, 192 insertions(+), 462 deletions(-) diff --git a/conf/fastproxy.conf b/conf/fastproxy.conf index 3036291..76075fd 100644 --- a/conf/fastproxy.conf +++ b/conf/fastproxy.conf @@ -16,6 +16,7 @@ allow-header=Host allow-header=If-Modified-Since allow-header=Last-Modified allow-header=Location +allow-header=Origin allow-header=Pragma allow-header=Accept allow-header=Accept-Charset @@ -32,6 +33,10 @@ allow-header=Cookie allow-header=Set-Cookie allow-header=X-Yandex-Antivirus allow-header=X-Requested-With +allow-header=X-GWT-Permutation +allow-header=X-GWT-Module-Base receive-timeout=999999 +connect-timeout=4 log-channel=proxy log-channel=resolver +name-server=127.0.0.1 diff --git a/debian/changelog b/debian/changelog index 6be67b7..daff883 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,57 @@ +fastproxy (0.4.9) precise; urgency=low + + * allow Origin header + + -- Denis Puchkin Mon, 27 Jul 2015 20:06:24 +0300 + +fastproxy (0.4.8) precise; urgency=low + + * tocuh version + + -- Denis Puchkin Tue, 31 Mar 2015 10:45:46 +0300 + +fastproxy (0.4.7) precise; urgency=low + + * new fastproxy config + + -- Denis Puchkin Fri, 10 Oct 2014 12:44:46 +0400 + +fastproxy (0.4.6) precise; urgency=low + + * build fix + + -- Denis Puchkin Wed, 24 Sep 2014 12:31:15 +0400 + +fastproxy (0.4.5) precise; urgency=low + + * new default config + + -- Denis Puchkin Wed, 24 Sep 2014 12:24:46 +0400 + +fastproxy (0.4.4) precise; urgency=low + + * disable recursive dns + + -- Denis Puchkin Wed, 10 Sep 2014 13:00:20 +0400 + +fastproxy (0.4.3) precise; urgency=low + + * test + + -- Denis Puchkin Wed, 18 Jun 2014 18:05:35 +0400 + +fastproxy (0.4.2) precise; urgency=low + + * upstart: added kill timeout to speedup stopping + + -- Nikolay Bryskin Tue, 13 Nov 2012 01:33:20 +0400 + +fastproxy (0.4.1) precise; urgency=low + + * resolv and connect timeouts raised to 4 sec + + -- Nikolay Bryskin Thu, 08 Nov 2012 21:18:22 +0400 + fastproxy (0.4) precise; urgency=low * Zabbix script fix: handle fastproxy restarts @@ -87,7 +141,7 @@ fastproxy (0.2.16) natty; urgency=low fastproxy (0.2.15) lucid; urgency=low - * allowed header X-Yandex-Antivirus + * allowed header X-Yandex-Antivirus -- Nikolay Bryskin Mon, 27 Jun 2011 15:52:29 +0400 @@ -182,7 +236,7 @@ fastproxy (0.2.2) lucid; urgency=low * zbx_fastproxy.py: return 0 for non-existent statistics values (to avoid backtraces in zabbix-agent logs) * zbx_fastproxy.py: return 0 for non-existent statistics sockets - * zbx_fastproxy.py: aggregation support (only `sum' for now using `*') + * zbx_fastproxy.py: aggregation support (only `sum' for now using `*') -- Nikolay Bryskin Thu, 24 Feb 2011 03:21:16 +0300 @@ -202,7 +256,7 @@ fastproxy (0.2ubuntu1) lucid; urgency=low fastproxy (0.1ubuntu1) lucid; urgency=low - * Fixed Ubuntu packaging scripts + * Fixed Ubuntu packaging scripts -- Nikolay Bryskin Sat, 29 Jan 2011 21:12:25 +0300 diff --git a/debian/control b/debian/control index cceed74..e6c330b 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,13 @@ Source: fastproxy Section: misc Priority: extra Maintainer: Nikolay Bryskin -Build-Depends: debhelper (>= 7.0.50~), libboost1.43-all-dev, python (>= 2.6), libunbound-dev +Build-Depends: debhelper (>= 7.0.50~), libudns-dev, libboost1.43-all-dev, python (>= 2.6) Standards-Version: 3.9.1 Homepage: http://git.yandex.ru/nbryskin/fastproxy.git Package: fastproxy Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.6), libunbound2 +Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.6) Description: Fast and reliable HTTP proxy server, no caching Fastproxy is simple, fast and reliable event-based HTTP proxy server with built-in stub DNS resolver. Written mostly in C++ and have diff --git a/debian/upstart b/debian/upstart index 7eb19d3..3c5231d 100644 --- a/debian/upstart +++ b/debian/upstart @@ -3,6 +3,7 @@ stop on stopping network or fastproxy-all-stop respawn respawn limit 10 5 limit nofile 65536 65536 +kill timeout 1 instance $ARGS script CONF_ARGS="`sed -e 's/^/ --/' /etc/fastproxy.conf | tr -d '\n'`" diff --git a/src/channel.cpp b/src/channel.cpp index 838d3c6..e121919 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -118,7 +118,7 @@ void channel::input_timeouted(const error_code& ec) void channel::splice_from_input() { - error_code ec(0, boost::system::generic_category()); + error_code ec(0, boost::system::generic_category); std::size_t avail = input.available(ec); if (ec) return finish(ec); diff --git a/src/channel.hpp b/src/channel.hpp index 8fa539f..3f6d9e6 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -71,7 +71,7 @@ class channel : public boost::noncopyable int pipe[2]; long pipe_size; session& parent_session; - static const std::size_t size_of_operation = sizeof(asio::detail::reactive_null_buffers_op); + static const std::size_t size_of_operation = sizeof(asio::detail::null_buffers_op); handler_t input_handler; char space_for_input_op[size_of_operation]; handler_t output_handler; diff --git a/src/fastproxy.cpp b/src/fastproxy.cpp index f9b704e..b2d1da7 100644 --- a/src/fastproxy.cpp +++ b/src/fastproxy.cpp @@ -50,29 +50,18 @@ void fastproxy::parse_config(int argc, char* argv[]) po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") - ("resolve-library", po::value()->default_value("unbound"), "DNS library to use for resolve ('udns', 'unbound')") - ("ingoing-http", po::value()->required(), "http listening addresses") ("ingoing-stat", po::value()->required(), "statistics listening socket") - ("outgoing-http", po::value()->default_value(ip::tcp::endpoint()), "outgoing address for HTTP requests") ("outgoing-ns", po::value()->default_value(ip::udp::endpoint()), "outgoing address for NS lookup") - + ("name-server", po::value()->required(), "name server address") ("log-level", po::value()->default_value(2), "logging level") ("log-channel", po::value(), "logging channel") - ("receive-timeout", po::value()->default_value(3600), "timeout for receive operations (in seconds)") ("connect-timeout", po::value()->default_value(3), "timeout for connect operation (in seconds)") - ("resolve-timeout", po::value()->default_value(3), "time out for resolve operation for 'unbound' (in seconds)") - - ("udns-name-server", po::value(), "name server address for 'udns' library") - ("allow-header", po::value()->default_value(string_vec(), "any"), "allowed header for requests") - ("rename-header", po::value()->default_value(string_vec(), ""), "header rename rule (:), only allowed headers are supported") - ("stat-socket-user", po::value()->default_value(getpwuid(getuid())->pw_name), "user for statistics socket") ("stat-socket-group", po::value()->default_value(getgrgid(getgid())->gr_name), "group for statistics socket") - ("stop-after-init", po::value()->default_value(false), "raise SIGSTOP after initialization (Upstart support)") ("error-page-dir", po::value()->default_value("/etc/fastproxy/errors"), "directory where error pages are located"); @@ -86,25 +75,9 @@ void fastproxy::parse_config(int argc, char* argv[]) exit(1); } - std::string resolve_library = vm["resolve-library"].as(); - if (resolve_library == "udns") - { - if (vm.count("udns-name-server") == 0) - { - throw boost::program_options::required_option("udns-name-server"); - } - } - else if (resolve_library == "unbound") - { - // No specific options yet - } - else - { - throw boost::program_options::invalid_option_value(resolve_library); - } po::notify(vm); } - catch (const boost::program_options::error& exc) + catch (const boost::program_options::required_option& exc) { std::cout << desc << std::endl; throw; @@ -204,31 +177,19 @@ void fastproxy::init_statistics() void fastproxy::init_proxy() { - bool use_unbound_resolve = (vm["resolve-library"].as() == "unbound"); - - ip::udp::endpoint name_server; - - if (!use_unbound_resolve) - { - name_server = vm["udns-name-server"].as(); - } - p.reset(new proxy(io, vm["ingoing-http"].as(), vm["outgoing-http"].as(), vm["outgoing-ns"].as(), - name_server, + vm["name-server"].as(), boost::posix_time::seconds(vm["receive-timeout"].as()), boost::posix_time::seconds(vm["connect-timeout"].as()), - boost::posix_time::seconds(vm["resolve-timeout"].as()), vm["allow-header"].as(), - vm["rename-header"].as(), - vm["error-page-dir"].as(), - use_unbound_resolve)); + vm["error-page-dir"].as())); } -void fastproxy::init_resolver() -{ - resolver::init(); +void fastproxy::init_resolver() +{ + resolver::init(); } template diff --git a/src/fastproxy.hpp b/src/fastproxy.hpp index d6d54c4..1adcf79 100644 --- a/src/fastproxy.hpp +++ b/src/fastproxy.hpp @@ -33,7 +33,7 @@ class fastproxy private: void parse_config(int argc, char* argv[]); - void init_logging(); + void init_logging(); void init_resolver(); void init_signals(); void init_statistics(); diff --git a/src/headers.hpp b/src/headers.hpp index 996c39f..caffb65 100644 --- a/src/headers.hpp +++ b/src/headers.hpp @@ -8,10 +8,9 @@ #ifndef HEADERS_HPP_ #define HEADERS_HPP_ -#include +#include #include #include -#include class lstring { @@ -34,16 +33,9 @@ class lstring { } - // Checks if header line is empty, i.e. has 0-length or contains just new line characters - bool empty() const + operator bool () const { - switch (size()) - { - case 0: return true; - case 1: return ((begin[0] == '\r') || (begin[0] == '\n')); - case 2: return (begin[1] == '\n'); - default: return false; - } + return size() != 0; } std::size_t size() const @@ -53,27 +45,13 @@ class lstring friend bool operator < (const lstring& lhs, const lstring& rhs) { - const char* l = lhs.begin; - const char* r = rhs.begin; - - for (; l != lhs.end && r != rhs.end; l++, r++) + for (const char *l = lhs.begin, *r = rhs.begin; l != lhs.end && r != rhs.end; l++, r++) { - char lc = tolower(*l); - char rc = tolower(*r); - - if (lc == rc) - continue; + if (tolower(*l) < tolower(*r)) + return true; - return (lc < rc); - } - - // It might be the case when 'allowed' header name is a prefix of another header - // name. We don't want to allow headers by prefix but rather by exact match. So - // the next character has to be checked and headers are equal if it is ':'. - // The only case we have to check, in all other cases condition result is 'false' - if (lhs.size() < rhs.size()) - { - return rhs.begin[lhs.size()] != ':'; + if (tolower(*l) > tolower(*r)) + break; } return false; @@ -83,6 +61,6 @@ class lstring const char* end; }; -typedef std::map headers_type; +typedef std::set headers_type; #endif /* HEADERS_HPP_ */ diff --git a/src/proxy.cpp b/src/proxy.cpp index 2af3df8..41239f5 100644 --- a/src/proxy.cpp +++ b/src/proxy.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "proxy.hpp" #include "statistics.hpp" @@ -25,52 +24,19 @@ bool session_less(const session& lhs, const session& rhs) } proxy::proxy(asio::io_service& io, std::vector inbound, const ip::tcp::endpoint& outbound_http, - const ip::udp::endpoint& outbound_ns, const ip::udp::endpoint& name_server, + const ip::udp::endpoint& outbound_ns, const ip::udp::endpoint& name_server, const time_duration& receive_timeout, const time_duration& connect_timeout, - const time_duration& resolve_timeout, const std::vector& allowed_headers, - const std::vector& rename_headers, - const std::string error_pages_dir, bool use_unbound_resolve) - : resolver_(io, outbound_ns, name_server, use_unbound_resolve) + const std::vector& allowed_headers, + const std::string error_pages_dir) + : resolver_(io, outbound_ns, name_server) , outbound_http(outbound_http) , receive_timeout(receive_timeout) , connect_timeout(connect_timeout) - , resolve_timeout(resolve_timeout) , sessions(std::ptr_fun(session_less)) + , headers_cont(allowed_headers) { - headers.push_back(""); - lstring empty(headers.back().c_str()); - - // Construct header mapping, every header which is mapped implies that - // it is allowed (and the same map can be used for mapped and allowed - // headers). - for (size_t i = 0; i < rename_headers.size(); i++) - { - std::vector value; - - // Assume that parameters validation was done before and - // we deal with exactly 2 values separated with ':'. - boost::split(value, rename_headers[i], boost::is_any_of(":")); - - headers.push_back(value[0]); - lstring original(headers.back().c_str()); - - headers.push_back(value[1]); - lstring replacement(headers.back().c_str()); - - this->allowed_headers[original] = replacement; - } - - // For allowed (not mapped) headers there is no replacement - // value. Empty string is used to indicate the case. - for (size_t i = 0; i < allowed_headers.size(); i++) - { - headers.push_back(allowed_headers[i]); - lstring header(headers.back().c_str()); - if (this->allowed_headers.find(header) == this->allowed_headers.end()) - { - this->allowed_headers[header] = empty; - } - } + for (auto it = headers_cont.begin(); it != headers_cont.end(); ++it) + this->allowed_headers.insert(it->c_str()); for (int httpec = HTTP_BEGIN; httpec < HTTP_END; ++httpec) { @@ -170,11 +136,6 @@ const time_duration& proxy::get_connect_timeout() const return connect_timeout; } -const time_duration& proxy::get_resolve_timeout() const -{ - return resolve_timeout; -} - const headers_type& proxy::get_allowed_headers() const { return allowed_headers; diff --git a/src/proxy.hpp b/src/proxy.hpp index 20b1337..bf7cdea 100644 --- a/src/proxy.hpp +++ b/src/proxy.hpp @@ -20,11 +20,10 @@ class proxy : public boost::noncopyable { public: proxy(asio::io_service& io, std::vector inbound, const ip::tcp::endpoint& outbound_http, - const ip::udp::endpoint& outbound_ns, const ip::udp::endpoint& name_server, - const time_duration& receive_timeout, const time_duration& connect_timeout, - const time_duration& resolve_timeout, const std::vector& allowed_headers, - const std::vector& rename_headers, - std::string error_pages_dir, bool use_unbound_resolve); + const ip::udp::endpoint& outbound_ns, const ip::udp::endpoint& name_server, + const time_duration& receive_timeout, const time_duration& connect_timeout, + const std::vector& allowed_headers, + std::string error_pages_dir); // called by main (parent) void start(); @@ -38,7 +37,6 @@ class proxy : public boost::noncopyable const ip::tcp::endpoint& get_outgoing_endpoint() const; const time_duration& get_receive_timeout() const; const time_duration& get_connect_timeout() const; - const time_duration& get_resolve_timeout() const; void dump_channels_state() const; @@ -60,10 +58,9 @@ class proxy : public boost::noncopyable ip::tcp::endpoint outbound_http; time_duration receive_timeout; time_duration connect_timeout; - time_duration resolve_timeout; session_cont sessions; - std::vector headers; // Stores actual header strings - headers_type allowed_headers; // Stores 'lstring' for quick header processing + headers_type allowed_headers; + std::vector headers_cont; std::vector error_pages[HTTP_END - HTTP_BEGIN]; static logger log; }; diff --git a/src/resolver.cpp b/src/resolver.cpp index 4d46077..a5f6ce2 100644 --- a/src/resolver.cpp +++ b/src/resolver.cpp @@ -11,54 +11,26 @@ logger resolver::log = logger(keywords::channel = "resolver"); -struct ub_create_error: std::exception { char const* what() const throw() { return "failed to create unbound context"; } }; -struct ub_config_error: std::exception { char const* what() const throw() { return "failed to configure libunbound"; } }; - void resolver::init() { dns_init(0, 0); } -resolver::resolver(asio::io_service& io, const ip::udp::endpoint& outbound, const ip::udp::endpoint& name_server, bool use_unbound_resolve) +resolver::resolver(asio::io_service& io, const ip::udp::endpoint& outbound, const ip::udp::endpoint& name_server) : socket(io) , timer(io) - , udns_context(dns_new(0)) - , unbound_context(ub_ctx_create()) - , use_unbound(use_unbound_resolve) + , context(dns_new(0)) { - if (unbound_resolve_enabled()) - { - if(!unbound_context) - throw ub_create_error(); - - if (ub_ctx_set_option(unbound_context, const_cast("outgoing-interface:"), const_cast(outbound.address().to_string().c_str()))) throw ub_config_error(); - if (ub_ctx_set_option(unbound_context, const_cast("use-syslog:"), const_cast("yes"))) throw ub_config_error(); - if (ub_ctx_set_option(unbound_context, const_cast("module-config:"), const_cast("iterator"))) throw ub_config_error(); - if (ub_ctx_set_option(unbound_context, const_cast("verbosity:"), const_cast("1"))) throw ub_config_error(); - if (ub_ctx_set_option(unbound_context, const_cast("outgoing-range:"), const_cast("4096"))) throw ub_config_error(); - if (ub_ctx_set_option(unbound_context, const_cast("num-queries-per-thread:"), const_cast("4096"))) throw ub_config_error(); - int fd = ub_fd(unbound_context); - socket.assign(ip::udp::v4(), fd); - } - else - { - dns_add_serv_s(udns_context, 0); - dns_add_serv_s(udns_context, name_server.data()); - socket.assign(ip::udp::v4(), dns_open(udns_context)); - socket.bind(outbound); - socket.connect(name_server); - } + dns_add_serv_s(context, 0); + dns_add_serv_s(context, name_server.data()); + socket.assign(ip::udp::v4(), dns_open(context)); + socket.bind(outbound); + socket.connect(name_server); } resolver::~resolver() { - dns_free(udns_context); - ub_ctx_delete(unbound_context); -} - -bool resolver::unbound_resolve_enabled() const -{ - return use_unbound; + dns_free(context); } void resolver::start() @@ -66,40 +38,13 @@ void resolver::start() start_waiting_receive(); } -int resolver::async_resolve(const char* host_name, const callback& completion) +void resolver::async_resolve(const char* host_name, const callback& completion) { TRACE() << host_name; - - int asyncid = 0; - - if (unbound_resolve_enabled()) - { - int retval = ub_resolve_async(unbound_context, const_cast(host_name), - 1 /* TYPE A (IPv4 address) */, - 1 /* CLASS IN (internet) */, - const_cast(&completion), &resolver::unbound_finished_resolve_raw, &asyncid); - if(retval != 0) - completion(boost::system::error_code(retval, boost::system::get_generic_category()), 0, 0); - } - else - { - dns_query* query = dns_submit_p(udns_context, host_name, DNS_C_IN, DNS_T_A, 0, dns_parse_a4, &resolver::udns_finished_resolve_raw, const_cast(&completion)); - if (query == 0) - completion(boost::system::error_code(dns_status(udns_context), boost::system::get_generic_category()), 0, 0); - start_waiting_timer(); - } - - return asyncid; -} - -int resolver::cancel(int asyncid) -{ - if (unbound_resolve_enabled()) - { - return ub_cancel(unbound_context, asyncid); - } - - return 0; + dns_query* query = dns_submit_p(context, host_name, DNS_C_IN, DNS_T_A, 0, dns_parse_a4, &resolver::finished_resolve_raw, const_cast(&completion)); + if (query == 0) + completion(boost::system::error_code(dns_status(context), boost::system::get_generic_category()), 0, 0); + start_waiting_timer(); } void resolver::start_waiting_receive() @@ -114,26 +59,15 @@ void resolver::finished_waiting_receive(const boost::system::error_code& ec) if (ec) return; - if (unbound_resolve_enabled()) - { - ub_process(unbound_context); - } - else - { - dns_ioevent(udns_context, 0); - } + dns_ioevent(context, 0); start_waiting_receive(); - - if (!unbound_resolve_enabled()) - { - start_waiting_timer(); - } + start_waiting_timer(); } void resolver::start_waiting_timer() { - int seconds = dns_timeouts(udns_context, -1, 0); + int seconds = dns_timeouts(context, -1, 0); TRACE() << seconds; if (seconds < 0) return; @@ -147,70 +81,26 @@ void resolver::finished_waiting_timer(const error_code& ec) if (ec) return; - dns_ioevent(udns_context, 0); + dns_ioevent(context, 0); start_waiting_timer(); } -void resolver::udns_finished_resolve_raw(dns_ctx* ctx, void* result, void* data) +void resolver::finished_resolve_raw(dns_ctx* ctx, void* result, void* data) { const callback& completion = *static_cast(data); dns_rr_a4& response = *static_cast(result); int status = dns_status(ctx); - udns_finished_resolve(status, response, completion); + finished_resolve(status, response, completion); free(result); } -void resolver::unbound_finished_resolve_raw(void* data, int status, ub_result* result) -{ - const callback& completion = *static_cast(data); - unbound_finished_resolve(status, result, completion); - ub_resolve_free(result); -} - -void resolver::udns_finished_resolve(int status, const dns_rr_a4& response, const callback& completion) -{ - TRACE() << status; - iterator begin, end; - boost::system::error_code ec = boost::system::error_code(status, boost::system::get_generic_category()); - - std::vector addrs; - - if (status >= 0) - { - for (int i = 0; i < response.dnsa4_nrr; i++) - { - addrs.push_back(reinterpret_cast(response.dnsa4_addr + i)); - } - - begin = &addrs[0]; - end = &addrs[addrs.size()]; - } - completion(ec, begin, end); -} - -void resolver::unbound_finished_resolve(int status, ub_result* result, const callback& completion) +void resolver::finished_resolve(int status, const dns_rr_a4& response, const callback& completion) { TRACE() << status; - iterator begin, end; - boost::system::error_code ec; - if (status == 0) - { - if (result->havedata) - { - begin = result->data; - for (end = begin; end; ++end); - } - else - { - ec = boost::system::error_code(result->rcode ? result->rcode : boost::system::errc::operation_canceled, boost::system::get_generic_category()); - } - } - else - { - ec = boost::system::error_code(status, boost::system::get_generic_category()); - } - completion(ec, begin, end); + completion(boost::system::error_code(status, boost::system::get_generic_category()), + status < 0 ? 0 : reinterpret_cast(response.dnsa4_addr), + status < 0 ? 0 : reinterpret_cast(response.dnsa4_addr) + response.dnsa4_nrr); } diff --git a/src/resolver.hpp b/src/resolver.hpp index 521b8e0..d1bb576 100644 --- a/src/resolver.hpp +++ b/src/resolver.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #include "common.hpp" @@ -23,64 +22,33 @@ using boost::system::error_code; class resolver { public: - class iterator - { - public: - iterator(char** ptr=0) - : ptr(reinterpret_cast(ptr)) - { - } - ip::address_v4& operator * () - { - return **ptr; - } - iterator& operator ++ () - { - ptr++; - return *this; - } - operator bool () - { - return *ptr; - } - private: - ip::address_v4** ptr; - }; - typedef boost::function callback; + typedef ip::address_v4* iterator; + typedef const ip::address_v4* const_iterator; + typedef boost::function callback; static void init(); - resolver(asio::io_service& io, const ip::udp::endpoint& outbound, const ip::udp::endpoint& name_server, bool use_unbound_resolve); + resolver(asio::io_service& io, const ip::udp::endpoint& outbound, const ip::udp::endpoint& name_server); ~resolver(); void start(); - - int async_resolve(const char* host_name, const callback& completion); - int cancel(int asyncid); + void async_resolve(const char* host_name, const callback& completion); protected: - bool unbound_resolve_enabled() const; - void start_waiting_receive(); void finished_waiting_receive(const boost::system::error_code& ec); void start_waiting_timer(); void finished_waiting_timer(const error_code& ec); - static void udns_finished_resolve_raw(dns_ctx* ctx, void* result, void* data); - static void udns_finished_resolve(int status, const dns_rr_a4& response, const callback& completion); - - static void unbound_finished_resolve_raw(void* data, int status, ub_result* result); - static void unbound_finished_resolve(int status, ub_result* result, const callback& completion); + static void finished_resolve_raw(dns_ctx* ctx, void* result, void* data); + static void finished_resolve(int status, const dns_rr_a4& response, const callback& completion); private: typedef boost::function resolve_callback_internal; - ip::udp::socket socket; asio::deadline_timer timer; - dns_ctx* udns_context; - ub_ctx* unbound_context; - bool use_unbound; + dns_ctx* context; static logger log; }; diff --git a/src/session.cpp b/src/session.cpp index 3ea4232..c53e19c 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -24,9 +24,7 @@ session::session(asio::io_service& io, proxy& parent_proxy) , opened_channels(2) , resolve_handler(boost::bind(&session::finished_resolving, this, placeholders::error(), _2, _3)) , connect_timeout(parent_proxy.get_connect_timeout()) - , resolve_timeout(parent_proxy.get_resolve_timeout()) - , timeout_timer(io) - , resolveid() + , connect_timer(io) { } @@ -103,34 +101,12 @@ void session::finished_receive_header(const error_code& ec, std::size_t bytes_tr void session::start_resolving(const char* peer) { TRACE() << peer << ":" << port; - resolveid = parent_proxy.get_resolver().async_resolve(peer, resolve_handler); - start_waiting_resolve_timer(); + parent_proxy.get_resolver().async_resolve(peer, resolve_handler); } -void session::start_waiting_resolve_timer() -{ - TRACE(); - timeout_timer.expires_from_now(asio::deadline_timer::duration_type(0, 0, resolve_timeout.seconds())); - timeout_timer.async_wait(boost::bind(&session::finished_waiting_resolve_timer, this, placeholders::error)); -} - -void session::finished_waiting_resolve_timer(const error_code& ec) -{ - TRACE_ERROR(ec); - if (ec) - return; - - if (parent_proxy.get_resolver().cancel(resolveid) == 0) { - finished_resolving(boost::system::errc::make_error_code(boost::system::errc::timed_out), 0, 0); - } -} - -void session::finished_resolving(const error_code& ec, resolver::iterator begin, resolver::iterator end) +void session::finished_resolving(const error_code& ec, resolver::const_iterator begin, resolver::const_iterator end) { TRACE_ERROR(ec); - - timeout_timer.cancel(); - if (ec) { statistics::increment("resolve_failed"); @@ -146,8 +122,8 @@ void session::finished_resolving(const error_code& ec, resolver::iterator begin, void session::start_waiting_connect_timer() { TRACE(); - timeout_timer.expires_from_now(asio::deadline_timer::duration_type(0, 0, connect_timeout.seconds())); - timeout_timer.async_wait(boost::bind(&session::finished_waiting_connect_timer, this, placeholders::error)); + connect_timer.expires_from_now(asio::deadline_timer::duration_type(0, 0, connect_timeout.seconds())); + connect_timer.async_wait(boost::bind(&session::finished_waiting_connect_timer, this, placeholders::error)); } void session::finished_waiting_connect_timer(const error_code& ec) @@ -191,7 +167,7 @@ void session::start_connecting_to_peer(const ip::tcp::endpoint& peer) void session::finished_connecting_to_peer(const error_code& ec) { TRACE_ERROR(ec); - timeout_timer.cancel(); + connect_timer.cancel(); if (ec) { statistics::increment("connect_failed"); @@ -213,7 +189,7 @@ void session::finished_connecting_to_peer(const error_code& ec) void session::start_sending_header() { prepare_header(); - process_headers(); + filter_headers(); responder.async_send(output_headers, boost::bind(&session::finished_sending_header, this, placeholders::error())); } @@ -295,11 +271,16 @@ void session::prepare_header() lstring get_next_header(const lstring& headers, const lstring& current) { + if (*current.end == '\r' || *current.end == '\n') + return lstring(); const char* header = std::find(current.end, headers.end, '\n'); - return lstring(current.end, header + 1); + if (header == headers.end) + return lstring(); + else + return lstring(current.end, header + 1); } -void session::process_headers() +void session::filter_headers() { const headers_type& allowed_headers = parent_proxy.get_allowed_headers(); if (allowed_headers.empty()) @@ -309,36 +290,30 @@ void session::process_headers() } const lstring headers(asio::buffer_cast(headers_tail), asio::buffer_cast(headers_tail) + asio::buffer_size(headers_tail)); + lstring allowed = get_next_header(headers, lstring(headers.begin, headers.begin)); + if (!allowed) + { + output_headers.push_back(headers_tail); + return; + } - lstring header(headers.begin, headers.begin); - - // The first line is request itself, it has to be passed as is - header = get_next_header(headers, header); - output_headers.push_back(asio::const_buffer(header.begin, header.size())); - - // Request line is followed by headers, process header lines one-by-one till the first empty line - for (header = get_next_header(headers, header); !header.empty(); header = get_next_header(headers, header)) + for (;;) { - std::map::const_iterator entry = allowed_headers.find(header); - if (entry != allowed_headers.end()) + const lstring& header = get_next_header(headers, allowed); + if (!header) + break; + if (allowed_headers.find(header) == allowed_headers.end()) { - if (entry->second.empty()) - { - // Just passthrough allowed header - output_headers.push_back(asio::const_buffer(header.begin, header.size())); - } - else - { - // Replace found header name with the new one... - output_headers.push_back(asio::const_buffer(entry->second.begin, entry->second.size())); - // ...and leave its value as-is. - output_headers.push_back(asio::const_buffer(header.begin + entry->first.size(), header.size() - entry->first.size())); - } + if (allowed) + output_headers.push_back(asio::const_buffer(allowed.begin, allowed.size())); + allowed.begin = header.end; } + allowed.end = header.end; } + allowed.end = headers.end; - // Copy everything left beyond headers - output_headers.push_back(asio::const_buffer(header.begin, headers.end - header.begin)); + if (allowed) + output_headers.push_back(asio::const_buffer(allowed.begin, allowed.size())); } const channel& session::get_request_channel() const diff --git a/src/session.hpp b/src/session.hpp index 997f600..33931e8 100644 --- a/src/session.hpp +++ b/src/session.hpp @@ -43,10 +43,7 @@ class session : public boost::noncopyable void finished_receive_header(const error_code& ec, std::size_t bytes_transferred); void start_resolving(const char* peer); - void finished_resolving(const error_code& ec, resolver::iterator begin, resolver::iterator end); - - void start_waiting_resolve_timer(); - void finished_waiting_resolve_timer(const error_code& ec); + void finished_resolving(const error_code& ec, resolver::const_iterator begin, resolver::const_iterator end); void start_waiting_connect_timer(); void finished_waiting_connect_timer(const error_code& ec); @@ -69,7 +66,7 @@ class session : public boost::noncopyable const char* parse_header(std::size_t size); void prepare_header(); - void process_headers(); + void filter_headers(); private: friend class channel; @@ -96,14 +93,12 @@ class session : public boost::noncopyable method_type method; int opened_channels; - boost::function resolve_handler; + boost::function resolve_handler; util::high_resolution_timer timer; error_code prev_ec; static logger log; const time_duration connect_timeout; - const time_duration resolve_timeout; - asio::deadline_timer timeout_timer; - int resolveid; + asio::deadline_timer connect_timer; }; typedef boost::shared_ptr session_ptr; diff --git a/src/wscript b/src/wscript index 3b1a0df..4ab6a13 100644 --- a/src/wscript +++ b/src/wscript @@ -4,18 +4,17 @@ def configure(conf): conf.env.LIB_BOOST = ['boost_program_options', 'boost_system', 'boost_log', 'boost_log_setup', 'pthread', 'boost_filesystem'] conf.env.LIB_UDNS = ['udns'] - conf.env.LIB_LDNS = ['ldns'] - conf.env.LIB_UNBOUND = ['unbound'] - conf.env.LIB_CRYPTO = ['ssl', 'crypto'] + conf.env.LINKFLAGS_UNBOUND = '-Wl,-Bstatic' conf.env.LIB_RT = ['rt'] conf.env.LIBPATH_BOOST = ['/usr/local/lib64'] def build(bld): + # 1. A simple c++ program bld( features = 'cxx cprogram', source = 'fastproxy.cpp channel.cpp session.cpp resolver.cpp proxy.cpp statistics.cpp stat_sess.cpp signal.cpp', target = 'fastproxy', defines = ['BOOST_ASIO_DISABLE_THREADS', 'BOOST_LOG_NO_THREADS'], - uselib = 'BOOST UNBOUND UDNS CRYPTO LDNS RT', + uselib = 'BOOST UDNS RT', cxxflags = '-std=c++0x') bld.install_dir('/var/log/fastproxy') diff --git a/test/fastproxy_test.py b/test/fastproxy_test.py index 26afc7b..fdced47 100644 --- a/test/fastproxy_test.py +++ b/test/fastproxy_test.py @@ -3,33 +3,29 @@ @author: nbryskin ''' -import os import unittest -import subprocess +from subprocess import Popen import urllib import time import socket -import signal class Test(unittest.TestCase): port = 32567 timeout = 5 + allowed_header = 'AllowedHeader' stat_sock = '/tmp/stat.sock' - allowed_header = 'AllowedHeader' - original_header = 'OriginalHeader' - replacement_header = 'MuchBetterHeader' - def setUp(self): - self.fastproxy = subprocess.Popen('../build/release/src/fastproxy \ - --ingoing-http=127.0.0.1:{0} --receive-timeout={1} --resolve-library=udns \ - --udns-name-server=95.108.198.4 --allow-header={2} --rename-header={3}:{4} --ingoing-stat={5}'.format( - self.port, self.timeout, self.allowed_header, self.original_header, self.replacement_header, self.stat_sock), - shell=True, env={'LD_LIBRARY_PATH': '/usr/local/lib64'}, preexec_fn=os.setsid) + self.fastproxy = Popen('../build/debug/src/fastproxy \ + --ingoing-http=127.0.0.1:{0} --receive-timeout={1}\ + --name-server=95.108.198.4 --allow-header={2} --ingoing-stat={3}'.format(self.port, self.timeout, self.allowed_header, self.stat_sock), + shell=True, env={'LD_LIBRARY_PATH': '/usr/local/lib64'}) time.sleep(1) def tearDown(self): - os.killpg(self.fastproxy.pid, signal.SIGTERM) + #pass + self.fastproxy.terminate() + self.fastproxy.wait() def test_running(self): self.assertFalse(self.fastproxy.poll()) @@ -39,7 +35,7 @@ def test_simple(self): def test_simple_ip(self): urllib.urlopen('http://77.88.21.3', proxies={'http': 'http://localhost:{0}'.format(self.port)}) - + def _send_request(self, headers=None, method='GET', host='localhost'): l = socket.socket(socket.AF_INET, socket.SOCK_STREAM) l.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -52,7 +48,7 @@ def _send_request(self, headers=None, method='GET', host='localhost'): l.settimeout(1) s, addr = l.accept() return s.recv(len(request)) - + def test_http(self): request = self._send_request() self.assertEqual(request, 'GET / HTTP/1.0\r\n\r\n') @@ -68,8 +64,8 @@ def test_timeout_crash(self): time.sleep(self.timeout) self.assertFalse(self.fastproxy.poll()) self.assertRaises(BaseException, self.c.send('a')) - - def test_allowed_header(self): + + def test_header_filter(self): allowed_header = '{0}: test\r\n'.format(self.allowed_header) disallowed_header = 'DisAllowedHeader: test\r\n' request = self._send_request(allowed_header) @@ -86,34 +82,6 @@ def test_allowed_header(self): request = self._send_request(header) self.assertEqual(request, 'GET / HTTP/1.0\r\n{0}{0}{0}\r\n'.format(allowed_header)) - def test_doesnot_allow_by_prefix(self): - allowed_header = '{0}: test\r\n'.format(self.allowed_header) - disallowed_header = 'AllowedHeaderExceptThis: test\r\n' - - header = '{0}{1}{0}{1}{0}'.format(allowed_header, disallowed_header) - request = self._send_request(header) - self.assertEqual(request, 'GET / HTTP/1.0\r\n{0}{0}{0}\r\n'.format(allowed_header)) - - def test_rename_header(self): - allowed_header = '{0}: test\r\n'.format(self.allowed_header) - original_header = '{0}: test\r\n'.format(self.original_header) - replacement_header = '{0}: test\r\n'.format(self.replacement_header) - request = self._send_request(original_header) - self.assertEqual(request, 'GET / HTTP/1.0\r\n{0}\r\n'.format(replacement_header)) - - header = '{1}{1}{0}{0}{1}{1}'.format(allowed_header, original_header) - request = self._send_request(header) - self.assertEqual(request, 'GET / HTTP/1.0\r\n{1}{1}{0}{0}{1}{1}\r\n'.format(allowed_header, replacement_header)) - - header = '{0}{1}{0}{1}{0}'.format(allowed_header, original_header) - request = self._send_request(header) - self.assertEqual(request, 'GET / HTTP/1.0\r\n{0}{1}{0}{1}{0}\r\n'.format(allowed_header, replacement_header)) - - def test_explicitly_allow_replacement_header(self): - replacement_header = '{0}: test\r\n'.format(self.replacement_header) - request = self._send_request(replacement_header) - self.assertEqual(request, 'GET / HTTP/1.0\r\n\r\n') - def test_http_methods(self): method='DELETE' request = self._send_request(method=method) @@ -127,16 +95,18 @@ def test_resolve_self_ip(self): def test_statistics(self): self.stat = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.stat.connect(self.stat_sock) - + self.test_http() self.c.close() self.stat.send('total_sessions current_sessions total_stat_sessions current_stat_sessions unexisting_stat\n') self.assertEqual(self.stat.recv(64), '1\t0\t1\t1\tunexisting_stat?\n') - + self.test_http() self.c.close() self.stat.send('total_sessions current_sessions total_stat_sessions current_stat_sessions unexisting_stat\n') self.assertEqual(self.stat.recv(64), '2\t0\t1\t1\tunexisting_stat?\n') if __name__ == "__main__": - unittest.main() + import sys + #sys.argv = ['', 'Test.test_simple_ip'] + unittest.main() \ No newline at end of file diff --git a/wscript b/wscript index dd5eb64..f87f10e 100644 --- a/wscript +++ b/wscript @@ -1,7 +1,6 @@ #! /usr/bin/env python # encoding: utf-8 -import os import Options # the following two variables are used by the target "waf dist" @@ -15,37 +14,13 @@ out = 'build' def set_options(opt): opt.tool_options('compiler_cxx') opt.tool_options('python') - - opt.add_option('--build-type', action='store', default='debug,release', help='build the selected variants') - opt.add_option('--boost-path', action='store', help="'boost' library location root path") - opt.add_option('--udns-path', action='store', help="'udns' library location root path") - opt.add_option('--ldns-path', action='store', help="'ldns' library location root path") - opt.add_option('--unbound-path', action='store', help="'unbound' library location root path") + opt.add_option('--build_kind', action='store', default='debug,release', help='build the selected variants') def configure(conf): - print Options.options.boost_path - if Options.options.boost_path: - conf.env.append_value('CXXFLAGS', ['-I', os.path.join(Options.options.boost_path, 'include')]) - conf.env.append_value('LIBPATH', [os.path.join(Options.options.boost_path, 'lib')]) - - if Options.options.udns_path: - conf.env.append_value('CXXFLAGS', ['-I', Options.options.udns_path]) - conf.env.append_value('LIBPATH', [Options.options.udns_path]) - - if Options.options.unbound_path: - conf.env.append_value('CXXFLAGS', ['-I', os.path.join(Options.options.unbound_path, 'include')]) - conf.env.append_value('LIBPATH', [os.path.join(Options.options.unbound_path, 'lib')]) - - if Options.options.ldns_path: - conf.env.append_value('CXXFLAGS',['-I', os.path.join(Options.options.ldns_path, 'include')]) - conf.env.append_value('LIBPATH', [os.path.join(Options.options.ldns_path,'lib')]) - conf.check_tool('compiler_cxx') conf.check_tool('boost') - conf.check_boost(version='1.45') + conf.check_boost(version='1.43') conf.check_cxx(fragment='#include \nint main(){return 0;}\n', lib='udns', mandatory=True) - conf.check_cxx(fragment='#include \nint main(){return 0;}\n', lib='ldns', mandatory=True) - conf.check_cxx(fragment='#include \nint main(){return 0;}\n', lib='unbound', mandatory=True) conf.sub_config('src') @@ -56,12 +31,13 @@ def configure(conf): dbg.set_variant('debug') conf.set_env_name('debug', dbg) conf.setenv('debug') - conf.env.append_value('CXXFLAGS', ['-D_REENTRANT', '-DDBG_ENABLED', '-Wall', '-O0', '-ggdb3', '-ftemplate-depth-128']) - + conf.env.CXXFLAGS = ['-D_REENTRANT', '-DDBG_ENABLED', '-Wall', '-O0', '-ggdb3', '-ftemplate-depth-128'] + #conf.env.SHLIB_MARKER = ['-Wl,-Bshared,-lgcc_s,-Bstatic'] + conf.env.LINKFLAGS_UNBOUND = '-Wl,-Bstatic' rel.set_variant('release') conf.set_env_name('release', rel) conf.setenv('release') - conf.env.append_value('CXXFLAGS', ['-O2', '-DNOTRACE', '-ggdb3']) + conf.env.CXXFLAGS = ['-O2', '-DNOTRACE', '-ggdb3'] def build(bld): @@ -86,7 +62,7 @@ def build(bld): obj.posted = 1 # disable the unwanted variant(s) - kind = Options.options.build_type + kind = Options.options.build_kind if kind.find('debug') < 0: debug_obj.posted = 1 if kind.find('release') < 0: From 3e5f80f2a33b8154ae1fbcc6431cc578c4c4c42a Mon Sep 17 00:00:00 2001 From: Alexander Duryagin Date: Wed, 20 Mar 2019 17:13:55 +0300 Subject: [PATCH 2/2] remove trailing tab --- src/fastproxy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fastproxy.cpp b/src/fastproxy.cpp index b2d1da7..79aea60 100644 --- a/src/fastproxy.cpp +++ b/src/fastproxy.cpp @@ -188,7 +188,7 @@ void fastproxy::init_proxy() } void fastproxy::init_resolver() -{ +{ resolver::init(); }