Skip to content

Commit

Permalink
support happy eyeball
Browse files Browse the repository at this point in the history
  • Loading branch information
Jackarain committed Apr 9, 2020
1 parent fb05f84 commit 04cef67
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 2 deletions.
13 changes: 12 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,17 @@ if (USE_HUNTER)
hunter_add_package(Boost COMPONENTS thread system filesystem coroutine context date_time regex program_options)
endif()


include(CheckCXXCompilerFlag)

CHECK_CXX_COMPILER_FLAG(-std=c++17 LIBCXX_HAS_STDCXX17_FLAG)

if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if(NOT LIBCXX_HAS_STDCXX17_FLAG)
message(FATAL_ERROR "need at least GCC 9 or clang 9")
endif()

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc")
else()
set(CompilerFlags
Expand All @@ -41,6 +50,7 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
endif()


set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
#set(Boost_USE_STATIC_RUNTIME ON)
Expand All @@ -62,6 +72,7 @@ add_executable(socks_server

src/io.hpp
src/socks_server.hpp
src/async_connect.hpp
src/v7.h
)

Expand Down
211 changes: 211 additions & 0 deletions src/async_connect.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
//
// async_connect.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2019 Jack (jack dot wgm at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#pragma once

#include <boost/asio/dispatch.hpp>
#include <boost/asio/connect.hpp>

#include <boost/core/ignore_unused.hpp>
#include <boost/smart_ptr/local_shared_ptr.hpp>
#include <boost/smart_ptr/make_local_shared.hpp>

#include <atomic>
#include <utility>
#include <memory>
#include <vector>
#include <type_traits>


namespace socks {

namespace detail {

template <typename Stream>
struct connect_params_detaila
{
std::atomic_int flag_;
std::atomic_int num_;
std::vector<boost::local_shared_ptr<Stream>> socket_;
};

template <typename Handler, typename ResultType>
void do_result(Handler&& handler, const boost::system::error_code& error, ResultType&& result)
{
handler(error, result);
}

struct initiate_do_connect
{
bool use_happy_eyeball = false;

template <typename Stream, typename Handler, typename Iterator, typename ResultType = void>
void do_async_connect(Handler&& handler, Stream& s, Iterator begin, Iterator end)
{
auto params = boost::make_local_shared<connect_params_detaila<Stream>>();

params->flag_ = false;
params->num_ = std::distance(begin, end);
if (params->num_ == 0)
{
{
boost::system::error_code error = boost::asio::error::no_data;
auto h = std::move(handler);

if constexpr (std::is_same_v<ResultType, typename Stream::endpoint_type>)
do_result(h, error, *begin);
if constexpr (!std::is_same_v<ResultType, typename Stream::endpoint_type>)
do_result(h, error, begin);
}

return;
}

// happy eyeballs detection
{
bool has_a = false, has_aaaa = false;

for (auto begin_ = begin; begin_ != end; begin_++)
{
has_aaaa |= boost::asio::ip::address(begin_->endpoint().address()).is_v6();
has_a |= boost::asio::ip::address(begin_->endpoint().address()).is_v4();
}

if (has_aaaa && has_a)
use_happy_eyeball = true;
}

for (; begin != end; begin++)
{
auto sock = boost::make_local_shared<Stream>(s.get_executor());
params->socket_.emplace_back(sock);

auto do_connect = [begin, &s, params, sock, handler]()
{
sock->async_connect(*begin, [&s, params, begin, sock, handler]
(const boost::system::error_code& error) mutable
{
if (!error)
{
if (params->flag_)
return;

params->flag_ = true;

s = std::move(*sock);
}

params->num_--;
bool is_last = params->num_ == 0;

if (error)
{
if (params->flag_ || !is_last)
return;
}

auto& sockets = params->socket_;
for (auto& t : sockets)
{
if (!t)
continue;
boost::system::error_code ignore_ec;
t->cancel(ignore_ec);
}

auto executor = boost::asio::get_associated_executor(handler);
auto h = std::move(handler);

boost::asio::dispatch(executor, [error, h, begin]() mutable
{
if constexpr (std::is_same_v<ResultType, typename Stream::endpoint_type>)
do_result(h, error, *begin);
if constexpr (!std::is_same_v<ResultType, typename Stream::endpoint_type>)
do_result(h, error, begin);
});
});
};

if (use_happy_eyeball && begin->endpoint().address().is_v4())
{
// delay 200ms
auto delay_timer = boost::make_local_shared<boost::asio::deadline_timer>(s.get_executor());
delay_timer->expires_from_now(boost::posix_time::milliseconds(200));
delay_timer->async_wait([delay_timer, do_connect, params](const boost::system::error_code& error)
{
boost::ignore_unused(error);
if (params->flag_)
return;
do_connect();
});
}
else
{
do_connect();
}
}
}

template <typename Stream, typename Iterator, typename Handler>
void operator()(Handler&& handler, Stream& s, Iterator begin, Iterator end)
{
do_async_connect(std::forward<Handler>(handler), s, begin, end);
}

template <typename Stream, typename EndpointSequence, typename Handler>
void operator()(Handler&& handler, Stream& s, const EndpointSequence& endpoints)
{
auto begin = endpoints.begin();
auto end = endpoints.end();
using Iterator = decltype(begin);

do_async_connect<Stream, Handler, Iterator, typename Stream::endpoint_type>(std::forward<Handler>(handler), s, begin, end);
}
};
}

template <typename Stream,
typename Iterator, typename IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void(boost::system::error_code, Iterator))
async_connect(Stream& stream, Iterator begin,
BOOST_ASIO_MOVE_ARG(IteratorConnectHandler) handler,
typename boost::asio::enable_if<!boost::asio::is_endpoint_sequence<Iterator>::value>::type* = 0)
{
return boost::asio::async_initiate<IteratorConnectHandler,
void(boost::system::error_code, Iterator)>
(detail::initiate_do_connect{}, handler, stream, begin, Iterator());
}

template <typename Stream,
typename Iterator, typename IteratorConnectHandler>
inline BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void(boost::system::error_code, Iterator))
async_connect(Stream& stream, Iterator begin, Iterator end,
BOOST_ASIO_MOVE_ARG(IteratorConnectHandler) handler)
{
return boost::asio::async_initiate<IteratorConnectHandler,
void(boost::system::error_code, Iterator)>
(detail::initiate_do_connect{}, handler, stream, begin, end);
}

template <typename Stream,
typename EndpointSequence, typename IteratorConnectHandler>
inline BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,
void(boost::system::error_code, typename Stream::endpoint_type))
async_connect(Stream& stream, const EndpointSequence& endpoints,
BOOST_ASIO_MOVE_ARG(IteratorConnectHandler) handler,
typename boost::asio::enable_if<boost::asio::is_endpoint_sequence<EndpointSequence>::value>::type* = 0)
{
return boost::asio::async_initiate<IteratorConnectHandler,
void(boost::system::error_code, typename Stream::endpoint_type)>
(detail::initiate_do_connect{}, handler, stream, endpoints);
}
}
4 changes: 3 additions & 1 deletion src/socks_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include <boost/smart_ptr/make_local_shared.hpp>

#include "io.hpp"
#include "async_connect.hpp"

extern "C"
{
#include "v7.h"
Expand Down Expand Up @@ -1019,7 +1021,7 @@ class socks_session
return;
}

boost::asio::async_connect(m_remote_socket, endpoint_iterator,
socks::async_connect(m_remote_socket, endpoint_iterator,
boost::bind(&socks_session::socks_handle_connect_3,
shared_from_this(), boost::asio::placeholders::error,
endpoint_iterator
Expand Down

0 comments on commit 04cef67

Please sign in to comment.