Add experimental SOCKS5 support for the clients.

This commit is contained in:
Roger A. Light 2014-09-30 00:56:57 +01:00
parent e9c18f8347
commit 42420cae46
15 changed files with 347 additions and 7 deletions

View file

@ -65,6 +65,11 @@ else (${WITH_TLS} STREQUAL ON)
set (OPENSSL_INCLUDE_DIR "") set (OPENSSL_INCLUDE_DIR "")
endif (${WITH_TLS} STREQUAL ON) endif (${WITH_TLS} STREQUAL ON)
option(WITH_SOCKS "Include SOCKS5 support?" ON)
if (${WITH_SOCKS} STREQUAL ON)
add_definitions("-DWITH_SOCKS")
endif (${WITH_SOCKS} STREQUAL ON)
option(WITH_SRV "Include SRV lookup support?" ON) option(WITH_SRV "Include SRV lookup support?" ON)
# ======================================== # ========================================

View file

@ -15,6 +15,7 @@ Important changes:
connected. connected.
- New use_username_as_clientid option on the broker, for preventing hijacking - New use_username_as_clientid option on the broker, for preventing hijacking
of a client id. of a client id.
- The client library and clients now have experimental SOCKS5 support.
Broker: Broker:
@ -46,8 +47,10 @@ Broker:
Clients: Clients:
- Both clients can now load default configuration options from a file. - Both clients can now load default configuration options from a file.
- Add -1 (oneshot) option to mosquitto_sub. - Add -1 (oneshot) option to mosquitto_sub.
- Add --proxy SOCKS5 support for both clients.
Client library: Client library:
- Add experimental SOCKS5 support.
- mosquitto_loop_forever now quits after a fatal error, rather than blindly - mosquitto_loop_forever now quits after a fatal error, rather than blindly
retrying. retrying.

View file

@ -31,6 +31,7 @@ Contributors:
#include <mosquitto.h> #include <mosquitto.h>
#include "client_shared.h" #include "client_shared.h"
static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]); static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]);
void init_config(struct mosq_config *cfg) void init_config(struct mosq_config *cfg)
@ -359,6 +360,18 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c
}else{ }else{
cfg->pub_mode = MSGMODE_NULL; cfg->pub_mode = MSGMODE_NULL;
} }
#ifdef WITH_SOCKS
}else if(!strcmp(argv[i], "--proxy")){
if(i==argc-1){
fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
return 1;
}else{
if(mosquitto__parse_socks_url(cfg, argv[i+1])){
return 1;
}
i++;
}
#endif
#ifdef WITH_TLS_PSK #ifdef WITH_TLS_PSK
}else if(!strcmp(argv[i], "--psk")){ }else if(!strcmp(argv[i], "--psk")){
if(i==argc-1){ if(i==argc-1){
@ -544,6 +557,8 @@ unknown_option:
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
{ {
int rc;
if(cfg->will_topic && mosquitto_will_set(mosq, cfg->will_topic, if(cfg->will_topic && mosquitto_will_set(mosq, cfg->will_topic,
cfg->will_payloadlen, cfg->will_payload, cfg->will_qos, cfg->will_payloadlen, cfg->will_payload, cfg->will_qos,
cfg->will_retain)){ cfg->will_retain)){
@ -584,6 +599,15 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
} }
#endif #endif
mosquitto_max_inflight_messages_set(mosq, cfg->max_inflight); mosquitto_max_inflight_messages_set(mosq, cfg->max_inflight);
#ifdef WITH_SOCKS
if(cfg->socks5_host){
rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
if(rc){
mosquitto_lib_cleanup();
return rc;
}
}
#endif
return MOSQ_ERR_SUCCESS; return MOSQ_ERR_SUCCESS;
} }
@ -648,3 +672,174 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
} }
return MOSQ_ERR_SUCCESS; return MOSQ_ERR_SUCCESS;
} }
#ifdef WITH_SOCKS
/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
static int mosquitto__urldecode(char *str)
{
int i, j;
int len;
if(!str) return 0;
if(!strchr(str, '%')) return 0;
len = strlen(str);
for(i=0; i<len; i++){
if(str[i] == '%'){
if(i+2 >= len){
return 1;
}
if(str[i+1] == '2' && str[i+2] == '5'){
str[i] = '%';
len -= 2;
for(j=i+1; j<len; j++){
str[j] = str[j+2];
}
str[j] = '\0';
}else if(str[i+1] == '3' && (str[i+2] == 'A' || str[i+2] == 'a')){
str[i] = ':';
len -= 2;
for(j=i+1; j<len; j++){
str[j] = str[j+2];
}
str[j] = '\0';
}else if(str[i+1] == '4' && str[i+2] == '0'){
str[i] = ':';
len -= 2;
for(j=i+1; j<len; j++){
str[j] = str[j+2];
}
str[j] = '\0';
}else{
return 1;
}
}
}
return 0;
}
static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url)
{
char *str;
int i;
char *username = NULL, *password = NULL, *host = NULL, *port = NULL;
char *username_or_host = NULL;
int start;
int len;
bool have_auth = false;
int port_int;
if(!strncmp(url, "socks5h://", strlen("socks5h://"))){
str = url + strlen("socks5h://");
}else{
fprintf(stderr, "Error: Unsupported proxy protocol: %s\n", url);
return 1;
}
start = 0;
for(i=0; i<strlen(str); i++){
if(str[i] == ':'){
if(i == start){
goto cleanup;
}
if(have_auth){
len = i-start;
host = malloc(len + 1);
memcpy(host, &(str[start]), len);
host[len] = '\0';
start = i+1;
}else if(!username_or_host){
len = i-start;
username_or_host = malloc(len + 1);
memcpy(username_or_host, &(str[start]), len);
username_or_host[len] = '\0';
start = i+1;
}
}else if(str[i] == '@'){
if(i == start){
goto cleanup;
}
have_auth = true;
if(username_or_host){
username = username_or_host;
username_or_host = NULL;
len = i-start;
password = malloc(len + 1);
memcpy(password, &(str[start]), len);
password[len] = '\0';
start = i+1;
}else{
len = i-start;
username = malloc(len + 1);
memcpy(username, &(str[start]), len);
username[len] = '\0';
start = i+1;
}
}
}
/* Deal with remainder */
if(i > start){
len = i-start;
if(host){
port = malloc(len + 1);
memcpy(port, &(str[start]), len);
port[len] = '\0';
}else if(username_or_host){
if(have_auth){
host = malloc(len + 1);
memcpy(host, &(str[start]), len);
host[len] = '\0';
}else{
host = username_or_host;
username_or_host = NULL;
port = malloc(len + 1);
memcpy(port, &(str[start]), len);
port[len] = '\0';
}
}else{
host = malloc(len + 1);
memcpy(host, &(str[start]), len);
host[len] = '\0';
}
}
if(!host){
fprintf(stderr, "Error: Invalid proxy.\n");
goto cleanup;
}
if(mosquitto__urldecode(username)){
goto cleanup;
}
if(mosquitto__urldecode(password)){
goto cleanup;
}
if(port){
port_int = atoi(port);
if(port_int < 1 || port_int > 65535){
fprintf(stderr, "Error: Invalid proxy port %d\n", port_int);
goto cleanup;
}
free(port);
}else{
port_int = 1080;
}
cfg->socks5_username = username;
cfg->socks5_password = password;
cfg->socks5_host = host;
cfg->socks5_port = port_int;
return 0;
cleanup:
if(username_or_host) free(username_or_host);
if(username) free(username);
if(password) free(password);
if(host) free(host);
if(port) free(port);
return 1;
}
#endif

View file

@ -78,6 +78,12 @@ struct mosq_config {
bool verbose; /* sub */ bool verbose; /* sub */
bool eol; /* sub */ bool eol; /* sub */
bool oneshot; /* sub */ bool oneshot; /* sub */
#ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
#endif
}; };
int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]); int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]);

View file

@ -217,6 +217,9 @@ void print_usage(void)
#ifdef WITH_TLS_PSK #ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif #endif
#endif
#ifdef WITH_SOCKS
printf(" [--proxy socks-url]\n");
#endif #endif
printf(" mosquitto_pub --help\n\n"); printf(" mosquitto_pub --help\n\n");
printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n");
@ -263,10 +266,15 @@ void print_usage(void)
printf(" hostname. Using this option means that you cannot be sure that the\n"); printf(" hostname. Using this option means that you cannot be sure that the\n");
printf(" remote host is the server you wish to connect to and so is insecure.\n"); printf(" remote host is the server you wish to connect to and so is insecure.\n");
printf(" Do not use this option in a production environment.\n"); printf(" Do not use this option in a production environment.\n");
#ifdef WITH_TLS_PSK # ifdef WITH_TLS_PSK
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
# endif
#endif #endif
#ifdef WITH_SOCKS
printf(" --proxy : SOCKS5 proxy URL of the form:\n");
printf(" socks5h://[username[:password]@]hostname[:port]\n");
printf(" Only \"none\" and \"username\" authentication is supported.\n");
#endif #endif
printf("\nSee http://mosquitto.org/ for more information.\n\n"); printf("\nSee http://mosquitto.org/ for more information.\n\n");
} }

View file

@ -142,6 +142,9 @@ void print_usage(void)
#ifdef WITH_TLS_PSK #ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif #endif
#endif
#ifdef WITH_SOCKS
printf(" [--proxy socks-url]\n");
#endif #endif
printf(" mosquitto_sub --help\n\n"); printf(" mosquitto_sub --help\n\n");
printf(" -1 : disconnect and exit after receiving the first message.\n"); printf(" -1 : disconnect and exit after receiving the first message.\n");
@ -192,6 +195,11 @@ void print_usage(void)
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
#endif #endif
#endif
#ifdef WITH_SOCKS
printf(" --proxy : SOCKS5 proxy URL of the form:\n");
printf(" socks5h://[username[:password]@]hostname[:port]\n");
printf(" Only \"none\" and \"username\" authentication is supported.\n");
#endif #endif
printf("\nSee http://mosquitto.org/ for more information.\n\n"); printf("\nSee http://mosquitto.org/ for more information.\n\n");
} }

View file

@ -70,6 +70,9 @@ WITH_EC:=yes
# Build man page documentation by default. # Build man page documentation by default.
WITH_DOCS:=yes WITH_DOCS:=yes
# Build with client support for SOCK5 proxy.
WITH_SOCKS:=yes
# ============================================================================= # =============================================================================
# End of user configuration # End of user configuration
# ============================================================================= # =============================================================================
@ -174,6 +177,11 @@ ifeq ($(WITH_THREADING),yes)
LIB_CFLAGS:=$(LIB_CFLAGS) -DWITH_THREADING LIB_CFLAGS:=$(LIB_CFLAGS) -DWITH_THREADING
endif endif
ifeq ($(WITH_SOCKS),yes)
LIB_CFLAGS:=$(LIB_CFLAGS) -DWITH_SOCKS
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_SOCKS
endif
ifeq ($(WITH_UUID),yes) ifeq ($(WITH_UUID),yes)
ifeq ($(UNAME),Linux) ifeq ($(UNAME),Linux)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_UUID BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_UUID

View file

@ -33,6 +33,7 @@ add_library(libmosquitto SHARED
read_handle_shared.c read_handle_shared.c
send_client_mosq.c send_client_mosq.c
send_mosq.c send_mosq.h send_mosq.c send_mosq.h
socks_mosq.c
srv_mosq.c srv_mosq.c
thread_mosq.c thread_mosq.c
time_mosq.c time_mosq.c

View file

@ -12,6 +12,7 @@ MOSQ_OBJS=mosquitto.o \
read_handle_shared.o \ read_handle_shared.o \
send_mosq.o \ send_mosq.o \
send_client_mosq.o \ send_client_mosq.o \
socks_mosq.o \
srv_mosq.o \ srv_mosq.o \
thread_mosq.o \ thread_mosq.o \
time_mosq.o \ time_mosq.o \
@ -76,6 +77,9 @@ send_mosq.o : send_mosq.c send_mosq.h
send_client_mosq.o : send_client_mosq.c send_mosq.h send_client_mosq.o : send_client_mosq.c send_mosq.h
${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@
socks_mosq.o : socks_mosq.c
${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@
srv_mosq.o : srv_mosq.c srv_mosq.o : srv_mosq.c
${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@

View file

@ -75,4 +75,5 @@ MOSQ_1.4 {
mosquitto_threaded_set; mosquitto_threaded_set;
mosquitto_pub_topic_check; mosquitto_pub_topic_check;
mosquitto_sub_topic_check; mosquitto_sub_topic_check;
mosquitto_socks5_set;
} MOSQ_1.3; } MOSQ_1.3;

View file

@ -38,6 +38,7 @@ typedef int ssize_t;
#include <net_mosq.h> #include <net_mosq.h>
#include <read_handle.h> #include <read_handle.h>
#include <send_mosq.h> #include <send_mosq.h>
#include <socks_mosq.h>
#include <time_mosq.h> #include <time_mosq.h>
#include <tls_mosq.h> #include <tls_mosq.h>
#include <util_mosq.h> #include <util_mosq.h>
@ -461,7 +462,11 @@ static int _mosquitto_reconnect(struct mosquitto *mosq, bool blocking)
if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL; if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL;
pthread_mutex_lock(&mosq->state_mutex); pthread_mutex_lock(&mosq->state_mutex);
mosq->state = mosq_cs_new; if(mosq->socks5_host){
mosq->state = mosq_cs_socks5_new;
}else{
mosq->state = mosq_cs_new;
}
pthread_mutex_unlock(&mosq->state_mutex); pthread_mutex_unlock(&mosq->state_mutex);
pthread_mutex_lock(&mosq->msgtime_mutex); pthread_mutex_lock(&mosq->msgtime_mutex);
@ -497,12 +502,20 @@ static int _mosquitto_reconnect(struct mosquitto *mosq, bool blocking)
_mosquitto_messages_reconnect_reset(mosq); _mosquitto_messages_reconnect_reset(mosq);
rc = _mosquitto_socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking); if(mosq->socks5_host){
rc = _mosquitto_socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking);
}else{
rc = _mosquitto_socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking);
}
if(rc){ if(rc){
return rc; return rc;
} }
return _mosquitto_send_connect(mosq, mosq->keepalive, mosq->clean_session); if(mosq->socks5_host){
return mosquitto__socks5_send(mosq);
}else{
return _mosquitto_send_connect(mosq, mosq->keepalive, mosq->clean_session);
}
} }
int mosquitto_disconnect(struct mosquitto *mosq) int mosquitto_disconnect(struct mosquitto *mosq)
@ -951,6 +964,7 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
case MOSQ_ERR_UNKNOWN: case MOSQ_ERR_UNKNOWN:
case MOSQ_ERR_ERRNO: case MOSQ_ERR_ERRNO:
case MOSQ_ERR_EAI: case MOSQ_ERR_EAI:
case MOSQ_ERR_PROXY:
return rc; return rc;
} }
if(errno == EPROTO){ if(errno == EPROTO){
@ -1075,7 +1089,11 @@ int mosquitto_loop_read(struct mosquitto *mosq, int max_packets)
* have QoS > 0. We should try to deal with that many in this loop in order * have QoS > 0. We should try to deal with that many in this loop in order
* to keep up. */ * to keep up. */
for(i=0; i<max_packets; i++){ for(i=0; i<max_packets; i++){
rc = _mosquitto_packet_read(mosq); if(mosq->socks5_host){
rc = mosquitto__socks5_read(mosq);
}else{
rc = _mosquitto_packet_read(mosq);
}
if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){
return _mosquitto_loop_rc_handle(mosq, rc); return _mosquitto_loop_rc_handle(mosq, rc);
} }
@ -1212,6 +1230,8 @@ const char *mosquitto_strerror(int mosq_errno)
return "Unknown error."; return "Unknown error.";
case MOSQ_ERR_ERRNO: case MOSQ_ERR_ERRNO:
return strerror(errno); return strerror(errno);
case MOSQ_ERR_PROXY:
return "Proxy error.";
default: default:
return "Unknown error."; return "Unknown error.";
} }

View file

@ -78,7 +78,8 @@ enum mosq_err_t {
MOSQ_ERR_ACL_DENIED = 12, MOSQ_ERR_ACL_DENIED = 12,
MOSQ_ERR_UNKNOWN = 13, MOSQ_ERR_UNKNOWN = 13,
MOSQ_ERR_ERRNO = 14, MOSQ_ERR_ERRNO = 14,
MOSQ_ERR_EAI = 15 MOSQ_ERR_EAI = 15,
MOSQ_ERR_PROXY = 16
}; };
/* MQTT specification restricts client ids to a maximum of 23 characters */ /* MQTT specification restricts client ids to a maximum of 23 characters */
@ -1294,6 +1295,29 @@ libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned
*/ */
libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj);
/* =============================================================================
*
* Section: SOCKS5 proxy functions
*
* =============================================================================
*/
/*
* Function: mosquitto_socks5_set
*
* Configure the client to use a SOCKS5 proxy when connecting. Must be called
* before connecting. "None" and "username/password" authentication is
* supported.
*
* Parameters:
* mosq - a valid mosquitto instance.
* host - the SOCKS5 proxy host to connect to.
* port - the SOCKS5 proxy port to use.
* username - if not NULL, use this username when authenticating with the proxy.
* password - if not NULL and username is not NULL, use this password when
* authenticating with the proxy.
*/
libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password);
/* ============================================================================= /* =============================================================================
* *

View file

@ -86,7 +86,14 @@ enum mosquitto_client_state {
mosq_cs_connect_pending = 4, mosq_cs_connect_pending = 4,
mosq_cs_connect_srv = 5, mosq_cs_connect_srv = 5,
mosq_cs_disconnect_ws = 6, mosq_cs_disconnect_ws = 6,
mosq_cs_disconnected = 7 mosq_cs_disconnected = 7,
mosq_cs_socks5_new = 8,
mosq_cs_socks5_start = 9,
mosq_cs_socks5_request = 10,
mosq_cs_socks5_reply = 11,
mosq_cs_socks5_auth_ok = 12,
mosq_cs_socks5_userpass_reply = 13,
mosq_cs_socks5_send_userpass = 14,
}; };
enum _mosquitto_protocol { enum _mosquitto_protocol {
@ -199,6 +206,12 @@ struct mosquitto {
struct libwebsocket *wsi; struct libwebsocket *wsi;
# endif # endif
#else #else
# ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
# endif
void *userdata; void *userdata;
bool in_callback; bool in_callback;
unsigned int message_retry; unsigned int message_retry;
@ -239,6 +252,9 @@ struct mosquitto {
# ifdef WITH_BRIDGE # ifdef WITH_BRIDGE
UT_hash_handle hh_bridge; UT_hash_handle hh_bridge;
# endif # endif
# ifdef WITH_WEBSOCKETS
UT_hash_handle hh_websockets;
# endif
#endif #endif
}; };

View file

@ -63,6 +63,8 @@
<arg><option>--tls-version</option> <replaceable>version</replaceable></arg> <arg><option>--tls-version</option> <replaceable>version</replaceable></arg>
</arg> </arg>
</group> </group>
<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>
<arg choice='plain'><option>-t</option> <replaceable>message-topic</replaceable></arg> <arg choice='plain'><option>-t</option> <replaceable>message-topic</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis> <cmdsynopsis>
@ -252,6 +254,25 @@
MQTT v3.1. See also the <option>--username</option> option.</para> MQTT v3.1. See also the <option>--username</option> option.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--proxy</option></term>
<listitem>
<para>Specify a SOCKS5 proxy to connect through. "None" and
"username" authentication types are supported. The
<option>socks-url</option> must be of the form
<option>socks5h://[username[:password]@]host[:port]</option>.
The protocol prefix <option>socks5h</option> means that
hostnames are resolved by the proxy. The symbols %25,
%3A and %40 are URL decoded into %, : and @
respectively, if present in the username or
password.</para>
<para>More SOCKS versions may be available in the future,
depending on demand, and will use different protocol
prefixes as described in <citerefentry>
<refentrytitle>curl</refentrytitle>
<manvolnum>1</manvolnum> </citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--psk</option></term> <term><option>--psk</option></term>
<listitem> <listitem>

View file

@ -61,6 +61,7 @@
</group> </group>
<arg choice='opt' rep='repeat'><option>-T</option> <replaceable>filter-out</replaceable></arg> <arg choice='opt' rep='repeat'><option>-T</option> <replaceable>filter-out</replaceable></arg>
<arg choice='plain' rep='repeat'><option>-t</option> <replaceable>message-topic</replaceable></arg> <arg choice='plain' rep='repeat'><option>-t</option> <replaceable>message-topic</replaceable></arg>
<arg><option>--proxy</option> <replaceable>socks-url</replaceable></arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis> <cmdsynopsis>
<command>mosquitto_sub</command> <command>mosquitto_sub</command>
@ -269,6 +270,25 @@
MQTT v3.1. See also the <option>--username</option> option.</para> MQTT v3.1. See also the <option>--username</option> option.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--proxy</option></term>
<listitem>
<para>Specify a SOCKS5 proxy to connect through. "None" and
"username" authentication types are supported. The
<option>socks-url</option> must be of the form
<option>socks5h://[username[:password]@]host[:port]</option>.
The protocol prefix <option>socks5h</option> means that
hostnames are resolved by the proxy. The symbols %25,
%3A and %40 are URL decoded into %, : and @
respectively, if present in the username or
password.</para>
<para>More SOCKS versions may be available in the future,
depending on demand, and will use different protocol
prefixes as described in <citerefentry>
<refentrytitle>curl</refentrytitle>
<manvolnum>1</manvolnum> </citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--psk</option></term> <term><option>--psk</option></term>
<listitem> <listitem>