mirror of
https://github.com/zeromq/libzmq.git
synced 2024-12-12 10:33:52 +01:00
Add support for SOCKS5 basic authentication
This commit is contained in:
parent
42cfa697f2
commit
58c30dc7d1
@ -891,10 +891,42 @@ Applicable socket types:: all
|
||||
ZMQ_SOCKS_PROXY: Set SOCKS5 proxy address
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the SOCKS5 proxy address that shall be used by the socket for the TCP
|
||||
connection(s). Does not support SOCKS5 authentication. If the endpoints are
|
||||
domain names instead of addresses they shall not be resolved and they shall
|
||||
be forwarded unchanged to the SOCKS proxy service in the client connection
|
||||
request message (address type 0x03 domain name).
|
||||
connection(s). Supported authentication methods are: no authentication
|
||||
or basic authentication when setup with ZMQ_SOCKS_USERNAME. If the endpoints
|
||||
are domain names instead of addresses they shall not be resolved and they
|
||||
shall be forwarded unchanged to the SOCKS proxy service in the client
|
||||
connection request message (address type 0x03 domain name).
|
||||
|
||||
[horizontal]
|
||||
Option value type:: character string
|
||||
Option value unit:: N/A
|
||||
Default value:: not set
|
||||
Applicable socket types:: all, when using TCP transport
|
||||
|
||||
|
||||
ZMQ_SOCKS_USERNAME: Set SOCKS username and select basic authentication
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the username for authenticated connection to the SOCKS5 proxy.
|
||||
If you set this to a non-null and non-empty value, the authentication
|
||||
method used for the SOCKS5 connection shall be basic authentication.
|
||||
In this case, use ZMQ_SOCKS_PASSWORD option in order to set the password.
|
||||
If you set this to a null value or empty value, the authentication method
|
||||
shall be no authentication, the default.
|
||||
|
||||
[horizontal]
|
||||
Option value type:: character string
|
||||
Option value unit:: N/A
|
||||
Default value:: not set
|
||||
Applicable socket types:: all, when using TCP transport
|
||||
|
||||
|
||||
ZMQ_SOCKS_PASSWORD: Set SOCKS basic authentication password
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Sets the password for authenticating to the SOCKS5 proxy server.
|
||||
This is used only when the SOCKS5 authentication method has been
|
||||
set to basic authentication through the ZMQ_SOCKS_USERNAME option.
|
||||
Setting this to a null value (the default) is equivalent to an
|
||||
empty password string.
|
||||
|
||||
[horizontal]
|
||||
Option value type:: character string
|
||||
|
@ -656,6 +656,8 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_);
|
||||
#define ZMQ_MULTICAST_LOOP 96
|
||||
#define ZMQ_ROUTER_NOTIFY 97
|
||||
#define ZMQ_XPUB_MANUAL_LAST_VALUE 98
|
||||
#define ZMQ_SOCKS_USERNAME 99
|
||||
#define ZMQ_SOCKS_PASSWORD 100
|
||||
|
||||
/* DRAFT Context options */
|
||||
#define ZMQ_ZERO_COPY_RECV 10
|
||||
|
@ -461,6 +461,24 @@ int zmq::options_t::setsockopt (int option_,
|
||||
return do_setsockopt_string_allow_empty_strict (
|
||||
optval_, optvallen_, &socks_proxy_address, SIZE_MAX);
|
||||
|
||||
case ZMQ_SOCKS_USERNAME:
|
||||
/* Make empty string or NULL equivalent. */
|
||||
if (optval_ == NULL || optvallen_ == 0) {
|
||||
socks_proxy_username.clear();
|
||||
return 0;
|
||||
} else {
|
||||
return do_setsockopt_string_allow_empty_strict (
|
||||
optval_, optvallen_, &socks_proxy_username, 255);
|
||||
}
|
||||
case ZMQ_SOCKS_PASSWORD:
|
||||
/* Make empty string or NULL equivalent. */
|
||||
if (optval_ == NULL || optvallen_ == 0) {
|
||||
socks_proxy_password.clear();
|
||||
return 0;
|
||||
} else {
|
||||
return do_setsockopt_string_allow_empty_strict (
|
||||
optval_, optvallen_, &socks_proxy_password, 255);
|
||||
}
|
||||
case ZMQ_TCP_KEEPALIVE:
|
||||
if (is_int && (value == -1 || value == 0 || value == 1)) {
|
||||
tcp_keepalive = value;
|
||||
@ -959,6 +977,14 @@ int zmq::options_t::getsockopt (int option_,
|
||||
return do_getsockopt (optval_, optvallen_, socks_proxy_address);
|
||||
break;
|
||||
|
||||
case ZMQ_SOCKS_USERNAME:
|
||||
return do_getsockopt (optval_, optvallen_, socks_proxy_username);
|
||||
break;
|
||||
|
||||
case ZMQ_SOCKS_PASSWORD:
|
||||
return do_getsockopt (optval_, optvallen_, socks_proxy_password);
|
||||
break;
|
||||
|
||||
case ZMQ_TCP_KEEPALIVE:
|
||||
if (is_int) {
|
||||
*value = tcp_keepalive;
|
||||
|
@ -159,6 +159,12 @@ struct options_t
|
||||
// Address of SOCKS proxy
|
||||
std::string socks_proxy_address;
|
||||
|
||||
// Credentials for SOCKS proxy.
|
||||
// Conneciton method will be basic auth if username
|
||||
// is not empty, no auth otherwise.
|
||||
std::string socks_proxy_username;
|
||||
std::string socks_proxy_password;
|
||||
|
||||
// TCP keep-alive settings.
|
||||
// Defaults to -1 = do not change socket options
|
||||
int tcp_keepalive;
|
||||
|
@ -668,8 +668,14 @@ zmq::own_t *zmq::session_base_t::create_connecter_tcp (io_thread_t *io_thread_,
|
||||
address_t *proxy_address = new (std::nothrow) address_t (
|
||||
protocol_name::tcp, options.socks_proxy_address, this->get_ctx ());
|
||||
alloc_assert (proxy_address);
|
||||
return new (std::nothrow) socks_connecter_t (
|
||||
socks_connecter_t *connecter = new (std::nothrow) socks_connecter_t (
|
||||
io_thread_, this, options, _addr, proxy_address, wait_);
|
||||
alloc_assert(connecter);
|
||||
if (!options.socks_proxy_username.empty ()) {
|
||||
connecter->set_auth_method_basic(options.socks_proxy_username,
|
||||
options.socks_proxy_password);
|
||||
}
|
||||
return connecter;
|
||||
}
|
||||
return new (std::nothrow)
|
||||
tcp_connecter_t (io_thread_, this, options, _addr, wait_);
|
||||
|
@ -129,6 +129,96 @@ void zmq::socks_choice_decoder_t::reset ()
|
||||
}
|
||||
|
||||
|
||||
zmq::socks_basic_auth_request_t::socks_basic_auth_request_t (std::string username_,
|
||||
std::string password_) :
|
||||
username (username_),
|
||||
password (password_)
|
||||
{
|
||||
zmq_assert (username_.size () <= UINT8_MAX);
|
||||
zmq_assert (password_.size () <= UINT8_MAX);
|
||||
}
|
||||
|
||||
|
||||
zmq::socks_basic_auth_request_encoder_t::socks_basic_auth_request_encoder_t () :
|
||||
_bytes_encoded (0),
|
||||
_bytes_written (0)
|
||||
{
|
||||
}
|
||||
|
||||
void zmq::socks_basic_auth_request_encoder_t::encode (const socks_basic_auth_request_t &req_)
|
||||
{
|
||||
|
||||
unsigned char *ptr = _buf;
|
||||
*ptr++ = 0x01;
|
||||
*ptr++ = static_cast<unsigned char> (req_.username.size ());
|
||||
memcpy (ptr, req_.username.c_str (), req_.username.size ());
|
||||
ptr += req_.username.size ();
|
||||
*ptr++ = static_cast<unsigned char> (req_.password.size ());
|
||||
memcpy (ptr, req_.password.c_str (), req_.password.size ());
|
||||
ptr += req_.password.size ();
|
||||
|
||||
_bytes_encoded = ptr - _buf;
|
||||
_bytes_written = 0;
|
||||
}
|
||||
|
||||
int zmq::socks_basic_auth_request_encoder_t::output (fd_t fd_)
|
||||
{
|
||||
const int rc =
|
||||
tcp_write (fd_, _buf + _bytes_written, _bytes_encoded - _bytes_written);
|
||||
if (rc > 0)
|
||||
_bytes_written += static_cast<size_t> (rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool zmq::socks_basic_auth_request_encoder_t::has_pending_data () const
|
||||
{
|
||||
return _bytes_written < _bytes_encoded;
|
||||
}
|
||||
|
||||
void zmq::socks_basic_auth_request_encoder_t::reset ()
|
||||
{
|
||||
_bytes_encoded = _bytes_written = 0;
|
||||
}
|
||||
|
||||
|
||||
zmq::socks_auth_response_t::socks_auth_response_t (uint8_t response_code_) :
|
||||
response_code (response_code_)
|
||||
{
|
||||
}
|
||||
|
||||
zmq::socks_auth_response_decoder_t::socks_auth_response_decoder_t () : _bytes_read (0)
|
||||
{
|
||||
}
|
||||
|
||||
int zmq::socks_auth_response_decoder_t::input (fd_t fd_)
|
||||
{
|
||||
zmq_assert (_bytes_read < 2);
|
||||
const int rc = tcp_read (fd_, _buf + _bytes_read, 2 - _bytes_read);
|
||||
if (rc > 0) {
|
||||
_bytes_read += static_cast<size_t> (rc);
|
||||
if (_buf[0] != 0x01)
|
||||
return -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool zmq::socks_auth_response_decoder_t::message_ready () const
|
||||
{
|
||||
return _bytes_read == 2;
|
||||
}
|
||||
|
||||
zmq::socks_auth_response_t zmq::socks_auth_response_decoder_t::decode ()
|
||||
{
|
||||
zmq_assert (message_ready ());
|
||||
return socks_auth_response_t (_buf[1]);
|
||||
}
|
||||
|
||||
void zmq::socks_auth_response_decoder_t::reset ()
|
||||
{
|
||||
_bytes_read = 0;
|
||||
}
|
||||
|
||||
|
||||
zmq::socks_request_t::socks_request_t (uint8_t command_,
|
||||
std::string hostname_,
|
||||
uint16_t port_) :
|
||||
|
@ -81,6 +81,50 @@ class socks_choice_decoder_t
|
||||
size_t _bytes_read;
|
||||
};
|
||||
|
||||
|
||||
struct socks_basic_auth_request_t
|
||||
{
|
||||
socks_basic_auth_request_t (std::string username_, std::string password_);
|
||||
|
||||
const std::string username;
|
||||
const std::string password;
|
||||
};
|
||||
|
||||
class socks_basic_auth_request_encoder_t
|
||||
{
|
||||
public:
|
||||
socks_basic_auth_request_encoder_t ();
|
||||
void encode (const socks_basic_auth_request_t &req_);
|
||||
int output (fd_t fd_);
|
||||
bool has_pending_data () const;
|
||||
void reset ();
|
||||
|
||||
private:
|
||||
size_t _bytes_encoded;
|
||||
size_t _bytes_written;
|
||||
uint8_t _buf[1 + 1 + UINT8_MAX + 1 + UINT8_MAX];
|
||||
};
|
||||
|
||||
struct socks_auth_response_t
|
||||
{
|
||||
socks_auth_response_t (uint8_t response_code_);
|
||||
uint8_t response_code;
|
||||
};
|
||||
|
||||
class socks_auth_response_decoder_t
|
||||
{
|
||||
public:
|
||||
socks_auth_response_decoder_t ();
|
||||
int input (fd_t fd_);
|
||||
bool message_ready () const;
|
||||
socks_auth_response_t decode ();
|
||||
void reset ();
|
||||
|
||||
private:
|
||||
int8_t _buf[2];
|
||||
size_t _bytes_read;
|
||||
};
|
||||
|
||||
struct socks_request_t
|
||||
{
|
||||
socks_request_t (uint8_t command_, std::string hostname_, uint16_t port_);
|
||||
|
@ -61,6 +61,7 @@ zmq::socks_connecter_t::socks_connecter_t (class io_thread_t *io_thread_,
|
||||
stream_connecter_base_t (
|
||||
io_thread_, session_, options_, addr_, delayed_start_),
|
||||
_proxy_addr (proxy_addr_),
|
||||
_auth_method(socks_no_auth_required),
|
||||
_status (unplugged)
|
||||
{
|
||||
zmq_assert (_addr->protocol == protocol_name::tcp);
|
||||
@ -72,8 +73,24 @@ zmq::socks_connecter_t::~socks_connecter_t ()
|
||||
LIBZMQ_DELETE (_proxy_addr);
|
||||
}
|
||||
|
||||
void zmq::socks_connecter_t::set_auth_method_none ()
|
||||
{
|
||||
_auth_method = socks_no_auth_required;
|
||||
_auth_username.clear();
|
||||
_auth_password.clear();
|
||||
}
|
||||
|
||||
void zmq::socks_connecter_t::set_auth_method_basic (const std::string username,
|
||||
const std::string password)
|
||||
{
|
||||
_auth_method = socks_basic_auth;
|
||||
_auth_username = username;
|
||||
_auth_password = password;
|
||||
}
|
||||
|
||||
void zmq::socks_connecter_t::in_event ()
|
||||
{
|
||||
int expected_status = -1;
|
||||
zmq_assert (_status != unplugged);
|
||||
|
||||
if (_status == waiting_for_choice) {
|
||||
@ -86,17 +103,21 @@ void zmq::socks_connecter_t::in_event ()
|
||||
if (rc == -1)
|
||||
error ();
|
||||
else {
|
||||
std::string hostname;
|
||||
uint16_t port = 0;
|
||||
if (parse_address (_addr->address, hostname, port) == -1)
|
||||
if (choice.method == socks_basic_auth) expected_status = sending_basic_auth_request;
|
||||
else expected_status = sending_request;
|
||||
}
|
||||
}
|
||||
} else if (_status == waiting_for_auth_response) {
|
||||
int rc = _auth_response_decoder.input (_s);
|
||||
if (rc == 0 || rc == -1)
|
||||
error ();
|
||||
else if (_auth_response_decoder.message_ready ()) {
|
||||
const socks_auth_response_t auth_response = _auth_response_decoder.decode ();
|
||||
rc = process_server_response (auth_response);
|
||||
if (rc == -1)
|
||||
error ();
|
||||
else {
|
||||
_request_encoder.encode (
|
||||
socks_request_t (1, hostname, port));
|
||||
reset_pollin (_handle);
|
||||
set_pollout (_handle);
|
||||
_status = sending_request;
|
||||
}
|
||||
expected_status = sending_request;
|
||||
}
|
||||
}
|
||||
} else if (_status == waiting_for_response) {
|
||||
@ -118,12 +139,33 @@ void zmq::socks_connecter_t::in_event ()
|
||||
}
|
||||
} else
|
||||
error ();
|
||||
|
||||
if (expected_status == sending_basic_auth_request) {
|
||||
_basic_auth_request_encoder.encode (socks_basic_auth_request_t (_auth_username, _auth_password));
|
||||
reset_pollin (_handle);
|
||||
set_pollout (_handle);
|
||||
_status = sending_basic_auth_request;
|
||||
} else if (expected_status == sending_request) {
|
||||
std::string hostname;
|
||||
uint16_t port = 0;
|
||||
if (parse_address (_addr->address, hostname, port) == -1)
|
||||
error ();
|
||||
else {
|
||||
_request_encoder.encode (socks_request_t (1, hostname, port));
|
||||
reset_pollin (_handle);
|
||||
set_pollout (_handle);
|
||||
_status = sending_request;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void zmq::socks_connecter_t::out_event ()
|
||||
{
|
||||
zmq_assert (_status == waiting_for_proxy_connection
|
||||
|| _status == sending_greeting || _status == sending_request);
|
||||
|| _status == sending_greeting
|
||||
|| _status == sending_basic_auth_request
|
||||
|| _status == sending_request);
|
||||
|
||||
if (_status == waiting_for_proxy_connection) {
|
||||
const int rc = static_cast<int> (check_proxy_connection ());
|
||||
@ -131,7 +173,7 @@ void zmq::socks_connecter_t::out_event ()
|
||||
error ();
|
||||
else {
|
||||
_greeting_encoder.encode (
|
||||
socks_greeting_t (socks_no_auth_required));
|
||||
socks_greeting_t (_auth_method));
|
||||
_status = sending_greeting;
|
||||
}
|
||||
} else if (_status == sending_greeting) {
|
||||
@ -144,6 +186,16 @@ void zmq::socks_connecter_t::out_event ()
|
||||
set_pollin (_handle);
|
||||
_status = waiting_for_choice;
|
||||
}
|
||||
} else if (_status == sending_basic_auth_request) {
|
||||
zmq_assert (_basic_auth_request_encoder.has_pending_data ());
|
||||
const int rc = _basic_auth_request_encoder.output (_s);
|
||||
if (rc == -1 || rc == 0)
|
||||
error ();
|
||||
else if (!_basic_auth_request_encoder.has_pending_data ()) {
|
||||
reset_pollout (_handle);
|
||||
set_pollin (_handle);
|
||||
_status = waiting_for_auth_response;
|
||||
}
|
||||
} else {
|
||||
zmq_assert (_request_encoder.has_pending_data ());
|
||||
const int rc = _request_encoder.output (_s);
|
||||
@ -189,8 +241,9 @@ void zmq::socks_connecter_t::start_connecting ()
|
||||
int zmq::socks_connecter_t::process_server_response (
|
||||
const socks_choice_t &response_)
|
||||
{
|
||||
// We do not support any authentication method for now.
|
||||
return response_.method == 0 ? 0 : -1;
|
||||
return response_.method == socks_no_auth_required ||
|
||||
response_.method == socks_basic_auth ?
|
||||
0 : -1;
|
||||
}
|
||||
|
||||
int zmq::socks_connecter_t::process_server_response (
|
||||
@ -199,12 +252,20 @@ int zmq::socks_connecter_t::process_server_response (
|
||||
return response_.response_code == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int zmq::socks_connecter_t::process_server_response (
|
||||
const socks_auth_response_t &response_)
|
||||
{
|
||||
return response_.response_code == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
void zmq::socks_connecter_t::error ()
|
||||
{
|
||||
rm_fd (_handle);
|
||||
close ();
|
||||
_greeting_encoder.reset ();
|
||||
_choice_decoder.reset ();
|
||||
_basic_auth_request_encoder.reset ();
|
||||
_auth_response_decoder.reset ();
|
||||
_request_encoder.reset ();
|
||||
_response_decoder.reset ();
|
||||
_status = unplugged;
|
||||
|
@ -54,6 +54,11 @@ class socks_connecter_t : public stream_connecter_base_t
|
||||
bool delayed_start_);
|
||||
~socks_connecter_t ();
|
||||
|
||||
void set_auth_method_basic (const std::string username,
|
||||
const std::string password);
|
||||
void set_auth_method_none ();
|
||||
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
@ -62,6 +67,8 @@ class socks_connecter_t : public stream_connecter_base_t
|
||||
waiting_for_proxy_connection,
|
||||
sending_greeting,
|
||||
waiting_for_choice,
|
||||
sending_basic_auth_request,
|
||||
waiting_for_auth_response,
|
||||
sending_request,
|
||||
waiting_for_response
|
||||
};
|
||||
@ -69,7 +76,9 @@ class socks_connecter_t : public stream_connecter_base_t
|
||||
// Method ID
|
||||
enum
|
||||
{
|
||||
socks_no_auth_required = 0
|
||||
socks_no_auth_required = 0x00,
|
||||
socks_basic_auth = 0x02,
|
||||
socks_no_acceptable_method = 0xff
|
||||
};
|
||||
|
||||
// Handlers for I/O events.
|
||||
@ -81,6 +90,7 @@ class socks_connecter_t : public stream_connecter_base_t
|
||||
|
||||
int process_server_response (const socks_choice_t &response_);
|
||||
int process_server_response (const socks_response_t &response_);
|
||||
int process_server_response (const socks_auth_response_t &response_);
|
||||
|
||||
int parse_address (const std::string &address_,
|
||||
std::string &hostname_,
|
||||
@ -101,12 +111,21 @@ class socks_connecter_t : public stream_connecter_base_t
|
||||
|
||||
socks_greeting_encoder_t _greeting_encoder;
|
||||
socks_choice_decoder_t _choice_decoder;
|
||||
socks_basic_auth_request_encoder_t _basic_auth_request_encoder;
|
||||
socks_auth_response_decoder_t _auth_response_decoder;
|
||||
socks_request_encoder_t _request_encoder;
|
||||
socks_response_decoder_t _response_decoder;
|
||||
|
||||
// SOCKS address; owned by this connecter.
|
||||
address_t *_proxy_addr;
|
||||
|
||||
// User defined authentication method
|
||||
int _auth_method;
|
||||
|
||||
// Credentials for basic authentication
|
||||
std::string _auth_username;
|
||||
std::string _auth_password;
|
||||
|
||||
int _status;
|
||||
|
||||
socks_connecter_t (const socks_connecter_t &);
|
||||
|
@ -53,6 +53,8 @@
|
||||
#define ZMQ_MULTICAST_LOOP 96
|
||||
#define ZMQ_ROUTER_NOTIFY 97
|
||||
#define ZMQ_XPUB_MANUAL_LAST_VALUE 98
|
||||
#define ZMQ_SOCKS_USERNAME 99
|
||||
#define ZMQ_SOCKS_PASSWORD 100
|
||||
|
||||
/* DRAFT Context options */
|
||||
#define ZMQ_ZERO_COPY_RECV 10
|
||||
|
Loading…
Reference in New Issue
Block a user