Merge pull request #1081 from gambels/WIP_add_generic_proxy_support

Add generic proxy support
This commit is contained in:
Ian Craggs 2021-10-04 15:14:06 +01:00 committed by GitHub
commit 879832f2bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 233 additions and 165 deletions

View File

@ -47,6 +47,7 @@ SET(common_src
Base64.c
SHA1.c
WebSocket.c
Proxy.c
)
IF (NOT PAHO_HIGH_PERFORMANCE)

View File

@ -148,8 +148,8 @@ typedef struct
void* context; /**< calling context - used when calling disconnect_internal */
int MQTTVersion; /**< the version of MQTT being used, 3, 4 or 5 */
int sessionExpiry; /**< MQTT 5 session expiry */
char* httpProxy; /**< HTTP proxy for websockets */
char* httpsProxy; /**< HTTPS proxy for websockets */
char* httpProxy; /**< HTTP proxy */
char* httpsProxy; /**< HTTPS proxy */
#if defined(OPENSSL)
MQTTClient_SSLOptions *sslopts; /**< the SSL/TLS connect options */
SSL_SESSION* session; /**< SSL session pointer for fast handhake */

View File

@ -1345,11 +1345,11 @@ typedef struct
*/
const MQTTAsync_nameValue* httpHeaders;
/**
* HTTP proxy for websockets
* HTTP proxy
*/
const char* httpProxy;
/**
* HTTPS proxy for websockets
* HTTPS proxy
*/
const char* httpsProxy;
} MQTTAsync_connectOptions;

View File

@ -12,7 +12,7 @@
*
* Contributors:
* Ian Craggs - initial implementation and documentation
*
* Sven Gambel - add generic proxy support
*******************************************************************************/
#include <stdlib.h>
@ -35,6 +35,7 @@
#include "Heap.h"
#include "OsWrapper.h"
#include "WebSocket.h"
#include "Proxy.h"
static int clientSockCompare(void* a, void* b);
static int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client);
@ -2735,9 +2736,9 @@ static int MQTTAsync_connecting(MQTTAsyncs* m)
size_t hostname_len;
int setSocketForSSLrc = 0;
if (m->websocket && m->c->net.https_proxy) {
if (m->c->net.https_proxy) {
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
if ((rc = WebSocket_proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
if ((rc = Proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
goto exit;
}
@ -2797,14 +2798,14 @@ static int MQTTAsync_connecting(MQTTAsyncs* m)
else
{
#endif
if (m->c->net.http_proxy) {
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
if ((rc = Proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
goto exit;
}
if ( m->websocket )
{
if (m->c->net.http_proxy) {
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
if ((rc = WebSocket_proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
goto exit;
}
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
if ((rc = WebSocket_connect(&m->c->net, serverURI)) == SOCKET_ERROR )
goto exit;

View File

@ -35,6 +35,7 @@
* Ian Craggs - check for NULL SSL options #334
* Ian Craggs - allocate username/password buffers #431
* Ian Craggs - MQTT 5.0 support
* Sven Gambel - add generic proxy support
*******************************************************************************/
/**
@ -80,6 +81,7 @@
#include "VersionInfo.h"
#include "WebSocket.h"
#include "Proxy.h"
const char *client_timestamp_eye = "MQTTClientV3_Timestamp " BUILD_TIMESTAMP;
const char *client_version_eye = "MQTTClientV3_Version " CLIENT_VERSION;
@ -1233,9 +1235,9 @@ static MQTTResponse MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_c
const char *topic;
int setSocketForSSLrc = 0;
if (m->websocket && m->c->net.https_proxy) {
if (m->c->net.https_proxy) {
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
if ((rc = WebSocket_proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
if ((rc = Proxy_connect( &m->c->net, 1, serverURI)) == SOCKET_ERROR )
goto exit;
}
@ -1290,28 +1292,31 @@ static MQTTResponse MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_c
}
}
#endif
else if (m->websocket)
else
{
if (m->c->net.http_proxy) {
m->c->connect_state = PROXY_CONNECT_IN_PROGRESS;
if ((rc = WebSocket_proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
if ((rc = Proxy_connect( &m->c->net, 0, serverURI)) == SOCKET_ERROR )
goto exit;
}
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
if ( WebSocket_connect(&m->c->net, serverURI) == SOCKET_ERROR )
if (m->websocket)
{
rc = SOCKET_ERROR;
goto exit;
m->c->connect_state = WEBSOCKET_IN_PROGRESS;
if ( WebSocket_connect(&m->c->net, serverURI) == SOCKET_ERROR )
{
rc = SOCKET_ERROR;
goto exit;
}
}
}
else
{
m->c->connect_state = WAIT_FOR_CONNACK; /* TCP connect completed, in which case send the MQTT connect packet */
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
else
{
rc = SOCKET_ERROR;
goto exit;
m->c->connect_state = WAIT_FOR_CONNACK; /* TCP connect completed, in which case send the MQTT connect packet */
if (MQTTPacket_send_connect(m->c, MQTTVersion, connectProperties, willProperties) == SOCKET_ERROR)
{
rc = SOCKET_ERROR;
goto exit;
}
}
}
}

View File

@ -951,11 +951,11 @@ typedef struct
*/
const MQTTClient_nameValue* httpHeaders;
/**
* HTTP proxy for websockets
* HTTP proxy
*/
const char* httpProxy;
/**
* HTTPS proxy for websockets
* HTTPS proxy
*/
const char* httpsProxy;
} MQTTClient_connectOptions;

View File

@ -21,6 +21,7 @@
* Ian Craggs - fix for issue #164
* Ian Craggs - fix for issue #179
* Ian Craggs - MQTT 5.0 support
* Sven Gambel - add generic proxy support
*******************************************************************************/
/**
@ -38,6 +39,7 @@
#include "StackTrace.h"
#include "Heap.h"
#include "WebSocket.h"
#include "Proxy.h"
#include "Base64.h"
extern ClientStates* bstate;
@ -247,11 +249,11 @@ int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket
Log(TRACE_PROTOCOL, -1, "Setting https proxy auth to %s", aClient->net.https_proxy_auth);
}
if (!ssl && websocket && aClient->net.http_proxy) {
if (!ssl && aClient->net.http_proxy) {
#else
if (websocket && aClient->net.http_proxy) {
if (aClient->net.http_proxy) {
#endif
addr_len = MQTTProtocol_addressPort(aClient->net.http_proxy, &port, NULL, WS_DEFAULT_PORT);
addr_len = MQTTProtocol_addressPort(aClient->net.http_proxy, &port, NULL, PROXY_DEFAULT_PORT);
#if defined(__GNUC__) && defined(__linux__)
if (timeout < 0)
rc = -1;
@ -262,8 +264,8 @@ int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket
#endif
}
#if defined(OPENSSL)
else if (ssl && websocket && aClient->net.https_proxy) {
addr_len = MQTTProtocol_addressPort(aClient->net.https_proxy, &port, NULL, WS_DEFAULT_PORT);
else if (ssl && aClient->net.https_proxy) {
addr_len = MQTTProtocol_addressPort(aClient->net.https_proxy, &port, NULL, PROXY_DEFAULT_PORT);
#if defined(__GNUC__) && defined(__linux__)
if (timeout < 0)
rc = -1;
@ -296,9 +298,9 @@ int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket
#if defined(OPENSSL)
if (ssl)
{
if (websocket && aClient->net.https_proxy) {
if (aClient->net.https_proxy) {
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = WebSocket_proxy_connect( &aClient->net, 1, ip_address);
rc = Proxy_connect( &aClient->net, 1, ip_address);
}
if (rc == 0 && SSLSocket_setSocketForSSL(&aClient->net, aClient->sslopts, ip_address, addr_len) == 1)
{
@ -313,12 +315,12 @@ int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket
else
rc = SOCKET_ERROR;
}
else if (websocket && aClient->net.http_proxy) {
else if (aClient->net.http_proxy) {
#else
if (websocket && aClient->net.http_proxy) {
if (aClient->net.http_proxy) {
#endif
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
rc = WebSocket_proxy_connect( &aClient->net, 0, ip_address);
rc = Proxy_connect( &aClient->net, 0, ip_address);
}
if ( websocket )
{

View File

@ -16,6 +16,7 @@
* Ian Craggs - MQTT 3.1.1 support
* Ian Craggs - SNI support
* Ian Craggs - MQTT 5.0 support
* Sven Gambel - add generic proxy support
*******************************************************************************/
#if !defined(MQTTPROTOCOLOUT_H)
@ -32,6 +33,7 @@
#define MQTT_DEFAULT_PORT 1883
#define SECURE_MQTT_DEFAULT_PORT 8883
#define WS_DEFAULT_PORT 80
#define PROXY_DEFAULT_PORT 8080
size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic, int default_port);
void MQTTProtocol_reconnect(const char* ip_address, Clients* client);

154
src/Proxy.c Normal file
View File

@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 Diehl Metering.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Keith Holman - initial implementation and documentation
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#include <stdio.h>
#include <string.h>
// for timeout process in Proxy_connect()
#include <time.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
#else
#include <unistd.h>
#endif
#include "Log.h"
#include "MQTTProtocolOut.h"
#include "StackTrace.h"
#include "Heap.h"
#if defined(OPENSSL)
#include "SSLSocket.h"
#include <openssl/rand.h>
#endif /* defined(OPENSSL) */
#include "Socket.h"
/**
* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint.
*
* @param[in] net network connection to proxy.
* @param[in] ssl enable ssl.
* @param[in] hostname hostname of endpoint.
*
* @retval SOCKET_ERROR failed to network connection
* @retval 0 connection to endpoint
*
*/
int Proxy_connect(networkHandles *net, int ssl, const char *hostname)
{
int port, i, rc = 0, buf_len=0;
char *buf = NULL;
size_t hostname_len, actual_len = 0;
time_t current, timeout;
PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
FUNC_ENTRY;
hostname_len = MQTTProtocol_addressPort(hostname, &port, NULL, PROXY_DEFAULT_PORT);
for ( i = 0; i < 2; ++i ) {
#if defined(OPENSSL)
if(ssl) {
if (net->https_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->https_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
}
else {
#endif
if (net->http_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->http_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
#if defined(OPENSSL)
}
#endif
if ( i==0 && buf_len > 0 ) {
++buf_len;
if ((buf = malloc( buf_len )) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
}
}
Log(TRACE_PROTOCOL, -1, "Proxy_connect: \"%s\"", buf);
Socket_putdatas(net->socket, buf, buf_len, nulbufs);
free(buf);
buf = NULL;
time(&timeout);
timeout += (time_t)10;
while(1) {
buf = Socket_getdata(net->socket, (size_t)12, &actual_len, &rc);
if(actual_len) {
if ( (strncmp( buf, "HTTP/1.0 200", 12 ) != 0) && (strncmp( buf, "HTTP/1.1 200", 12 ) != 0) )
rc = SOCKET_ERROR;
break;
}
else {
time(&current);
if(current > timeout) {
rc = SOCKET_ERROR;
break;
}
#if defined(_WIN32) || defined(_WIN64)
Sleep(250);
#else
usleep(250000);
#endif
}
}
/* flush the SocketBuffer */
actual_len = 1;
while (actual_len)
{
int rc1;
buf = Socket_getdata(net->socket, (size_t)1, &actual_len, &rc1);
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}

25
src/Proxy.h Normal file
View File

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2009, 2021 Diehl Metering.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0/
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#if !defined(PROXY_H)
#define PROXY_H
#include "Clients.h"
/* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint */
int Proxy_connect(networkHandles *net, int ssl, const char *hostname );
#endif /* PROXY_H */

View File

@ -14,18 +14,12 @@
* Keith Holman - initial implementation and documentation
* Ian Craggs - use memory tracking
* Ian Craggs - fix for one MQTT packet spread over >1 ws frame
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// for timeout process in WebSocket_proxy_connect()
#include <time.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "WebSocket.h"
@ -1436,117 +1430,3 @@ exit:
FUNC_EXIT_RC(rc);
return rc;
}
/**
* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint.
*
* @param[in] net network connection to proxy.
* @param[in] ssl enable ssl.
* @param[in] hostname hostname of endpoint.
*
* @retval SOCKET_ERROR failed to network connection
* @retval 0 connection to endpoint
*
*/
int WebSocket_proxy_connect( networkHandles *net, int ssl, const char *hostname)
{
int port, i, rc = 0, buf_len=0;
char *buf = NULL;
size_t hostname_len, actual_len = 0;
time_t current, timeout;
PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
FUNC_ENTRY;
hostname_len = MQTTProtocol_addressPort(hostname, &port, NULL, WS_DEFAULT_PORT);
for ( i = 0; i < 2; ++i ) {
#if defined(OPENSSL)
if(ssl) {
if (net->https_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->https_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
}
else {
#endif
if (net->http_proxy_auth) {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"Proxy-authorization: Basic %s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname, net->http_proxy_auth);
}
else {
buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n"
"Host: %.*s\r\n"
"\r\n",
(int)hostname_len, hostname, port,
(int)hostname_len, hostname);
}
#if defined(OPENSSL)
}
#endif
if ( i==0 && buf_len > 0 ) {
++buf_len;
if ((buf = malloc( buf_len )) == NULL)
{
rc = PAHO_MEMORY_ERROR;
goto exit;
}
}
}
Log(TRACE_PROTOCOL, -1, "WebSocket_proxy_connect: \"%s\"", buf);
Socket_putdatas(net->socket, buf, buf_len, nulbufs);
free(buf);
buf = NULL;
time(&timeout);
timeout += (time_t)10;
while(1) {
buf = Socket_getdata(net->socket, (size_t)12, &actual_len, &rc);
if(actual_len) {
if ( (strncmp( buf, "HTTP/1.0 200", 12 ) != 0) && (strncmp( buf, "HTTP/1.1 200", 12 ) != 0) )
rc = SOCKET_ERROR;
break;
}
else {
time(&current);
if(current > timeout) {
rc = SOCKET_ERROR;
break;
}
#if defined(_WIN32) || defined(_WIN64)
Sleep(250);
#else
usleep(250000);
#endif
}
}
/* flush the SocketBuffer */
actual_len = 1;
while (actual_len)
{
int rc1;
buf = Socket_getdata(net->socket, (size_t)1, &actual_len, &rc1);
}
exit:
FUNC_EXIT_RC(rc);
return rc;
}

View File

@ -12,6 +12,7 @@
*
* Contributors:
* Keith Holman - initial implementation and documentation
* Sven Gambel - move WebSocket proxy support to generic proxy support
*******************************************************************************/
#if !defined(WEBSOCKET_H)
@ -71,7 +72,4 @@ void WebSocket_terminate(void);
/* handles websocket upgrade request */
int WebSocket_upgrade(networkHandles *net);
/* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint */
int WebSocket_proxy_connect( networkHandles *net, int ssl, const char *hostname);
#endif /* WEBSOCKET_H */

View File

@ -69,7 +69,7 @@ struct pubsub_opts
char *name;
char *value;
} user_property;
/* websocket HTTP proxies */
/* HTTP proxies */
char* http_proxy;
char* https_proxy;
};