mirror of https://github.com/eclipse/paho.mqtt.c
Add first pass of no_proxy processing #1473
This commit is contained in:
parent
4c19121874
commit
dd9c7a652c
|
|
@ -103,103 +103,6 @@ size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow user or password characters to be expressed in the form of %XX, XX being the
|
||||
* hexadecimal value of the character. This will avoid problems when a user code or a password
|
||||
* contains a '@' or another special character ('%' included)
|
||||
* @param p0 output string
|
||||
* @param p1 input string
|
||||
* @param basic_auth_in_len
|
||||
*/
|
||||
void MQTTProtocol_specialChars(char* p0, char* p1, b64_size_t *basic_auth_in_len)
|
||||
{
|
||||
while (*p1 != '@')
|
||||
{
|
||||
if (*p1 != '%')
|
||||
{
|
||||
*p0++ = *p1++;
|
||||
}
|
||||
else if (isxdigit(*(p1 + 1)) && isxdigit(*(p1 + 2)))
|
||||
{
|
||||
/* next 2 characters are hexa digits */
|
||||
char hex[3];
|
||||
p1++;
|
||||
hex[0] = *p1++;
|
||||
hex[1] = *p1++;
|
||||
hex[2] = '\0';
|
||||
*p0++ = (char)strtol(hex, 0, 16);
|
||||
/* 3 input char => 1 output char */
|
||||
*basic_auth_in_len -= 2;
|
||||
}
|
||||
}
|
||||
*p0 = 0x0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the HTTP proxy for connecting
|
||||
* Examples of proxy settings:
|
||||
* http://your.proxy.server:8080/
|
||||
* http://user:pass@my.proxy.server:8080/
|
||||
*
|
||||
* @param aClient pointer to Clients object
|
||||
* @param source the proxy setting from environment or API
|
||||
* @param [out] dest pointer to output proxy info
|
||||
* @param [out] auth_dest pointer to output authentication material
|
||||
* @param prefix expected URI prefix: http:// or https://
|
||||
* @return 0 on success, non-zero otherwise
|
||||
*/
|
||||
int MQTTProtocol_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix)
|
||||
{
|
||||
b64_size_t basic_auth_in_len, basic_auth_out_len;
|
||||
b64_data_t *basic_auth;
|
||||
char *p1;
|
||||
int rc = 0;
|
||||
|
||||
if (*auth_dest)
|
||||
{
|
||||
free(*auth_dest);
|
||||
*auth_dest = NULL;
|
||||
}
|
||||
|
||||
if (source)
|
||||
{
|
||||
if ((p1 = strstr(source, prefix)) != NULL) /* skip http:// prefix, if any */
|
||||
source += strlen(prefix);
|
||||
*dest = source;
|
||||
if ((p1 = strchr(source, '@')) != NULL) /* find user.pass separator */
|
||||
*dest = p1 + 1;
|
||||
|
||||
if (p1)
|
||||
{
|
||||
/* basic auth len is string between http:// and @ */
|
||||
basic_auth_in_len = (b64_size_t)(p1 - source);
|
||||
if (basic_auth_in_len > 0)
|
||||
{
|
||||
basic_auth = (b64_data_t *)malloc(sizeof(char)*(basic_auth_in_len+1));
|
||||
if (!basic_auth)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
MQTTProtocol_specialChars((char*)basic_auth, source, &basic_auth_in_len);
|
||||
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len) + 1; /* add 1 for trailing NULL */
|
||||
if ((*auth_dest = (char *)malloc(sizeof(char)*basic_auth_out_len)) == NULL)
|
||||
{
|
||||
free(basic_auth);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
Base64_encode(*auth_dest, basic_auth_out_len, basic_auth, basic_auth_in_len);
|
||||
free(basic_auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MQTT outgoing connect processing for a client
|
||||
* @param address The address of the server. For TCP this is in the form
|
||||
|
|
@ -241,61 +144,93 @@ int MQTTProtocol_connect(const char* address, Clients* aClient, int unixsock, in
|
|||
FUNC_ENTRY;
|
||||
aClient->good = 1;
|
||||
|
||||
if (aClient->httpProxy)
|
||||
p0 = aClient->httpProxy;
|
||||
else
|
||||
if (!unixsock)
|
||||
{
|
||||
/* Don't use the environment HTTP proxy settings by default - for backwards compatibility */
|
||||
char* use_proxy = getenv("PAHO_C_CLIENT_USE_HTTP_PROXY");
|
||||
if (use_proxy)
|
||||
if (aClient->httpProxy)
|
||||
p0 = aClient->httpProxy;
|
||||
else /* if the proxy isn't set in the API, then we can look in the environment */
|
||||
{
|
||||
if (strncmp(use_proxy, "TRUE", strlen("TRUE")) == 0)
|
||||
p0 = getenv("http_proxy");
|
||||
/* Don't use the environment HTTP proxy settings by default - for backwards compatibility */
|
||||
char* use_proxy = getenv("PAHO_C_CLIENT_USE_HTTP_PROXY");
|
||||
if (use_proxy)
|
||||
{
|
||||
if (strncmp(use_proxy, "TRUE", strlen("TRUE")) == 0)
|
||||
{
|
||||
char* http_proxy = getenv("http_proxy");
|
||||
if (http_proxy)
|
||||
{
|
||||
char* no_proxy = getenv("no_proxy");
|
||||
if (no_proxy)
|
||||
{
|
||||
if (Proxy_noProxy(address, no_proxy))
|
||||
p0 = http_proxy;
|
||||
}
|
||||
else
|
||||
p0 = http_proxy; /* no no_proxy set */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p0)
|
||||
{
|
||||
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.http_proxy, &aClient->net.http_proxy_auth, "http://")) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy to %s", aClient->net.http_proxy);
|
||||
if (aClient->net.http_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy auth to %s", aClient->net.http_proxy_auth);
|
||||
if (p0)
|
||||
{
|
||||
if ((rc = Proxy_setHTTPProxy(aClient, p0, &aClient->net.http_proxy, &aClient->net.http_proxy_auth, "http://")) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy to %s", aClient->net.http_proxy);
|
||||
if (aClient->net.http_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy auth to %s", aClient->net.http_proxy_auth);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (aClient->httpsProxy)
|
||||
p0 = aClient->httpsProxy;
|
||||
else
|
||||
if (!unixsock)
|
||||
{
|
||||
/* Don't use the environment HTTP proxy settings by default - for backwards compatibility */
|
||||
char* use_proxy = getenv("PAHO_C_CLIENT_USE_HTTP_PROXY");
|
||||
if (use_proxy)
|
||||
if (aClient->httpsProxy)
|
||||
p0 = aClient->httpsProxy;
|
||||
else /* if the proxy isn't set in the API then we can look in the environment */
|
||||
{
|
||||
if (strncmp(use_proxy, "TRUE", strlen("TRUE")) == 0)
|
||||
p0 = getenv("https_proxy");
|
||||
}
|
||||
}
|
||||
|
||||
if (p0)
|
||||
{
|
||||
char* prefix = NULL;
|
||||
|
||||
if (memcmp(p0, "http://", 7) == 0)
|
||||
prefix = "http://";
|
||||
else if (memcmp(p0, "https://", 8) == 0)
|
||||
prefix = "https://";
|
||||
else
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
/* Don't use the environment HTTP proxy settings by default - for backwards compatibility */
|
||||
char* use_proxy = getenv("PAHO_C_CLIENT_USE_HTTP_PROXY");
|
||||
if (use_proxy)
|
||||
{
|
||||
if (strncmp(use_proxy, "TRUE", strlen("TRUE")) == 0)
|
||||
{
|
||||
char* https_proxy = getenv("https_proxy");
|
||||
if (https_proxy)
|
||||
{
|
||||
char* no_proxy = getenv("no_proxy");
|
||||
if (no_proxy)
|
||||
{
|
||||
if (Proxy_noProxy(address, no_proxy))
|
||||
p0 = https_proxy;
|
||||
}
|
||||
else
|
||||
p0 = https_proxy; /* no no_proxy set */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.https_proxy, &aClient->net.https_proxy_auth, prefix)) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy to %s", aClient->net.https_proxy);
|
||||
if (aClient->net.https_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy auth to %s", aClient->net.https_proxy_auth);
|
||||
if (p0)
|
||||
{
|
||||
char* prefix = NULL;
|
||||
|
||||
if (memcmp(p0, "http://", 7) == 0)
|
||||
prefix = "http://";
|
||||
else if (memcmp(p0, "https://", 8) == 0)
|
||||
prefix = "https://";
|
||||
else
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if ((rc = Proxy_setHTTPProxy(aClient, p0, &aClient->net.https_proxy, &aClient->net.https_proxy_auth, prefix)) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy to %s", aClient->net.https_proxy);
|
||||
if (aClient->net.https_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy auth to %s", aClient->net.https_proxy_auth);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ssl && aClient->net.http_proxy) {
|
||||
|
|
|
|||
201
src/Proxy.c
201
src/Proxy.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2021 Diehl Metering.
|
||||
* Copyright (c) 2009, 2024 Diehl Metering, Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
|
|
@ -38,6 +38,9 @@
|
|||
#include <openssl/rand.h>
|
||||
#endif /* defined(OPENSSL) */
|
||||
#include "Socket.h"
|
||||
#include "Base64.h"
|
||||
#include "ctype.h"
|
||||
|
||||
|
||||
/**
|
||||
* Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint.
|
||||
|
|
@ -152,3 +155,199 @@ exit:
|
|||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the dest parameter against the no_proxy blacklist
|
||||
*
|
||||
* no_proxy:
|
||||
* 1. Use lowercase form.
|
||||
* 2. Use comma-separated hostname:port values.
|
||||
* 3. IP addresses are okay, but hostnames are never resolved.
|
||||
* 4. Suffixes are always matched (e.g. example.com will match test.example.com).
|
||||
* 5. If top-level domains need to be matched, leading dots are accepted e.g. .com
|
||||
*
|
||||
* @param dest the destination hostname/ip address
|
||||
* @param no_proxy the no_proxy list, probably from the environment
|
||||
* @return 1 - use the proxy, 0 - don't use the proxy
|
||||
*/
|
||||
int Proxy_noProxy(const char* dest, char* no_proxy)
|
||||
{
|
||||
char* saveptr = NULL;
|
||||
char* curtok = NULL;
|
||||
int port = 0;
|
||||
int destport = 0;
|
||||
int port_matches = 0;
|
||||
size_t hostlen = 0;
|
||||
size_t desthostlen = 0;
|
||||
int rc = 1;
|
||||
char* no_proxy_list = NULL;
|
||||
|
||||
printf("dest: %s\n", dest);
|
||||
printf("no_proxy list: %s\n", no_proxy);
|
||||
if ((no_proxy_list = MQTTStrdup(no_proxy)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
curtok = strtok_r(no_proxy_list, ",", &saveptr);
|
||||
while (curtok)
|
||||
{
|
||||
char* host = curtok;
|
||||
int matched = 0;
|
||||
int pos = 1;
|
||||
const char* topic;
|
||||
|
||||
printf("no_proxy to match against: %s\n", host);
|
||||
if (curtok == NULL)
|
||||
break;
|
||||
if (host[0] == '.')
|
||||
host++;
|
||||
|
||||
hostlen = MQTTProtocol_addressPort(host, &port, &topic, -99);
|
||||
|
||||
desthostlen = MQTTProtocol_addressPort(dest, &destport, &topic, -99);
|
||||
if (dest[desthostlen] == '/')
|
||||
desthostlen--;
|
||||
|
||||
/* check if port matches */
|
||||
if (port == -99)
|
||||
port_matches = 1; /* no_proxy port absence matches any */
|
||||
else if (destport != -99)
|
||||
{
|
||||
if (destport == port)
|
||||
port_matches = 1;
|
||||
}
|
||||
if (memcmp(host, "*", 1) == 0) /* * matches anything */
|
||||
{
|
||||
/* match any host or address */
|
||||
if (port_matches == 1)
|
||||
{
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* now see if .host matches the end of the dest string */
|
||||
/* match backwards from the end */
|
||||
while (host[hostlen - pos] == dest[desthostlen - pos])
|
||||
{
|
||||
if (pos == hostlen) /* reached the beginning of the no_proxy definition */
|
||||
{
|
||||
if (pos == desthostlen || dest[desthostlen - pos - 1] == '.')
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
else if (pos == desthostlen) /* reached the beginning of the destination string */
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
if (matched)
|
||||
{
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
curtok = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
free(no_proxy_list);
|
||||
exit:
|
||||
printf("matched %d\n", !rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow user or password characters to be expressed in the form of %XX, XX being the
|
||||
* hexadecimal value of the character. This will avoid problems when a user code or a password
|
||||
* contains a '@' or another special character ('%' included)
|
||||
* @param p0 output string
|
||||
* @param p1 input string
|
||||
* @param basic_auth_in_len
|
||||
*/
|
||||
void Proxy_specialChars(char* p0, char* p1, b64_size_t *basic_auth_in_len)
|
||||
{
|
||||
while (*p1 != '@')
|
||||
{
|
||||
if (*p1 != '%')
|
||||
{
|
||||
*p0++ = *p1++;
|
||||
}
|
||||
else if (isxdigit(*(p1 + 1)) && isxdigit(*(p1 + 2)))
|
||||
{
|
||||
/* next 2 characters are hexa digits */
|
||||
char hex[3];
|
||||
p1++;
|
||||
hex[0] = *p1++;
|
||||
hex[1] = *p1++;
|
||||
hex[2] = '\0';
|
||||
*p0++ = (char)strtol(hex, 0, 16);
|
||||
/* 3 input char => 1 output char */
|
||||
*basic_auth_in_len -= 2;
|
||||
}
|
||||
}
|
||||
*p0 = 0x0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the HTTP proxy for connecting
|
||||
* Examples of proxy settings:
|
||||
* http://your.proxy.server:8080/
|
||||
* http://user:pass@my.proxy.server:8080/
|
||||
*
|
||||
* @param aClient pointer to Clients object
|
||||
* @param source the proxy setting from environment or API
|
||||
* @param [out] dest pointer to output proxy info
|
||||
* @param [out] auth_dest pointer to output authentication material
|
||||
* @param prefix expected URI prefix: http:// or https://
|
||||
* @return 0 on success, non-zero otherwise
|
||||
*/
|
||||
int Proxy_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix)
|
||||
{
|
||||
b64_size_t basic_auth_in_len, basic_auth_out_len;
|
||||
b64_data_t *basic_auth;
|
||||
char *p1;
|
||||
int rc = 0;
|
||||
|
||||
if (*auth_dest)
|
||||
{
|
||||
free(*auth_dest);
|
||||
*auth_dest = NULL;
|
||||
}
|
||||
|
||||
if (source)
|
||||
{
|
||||
if ((p1 = strstr(source, prefix)) != NULL) /* skip http:// prefix, if any */
|
||||
source += strlen(prefix);
|
||||
*dest = source;
|
||||
if ((p1 = strchr(source, '@')) != NULL) /* find user.pass separator */
|
||||
*dest = p1 + 1;
|
||||
|
||||
if (p1)
|
||||
{
|
||||
/* basic auth len is string between http:// and @ */
|
||||
basic_auth_in_len = (b64_size_t)(p1 - source);
|
||||
if (basic_auth_in_len > 0)
|
||||
{
|
||||
basic_auth = (b64_data_t *)malloc(sizeof(char)*(basic_auth_in_len+1));
|
||||
if (!basic_auth)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
Proxy_specialChars((char*)basic_auth, source, &basic_auth_in_len);
|
||||
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len) + 1; /* add 1 for trailing NULL */
|
||||
if ((*auth_dest = (char *)malloc(sizeof(char)*basic_auth_out_len)) == NULL)
|
||||
{
|
||||
free(basic_auth);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
Base64_encode(*auth_dest, basic_auth_out_len, basic_auth, basic_auth_in_len);
|
||||
free(basic_auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2021 Diehl Metering.
|
||||
* Copyright (c) 2009, 2024 Diehl Metering, Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
|
|
@ -20,6 +20,10 @@
|
|||
#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 );
|
||||
int Proxy_connect(networkHandles *net, int ssl, const char *hostname);
|
||||
|
||||
int Proxy_noProxy(const char* dest, char* no_proxy);
|
||||
|
||||
int Proxy_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix);
|
||||
|
||||
#endif /* PROXY_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue