Skip to content

Commit

Permalink
Add experimental SOCKS5 support for the clients.
Browse files Browse the repository at this point in the history
  • Loading branch information
ralight committed Sep 29, 2014
1 parent e9c18f8 commit 42420ca
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ else (${WITH_TLS} STREQUAL ON)
set (OPENSSL_INCLUDE_DIR "")
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)

# ========================================
Expand Down
3 changes: 3 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Important changes:
connected.
- New use_username_as_clientid option on the broker, for preventing hijacking
of a client id.
- The client library and clients now have experimental SOCKS5 support.


Broker:
Expand Down Expand Up @@ -46,8 +47,10 @@ Broker:
Clients:
- Both clients can now load default configuration options from a file.
- Add -1 (oneshot) option to mosquitto_sub.
- Add --proxy SOCKS5 support for both clients.

Client library:
- Add experimental SOCKS5 support.
- mosquitto_loop_forever now quits after a fatal error, rather than blindly
retrying.

Expand Down
195 changes: 195 additions & 0 deletions client/client_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and the Eclipse Distribution License is available at
#include <mosquitto.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[]);

void init_config(struct mosq_config *cfg)
Expand Down Expand Up @@ -359,6 +360,18 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c
}else{
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
}else if(!strcmp(argv[i], "--psk")){
if(i==argc-1){
Expand Down Expand Up @@ -544,6 +557,8 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c

int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
{
int rc;

if(cfg->will_topic && mosquitto_will_set(mosq, cfg->will_topic,
cfg->will_payloadlen, cfg->will_payload, cfg->will_qos,
cfg->will_retain)){
Expand Down Expand Up @@ -584,6 +599,15 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
}
#endif
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;
}

Expand Down Expand Up @@ -648,3 +672,174 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
}
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:https://", strlen("socks5h:https://"))){
str = url + strlen("socks5h:https://");
}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
6 changes: 6 additions & 0 deletions client/client_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ struct mosq_config {
bool verbose; /* sub */
bool eol; /* 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[]);
Expand Down
10 changes: 9 additions & 1 deletion client/pub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ void print_usage(void)
#ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
#endif
#ifdef WITH_SOCKS
printf(" [--proxy socks-url]\n");
#endif
printf(" mosquitto_pub --help\n\n");
printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n");
Expand Down Expand Up @@ -263,10 +266,15 @@ void print_usage(void)
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(" 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-identity : client identity string for TLS-PSK mode.\n");
# endif
#endif
#ifdef WITH_SOCKS
printf(" --proxy : SOCKS5 proxy URL of the form:\n");
printf(" socks5h:https://[username[:password]@]hostname[:port]\n");
printf(" Only \"none\" and \"username\" authentication is supported.\n");
#endif
printf("\nSee http:https://mosquitto.org/ for more information.\n\n");
}
Expand Down
8 changes: 8 additions & 0 deletions client/sub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ void print_usage(void)
#ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
#endif
#ifdef WITH_SOCKS
printf(" [--proxy socks-url]\n");
#endif
printf(" mosquitto_sub --help\n\n");
printf(" -1 : disconnect and exit after receiving the first message.\n");
Expand Down Expand Up @@ -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-identity : client identity string for TLS-PSK mode.\n");
#endif
#endif
#ifdef WITH_SOCKS
printf(" --proxy : SOCKS5 proxy URL of the form:\n");
printf(" socks5h:https://[username[:password]@]hostname[:port]\n");
printf(" Only \"none\" and \"username\" authentication is supported.\n");
#endif
printf("\nSee http:https://mosquitto.org/ for more information.\n\n");
}
Expand Down
8 changes: 8 additions & 0 deletions config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ WITH_EC:=yes
# Build man page documentation by default.
WITH_DOCS:=yes

# Build with client support for SOCK5 proxy.
WITH_SOCKS:=yes

# =============================================================================
# End of user configuration
# =============================================================================
Expand Down Expand Up @@ -174,6 +177,11 @@ ifeq ($(WITH_THREADING),yes)
LIB_CFLAGS:=$(LIB_CFLAGS) -DWITH_THREADING
endif

ifeq ($(WITH_SOCKS),yes)
LIB_CFLAGS:=$(LIB_CFLAGS) -DWITH_SOCKS
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_SOCKS
endif

ifeq ($(WITH_UUID),yes)
ifeq ($(UNAME),Linux)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_UUID
Expand Down
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_library(libmosquitto SHARED
read_handle_shared.c
send_client_mosq.c
send_mosq.c send_mosq.h
socks_mosq.c
srv_mosq.c
thread_mosq.c
time_mosq.c
Expand Down
4 changes: 4 additions & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ MOSQ_OBJS=mosquitto.o \
read_handle_shared.o \
send_mosq.o \
send_client_mosq.o \
socks_mosq.o \
srv_mosq.o \
thread_mosq.o \
time_mosq.o \
Expand Down Expand Up @@ -76,6 +77,9 @@ send_mosq.o : send_mosq.c send_mosq.h
send_client_mosq.o : send_client_mosq.c send_mosq.h
${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
${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@

Expand Down
1 change: 1 addition & 0 deletions lib/linker.version
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,5 @@ MOSQ_1.4 {
mosquitto_threaded_set;
mosquitto_pub_topic_check;
mosquitto_sub_topic_check;
mosquitto_socks5_set;
} MOSQ_1.3;

0 comments on commit 42420ca

Please sign in to comment.